Skip to content

Commit a95ffde

Browse files
Vladimir ZapolskiyHans Verkuil
authored andcommitted
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>
1 parent 1c004ef commit a95ffde

1 file changed

Lines changed: 40 additions & 6 deletions

File tree

drivers/media/i2c/og01a1b.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <asm/unaligned.h>
55
#include <linux/acpi.h>
6+
#include <linux/clk.h>
67
#include <linux/delay.h>
78
#include <linux/i2c.h>
89
#include <linux/module.h>
@@ -418,6 +419,8 @@ static const struct og01a1b_mode supported_modes[] = {
418419
};
419420

420421
struct og01a1b {
422+
struct clk *xvclk;
423+
421424
struct v4l2_subdev sd;
422425
struct media_pad pad;
423426
struct v4l2_ctrl_handler ctrl_handler;
@@ -898,8 +901,10 @@ static int og01a1b_identify_module(struct og01a1b *og01a1b)
898901
return 0;
899902
}
900903

901-
static int og01a1b_check_hwcfg(struct device *dev)
904+
static int og01a1b_check_hwcfg(struct og01a1b *og01a1b)
902905
{
906+
struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd);
907+
struct device *dev = &client->dev;
903908
struct fwnode_handle *ep;
904909
struct fwnode_handle *fwnode = dev_fwnode(dev);
905910
struct v4l2_fwnode_endpoint bus_cfg = {
@@ -913,10 +918,13 @@ static int og01a1b_check_hwcfg(struct device *dev)
913918
return -ENXIO;
914919

915920
ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
916-
917921
if (ret) {
918-
dev_err(dev, "can't get clock frequency");
919-
return ret;
922+
if (!og01a1b->xvclk) {
923+
dev_err(dev, "can't get clock frequency");
924+
return ret;
925+
}
926+
927+
mclk = clk_get_rate(og01a1b->xvclk);
920928
}
921929

922930
if (mclk != OG01A1B_MCLK) {
@@ -970,13 +978,32 @@ static int og01a1b_check_hwcfg(struct device *dev)
970978
/* Power/clock management functions */
971979
static int og01a1b_power_on(struct device *dev)
972980
{
973-
/* Device is already turned on by i2c-core with ACPI domain PM. */
981+
unsigned long delay = DIV_ROUND_UP(8192UL * USEC_PER_SEC, OG01A1B_MCLK);
982+
struct v4l2_subdev *sd = dev_get_drvdata(dev);
983+
struct og01a1b *og01a1b = to_og01a1b(sd);
984+
int ret;
985+
986+
ret = clk_prepare_enable(og01a1b->xvclk);
987+
if (ret)
988+
return ret;
989+
990+
if (og01a1b->xvclk)
991+
usleep_range(delay, 2 * delay);
974992

975993
return 0;
976994
}
977995

978996
static int og01a1b_power_off(struct device *dev)
979997
{
998+
unsigned long delay = DIV_ROUND_UP(512 * USEC_PER_SEC, OG01A1B_MCLK);
999+
struct v4l2_subdev *sd = dev_get_drvdata(dev);
1000+
struct og01a1b *og01a1b = to_og01a1b(sd);
1001+
1002+
if (og01a1b->xvclk)
1003+
usleep_range(delay, 2 * delay);
1004+
1005+
clk_disable_unprepare(og01a1b->xvclk);
1006+
9801007
return 0;
9811008
}
9821009

@@ -1003,7 +1030,14 @@ static int og01a1b_probe(struct i2c_client *client)
10031030

10041031
v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops);
10051032

1006-
ret = og01a1b_check_hwcfg(&client->dev);
1033+
og01a1b->xvclk = devm_clk_get_optional(&client->dev, NULL);
1034+
if (IS_ERR(og01a1b->xvclk)) {
1035+
ret = PTR_ERR(og01a1b->xvclk);
1036+
dev_err(&client->dev, "failed to get xvclk clock: %d\n", ret);
1037+
return ret;
1038+
}
1039+
1040+
ret = og01a1b_check_hwcfg(og01a1b);
10071041
if (ret) {
10081042
dev_err(&client->dev, "failed to check HW configuration: %d",
10091043
ret);

0 commit comments

Comments
 (0)