mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
media: i2c: og01a1b: Add support of xvclk supply clock in power management
The OmniVision OG01A1B camera sensor has an xvclk supply clock, which could be described and then explicitly controlled on OF platforms. Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> [Sakari Ailus: Use UL specifier for power-up delay cycle value.] Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
This commit is contained in:
parent
1c004ef7ff
commit
a95ffde287
@ -3,6 +3,7 @@
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
@ -418,6 +419,8 @@ static const struct og01a1b_mode supported_modes[] = {
|
||||
};
|
||||
|
||||
struct og01a1b {
|
||||
struct clk *xvclk;
|
||||
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pad;
|
||||
struct v4l2_ctrl_handler ctrl_handler;
|
||||
@ -898,8 +901,10 @@ static int og01a1b_identify_module(struct og01a1b *og01a1b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int og01a1b_check_hwcfg(struct device *dev)
|
||||
static int og01a1b_check_hwcfg(struct og01a1b *og01a1b)
|
||||
{
|
||||
struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd);
|
||||
struct device *dev = &client->dev;
|
||||
struct fwnode_handle *ep;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct v4l2_fwnode_endpoint bus_cfg = {
|
||||
@ -913,10 +918,13 @@ static int og01a1b_check_hwcfg(struct device *dev)
|
||||
return -ENXIO;
|
||||
|
||||
ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "can't get clock frequency");
|
||||
return ret;
|
||||
if (!og01a1b->xvclk) {
|
||||
dev_err(dev, "can't get clock frequency");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mclk = clk_get_rate(og01a1b->xvclk);
|
||||
}
|
||||
|
||||
if (mclk != OG01A1B_MCLK) {
|
||||
@ -970,13 +978,32 @@ check_hwcfg_error:
|
||||
/* Power/clock management functions */
|
||||
static int og01a1b_power_on(struct device *dev)
|
||||
{
|
||||
/* Device is already turned on by i2c-core with ACPI domain PM. */
|
||||
unsigned long delay = DIV_ROUND_UP(8192UL * USEC_PER_SEC, OG01A1B_MCLK);
|
||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||
struct og01a1b *og01a1b = to_og01a1b(sd);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(og01a1b->xvclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (og01a1b->xvclk)
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int og01a1b_power_off(struct device *dev)
|
||||
{
|
||||
unsigned long delay = DIV_ROUND_UP(512 * USEC_PER_SEC, OG01A1B_MCLK);
|
||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||
struct og01a1b *og01a1b = to_og01a1b(sd);
|
||||
|
||||
if (og01a1b->xvclk)
|
||||
usleep_range(delay, 2 * delay);
|
||||
|
||||
clk_disable_unprepare(og01a1b->xvclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1003,7 +1030,14 @@ static int og01a1b_probe(struct i2c_client *client)
|
||||
|
||||
v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops);
|
||||
|
||||
ret = og01a1b_check_hwcfg(&client->dev);
|
||||
og01a1b->xvclk = devm_clk_get_optional(&client->dev, NULL);
|
||||
if (IS_ERR(og01a1b->xvclk)) {
|
||||
ret = PTR_ERR(og01a1b->xvclk);
|
||||
dev_err(&client->dev, "failed to get xvclk clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = og01a1b_check_hwcfg(og01a1b);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to check HW configuration: %d",
|
||||
ret);
|
||||
|
Loading…
Reference in New Issue
Block a user