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

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
Loading
Loading
Loading
Loading
+69 −9
Original line number Diff line number Diff line
// 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 @@ static int ov5670_set_stream(struct v4l2_subdev *sd, int enable)
	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 @@ static int ov5670_probe(struct i2c_client *client)
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