Re: [PATCH 3/4] media: ov13b10: add PM control support based on power resources

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Bingbu,

On Fri, May 26, 2023 at 11:21:20AM +0000, Cao, Bingbu wrote:
> Sakari, 
> 
> Thanks for your review.
> 
> ------------------------------------------------------------------------
> BRs,  
> Bingbu Cao 
> 
> >-----Original Message-----
> >From: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
> >Sent: Friday, May 26, 2023 18:02
> >To: Cao, Bingbu <bingbu.cao@xxxxxxxxx>
> >Cc: linux-media@xxxxxxxxxxxxxxx; Kao, Arec <arec.kao@xxxxxxxxx>; Yao, Hao
> ><hao.yao@xxxxxxxxx>; bingbu.cao@xxxxxxxxxxxxxxx
> >Subject: Re: [PATCH 3/4] media: ov13b10: add PM control support based on
> >power resources
> >
> >Hi Bingbu,
> >
> >Thanks for the patch.
> >
> >On Fri, May 26, 2023 at 05:58:39PM +0800, bingbu.cao@xxxxxxxxx wrote:
> >> From: Bingbu Cao <bingbu.cao@xxxxxxxxx>
> >>
> >> On ACPI based platforms, the ov13b10 camera sensor need to be powered
> >> up by acquire the PM resource from INT3472. On such platform, 1 GPIO
> >> can be used to enable AVDD and DOVDD, 1 GPIO to reset, we just have
> >> one power supply 'vdd' for camera. Add 2 power interfaces and also
> >> registered as runtime PM callbacks to support that.
> >>
> >> Signed-off-by: Bingbu Cao <bingbu.cao@xxxxxxxxx>
> >> Signed-off-by: Hao Yao <hao.yao@xxxxxxxxx>
> >> ---
> >>  drivers/media/i2c/ov13b10.c | 98
> >> ++++++++++++++++++++++++++++++++++++-
> >>  1 file changed, 96 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c
> >> index 2d48c94659a4..b1faa89a3571 100644
> >> --- a/drivers/media/i2c/ov13b10.c
> >> +++ b/drivers/media/i2c/ov13b10.c
> >> @@ -2,6 +2,9 @@
> >>  // Copyright (c) 2021 Intel Corporation.
> >>
> >>  #include <linux/acpi.h>
> >> +#include <linux/clk.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/gpio/consumer.h>
> >>  #include <linux/i2c.h>
> >>  #include <linux/module.h>
> >>  #include <linux/pm_runtime.h>
> >> @@ -573,6 +576,11 @@ struct ov13b10 {
> >>  	struct media_pad pad;
> >>
> >>  	struct v4l2_ctrl_handler ctrl_handler;
> >> +
> >> +	struct clk *img_clk;
> >> +	struct regulator *vdd;
> >> +	struct gpio_desc *reset;
> >> +
> >>  	/* V4L2 Controls */
> >>  	struct v4l2_ctrl *link_freq;
> >>  	struct v4l2_ctrl *pixel_rate;
> >> @@ -1051,6 +1059,50 @@ static int ov13b10_identify_module(struct ov13b10
> >*ov13b)
> >>  	return 0;
> >>  }
> >>
> >> +static int ov13b10_power_off(struct device *dev) {
> >> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> >> +	struct ov13b10 *ov13b10 = to_ov13b10(sd);
> >> +
> >> +	if (!ov13b10->vdd || !ov13b10->reset || !ov13b10->img_clk)
> >> +		return 0;
> >
> >This can be dropped.
> >
> >> +
> >> +	gpiod_set_value_cansleep(ov13b10->reset, 1);
> >> +	regulator_disable(ov13b10->vdd);
> >> +	clk_disable_unprepare(ov13b10->img_clk);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int ov13b10_power_on(struct device *dev) {
> >> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> >> +	struct ov13b10 *ov13b10 = to_ov13b10(sd);
> >> +	int ret;
> >> +
> >> +	if (!ov13b10->vdd || !ov13b10->reset || !ov13b10->img_clk)
> >> +		return 0;
> >
> >Instead I'd execute the sleep below if any of these is non-NULL. No need to
> >return here.
> >
> >> +
> >> +	ret = clk_prepare_enable(ov13b10->img_clk);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to enable imaging clock: %d", ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	ret = regulator_enable(ov13b10->vdd);
> >> +	if (ret < 0) {
> >> +		dev_err(dev, "failed to enable vdd: %d", ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	gpiod_set_value_cansleep(ov13b10->reset, 0);
> >> +
> >> +	/* 5ms to wait ready after XSHUTDN assert */
> >> +	usleep_range(5000, 5500);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>  static int ov13b10_start_streaming(struct ov13b10 *ov13b)  {
> >>  	struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); @@
> >> -1317,6 +1369,37 @@ static void ov13b10_free_controls(struct ov13b10
> >*ov13b)
> >>  	mutex_destroy(&ov13b->mutex);
> >>  }
> >>
> >> +static void ov13b10_get_pm_resources(struct device *dev) {
> >> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> >> +	struct ov13b10 *ov13b = to_ov13b10(sd);
> >> +
> >> +	if (!is_acpi_node(dev_fwnode(dev)))
> >> +		return;
> >
> >I'd omit this check. This would work on DT although bindings should still
> >be written. That may be left for a future excercise I think.
> >
> >> +
> >> +	ov13b->reset = devm_gpiod_get_optional(dev, "reset",
> >> +					       GPIOD_OUT_LOW);
> >> +	if (IS_ERR(ov13b->reset)) {
> >> +		dev_dbg(dev, "failed to get reset gpio: %ld",
> >> +			PTR_ERR(ov13b->reset));
> >> +		ov13b->reset = NULL;
> >> +	}
> >> +
> >> +	ov13b->img_clk = devm_clk_get_optional(dev, NULL);
> >> +	if (IS_ERR(ov13b->img_clk)) {
> >> +		dev_dbg(dev, "failed to get imaging clock: %ld",
> >> +			PTR_ERR(ov13b->img_clk));
> >> +		ov13b->img_clk = NULL;
> >> +	}
> >> +
> >> +	ov13b->vdd = devm_regulator_get_optional(dev, "vdd");
> >> +	if (IS_ERR(ov13b->vdd)) {
> >> +		dev_dbg(dev, "failed to get vdd regulator: %ld",
> >> +			PTR_ERR(ov13b->vdd));
> >> +		ov13b->vdd = NULL;
> >
> >You should return the error code instead, in all three cases.
> 
> On some platforms such as Chromebook, there is no INT3472 requirement.
> Here it ignores that case and return without errors, thus the power on
> and off callback would be NULL functions then.

Don't the _optional() variants of these functions return NULL if there's no
such thing for the device?

> 
> 
> >
> >> +	}
> >> +}
> >> +
> >>  static int ov13b10_check_hwcfg(struct device *dev)  {
> >>  	struct v4l2_fwnode_endpoint bus_cfg = { @@ -1407,13 +1490,21 @@
> >> static int ov13b10_probe(struct i2c_client *client)
> >>  	/* Initialize subdev */
> >>  	v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops);
> >>
> >> +	ov13b10_get_pm_resources(&client->dev);
> >> +
> >>  	full_power = acpi_dev_state_d0(&client->dev);
> >>  	if (full_power) {
> >> +		ov13b10_power_on(&client->dev);
> >> +		if (ret) {
> >> +			dev_err(&client->dev, "failed to power on\n");
> >> +			goto error_power_off;
> >
> >return ret;
> 
> After power on, the caller has no idea which PM resource was
> set, so here call power off as a rollback.

Sure, but it needs to be done in ov13b10_power_on() instead. If there's an
error, some of the resources weren't enabled so you can't undo that here.

> 
> >
> >See also below...
> >
> >> +		}
> >> +
> >>  		/* Check module identity */
> >>  		ret = ov13b10_identify_module(ov13b);
> >>  		if (ret) {
> >>  			dev_err(&client->dev, "failed to find sensor: %d\n",
> >ret);
> >> -			return ret;
> >> +			goto error_power_off;
> >>  		}
> >>  	}
> >>
> >> @@ -1422,7 +1513,7 @@ static int ov13b10_probe(struct i2c_client
> >> *client)
> >>
> >>  	ret = ov13b10_init_controls(ov13b);
> >>  	if (ret)
> >> -		return ret;
> >> +		goto error_power_off;
> >>
> >>  	/* Initialize subdev */
> >>  	ov13b->sd.internal_ops = &ov13b10_internal_ops; @@ -1461,6 +1552,8
> >> @@ static int ov13b10_probe(struct i2c_client *client)
> >>  error_handler_free:
> >>  	ov13b10_free_controls(ov13b);
> >>  	dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
> >> +error_power_off:
> >> +	ov13b10_power_off(&client->dev);
> >
> >This may only be called if ov13b10_power_on() was successfully called first.
> >
> >>
> >>  	return ret;
> >>  }
> >> @@ -1479,6 +1572,7 @@ static void ov13b10_remove(struct i2c_client
> >> *client)
> >>
> >>  static const struct dev_pm_ops ov13b10_pm_ops = {
> >>  	SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume)
> >> +	SET_RUNTIME_PM_OPS(ov13b10_power_off, ov13b10_power_on, NULL)
> >>  };
> >>
> >>  #ifdef CONFIG_ACPI
> >>

-- 
Kind regards,

Sakari Ailus



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux