Commit 62ab1e32 authored by Jacopo Mondi's avatar Jacopo Mondi Committed by Mauro Carvalho Chehab

media: i2c: ov5670: Add runtime_pm operations

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: default avatarJacopo Mondi <jacopo.mondi@ideasonboard.com>
Reviewed-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: default avatarLuca Weiss <luca@z3ntu.xyz>
Signed-off-by: default avatarSakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 0a844ab7
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017 Intel Corporation. // Copyright (c) 2017 Intel Corporation.
#include <asm/unaligned.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
...@@ -2429,6 +2431,49 @@ static int ov5670_set_stream(struct v4l2_subdev *sd, int enable) ...@@ -2429,6 +2431,49 @@ static int ov5670_set_stream(struct v4l2_subdev *sd, int enable)
return ret; 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) static int __maybe_unused ov5670_suspend(struct device *dev)
{ {
struct v4l2_subdev *sd = dev_get_drvdata(dev); struct v4l2_subdev *sd = dev_get_drvdata(dev);
...@@ -2570,11 +2615,17 @@ static int ov5670_probe(struct i2c_client *client) ...@@ -2570,11 +2615,17 @@ static int ov5670_probe(struct i2c_client *client)
full_power = acpi_dev_state_d0(&client->dev); full_power = acpi_dev_state_d0(&client->dev);
if (full_power) { if (full_power) {
ret = ov5670_runtime_resume(&client->dev);
if (ret) {
err_msg = "Power up failed";
goto error_print;
}
/* Check module identity */ /* Check module identity */
ret = ov5670_identify_module(ov5670); ret = ov5670_identify_module(ov5670);
if (ret) { if (ret) {
err_msg = "ov5670_identify_module() error"; 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) ...@@ -2603,24 +2654,27 @@ static int ov5670_probe(struct i2c_client *client)
goto error_handler_free; 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; ov5670->streaming = false;
/* Set the device's state to active if it's in D0 state. */ /* Set the device's state to active if it's in D0 state. */
if (full_power) if (full_power)
pm_runtime_set_active(&client->dev); pm_runtime_set_active(&client->dev);
pm_runtime_enable(&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); pm_runtime_idle(&client->dev);
return 0; return 0;
error_entity_cleanup: error_pm_disable:
pm_runtime_disable(&client->dev);
media_entity_cleanup(&ov5670->sd.entity); media_entity_cleanup(&ov5670->sd.entity);
error_handler_free: error_handler_free:
...@@ -2629,6 +2683,10 @@ static int ov5670_probe(struct i2c_client *client) ...@@ -2629,6 +2683,10 @@ static int ov5670_probe(struct i2c_client *client)
error_mutex_destroy: error_mutex_destroy:
mutex_destroy(&ov5670->mutex); mutex_destroy(&ov5670->mutex);
error_power_off:
if (full_power)
ov5670_runtime_suspend(&client->dev);
error_print: error_print:
dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret); dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret);
...@@ -2646,10 +2704,12 @@ static void ov5670_remove(struct i2c_client *client) ...@@ -2646,10 +2704,12 @@ static void ov5670_remove(struct i2c_client *client)
mutex_destroy(&ov5670->mutex); mutex_destroy(&ov5670->mutex);
pm_runtime_disable(&client->dev); pm_runtime_disable(&client->dev);
ov5670_runtime_suspend(&client->dev);
} }
static const struct dev_pm_ops ov5670_pm_ops = { static const struct dev_pm_ops ov5670_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume) SET_SYSTEM_SLEEP_PM_OPS(ov5670_suspend, ov5670_resume)
SET_RUNTIME_PM_OPS(ov5670_runtime_suspend, ov5670_runtime_resume, NULL)
}; };
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment