media: i2c: ov5670: Add runtime_pm operations
authorJacopo Mondi <jacopo.mondi@ideasonboard.com>
Thu, 26 Jan 2023 16:59:06 +0000 (17:59 +0100)
committerMauro Carvalho Chehab <mchehab@kernel.org>
Mon, 6 Feb 2023 07:49:04 +0000 (08:49 +0100)
Implement the runtime resume and suspend routines and install them as
runtime_pm handlers.

While at it rework the probe() sequence in order to enable runtime_pm
before registering the async subdevice.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Luca Weiss <luca@z3ntu.xyz>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
drivers/media/i2c/ov5670.c

index 0290f33..47fedbe 100644 (file)
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (c) 2017 Intel Corporation.
 
+#include <asm/unaligned.h>
 #include <linux/acpi.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/mod_devicetable.h>
@@ -2429,6 +2431,49 @@ unlock_and_return:
        return ret;
 }
 
+static int __maybe_unused ov5670_runtime_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct ov5670 *ov5670 = to_ov5670(sd);
+       unsigned long delay_us;
+       int ret;
+
+       ret = clk_prepare_enable(ov5670->xvclk);
+       if (ret)
+               return ret;
+
+       ret = regulator_bulk_enable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+       if (ret) {
+               clk_disable_unprepare(ov5670->xvclk);
+               return ret;
+       }
+
+       gpiod_set_value_cansleep(ov5670->pwdn_gpio, 0);
+       gpiod_set_value_cansleep(ov5670->reset_gpio, 0);
+
+       /* 8192 * 2 clock pulses before the first SCCB transaction. */
+       delay_us = DIV_ROUND_UP(8192 * 2 * 1000,
+                               DIV_ROUND_UP(OV5670_XVCLK_FREQ, 1000));
+       fsleep(delay_us);
+
+       return 0;
+}
+
+static int __maybe_unused ov5670_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct ov5670 *ov5670 = to_ov5670(sd);
+
+       gpiod_set_value_cansleep(ov5670->reset_gpio, 1);
+       gpiod_set_value_cansleep(ov5670->pwdn_gpio, 1);
+       regulator_bulk_disable(OV5670_NUM_SUPPLIES, ov5670->supplies);
+       clk_disable_unprepare(ov5670->xvclk);
+
+       return 0;
+}
+
 static int __maybe_unused ov5670_suspend(struct device *dev)
 {
        struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -2570,11 +2615,17 @@ static int ov5670_probe(struct i2c_client *client)
 
        full_power = acpi_dev_state_d0(&client->dev);
        if (full_power) {
+               ret = ov5670_runtime_resume(&client->dev);
+               if (ret) {
+                       err_msg = "Power up failed";
+                       goto error_print;
+               }
+
                /* Check module identity */
                ret = ov5670_identify_module(ov5670);
                if (ret) {
                        err_msg = "ov5670_identify_module() error";
-                       goto error_print;
+                       goto error_power_off;
                }
        }
 
@@ -2603,24 +2654,27 @@ static int ov5670_probe(struct i2c_client *client)
                goto error_handler_free;
        }
 
-       /* Async register for subdev */
-       ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
-       if (ret < 0) {
-               err_msg = "v4l2_async_register_subdev() error";
-               goto error_entity_cleanup;
-       }
-
        ov5670->streaming = false;
 
        /* Set the device's state to active if it's in D0 state. */
        if (full_power)
                pm_runtime_set_active(&client->dev);
        pm_runtime_enable(&client->dev);
+
+       /* Async register for subdev */
+       ret = v4l2_async_register_subdev_sensor(&ov5670->sd);
+       if (ret < 0) {
+               err_msg = "v4l2_async_register_subdev() error";
+               goto error_pm_disable;
+       }
+
        pm_runtime_idle(&client->dev);
 
        return 0;
 
-error_entity_cleanup:
+error_pm_disable:
+       pm_runtime_disable(&client->dev);
+
        media_entity_cleanup(&ov5670->sd.entity);
 
 error_handler_free:
@@ -2629,6 +2683,10 @@ error_handler_free:
 error_mutex_destroy:
        mutex_destroy(&ov5670->mutex);
 
+error_power_off:
+       if (full_power)
+               ov5670_runtime_suspend(&client->dev);
+
 error_print:
        dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret);
 
@@ -2646,10 +2704,12 @@ static void ov5670_remove(struct i2c_client *client)
        mutex_destroy(&ov5670->mutex);
 
        pm_runtime_disable(&client->dev);
+       ov5670_runtime_suspend(&client->dev);
 }
 
 static const struct dev_pm_ops ov5670_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume)
+       SET_RUNTIME_PM_OPS(ov5670_runtime_suspend, ov5670_runtime_resume, NULL)
 };
 
 #ifdef CONFIG_ACPI