diff mbox series

[v2,5/7] media: i2c: ov2659: Add powerdown/reset gpio handling

Message ID 20190919203955.15125-6-bparrot@ti.com (mailing list archive)
State New, archived
Headers show
Series media: i2c: ov2659: maintenance series | expand

Commit Message

Benoit Parrot Sept. 19, 2019, 8:39 p.m. UTC
On some board it is possible that the sensor 'powerdown' and or 'reset'
pin might be controlled by gpio instead of being tied.

To implement we add pm_runtime support which will handle the power
up/down sequence.

Now originally the driver assumed tat the sensor would always stay
powered and there keep its register setting. We cannot assume that this
anymore, so every time we "power up" we need to re-program the initial
registers configuration first. This was previously done only at probe
time.

Signed-off-by: Benoit Parrot <bparrot@ti.com>
---
 drivers/media/i2c/Kconfig  |  2 +-
 drivers/media/i2c/ov2659.c | 88 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 88 insertions(+), 2 deletions(-)

Comments

Sakari Ailus Sept. 20, 2019, 10:17 a.m. UTC | #1
Hi Benoit,

Thanks for the update.

On Thu, Sep 19, 2019 at 03:39:53PM -0500, Benoit Parrot wrote:
> On some board it is possible that the sensor 'powerdown' and or 'reset'
> pin might be controlled by gpio instead of being tied.
> 
> To implement we add pm_runtime support which will handle the power
> up/down sequence.
> 
> Now originally the driver assumed tat the sensor would always stay
> powered and there keep its register setting. We cannot assume that this
> anymore, so every time we "power up" we need to re-program the initial
> registers configuration first. This was previously done only at probe
> time.
> 
> Signed-off-by: Benoit Parrot <bparrot@ti.com>
> ---
>  drivers/media/i2c/Kconfig  |  2 +-
>  drivers/media/i2c/ov2659.c | 88 +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 88 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 7eee1812bba3..315c1d8bdb7b 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -634,7 +634,7 @@ config VIDEO_OV2640
>  config VIDEO_OV2659
>  	tristate "OmniVision OV2659 sensor support"
>  	depends on VIDEO_V4L2 && I2C
> -	depends on MEDIA_CAMERA_SUPPORT
> +	depends on MEDIA_CAMERA_SUPPORT && GPIOLIB
>  	select V4L2_FWNODE
>  	help
>  	  This is a Video4Linux2 sensor driver for the OmniVision
> diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
> index f77320e8a60d..170f80a1a51f 100644
> --- a/drivers/media/i2c/ov2659.c
> +++ b/drivers/media/i2c/ov2659.c
> @@ -22,9 +22,11 @@
>  
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/of_graph.h>
> +#include <linux/pm_runtime.h>
>  
>  #include <media/i2c/ov2659.h>
>  #include <media/v4l2-ctrls.h>
> @@ -218,6 +220,11 @@ struct ov2659 {
>  	struct sensor_register *format_ctrl_regs;
>  	struct ov2659_pll_ctrl pll;
>  	int streaming;
> +	/* used to control the sensor PWDN pin */
> +	struct gpio_desc *pwdn_gpio;
> +	/* used to control the sensor RESETB pin */
> +	struct gpio_desc *resetb_gpio;
> +	int on;
>  };
>  
>  static const struct sensor_register ov2659_init_regs[] = {
> @@ -1184,9 +1191,17 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on)
>  		/* Stop Streaming Sequence */
>  		ov2659_set_streaming(ov2659, 0);
>  		ov2659->streaming = on;
> +		pm_runtime_put(&client->dev);
>  		goto unlock;
>  	}
>  
> +	ret = pm_runtime_get_sync(&client->dev);
> +	if (ret < 0) {
> +		pm_runtime_put_noidle(&client->dev);
> +		goto unlock;
> +	}
> +
> +	ov2659_init(sd, 0);
>  	ov2659_set_pixel_clock(ov2659);
>  	ov2659_set_frame_size(ov2659);
>  	ov2659_set_format(ov2659);
> @@ -1243,6 +1258,32 @@ static const char * const ov2659_test_pattern_menu[] = {
>  	"Vertical Color Bars",
>  };
>  
> +static int ov2659_set_power(struct ov2659 *ov2659, int on)
> +{
> +	struct i2c_client *client = ov2659->client;
> +
> +	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
> +
> +	if (on) {
> +		if (ov2659->pwdn_gpio)
> +			gpiod_direction_output(ov2659->pwdn_gpio, 0);
> +
> +		if (ov2659->resetb_gpio) {
> +			gpiod_set_value(ov2659->resetb_gpio, 1);
> +			usleep_range(500, 1000);
> +			gpiod_set_value(ov2659->resetb_gpio, 0);
> +			usleep_range(3000, 5000);
> +		}

Please move the code to the runtime PM callbacks.

> +	} else {
> +		if (ov2659->pwdn_gpio)
> +			gpiod_direction_output(ov2659->pwdn_gpio, 1);

Gpiod API works with NULL GPIOs, too, so no need to check here.

Isn't the direction supposed to be already output, so set_value would be
more appropriate here, and above.

> +	}
> +
> +	ov2659->on = on;
> +
> +	return 0;
> +}
> +
>  /* -----------------------------------------------------------------------------
>   * V4L2 subdev internal operations
>   */
> @@ -1323,7 +1364,6 @@ static int ov2659_detect(struct v4l2_subdev *sd)
>  			ret = -ENODEV;
>  		} else {
>  			dev_info(&client->dev, "Found OV%04X sensor\n", id);
> -			ret = ov2659_init(sd, 0);
>  		}
>  	}
>  
> @@ -1400,6 +1440,18 @@ static int ov2659_probe(struct i2c_client *client)
>  	    ov2659->xvclk_frequency > 27000000)
>  		return -EINVAL;
>  
> +	/* Optional gpio don't fail if not present */
> +	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
> +						    GPIOD_OUT_LOW);
> +	if (IS_ERR(ov2659->pwdn_gpio))
> +		return PTR_ERR(ov2659->pwdn_gpio);
> +
> +	/* Optional gpio don't fail if not present */
> +	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> +						      GPIOD_OUT_HIGH);
> +	if (IS_ERR(ov2659->resetb_gpio))
> +		return PTR_ERR(ov2659->resetb_gpio);
> +
>  	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
>  	ov2659->link_frequency =
>  			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
> @@ -1445,6 +1497,9 @@ static int ov2659_probe(struct i2c_client *client)
>  	ov2659->frame_size = &ov2659_framesizes[2];
>  	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
>  
> +	pm_runtime_enable(&client->dev);
> +	pm_runtime_get_sync(&client->dev);

This makes the driver depend on runtime PM.

See e.g. the smiapp driver for an example how to make it work without. It
wasn't trivial. :I You won't need autosuspend.

> +
>  	ret = ov2659_detect(sd);
>  	if (ret < 0)
>  		goto error;
> @@ -1458,10 +1513,14 @@ static int ov2659_probe(struct i2c_client *client)
>  
>  	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
>  
> +	pm_runtime_put(&client->dev);
> +
>  	return 0;
>  
>  error:
>  	v4l2_ctrl_handler_free(&ov2659->ctrls);
> +	pm_runtime_put(&client->dev);
> +	pm_runtime_disable(&client->dev);
>  	media_entity_cleanup(&sd->entity);
>  	mutex_destroy(&ov2659->lock);
>  	return ret;
> @@ -1477,9 +1536,35 @@ static int ov2659_remove(struct i2c_client *client)
>  	media_entity_cleanup(&sd->entity);
>  	mutex_destroy(&ov2659->lock);
>  
> +	pm_runtime_disable(&client->dev);
> +
>  	return 0;
>  }
>  
> +static int ov2659_runtime_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov2659 *ov2659 = to_ov2659(sd);
> +
> +	ov2659_set_power(ov2659, 0);
> +
> +	return 0;
> +}
> +
> +static int ov2659_runtime_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov2659 *ov2659 = to_ov2659(sd);
> +
> +	return ov2659_set_power(ov2659, 1);
> +}
> +
> +static const struct dev_pm_ops ov2659_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ov2659_runtime_suspend, ov2659_runtime_resume, NULL)
> +};
> +
>  static const struct i2c_device_id ov2659_id[] = {
>  	{ "ov2659", 0 },
>  	{ /* sentinel */ },
> @@ -1497,6 +1582,7 @@ MODULE_DEVICE_TABLE(of, ov2659_of_match);
>  static struct i2c_driver ov2659_i2c_driver = {
>  	.driver = {
>  		.name	= DRIVER_NAME,
> +		.pm	= &ov2659_pm_ops,
>  		.of_match_table = of_match_ptr(ov2659_of_match),
>  	},
>  	.probe_new	= ov2659_probe,
Benoit Parrot Sept. 20, 2019, 4:55 p.m. UTC | #2
Sakari Ailus <sakari.ailus@linux.intel.com> wrote on Fri [2019-Sep-20 13:17:06 +0300]:
> Hi Benoit,
> 
> Thanks for the update.
> 
> On Thu, Sep 19, 2019 at 03:39:53PM -0500, Benoit Parrot wrote:
> > On some board it is possible that the sensor 'powerdown' and or 'reset'
> > pin might be controlled by gpio instead of being tied.
> > 
> > To implement we add pm_runtime support which will handle the power
> > up/down sequence.
> > 
> > Now originally the driver assumed tat the sensor would always stay
> > powered and there keep its register setting. We cannot assume that this
> > anymore, so every time we "power up" we need to re-program the initial
> > registers configuration first. This was previously done only at probe
> > time.
> > 
> > Signed-off-by: Benoit Parrot <bparrot@ti.com>
> > ---
> >  drivers/media/i2c/Kconfig  |  2 +-
> >  drivers/media/i2c/ov2659.c | 88 +++++++++++++++++++++++++++++++++++++-
> >  2 files changed, 88 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > index 7eee1812bba3..315c1d8bdb7b 100644
> > --- a/drivers/media/i2c/Kconfig
> > +++ b/drivers/media/i2c/Kconfig
> > @@ -634,7 +634,7 @@ config VIDEO_OV2640
> >  config VIDEO_OV2659
> >  	tristate "OmniVision OV2659 sensor support"
> >  	depends on VIDEO_V4L2 && I2C
> > -	depends on MEDIA_CAMERA_SUPPORT
> > +	depends on MEDIA_CAMERA_SUPPORT && GPIOLIB
> >  	select V4L2_FWNODE
> >  	help
> >  	  This is a Video4Linux2 sensor driver for the OmniVision
> > diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
> > index f77320e8a60d..170f80a1a51f 100644
> > --- a/drivers/media/i2c/ov2659.c
> > +++ b/drivers/media/i2c/ov2659.c
> > @@ -22,9 +22,11 @@
> >  
> >  #include <linux/clk.h>
> >  #include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> >  #include <linux/i2c.h>
> >  #include <linux/module.h>
> >  #include <linux/of_graph.h>
> > +#include <linux/pm_runtime.h>
> >  
> >  #include <media/i2c/ov2659.h>
> >  #include <media/v4l2-ctrls.h>
> > @@ -218,6 +220,11 @@ struct ov2659 {
> >  	struct sensor_register *format_ctrl_regs;
> >  	struct ov2659_pll_ctrl pll;
> >  	int streaming;
> > +	/* used to control the sensor PWDN pin */
> > +	struct gpio_desc *pwdn_gpio;
> > +	/* used to control the sensor RESETB pin */
> > +	struct gpio_desc *resetb_gpio;
> > +	int on;
> >  };
> >  
> >  static const struct sensor_register ov2659_init_regs[] = {
> > @@ -1184,9 +1191,17 @@ static int ov2659_s_stream(struct v4l2_subdev *sd, int on)
> >  		/* Stop Streaming Sequence */
> >  		ov2659_set_streaming(ov2659, 0);
> >  		ov2659->streaming = on;
> > +		pm_runtime_put(&client->dev);
> >  		goto unlock;
> >  	}
> >  
> > +	ret = pm_runtime_get_sync(&client->dev);
> > +	if (ret < 0) {
> > +		pm_runtime_put_noidle(&client->dev);
> > +		goto unlock;
> > +	}
> > +
> > +	ov2659_init(sd, 0);
> >  	ov2659_set_pixel_clock(ov2659);
> >  	ov2659_set_frame_size(ov2659);
> >  	ov2659_set_format(ov2659);
> > @@ -1243,6 +1258,32 @@ static const char * const ov2659_test_pattern_menu[] = {
> >  	"Vertical Color Bars",
> >  };
> >  
> > +static int ov2659_set_power(struct ov2659 *ov2659, int on)
> > +{
> > +	struct i2c_client *client = ov2659->client;
> > +
> > +	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
> > +
> > +	if (on) {
> > +		if (ov2659->pwdn_gpio)
> > +			gpiod_direction_output(ov2659->pwdn_gpio, 0);
> > +
> > +		if (ov2659->resetb_gpio) {
> > +			gpiod_set_value(ov2659->resetb_gpio, 1);
> > +			usleep_range(500, 1000);
> > +			gpiod_set_value(ov2659->resetb_gpio, 0);
> > +			usleep_range(3000, 5000);
> > +		}
> 
> Please move the code to the runtime PM callbacks.
> 
> > +	} else {
> > +		if (ov2659->pwdn_gpio)
> > +			gpiod_direction_output(ov2659->pwdn_gpio, 1);
> 
> Gpiod API works with NULL GPIOs, too, so no need to check here.

Didn't realize that. I'll check.

> 
> Isn't the direction supposed to be already output, so set_value would be
> more appropriate here, and above.

Oh yeah the direction should already be set.

> 
> > +	}
> > +
> > +	ov2659->on = on;
> > +
> > +	return 0;
> > +}
> > +
> >  /* -----------------------------------------------------------------------------
> >   * V4L2 subdev internal operations
> >   */
> > @@ -1323,7 +1364,6 @@ static int ov2659_detect(struct v4l2_subdev *sd)
> >  			ret = -ENODEV;
> >  		} else {
> >  			dev_info(&client->dev, "Found OV%04X sensor\n", id);
> > -			ret = ov2659_init(sd, 0);
> >  		}
> >  	}
> >  
> > @@ -1400,6 +1440,18 @@ static int ov2659_probe(struct i2c_client *client)
> >  	    ov2659->xvclk_frequency > 27000000)
> >  		return -EINVAL;
> >  
> > +	/* Optional gpio don't fail if not present */
> > +	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
> > +						    GPIOD_OUT_LOW);
> > +	if (IS_ERR(ov2659->pwdn_gpio))
> > +		return PTR_ERR(ov2659->pwdn_gpio);
> > +
> > +	/* Optional gpio don't fail if not present */
> > +	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> > +						      GPIOD_OUT_HIGH);
> > +	if (IS_ERR(ov2659->resetb_gpio))
> > +		return PTR_ERR(ov2659->resetb_gpio);
> > +
> >  	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
> >  	ov2659->link_frequency =
> >  			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
> > @@ -1445,6 +1497,9 @@ static int ov2659_probe(struct i2c_client *client)
> >  	ov2659->frame_size = &ov2659_framesizes[2];
> >  	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
> >  
> > +	pm_runtime_enable(&client->dev);
> > +	pm_runtime_get_sync(&client->dev);
> 
> This makes the driver depend on runtime PM.

Obviously.
Why? Is that bad?

> 
> See e.g. the smiapp driver for an example how to make it work without. It
> wasn't trivial. :I You won't need autosuspend.

I took a look at that driver, but I don't get your reference to being able
to work without runtime pm!
That driver looks pretty similar to ov7740.c which I used as a reference
for this.

> 
> > +
> >  	ret = ov2659_detect(sd);
> >  	if (ret < 0)
> >  		goto error;
> > @@ -1458,10 +1513,14 @@ static int ov2659_probe(struct i2c_client *client)
> >  
> >  	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
> >  
> > +	pm_runtime_put(&client->dev);
> > +
> >  	return 0;
> >  
> >  error:
> >  	v4l2_ctrl_handler_free(&ov2659->ctrls);
> > +	pm_runtime_put(&client->dev);
> > +	pm_runtime_disable(&client->dev);
> >  	media_entity_cleanup(&sd->entity);
> >  	mutex_destroy(&ov2659->lock);
> >  	return ret;
> > @@ -1477,9 +1536,35 @@ static int ov2659_remove(struct i2c_client *client)
> >  	media_entity_cleanup(&sd->entity);
> >  	mutex_destroy(&ov2659->lock);
> >  
> > +	pm_runtime_disable(&client->dev);
> > +
> >  	return 0;
> >  }
> >  
> > +static int ov2659_runtime_suspend(struct device *dev)
> > +{
> > +	struct i2c_client *client = to_i2c_client(dev);
> > +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +	struct ov2659 *ov2659 = to_ov2659(sd);
> > +
> > +	ov2659_set_power(ov2659, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ov2659_runtime_resume(struct device *dev)
> > +{
> > +	struct i2c_client *client = to_i2c_client(dev);
> > +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +	struct ov2659 *ov2659 = to_ov2659(sd);
> > +
> > +	return ov2659_set_power(ov2659, 1);
> > +}
> > +
> > +static const struct dev_pm_ops ov2659_pm_ops = {
> > +	SET_RUNTIME_PM_OPS(ov2659_runtime_suspend, ov2659_runtime_resume, NULL)
> > +};
> > +
> >  static const struct i2c_device_id ov2659_id[] = {
> >  	{ "ov2659", 0 },
> >  	{ /* sentinel */ },
> > @@ -1497,6 +1582,7 @@ MODULE_DEVICE_TABLE(of, ov2659_of_match);
> >  static struct i2c_driver ov2659_i2c_driver = {
> >  	.driver = {
> >  		.name	= DRIVER_NAME,
> > +		.pm	= &ov2659_pm_ops,
> >  		.of_match_table = of_match_ptr(ov2659_of_match),
> >  	},
> >  	.probe_new	= ov2659_probe,
> 
> -- 
> Kind regards,
> 
> Sakari Ailus
> sakari.ailus@linux.intel.com
Sakari Ailus Sept. 23, 2019, 6:17 a.m. UTC | #3
Hi Benoit,

On Fri, Sep 20, 2019 at 11:55:29AM -0500, Benoit Parrot wrote:
...
> > > @@ -1400,6 +1440,18 @@ static int ov2659_probe(struct i2c_client *client)
> > >  	    ov2659->xvclk_frequency > 27000000)
> > >  		return -EINVAL;
> > >  
> > > +	/* Optional gpio don't fail if not present */
> > > +	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
> > > +						    GPIOD_OUT_LOW);
> > > +	if (IS_ERR(ov2659->pwdn_gpio))
> > > +		return PTR_ERR(ov2659->pwdn_gpio);
> > > +
> > > +	/* Optional gpio don't fail if not present */
> > > +	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> > > +						      GPIOD_OUT_HIGH);
> > > +	if (IS_ERR(ov2659->resetb_gpio))
> > > +		return PTR_ERR(ov2659->resetb_gpio);
> > > +
> > >  	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
> > >  	ov2659->link_frequency =
> > >  			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
> > > @@ -1445,6 +1497,9 @@ static int ov2659_probe(struct i2c_client *client)
> > >  	ov2659->frame_size = &ov2659_framesizes[2];
> > >  	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
> > >  
> > > +	pm_runtime_enable(&client->dev);
> > > +	pm_runtime_get_sync(&client->dev);
> > 
> > This makes the driver depend on runtime PM.
> 
> Obviously.
> Why? Is that bad?

Well, if it is, then it should be listed in driver's dependencies in
Kconfig.

> 
> > 
> > See e.g. the smiapp driver for an example how to make it work without. It
> > wasn't trivial. :I You won't need autosuspend.
> 
> I took a look at that driver, but I don't get your reference to being able
> to work without runtime pm!

The driver didn't need runtime PM, so it'd be nice to continue work
without.

What smiapp does is that it powers the sensor on first *without* runtime
PM, and then proceeds to set up runtime PM if it's available. The sensor
will only be powered off when the device is unbound with runtime PM
disabled.

Regarding the smiapp driver, you can replace pm_runtime_get_noresume() and
all the autoidle lines with pm_runtime_idle() call after
pm_runtime_enable() in the ov2659 driver.

> That driver looks pretty similar to ov7740.c which I used as a reference
> for this.

I guess in practice many sensor drivers don't work without it on DT-based
systems I'm afraid. :-( They should be fixed.
Benoit Parrot Sept. 23, 2019, 5:05 p.m. UTC | #4
Sakari Ailus <sakari.ailus@linux.intel.com> wrote on Mon [2019-Sep-23 09:17:32 +0300]:
> Hi Benoit,
> 
> On Fri, Sep 20, 2019 at 11:55:29AM -0500, Benoit Parrot wrote:
> ...
> > > > @@ -1400,6 +1440,18 @@ static int ov2659_probe(struct i2c_client *client)
> > > >  	    ov2659->xvclk_frequency > 27000000)
> > > >  		return -EINVAL;
> > > >  
> > > > +	/* Optional gpio don't fail if not present */
> > > > +	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
> > > > +						    GPIOD_OUT_LOW);
> > > > +	if (IS_ERR(ov2659->pwdn_gpio))
> > > > +		return PTR_ERR(ov2659->pwdn_gpio);
> > > > +
> > > > +	/* Optional gpio don't fail if not present */
> > > > +	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> > > > +						      GPIOD_OUT_HIGH);
> > > > +	if (IS_ERR(ov2659->resetb_gpio))
> > > > +		return PTR_ERR(ov2659->resetb_gpio);
> > > > +
> > > >  	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
> > > >  	ov2659->link_frequency =
> > > >  			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
> > > > @@ -1445,6 +1497,9 @@ static int ov2659_probe(struct i2c_client *client)
> > > >  	ov2659->frame_size = &ov2659_framesizes[2];
> > > >  	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
> > > >  
> > > > +	pm_runtime_enable(&client->dev);
> > > > +	pm_runtime_get_sync(&client->dev);
> > > 
> > > This makes the driver depend on runtime PM.
> > 
> > Obviously.
> > Why? Is that bad?
> 
> Well, if it is, then it should be listed in driver's dependencies in
> Kconfig.

And I see that most camera driver using pm_runtime calls don't mark it as a
dependency in their own Kconfig. There is actually only one that does.

> 
> > 
> > > 
> > > See e.g. the smiapp driver for an example how to make it work without. It
> > > wasn't trivial. :I You won't need autosuspend.
> > 
> > I took a look at that driver, but I don't get your reference to being able
> > to work without runtime pm!
> 
> The driver didn't need runtime PM, so it'd be nice to continue work
> without.

Ok.

> 
> What smiapp does is that it powers the sensor on first *without* runtime
> PM, and then proceeds to set up runtime PM if it's available. The sensor
> will only be powered off when the device is unbound with runtime PM
> disabled.

In that case in your original reply you suggested that I move the content
of ov2659_set_power() into the pm_runtime callback, but based on this
comments then I should keep ov2659_set_power() as is as I  would need to
use it for this specific purpose.

> 
> Regarding the smiapp driver, you can replace pm_runtime_get_noresume() and
> all the autoidle lines with pm_runtime_idle() call after
> pm_runtime_enable() in the ov2659 driver.

Ok, I'll study this mechanics a little more, as this is not immediately
clear.

Benoit

> 
> > That driver looks pretty similar to ov7740.c which I used as a reference
> > for this.
> 
> I guess in practice many sensor drivers don't work without it on DT-based
> systems I'm afraid. :-( They should be fixed.
> 
> -- 
> Kind regards,
> 
> Sakari Ailus
> sakari.ailus@linux.intel.com
Sakari Ailus Sept. 23, 2019, 5:07 p.m. UTC | #5
On Mon, Sep 23, 2019 at 12:05:57PM -0500, Benoit Parrot wrote:
> Sakari Ailus <sakari.ailus@linux.intel.com> wrote on Mon [2019-Sep-23 09:17:32 +0300]:
> > Hi Benoit,
> > 
> > On Fri, Sep 20, 2019 at 11:55:29AM -0500, Benoit Parrot wrote:
> > ...
> > > > > @@ -1400,6 +1440,18 @@ static int ov2659_probe(struct i2c_client *client)
> > > > >  	    ov2659->xvclk_frequency > 27000000)
> > > > >  		return -EINVAL;
> > > > >  
> > > > > +	/* Optional gpio don't fail if not present */
> > > > > +	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
> > > > > +						    GPIOD_OUT_LOW);
> > > > > +	if (IS_ERR(ov2659->pwdn_gpio))
> > > > > +		return PTR_ERR(ov2659->pwdn_gpio);
> > > > > +
> > > > > +	/* Optional gpio don't fail if not present */
> > > > > +	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
> > > > > +						      GPIOD_OUT_HIGH);
> > > > > +	if (IS_ERR(ov2659->resetb_gpio))
> > > > > +		return PTR_ERR(ov2659->resetb_gpio);
> > > > > +
> > > > >  	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
> > > > >  	ov2659->link_frequency =
> > > > >  			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
> > > > > @@ -1445,6 +1497,9 @@ static int ov2659_probe(struct i2c_client *client)
> > > > >  	ov2659->frame_size = &ov2659_framesizes[2];
> > > > >  	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
> > > > >  
> > > > > +	pm_runtime_enable(&client->dev);
> > > > > +	pm_runtime_get_sync(&client->dev);
> > > > 
> > > > This makes the driver depend on runtime PM.
> > > 
> > > Obviously.
> > > Why? Is that bad?
> > 
> > Well, if it is, then it should be listed in driver's dependencies in
> > Kconfig.
> 
> And I see that most camera driver using pm_runtime calls don't mark it as a
> dependency in their own Kconfig. There is actually only one that does.

Drivers should also work without runtime PM even if they can use it.

> 
> > 
> > > 
> > > > 
> > > > See e.g. the smiapp driver for an example how to make it work without. It
> > > > wasn't trivial. :I You won't need autosuspend.
> > > 
> > > I took a look at that driver, but I don't get your reference to being able
> > > to work without runtime pm!
> > 
> > The driver didn't need runtime PM, so it'd be nice to continue work
> > without.
> 
> Ok.
> 
> > 
> > What smiapp does is that it powers the sensor on first *without* runtime
> > PM, and then proceeds to set up runtime PM if it's available. The sensor
> > will only be powered off when the device is unbound with runtime PM
> > disabled.
> 
> In that case in your original reply you suggested that I move the content
> of ov2659_set_power() into the pm_runtime callback, but based on this
> comments then I should keep ov2659_set_power() as is as I  would need to
> use it for this specific purpose.

No need to; you can call the same function elsewhere in the driver (just
like the smiapp driver does).

> 
> > 
> > Regarding the smiapp driver, you can replace pm_runtime_get_noresume() and
> > all the autoidle lines with pm_runtime_idle() call after
> > pm_runtime_enable() in the ov2659 driver.
> 
> Ok, I'll study this mechanics a little more, as this is not immediately
> clear.
> 
> Benoit
> 
> > 
> > > That driver looks pretty similar to ov7740.c which I used as a reference
> > > for this.
> > 
> > I guess in practice many sensor drivers don't work without it on DT-based
> > systems I'm afraid. :-( They should be fixed.
> > 
> > -- 
> > Kind regards,
> > 
> > Sakari Ailus
> > sakari.ailus@linux.intel.com
diff mbox series

Patch

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 7eee1812bba3..315c1d8bdb7b 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -634,7 +634,7 @@  config VIDEO_OV2640
 config VIDEO_OV2659
 	tristate "OmniVision OV2659 sensor support"
 	depends on VIDEO_V4L2 && I2C
-	depends on MEDIA_CAMERA_SUPPORT
+	depends on MEDIA_CAMERA_SUPPORT && GPIOLIB
 	select V4L2_FWNODE
 	help
 	  This is a Video4Linux2 sensor driver for the OmniVision
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index f77320e8a60d..170f80a1a51f 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -22,9 +22,11 @@ 
 
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
 
 #include <media/i2c/ov2659.h>
 #include <media/v4l2-ctrls.h>
@@ -218,6 +220,11 @@  struct ov2659 {
 	struct sensor_register *format_ctrl_regs;
 	struct ov2659_pll_ctrl pll;
 	int streaming;
+	/* used to control the sensor PWDN pin */
+	struct gpio_desc *pwdn_gpio;
+	/* used to control the sensor RESETB pin */
+	struct gpio_desc *resetb_gpio;
+	int on;
 };
 
 static const struct sensor_register ov2659_init_regs[] = {
@@ -1184,9 +1191,17 @@  static int ov2659_s_stream(struct v4l2_subdev *sd, int on)
 		/* Stop Streaming Sequence */
 		ov2659_set_streaming(ov2659, 0);
 		ov2659->streaming = on;
+		pm_runtime_put(&client->dev);
 		goto unlock;
 	}
 
+	ret = pm_runtime_get_sync(&client->dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(&client->dev);
+		goto unlock;
+	}
+
+	ov2659_init(sd, 0);
 	ov2659_set_pixel_clock(ov2659);
 	ov2659_set_frame_size(ov2659);
 	ov2659_set_format(ov2659);
@@ -1243,6 +1258,32 @@  static const char * const ov2659_test_pattern_menu[] = {
 	"Vertical Color Bars",
 };
 
+static int ov2659_set_power(struct ov2659 *ov2659, int on)
+{
+	struct i2c_client *client = ov2659->client;
+
+	dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
+
+	if (on) {
+		if (ov2659->pwdn_gpio)
+			gpiod_direction_output(ov2659->pwdn_gpio, 0);
+
+		if (ov2659->resetb_gpio) {
+			gpiod_set_value(ov2659->resetb_gpio, 1);
+			usleep_range(500, 1000);
+			gpiod_set_value(ov2659->resetb_gpio, 0);
+			usleep_range(3000, 5000);
+		}
+	} else {
+		if (ov2659->pwdn_gpio)
+			gpiod_direction_output(ov2659->pwdn_gpio, 1);
+	}
+
+	ov2659->on = on;
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * V4L2 subdev internal operations
  */
@@ -1323,7 +1364,6 @@  static int ov2659_detect(struct v4l2_subdev *sd)
 			ret = -ENODEV;
 		} else {
 			dev_info(&client->dev, "Found OV%04X sensor\n", id);
-			ret = ov2659_init(sd, 0);
 		}
 	}
 
@@ -1400,6 +1440,18 @@  static int ov2659_probe(struct i2c_client *client)
 	    ov2659->xvclk_frequency > 27000000)
 		return -EINVAL;
 
+	/* Optional gpio don't fail if not present */
+	ov2659->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+						    GPIOD_OUT_LOW);
+	if (IS_ERR(ov2659->pwdn_gpio))
+		return PTR_ERR(ov2659->pwdn_gpio);
+
+	/* Optional gpio don't fail if not present */
+	ov2659->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+						      GPIOD_OUT_HIGH);
+	if (IS_ERR(ov2659->resetb_gpio))
+		return PTR_ERR(ov2659->resetb_gpio);
+
 	v4l2_ctrl_handler_init(&ov2659->ctrls, 2);
 	ov2659->link_frequency =
 			v4l2_ctrl_new_std(&ov2659->ctrls, &ov2659_ctrl_ops,
@@ -1445,6 +1497,9 @@  static int ov2659_probe(struct i2c_client *client)
 	ov2659->frame_size = &ov2659_framesizes[2];
 	ov2659->format_ctrl_regs = ov2659_formats[0].format_ctrl_regs;
 
+	pm_runtime_enable(&client->dev);
+	pm_runtime_get_sync(&client->dev);
+
 	ret = ov2659_detect(sd);
 	if (ret < 0)
 		goto error;
@@ -1458,10 +1513,14 @@  static int ov2659_probe(struct i2c_client *client)
 
 	dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
 
+	pm_runtime_put(&client->dev);
+
 	return 0;
 
 error:
 	v4l2_ctrl_handler_free(&ov2659->ctrls);
+	pm_runtime_put(&client->dev);
+	pm_runtime_disable(&client->dev);
 	media_entity_cleanup(&sd->entity);
 	mutex_destroy(&ov2659->lock);
 	return ret;
@@ -1477,9 +1536,35 @@  static int ov2659_remove(struct i2c_client *client)
 	media_entity_cleanup(&sd->entity);
 	mutex_destroy(&ov2659->lock);
 
+	pm_runtime_disable(&client->dev);
+
 	return 0;
 }
 
+static int ov2659_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2659 *ov2659 = to_ov2659(sd);
+
+	ov2659_set_power(ov2659, 0);
+
+	return 0;
+}
+
+static int ov2659_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2659 *ov2659 = to_ov2659(sd);
+
+	return ov2659_set_power(ov2659, 1);
+}
+
+static const struct dev_pm_ops ov2659_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov2659_runtime_suspend, ov2659_runtime_resume, NULL)
+};
+
 static const struct i2c_device_id ov2659_id[] = {
 	{ "ov2659", 0 },
 	{ /* sentinel */ },
@@ -1497,6 +1582,7 @@  MODULE_DEVICE_TABLE(of, ov2659_of_match);
 static struct i2c_driver ov2659_i2c_driver = {
 	.driver = {
 		.name	= DRIVER_NAME,
+		.pm	= &ov2659_pm_ops,
 		.of_match_table = of_match_ptr(ov2659_of_match),
 	},
 	.probe_new	= ov2659_probe,