diff mbox series

[v2,6/6] media: i2c: og01a1b: Add management of optional sensor supply lines

Message ID 20240813102035.1763559-7-vladimir.zapolskiy@linaro.org (mailing list archive)
State New
Headers show
Series media: i2c: og01a1b: Add OF support to OmniVision OG01A1B | expand

Commit Message

Vladimir Zapolskiy Aug. 13, 2024, 10:20 a.m. UTC
Omnivision OG01A1B camera sensor is supplied by tree power rails,
if supplies are present as device properties, include them into
sensor power up sequence.

Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
---
 drivers/media/i2c/og01a1b.c | 86 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 85 insertions(+), 1 deletion(-)

Comments

Kieran Bingham Aug. 13, 2024, 12:53 p.m. UTC | #1
Quoting Vladimir Zapolskiy (2024-08-13 11:20:35)
> Omnivision OG01A1B camera sensor is supplied by tree power rails,

three?

> if supplies are present as device properties, include them into
> sensor power up sequence.
> 
> Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
> ---
>  drivers/media/i2c/og01a1b.c | 86 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 85 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c
> index 90a68201f43f..0150fdd2f424 100644
> --- a/drivers/media/i2c/og01a1b.c
> +++ b/drivers/media/i2c/og01a1b.c
> @@ -9,6 +9,7 @@
>  #include <linux/i2c.h>
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-fwnode.h>
> @@ -422,6 +423,9 @@ static const struct og01a1b_mode supported_modes[] = {
>  struct og01a1b {
>         struct clk *xvclk;
>         struct gpio_desc *reset_gpio;
> +       struct regulator *avdd;
> +       struct regulator *dovdd;
> +       struct regulator *dvdd;
>  
>         struct v4l2_subdev sd;
>         struct media_pad pad;
> @@ -982,11 +986,46 @@ static int og01a1b_power_on(struct device *dev)
>  {
>         struct v4l2_subdev *sd = dev_get_drvdata(dev);
>         struct og01a1b *og01a1b = to_og01a1b(sd);
> +       int ret;
> +
> +       if (og01a1b->avdd) {
> +               ret = regulator_enable(og01a1b->avdd);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       if (og01a1b->dovdd) {
> +               ret = regulator_enable(og01a1b->dovdd);
> +               if (ret)
> +                       goto avdd_disable;
> +       }
> +
> +       if (og01a1b->dvdd) {
> +               ret = regulator_enable(og01a1b->dvdd);
> +               if (ret)
> +                       goto dovdd_disable;
> +       }

Perhaps regulator_bulk_enable()/regulator_bulk_disable() would be
suitable here to reduce lots of repetitive code and error handling?

>  
>         gpiod_set_value_cansleep(og01a1b->reset_gpio, 0);
>         usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
>  
> -       return clk_prepare_enable(og01a1b->xvclk);
> +       ret = clk_prepare_enable(og01a1b->xvclk);
> +       if (ret)
> +               goto dvdd_disable;
> +
> +       return 0;
> +
> +dvdd_disable:
> +       if (og01a1b->dvdd)
> +               regulator_disable(og01a1b->dvdd);
> +dovdd_disable:
> +       if (og01a1b->dovdd)
> +               regulator_disable(og01a1b->dovdd);
> +avdd_disable:
> +       if (og01a1b->avdd)
> +               regulator_disable(og01a1b->avdd);
> +
> +       return ret;
>  }
>  
>  static int og01a1b_power_off(struct device *dev)
> @@ -998,6 +1037,15 @@ static int og01a1b_power_off(struct device *dev)
>  
>         gpiod_set_value_cansleep(og01a1b->reset_gpio, 1);
>  
> +       if (og01a1b->dvdd)
> +               regulator_disable(og01a1b->dvdd);
> +
> +       if (og01a1b->dovdd)
> +               regulator_disable(og01a1b->dovdd);
> +
> +       if (og01a1b->avdd)
> +               regulator_disable(og01a1b->avdd);
> +
>         return 0;
>  }
>  
> @@ -1045,6 +1093,42 @@ static int og01a1b_probe(struct i2c_client *client)
>                 return PTR_ERR(og01a1b->reset_gpio);
>         }
>  
> +       og01a1b->avdd = devm_regulator_get_optional(&client->dev, "avdd");
> +       if (IS_ERR(og01a1b->avdd)) {
> +               ret = PTR_ERR(og01a1b->avdd);
> +               if (ret != -ENODEV) {
> +                       dev_err_probe(&client->dev, ret,
> +                                     "Failed to get 'avdd' regulator\n");
> +                       return ret;
> +               }
> +
> +               og01a1b->avdd = NULL;
> +       }
> +
> +       og01a1b->dovdd = devm_regulator_get_optional(&client->dev, "dovdd");
> +       if (IS_ERR(og01a1b->dovdd)) {
> +               ret = PTR_ERR(og01a1b->dovdd);
> +               if (ret != -ENODEV) {
> +                       dev_err_probe(&client->dev, ret,
> +                                     "Failed to get 'dovdd' regulator\n");
> +                       return ret;
> +               }
> +
> +               og01a1b->dovdd = NULL;
> +       }
> +
> +       og01a1b->dvdd = devm_regulator_get_optional(&client->dev, "dvdd");
> +       if (IS_ERR(og01a1b->dvdd)) {
> +               ret = PTR_ERR(og01a1b->dvdd);
> +               if (ret != -ENODEV) {
> +                       dev_err_probe(&client->dev, ret,
> +                                     "Failed to get 'dvdd' regulator\n");
> +                       return ret;
> +               }
> +
> +               og01a1b->dvdd = NULL;
> +       }
> +
>         /* The sensor must be powered on to read the CHIP_ID register */
>         ret = og01a1b_power_on(&client->dev);
>         if (ret)
> -- 
> 2.45.2
>
Vladimir Zapolskiy Aug. 13, 2024, 1:35 p.m. UTC | #2
Hi Kieran,

On 8/13/24 15:53, Kieran Bingham wrote:
> Quoting Vladimir Zapolskiy (2024-08-13 11:20:35)
>> Omnivision OG01A1B camera sensor is supplied by tree power rails,
> 
> three?

that's it, thanks for catching the typo.

>> if supplies are present as device properties, include them into
>> sensor power up sequence.
>>
>> Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
>> ---
>>   drivers/media/i2c/og01a1b.c | 86 ++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 85 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c
>> index 90a68201f43f..0150fdd2f424 100644
>> --- a/drivers/media/i2c/og01a1b.c
>> +++ b/drivers/media/i2c/og01a1b.c
>> @@ -9,6 +9,7 @@
>>   #include <linux/i2c.h>
>>   #include <linux/module.h>
>>   #include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>>   #include <media/v4l2-ctrls.h>
>>   #include <media/v4l2-device.h>
>>   #include <media/v4l2-fwnode.h>
>> @@ -422,6 +423,9 @@ static const struct og01a1b_mode supported_modes[] = {
>>   struct og01a1b {
>>          struct clk *xvclk;
>>          struct gpio_desc *reset_gpio;
>> +       struct regulator *avdd;
>> +       struct regulator *dovdd;
>> +       struct regulator *dvdd;
>>   
>>          struct v4l2_subdev sd;
>>          struct media_pad pad;
>> @@ -982,11 +986,46 @@ static int og01a1b_power_on(struct device *dev)
>>   {
>>          struct v4l2_subdev *sd = dev_get_drvdata(dev);
>>          struct og01a1b *og01a1b = to_og01a1b(sd);
>> +       int ret;
>> +
>> +       if (og01a1b->avdd) {
>> +               ret = regulator_enable(og01a1b->avdd);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       if (og01a1b->dovdd) {
>> +               ret = regulator_enable(og01a1b->dovdd);
>> +               if (ret)
>> +                       goto avdd_disable;
>> +       }
>> +
>> +       if (og01a1b->dvdd) {
>> +               ret = regulator_enable(og01a1b->dvdd);
>> +               if (ret)
>> +                       goto dovdd_disable;
>> +       }
> 
> Perhaps regulator_bulk_enable()/regulator_bulk_disable() would be
> suitable here to reduce lots of repetitive code and error handling?

It won't be possible to support optional regulators with bulk operations,
thus likely it's not an option.

Note, that in ACPI case there are no regulators at all, at least this
should be functionally preserved in the driver.

It might be an option to define all supply regulators as strictly required
for the OF case, but since there is actually no need for it, I'm reluctant
to push the limits into the device tree node schema.

So, from my opinion the left option is the published one.

--
Best wishes,
Vladimir
diff mbox series

Patch

diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c
index 90a68201f43f..0150fdd2f424 100644
--- a/drivers/media/i2c/og01a1b.c
+++ b/drivers/media/i2c/og01a1b.c
@@ -9,6 +9,7 @@ 
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-fwnode.h>
@@ -422,6 +423,9 @@  static const struct og01a1b_mode supported_modes[] = {
 struct og01a1b {
 	struct clk *xvclk;
 	struct gpio_desc *reset_gpio;
+	struct regulator *avdd;
+	struct regulator *dovdd;
+	struct regulator *dvdd;
 
 	struct v4l2_subdev sd;
 	struct media_pad pad;
@@ -982,11 +986,46 @@  static int og01a1b_power_on(struct device *dev)
 {
 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
 	struct og01a1b *og01a1b = to_og01a1b(sd);
+	int ret;
+
+	if (og01a1b->avdd) {
+		ret = regulator_enable(og01a1b->avdd);
+		if (ret)
+			return ret;
+	}
+
+	if (og01a1b->dovdd) {
+		ret = regulator_enable(og01a1b->dovdd);
+		if (ret)
+			goto avdd_disable;
+	}
+
+	if (og01a1b->dvdd) {
+		ret = regulator_enable(og01a1b->dvdd);
+		if (ret)
+			goto dovdd_disable;
+	}
 
 	gpiod_set_value_cansleep(og01a1b->reset_gpio, 0);
 	usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
 
-	return clk_prepare_enable(og01a1b->xvclk);
+	ret = clk_prepare_enable(og01a1b->xvclk);
+	if (ret)
+		goto dvdd_disable;
+
+	return 0;
+
+dvdd_disable:
+	if (og01a1b->dvdd)
+		regulator_disable(og01a1b->dvdd);
+dovdd_disable:
+	if (og01a1b->dovdd)
+		regulator_disable(og01a1b->dovdd);
+avdd_disable:
+	if (og01a1b->avdd)
+		regulator_disable(og01a1b->avdd);
+
+	return ret;
 }
 
 static int og01a1b_power_off(struct device *dev)
@@ -998,6 +1037,15 @@  static int og01a1b_power_off(struct device *dev)
 
 	gpiod_set_value_cansleep(og01a1b->reset_gpio, 1);
 
+	if (og01a1b->dvdd)
+		regulator_disable(og01a1b->dvdd);
+
+	if (og01a1b->dovdd)
+		regulator_disable(og01a1b->dovdd);
+
+	if (og01a1b->avdd)
+		regulator_disable(og01a1b->avdd);
+
 	return 0;
 }
 
@@ -1045,6 +1093,42 @@  static int og01a1b_probe(struct i2c_client *client)
 		return PTR_ERR(og01a1b->reset_gpio);
 	}
 
+	og01a1b->avdd = devm_regulator_get_optional(&client->dev, "avdd");
+	if (IS_ERR(og01a1b->avdd)) {
+		ret = PTR_ERR(og01a1b->avdd);
+		if (ret != -ENODEV) {
+			dev_err_probe(&client->dev, ret,
+				      "Failed to get 'avdd' regulator\n");
+			return ret;
+		}
+
+		og01a1b->avdd = NULL;
+	}
+
+	og01a1b->dovdd = devm_regulator_get_optional(&client->dev, "dovdd");
+	if (IS_ERR(og01a1b->dovdd)) {
+		ret = PTR_ERR(og01a1b->dovdd);
+		if (ret != -ENODEV) {
+			dev_err_probe(&client->dev, ret,
+				      "Failed to get 'dovdd' regulator\n");
+			return ret;
+		}
+
+		og01a1b->dovdd = NULL;
+	}
+
+	og01a1b->dvdd = devm_regulator_get_optional(&client->dev, "dvdd");
+	if (IS_ERR(og01a1b->dvdd)) {
+		ret = PTR_ERR(og01a1b->dvdd);
+		if (ret != -ENODEV) {
+			dev_err_probe(&client->dev, ret,
+				      "Failed to get 'dvdd' regulator\n");
+			return ret;
+		}
+
+		og01a1b->dvdd = NULL;
+	}
+
 	/* The sensor must be powered on to read the CHIP_ID register */
 	ret = og01a1b_power_on(&client->dev);
 	if (ret)