diff mbox

[v2,3/7,media] ov9650: add device tree support

Message ID 1499073368-31905-4-git-send-email-hugues.fruchet@st.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hugues FRUCHET July 3, 2017, 9:16 a.m. UTC
Allows use of device tree configuration data.
If no device tree data is there, configuration is taken from platform data.
In order to keep GPIOs configuration compatible between both way of doing,
GPIOs are switched to descriptor-based interface.

Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
---
 drivers/media/i2c/Kconfig  |  2 +-
 drivers/media/i2c/ov9650.c | 77 ++++++++++++++++++++++++++++++++++------------
 2 files changed, 59 insertions(+), 20 deletions(-)

Comments

Sakari Ailus July 8, 2017, 11:06 p.m. UTC | #1
Hi Hugues,

On Mon, Jul 03, 2017 at 11:16:04AM +0200, Hugues Fruchet wrote:
> Allows use of device tree configuration data.
> If no device tree data is there, configuration is taken from platform data.
> In order to keep GPIOs configuration compatible between both way of doing,
> GPIOs are switched to descriptor-based interface.
> 
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
> ---
>  drivers/media/i2c/Kconfig  |  2 +-
>  drivers/media/i2c/ov9650.c | 77 ++++++++++++++++++++++++++++++++++------------
>  2 files changed, 59 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 121b3b5..168115c 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -615,7 +615,7 @@ config VIDEO_OV7670
>  
>  config VIDEO_OV9650
>  	tristate "OmniVision OV9650/OV9652 sensor support"
> -	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>  	---help---
>  	  This is a V4L2 sensor-level driver for the Omnivision
>  	  OV9650 and OV9652 camera sensors.
> diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
> index 1e4e99e..7e9a902 100644
> --- a/drivers/media/i2c/ov9650.c
> +++ b/drivers/media/i2c/ov9650.c
> @@ -11,12 +11,14 @@
>   * 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/i2c.h>
>  #include <linux/kernel.h>
>  #include <linux/media.h>
>  #include <linux/module.h>
> +#include <linux/of_gpio.h>
>  #include <linux/ratelimit.h>
>  #include <linux/slab.h>
>  #include <linux/string.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;
> @@ -511,10 +514,10 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
>  	return 0;
>  }
>  
> -static void ov965x_gpio_set(int gpio, int val)
> +static void ov965x_gpio_set(struct gpio_desc *gpio, int val)
>  {
> -	if (gpio_is_valid(gpio))
> -		gpio_set_value(gpio, val);
> +	if (gpio)
> +		gpiod_set_value_cansleep(gpio, val);

gpiod_set_value_cansleep() can manage with NULL gpio parameter, no need to
check it.

>  }
>  
>  static void __ov965x_set_power(struct ov965x *ov965x, int on)
> @@ -1406,24 +1409,28 @@ static int ov965x_configure_gpios(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];
> +	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
> +		int gpio = gpios[i];
>  
>  		if (!gpio_is_valid(gpio))
>  			continue;
>  		ret = devm_gpio_request_one(&ov965x->client->dev, gpio,
> -					    GPIOF_OUT_INIT_HIGH, "OV965X");
> -		if (ret < 0)
> +					    GPIOF_OUT_INIT_HIGH, DRIVER_NAME);

DRIVER_NAME is different from "OV965X". Is this an intended change?

> +		if (ret < 0) {
> +			dev_err(&ov965x->client->dev,
> +				"Failed to request gpio%d (%d)\n", gpio, ret);
>  			return ret;
> +		}
>  		v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
>  
>  		gpio_set_value(gpio, 1);
>  		gpio_export(gpio, 0);
> -		ov965x->gpios[i] = gpio;
> +		ov965x->gpios[i] = gpio_to_desc(gpio);
>  	}
>  
>  	return 0;
> @@ -1469,14 +1476,10 @@ static int ov965x_probe(struct i2c_client *client,
>  	struct v4l2_subdev *sd;
>  	struct ov965x *ov965x;
>  	int ret;
> +	struct device_node *np = client->dev.of_node;

It'd be nice to declare this next to pdata, rather than after ret and other
short declarations.

>  
> -	if (pdata == NULL) {
> -		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");
> +	if (!pdata && !np) {
> +		dev_err(&client->dev, "Platform data or device tree data must be provided\n");
>  		return -EINVAL;
>  	}
>  
> @@ -1486,7 +1489,35 @@ static int ov965x_probe(struct i2c_client *client,
>  
>  	mutex_init(&ov965x->lock);
>  	ov965x->client = client;
> -	ov965x->mclk_frequency = pdata->mclk_frequency;
> +	mutex_init(&ov965x->lock);
> +
> +	if (np) {
> +		/* Device tree */
> +		ov965x->gpios[GPIO_RST] =
> +			devm_gpiod_get_optional(&client->dev, "resetb",
> +						GPIOD_OUT_LOW);
> +		ov965x->gpios[GPIO_PWDN] =
> +			devm_gpiod_get_optional(&client->dev, "pwdn",
> +						GPIOD_OUT_HIGH);
> +
> +		ov965x->clk = devm_clk_get(&client->dev, NULL);
> +		if (IS_ERR(ov965x->clk)) {
> +			dev_err(&client->dev, "Could not get clock\n");

mutex_destroy() should called on an initialised mutex if probe is going to
fail. It's certainly not a problem introduced by this patch, but it'd be
nice to fix that (in a separate patch) now that it's found. The same goes
for remove below.

> +			return PTR_ERR(ov965x->clk);
> +		}
> +		ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
> +	} else {
> +		/* Platform data */
> +		ret = ov965x_configure_gpios(ov965x, pdata);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (pdata->mclk_frequency == 0) {
> +			dev_err(&client->dev, "MCLK frequency is mandatory\n");
> +			return -EINVAL;
> +		}
> +		ov965x->mclk_frequency = pdata->mclk_frequency;
> +	}
>  
>  	sd = &ov965x->sd;
>  	v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
> @@ -1551,9 +1582,17 @@ static int ov965x_remove(struct i2c_client *client)
>  };
>  MODULE_DEVICE_TABLE(i2c, ov965x_id);
>  
> +static const struct of_device_id ov965x_of_match[] = {
> +	{ .compatible = "ovti,ov9650", },
> +	{ .compatible = "ovti,ov9652", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ov965x_of_match);
> +
>  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,
Sylwester Nawrocki July 12, 2017, 7:33 p.m. UTC | #2
On 07/03/2017 11:16 AM, Hugues Fruchet wrote:
> Allows use of device tree configuration data.
> If no device tree data is there, configuration is taken from platform data.
> In order to keep GPIOs configuration compatible between both way of doing,
> GPIOs are switched to descriptor-based interface.
> 
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
> ---
>   drivers/media/i2c/Kconfig  |  2 +-
>   drivers/media/i2c/ov9650.c | 77 ++++++++++++++++++++++++++++++++++------------
>   2 files changed, 59 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 121b3b5..168115c 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -615,7 +615,7 @@ config VIDEO_OV7670
>   
>   config VIDEO_OV9650
>   	tristate "OmniVision OV9650/OV9652 sensor support"
> -	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>   	---help---
>   	  This is a V4L2 sensor-level driver for the Omnivision
>   	  OV9650 and OV9652 camera sensors.
> diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
> index 1e4e99e..7e9a902 100644
> --- a/drivers/media/i2c/ov9650.c
> +++ b/drivers/media/i2c/ov9650.c
> @@ -11,12 +11,14 @@
>    * 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/i2c.h>
>   #include <linux/kernel.h>
>   #include <linux/media.h>
>   #include <linux/module.h>
> +#include <linux/of_gpio.h>
>   #include <linux/ratelimit.h>
>   #include <linux/slab.h>
>   #include <linux/string.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;
> @@ -511,10 +514,10 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
>   	return 0;
>   }
>   
> -static void ov965x_gpio_set(int gpio, int val)
> +static void ov965x_gpio_set(struct gpio_desc *gpio, int val)
>   {
> -	if (gpio_is_valid(gpio))
> -		gpio_set_value(gpio, val);
> +	if (gpio)
> +		gpiod_set_value_cansleep(gpio, val);
>   }
>   
>   static void __ov965x_set_power(struct ov965x *ov965x, int on)
> @@ -1406,24 +1409,28 @@ static int ov965x_configure_gpios(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];
> +	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
> +		int gpio = gpios[i];
>   
>   		if (!gpio_is_valid(gpio))
>   			continue;
>   		ret = devm_gpio_request_one(&ov965x->client->dev, gpio,
> -					    GPIOF_OUT_INIT_HIGH, "OV965X");
> -		if (ret < 0)
> +					    GPIOF_OUT_INIT_HIGH, DRIVER_NAME);
> +		if (ret < 0) {
> +			dev_err(&ov965x->client->dev,
> +				"Failed to request gpio%d (%d)\n", gpio, ret);
>   			return ret;
> +		}
>   		v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
>   
>   		gpio_set_value(gpio, 1);
>   		gpio_export(gpio, 0);
> -		ov965x->gpios[i] = gpio;
> +		ov965x->gpios[i] = gpio_to_desc(gpio);
>   	}
>   
>   	return 0;
> @@ -1469,14 +1476,10 @@ static int ov965x_probe(struct i2c_client *client,
>   	struct v4l2_subdev *sd;
>   	struct ov965x *ov965x;
>   	int ret;
> +	struct device_node *np = client->dev.of_node;
>   
> -	if (pdata == NULL) {
> -		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");
> +	if (!pdata && !np) {
> +		dev_err(&client->dev, "Platform data or device tree data must be provided\n");
>   		return -EINVAL;
>   	}
>   
> @@ -1486,7 +1489,35 @@ static int ov965x_probe(struct i2c_client *client,
>   
>   	mutex_init(&ov965x->lock);
>   	ov965x->client = client;
> -	ov965x->mclk_frequency = pdata->mclk_frequency;
> +	mutex_init(&ov965x->lock);

Are you initializing the mutex twice?

> +	if (np) {
> +		/* Device tree */
> +		ov965x->gpios[GPIO_RST] =
> +			devm_gpiod_get_optional(&client->dev, "resetb",
> +						GPIOD_OUT_LOW);
> +		ov965x->gpios[GPIO_PWDN] =
> +			devm_gpiod_get_optional(&client->dev, "pwdn",
> +						GPIOD_OUT_HIGH);
> +
> +		ov965x->clk = devm_clk_get(&client->dev, NULL);
> +		if (IS_ERR(ov965x->clk)) {
> +			dev_err(&client->dev, "Could not get clock\n");
> +			return PTR_ERR(ov965x->clk);
> +		}
> +		ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
> +	} else {
> +		/* Platform data */
> +		ret = ov965x_configure_gpios(ov965x, pdata);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (pdata->mclk_frequency == 0) {
> +			dev_err(&client->dev, "MCLK frequency is mandatory\n");

I think the original message ("MCLK frequency not specified\n") was more
helpful.

Why you don't need this check in DT case? The clock is defined as mandatory 
in the DT binding.

> +			return -EINVAL;
> +		}
> +		ov965x->mclk_frequency = pdata->mclk_frequency;
> +	}
>   
>   	sd = &ov965x->sd;
>   	v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
> @@ -1551,9 +1582,17 @@ static int ov965x_remove(struct i2c_client *client)
>   };
>   MODULE_DEVICE_TABLE(i2c, ov965x_id);
>   
> +static const struct of_device_id ov965x_of_match[] = {
> +	{ .compatible = "ovti,ov9650", },
> +	{ .compatible = "ovti,ov9652", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ov965x_of_match);
> +
>   static struct i2c_driver ov965x_i2c_driver = {
>   	.driver = {
>   		.name	= DRIVER_NAME,
> +		.of_match_table = of_match_ptr(ov965x_of_match),

You don't need of_match_ptr() as ov965x_of_match table is always built in.

>   	},

Otherwise looks good.
Hugues FRUCHET July 18, 2017, 10:26 a.m. UTC | #3
Hi Sakari, thks for review.

On 07/09/2017 01:06 AM, Sakari Ailus wrote:
> Hi Hugues,
> 
> On Mon, Jul 03, 2017 at 11:16:04AM +0200, Hugues Fruchet wrote:
>> Allows use of device tree configuration data.
>> If no device tree data is there, configuration is taken from platform data.
>> In order to keep GPIOs configuration compatible between both way of doing,
>> GPIOs are switched to descriptor-based interface.
>>
>> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
>> Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
>> ---
>>   drivers/media/i2c/Kconfig  |  2 +-
>>   drivers/media/i2c/ov9650.c | 77 ++++++++++++++++++++++++++++++++++------------
>>   2 files changed, 59 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 121b3b5..168115c 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -615,7 +615,7 @@ config VIDEO_OV7670
>>   
>>   config VIDEO_OV9650
>>   	tristate "OmniVision OV9650/OV9652 sensor support"
>> -	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>>   	---help---
>>   	  This is a V4L2 sensor-level driver for the Omnivision
>>   	  OV9650 and OV9652 camera sensors.
>> diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
>> index 1e4e99e..7e9a902 100644
>> --- a/drivers/media/i2c/ov9650.c
>> +++ b/drivers/media/i2c/ov9650.c
>> @@ -11,12 +11,14 @@
>>    * 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/i2c.h>
>>   #include <linux/kernel.h>
>>   #include <linux/media.h>
>>   #include <linux/module.h>
>> +#include <linux/of_gpio.h>
>>   #include <linux/ratelimit.h>
>>   #include <linux/slab.h>
>>   #include <linux/string.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;
>> @@ -511,10 +514,10 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x)
>>   	return 0;
>>   }
>>   
>> -static void ov965x_gpio_set(int gpio, int val)
>> +static void ov965x_gpio_set(struct gpio_desc *gpio, int val)
>>   {
>> -	if (gpio_is_valid(gpio))
>> -		gpio_set_value(gpio, val);
>> +	if (gpio)
>> +		gpiod_set_value_cansleep(gpio, val);
> 
> gpiod_set_value_cansleep() can manage with NULL gpio parameter, no need to
> check it.

done

> 
>>   }
>>   
>>   static void __ov965x_set_power(struct ov965x *ov965x, int on)
>> @@ -1406,24 +1409,28 @@ static int ov965x_configure_gpios(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];
>> +	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
>> +		int gpio = gpios[i];
>>   
>>   		if (!gpio_is_valid(gpio))
>>   			continue;
>>   		ret = devm_gpio_request_one(&ov965x->client->dev, gpio,
>> -					    GPIOF_OUT_INIT_HIGH, "OV965X");
>> -		if (ret < 0)
>> +					    GPIOF_OUT_INIT_HIGH, DRIVER_NAME);
> 
> DRIVER_NAME is different from "OV965X". Is this an intended change?

Yes it was to unify namings around a single DRIVER_NAME definition.

> 
>> +		if (ret < 0) {
>> +			dev_err(&ov965x->client->dev,
>> +				"Failed to request gpio%d (%d)\n", gpio, ret);
>>   			return ret;
>> +		}
>>   		v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
>>   
>>   		gpio_set_value(gpio, 1);
>>   		gpio_export(gpio, 0);
>> -		ov965x->gpios[i] = gpio;
>> +		ov965x->gpios[i] = gpio_to_desc(gpio);
>>   	}
>>   
>>   	return 0;
>> @@ -1469,14 +1476,10 @@ static int ov965x_probe(struct i2c_client *client,
>>   	struct v4l2_subdev *sd;
>>   	struct ov965x *ov965x;
>>   	int ret;
>> +	struct device_node *np = client->dev.of_node;
> 
> It'd be nice to declare this next to pdata, rather than after ret and other
> short declarations.

done

> 
>>   
>> -	if (pdata == NULL) {
>> -		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");
>> +	if (!pdata && !np) {
>> +		dev_err(&client->dev, "Platform data or device tree data must be provided\n");
>>   		return -EINVAL;
>>   	}
>>   
>> @@ -1486,7 +1489,35 @@ static int ov965x_probe(struct i2c_client *client,
>>   
>>   	mutex_init(&ov965x->lock);
>>   	ov965x->client = client;
>> -	ov965x->mclk_frequency = pdata->mclk_frequency;
>> +	mutex_init(&ov965x->lock);
>> +
>> +	if (np) {
>> +		/* Device tree */
>> +		ov965x->gpios[GPIO_RST] =
>> +			devm_gpiod_get_optional(&client->dev, "resetb",
>> +						GPIOD_OUT_LOW);
>> +		ov965x->gpios[GPIO_PWDN] =
>> +			devm_gpiod_get_optional(&client->dev, "pwdn",
>> +						GPIOD_OUT_HIGH);
>> +
>> +		ov965x->clk = devm_clk_get(&client->dev, NULL);
>> +		if (IS_ERR(ov965x->clk)) {
>> +			dev_err(&client->dev, "Could not get clock\n");
> 
> mutex_destroy() should called on an initialised mutex if probe is going to
> fail. It's certainly not a problem introduced by this patch, but it'd be
> nice to fix that (in a separate patch) now that it's found. The same goes
> for remove below.

Will do.

> 
>> +			return PTR_ERR(ov965x->clk);
>> +		}
>> +		ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
>> +	} else {
>> +		/* Platform data */
>> +		ret = ov965x_configure_gpios(ov965x, pdata);
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		if (pdata->mclk_frequency == 0) {
>> +			dev_err(&client->dev, "MCLK frequency is mandatory\n");
>> +			return -EINVAL;
>> +		}
>> +		ov965x->mclk_frequency = pdata->mclk_frequency;
>> +	}
>>   
>>   	sd = &ov965x->sd;
>>   	v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
>> @@ -1551,9 +1582,17 @@ static int ov965x_remove(struct i2c_client *client)
>>   };
>>   MODULE_DEVICE_TABLE(i2c, ov965x_id);
>>   
>> +static const struct of_device_id ov965x_of_match[] = {
>> +	{ .compatible = "ovti,ov9650", },
>> +	{ .compatible = "ovti,ov9652", },
>> +	{ /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, ov965x_of_match);
>> +
>>   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,
>
diff mbox

Patch

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 121b3b5..168115c 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -615,7 +615,7 @@  config VIDEO_OV7670
 
 config VIDEO_OV9650
 	tristate "OmniVision OV9650/OV9652 sensor support"
-	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
 	---help---
 	  This is a V4L2 sensor-level driver for the Omnivision
 	  OV9650 and OV9652 camera sensors.
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 1e4e99e..7e9a902 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -11,12 +11,14 @@ 
  * 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/i2c.h>
 #include <linux/kernel.h>
 #include <linux/media.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/ratelimit.h>
 #include <linux/slab.h>
 #include <linux/string.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;
@@ -511,10 +514,10 @@  static int ov965x_set_color_matrix(struct ov965x *ov965x)
 	return 0;
 }
 
-static void ov965x_gpio_set(int gpio, int val)
+static void ov965x_gpio_set(struct gpio_desc *gpio, int val)
 {
-	if (gpio_is_valid(gpio))
-		gpio_set_value(gpio, val);
+	if (gpio)
+		gpiod_set_value_cansleep(gpio, val);
 }
 
 static void __ov965x_set_power(struct ov965x *ov965x, int on)
@@ -1406,24 +1409,28 @@  static int ov965x_configure_gpios(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];
+	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
+		int gpio = gpios[i];
 
 		if (!gpio_is_valid(gpio))
 			continue;
 		ret = devm_gpio_request_one(&ov965x->client->dev, gpio,
-					    GPIOF_OUT_INIT_HIGH, "OV965X");
-		if (ret < 0)
+					    GPIOF_OUT_INIT_HIGH, DRIVER_NAME);
+		if (ret < 0) {
+			dev_err(&ov965x->client->dev,
+				"Failed to request gpio%d (%d)\n", gpio, ret);
 			return ret;
+		}
 		v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio);
 
 		gpio_set_value(gpio, 1);
 		gpio_export(gpio, 0);
-		ov965x->gpios[i] = gpio;
+		ov965x->gpios[i] = gpio_to_desc(gpio);
 	}
 
 	return 0;
@@ -1469,14 +1476,10 @@  static int ov965x_probe(struct i2c_client *client,
 	struct v4l2_subdev *sd;
 	struct ov965x *ov965x;
 	int ret;
+	struct device_node *np = client->dev.of_node;
 
-	if (pdata == NULL) {
-		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");
+	if (!pdata && !np) {
+		dev_err(&client->dev, "Platform data or device tree data must be provided\n");
 		return -EINVAL;
 	}
 
@@ -1486,7 +1489,35 @@  static int ov965x_probe(struct i2c_client *client,
 
 	mutex_init(&ov965x->lock);
 	ov965x->client = client;
-	ov965x->mclk_frequency = pdata->mclk_frequency;
+	mutex_init(&ov965x->lock);
+
+	if (np) {
+		/* Device tree */
+		ov965x->gpios[GPIO_RST] =
+			devm_gpiod_get_optional(&client->dev, "resetb",
+						GPIOD_OUT_LOW);
+		ov965x->gpios[GPIO_PWDN] =
+			devm_gpiod_get_optional(&client->dev, "pwdn",
+						GPIOD_OUT_HIGH);
+
+		ov965x->clk = devm_clk_get(&client->dev, NULL);
+		if (IS_ERR(ov965x->clk)) {
+			dev_err(&client->dev, "Could not get clock\n");
+			return PTR_ERR(ov965x->clk);
+		}
+		ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
+	} else {
+		/* Platform data */
+		ret = ov965x_configure_gpios(ov965x, pdata);
+		if (ret < 0)
+			return ret;
+
+		if (pdata->mclk_frequency == 0) {
+			dev_err(&client->dev, "MCLK frequency is mandatory\n");
+			return -EINVAL;
+		}
+		ov965x->mclk_frequency = pdata->mclk_frequency;
+	}
 
 	sd = &ov965x->sd;
 	v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
@@ -1551,9 +1582,17 @@  static int ov965x_remove(struct i2c_client *client)
 };
 MODULE_DEVICE_TABLE(i2c, ov965x_id);
 
+static const struct of_device_id ov965x_of_match[] = {
+	{ .compatible = "ovti,ov9650", },
+	{ .compatible = "ovti,ov9652", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov965x_of_match);
+
 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,