Commit b1f5d0ae authored by Akinobu Mita's avatar Akinobu Mita Committed by Mauro Carvalho Chehab

media: ov9650: support device tree probing

The ov9650 driver currently only supports legacy platform data probe.
This change adds device tree probing.

There has been an attempt to add device tree support for ov9650 driver
by Hugues Fruchet as a part of the patchset that adds support of OV9655
camera (http://www.spinics.net/lists/linux-media/msg117903.html), but
it wasn't merged into mainline because creating a separate driver for
OV9655 is preferred.

This is very similar to Hugues's patch, but not supporting new device.

Cc: Jacopo Mondi <jacopo@jmondi.org>
Cc: H. Nikolaus Schaller <hns@goldelico.com>
Cc: Hugues Fruchet <hugues.fruchet@st.com>
Cc: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Cc: Rob Herring <robh@kernel.org>
Reviewed-by: default avatarSylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: default avatarAkinobu Mita <akinobu.mita@gmail.com>
Reviewed-by: default avatarJacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent 7b05db63
......@@ -11,8 +11,10 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/media.h>
......@@ -249,9 +251,10 @@ struct ov965x {
struct v4l2_subdev sd;
struct media_pad pad;
enum v4l2_mbus_type bus_type;
int gpios[NUM_GPIOS];
struct gpio_desc *gpios[NUM_GPIOS];
/* External master clock frequency */
unsigned long mclk_frequency;
struct clk *clk;
/* Protects the struct fields below */
struct mutex lock;
......@@ -513,24 +516,27 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
return 0;
}
static void ov965x_gpio_set(int gpio, int val)
{
if (gpio_is_valid(gpio))
gpio_set_value(gpio, val);
}
static void __ov965x_set_power(struct ov965x *ov965x, int on)
static int __ov965x_set_power(struct ov965x *ov965x, int on)
{
if (on) {
ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0);
ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0);
int ret = clk_prepare_enable(ov965x->clk);
if (ret)
return ret;
gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0);
gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0);
msleep(25);
} else {
ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1);
ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1);
gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1);
gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1);
clk_disable_unprepare(ov965x->clk);
}
ov965x->streaming = 0;
return 0;
}
static int ov965x_s_power(struct v4l2_subdev *sd, int on)
......@@ -543,8 +549,8 @@ static int ov965x_s_power(struct v4l2_subdev *sd, int on)
mutex_lock(&ov965x->lock);
if (ov965x->power == !on) {
__ov965x_set_power(ov965x, on);
if (on) {
ret = __ov965x_set_power(ov965x, on);
if (!ret && on) {
ret = ov965x_write_array(client,
ov965x_init_regs);
ov965x->apply_frame_fmt = 1;
......@@ -1410,16 +1416,17 @@ static const struct v4l2_subdev_ops ov965x_subdev_ops = {
/*
* Reset and power down GPIOs configuration
*/
static int ov965x_configure_gpios(struct ov965x *ov965x,
const struct ov9650_platform_data *pdata)
static int ov965x_configure_gpios_pdata(struct ov965x *ov965x,
const struct ov9650_platform_data *pdata)
{
int ret, i;
int gpios[NUM_GPIOS];
ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn;
ov965x->gpios[GPIO_RST] = pdata->gpio_reset;
gpios[GPIO_PWDN] = pdata->gpio_pwdn;
gpios[GPIO_RST] = pdata->gpio_reset;
for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) {
int gpio = ov965x->gpios[i];
int gpio = gpios[i];
if (!gpio_is_valid(gpio))
continue;
......@@ -1429,9 +1436,30 @@ static int ov965x_configure_gpios(struct ov965x *ov965x,
return ret;
v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
gpio_set_value(gpio, 1);
gpio_set_value_cansleep(gpio, 1);
gpio_export(gpio, 0);
ov965x->gpios[i] = gpio;
ov965x->gpios[i] = gpio_to_desc(gpio);
}
return 0;
}
static int ov965x_configure_gpios(struct ov965x *ov965x)
{
struct device *dev = &ov965x->client->dev;
ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(ov965x->gpios[GPIO_PWDN])) {
dev_info(dev, "can't get %s GPIO\n", "powerdown");
return PTR_ERR(ov965x->gpios[GPIO_PWDN]);
}
ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(ov965x->gpios[GPIO_RST])) {
dev_info(dev, "can't get %s GPIO\n", "reset");
return PTR_ERR(ov965x->gpios[GPIO_RST]);
}
return 0;
......@@ -1445,7 +1473,10 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd)
int ret;
mutex_lock(&ov965x->lock);
__ov965x_set_power(ov965x, 1);
ret = __ov965x_set_power(ov965x, 1);
if (ret)
goto out;
msleep(25);
/* Check sensor revision */
......@@ -1465,6 +1496,7 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd)
ret = -ENODEV;
}
}
out:
mutex_unlock(&ov965x->lock);
return ret;
......@@ -1478,23 +1510,39 @@ static int ov965x_probe(struct i2c_client *client,
struct ov965x *ov965x;
int ret;
if (!pdata) {
dev_err(&client->dev, "platform data not specified\n");
return -EINVAL;
}
if (pdata->mclk_frequency == 0) {
dev_err(&client->dev, "MCLK frequency not specified\n");
return -EINVAL;
}
ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL);
if (!ov965x)
return -ENOMEM;
mutex_init(&ov965x->lock);
ov965x->client = client;
ov965x->mclk_frequency = pdata->mclk_frequency;
if (pdata) {
if (pdata->mclk_frequency == 0) {
dev_err(&client->dev, "MCLK frequency not specified\n");
return -EINVAL;
}
ov965x->mclk_frequency = pdata->mclk_frequency;
ret = ov965x_configure_gpios_pdata(ov965x, pdata);
if (ret < 0)
return ret;
} else if (dev_fwnode(&client->dev)) {
ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL);
if (IS_ERR(ov965x->clk))
return PTR_ERR(ov965x->clk);
ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
ret = ov965x_configure_gpios(ov965x);
if (ret < 0)
return ret;
} else {
dev_err(&client->dev,
"Neither platform data nor device property specified\n");
return -EINVAL;
}
mutex_init(&ov965x->lock);
sd = &ov965x->sd;
v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
......@@ -1504,10 +1552,6 @@ static int ov965x_probe(struct i2c_client *client,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
ret = ov965x_configure_gpios(ov965x, pdata);
if (ret < 0)
goto err_mutex;
ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
......@@ -1563,9 +1607,19 @@ static const struct i2c_device_id ov965x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ov965x_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id ov965x_of_match[] = {
{ .compatible = "ovti,ov9650", },
{ .compatible = "ovti,ov9652", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ov965x_of_match);
#endif
static struct i2c_driver ov965x_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(ov965x_of_match),
},
.probe = ov965x_probe,
.remove = ov965x_remove,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment