media: i2c: ak7375: Add regulator management
authorYassine Oudjana <y.oudjana@protonmail.com>
Fri, 9 Dec 2022 14:37:41 +0000 (15:37 +0100)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Mon, 6 Feb 2023 07:38:34 +0000 (08:38 +0100)
Make the driver get needed regulators on probe and enable/disable
them on runtime PM callbacks.

Signed-off-by: Yassine Oudjana <y.oudjana@protonmail.com>
Tested-by: Umang Jain <umang.jain@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/i2c/ak7375.c

index 1af9f698eecf89fc581112c2c87a9a8782a7df61..e7cec45bc271af28f291e1a27b0a7b4323fda6e0 100644 (file)
@@ -6,6 +6,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>
 
  */
 #define AK7375_CTRL_STEPS      64
 #define AK7375_CTRL_DELAY_US   1000
+/*
+ * The vcm may take up 10 ms (tDELAY) to power on and start taking
+ * I2C messages. Based on AK7371 datasheet.
+ */
+#define AK7375_POWER_DELAY_US  10000
 
 #define AK7375_REG_POSITION    0x0
 #define AK7375_REG_CONT                0x2
 #define AK7375_MODE_ACTIVE     0x0
 #define AK7375_MODE_STANDBY    0x40
 
+static const char * const ak7375_supply_names[] = {
+       "vdd",
+       "vio",
+};
+
 /* ak7375 device structure */
 struct ak7375_device {
        struct v4l2_ctrl_handler ctrls_vcm;
        struct v4l2_subdev sd;
        struct v4l2_ctrl *focus;
+       struct regulator_bulk_data supplies[ARRAY_SIZE(ak7375_supply_names)];
+
        /* active or standby mode */
        bool active;
 };
@@ -133,12 +146,24 @@ static int ak7375_probe(struct i2c_client *client)
 {
        struct ak7375_device *ak7375_dev;
        int ret;
+       unsigned int i;
 
        ak7375_dev = devm_kzalloc(&client->dev, sizeof(*ak7375_dev),
                                  GFP_KERNEL);
        if (!ak7375_dev)
                return -ENOMEM;
 
+       for (i = 0; i < ARRAY_SIZE(ak7375_supply_names); i++)
+               ak7375_dev->supplies[i].supply = ak7375_supply_names[i];
+
+       ret = devm_regulator_bulk_get(&client->dev,
+                                     ARRAY_SIZE(ak7375_supply_names),
+                                     ak7375_dev->supplies);
+       if (ret) {
+               dev_err_probe(&client->dev, ret, "Failed to get regulators\n");
+               return ret;
+       }
+
        v4l2_i2c_subdev_init(&ak7375_dev->sd, client, &ak7375_ops);
        ak7375_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
        ak7375_dev->sd.internal_ops = &ak7375_int_ops;
@@ -208,6 +233,11 @@ static int __maybe_unused ak7375_vcm_suspend(struct device *dev)
        if (ret)
                dev_err(dev, "%s I2C failure: %d\n", __func__, ret);
 
+       ret = regulator_bulk_disable(ARRAY_SIZE(ak7375_supply_names),
+                                    ak7375_dev->supplies);
+       if (ret)
+               return ret;
+
        ak7375_dev->active = false;
 
        return 0;
@@ -228,6 +258,14 @@ static int __maybe_unused ak7375_vcm_resume(struct device *dev)
        if (ak7375_dev->active)
                return 0;
 
+       ret = regulator_bulk_enable(ARRAY_SIZE(ak7375_supply_names),
+                                   ak7375_dev->supplies);
+       if (ret)
+               return ret;
+
+       /* Wait for vcm to become ready */
+       usleep_range(AK7375_POWER_DELAY_US, AK7375_POWER_DELAY_US + 500);
+
        ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT,
                AK7375_MODE_ACTIVE, 1);
        if (ret) {