[3/3] media: i2c: og01a1b: Add support of an input system clock
Commit Message
To be properly configured at probe time the sensor module contains
a required reference to xvclk clock on OF platforms, which is now
enabled on probe and checked for a supported frequency rate.
Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
---
drivers/media/i2c/og01a1b.c | 43 +++++++++++++++++++++++++++++--------
1 file changed, 34 insertions(+), 9 deletions(-)
@@ -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,9 @@ static const struct og01a1b_mode supported_modes[] = {
};
struct og01a1b {
+ struct device *dev;
+ struct clk *xvclk;
+
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
@@ -898,8 +902,9 @@ 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 device *dev = og01a1b->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) {
@@ -977,6 +985,7 @@ static void og01a1b_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(&client->dev);
mutex_destroy(&og01a1b->mutex);
+ clk_disable_unprepare(og01a1b->xvclk);
}
static int og01a1b_probe(struct i2c_client *client)
@@ -984,22 +993,35 @@ static int og01a1b_probe(struct i2c_client *client)
struct og01a1b *og01a1b;
int ret;
- ret = og01a1b_check_hwcfg(&client->dev);
+ og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL);
+ if (!og01a1b)
+ return -ENOMEM;
+
+ og01a1b->dev = &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 clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = og01a1b_check_hwcfg(og01a1b);
if (ret) {
dev_err(&client->dev, "failed to check HW configuration: %d",
ret);
return ret;
}
- og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL);
- if (!og01a1b)
- return -ENOMEM;
+ ret = clk_prepare_enable(og01a1b->xvclk);
+ if (ret)
+ return ret;
v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops);
ret = og01a1b_identify_module(og01a1b);
if (ret) {
dev_err(&client->dev, "failed to find sensor: %d", ret);
- return ret;
+ goto disable_clk;
}
mutex_init(&og01a1b->mutex);
@@ -1045,6 +1067,9 @@ static int og01a1b_probe(struct i2c_client *client)
v4l2_ctrl_handler_free(og01a1b->sd.ctrl_handler);
mutex_destroy(&og01a1b->mutex);
+disable_clk:
+ clk_disable_unprepare(og01a1b->xvclk);
+
return ret;
}