summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov13b10.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov13b10.c')
-rw-r--r--drivers/media/i2c/ov13b10.c129
1 files changed, 115 insertions, 14 deletions
diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c
index 6110fb1e6bc6..dbc642c5995b 100644
--- a/drivers/media/i2c/ov13b10.c
+++ b/drivers/media/i2c/ov13b10.c
@@ -2,6 +2,9 @@
// Copyright (c) 2021 Intel Corporation.
#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -573,6 +576,11 @@ struct ov13b10 {
struct media_pad pad;
struct v4l2_ctrl_handler ctrl_handler;
+
+ struct clk *img_clk;
+ struct regulator *avdd;
+ struct gpio_desc *reset;
+
/* V4L2 Controls */
struct v4l2_ctrl *link_freq;
struct v4l2_ctrl *pixel_rate;
@@ -1051,6 +1059,49 @@ static int ov13b10_identify_module(struct ov13b10 *ov13b)
return 0;
}
+static int ov13b10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov13b10 *ov13b10 = to_ov13b10(sd);
+
+ gpiod_set_value_cansleep(ov13b10->reset, 1);
+
+ if (ov13b10->avdd)
+ regulator_disable(ov13b10->avdd);
+
+ clk_disable_unprepare(ov13b10->img_clk);
+
+ return 0;
+}
+
+static int ov13b10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov13b10 *ov13b10 = to_ov13b10(sd);
+ int ret;
+
+ ret = clk_prepare_enable(ov13b10->img_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable imaging clock: %d", ret);
+ return ret;
+ }
+
+ if (ov13b10->avdd) {
+ ret = regulator_enable(ov13b10->avdd);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable avdd: %d", ret);
+ clk_disable_unprepare(ov13b10->img_clk);
+ return ret;
+ }
+ }
+
+ gpiod_set_value_cansleep(ov13b10->reset, 0);
+ /* 5ms to wait ready after XSHUTDN assert */
+ usleep_range(5000, 5500);
+
+ return 0;
+}
+
static int ov13b10_start_streaming(struct ov13b10 *ov13b)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd);
@@ -1145,7 +1196,7 @@ err_unlock:
return ret;
}
-static int __maybe_unused ov13b10_suspend(struct device *dev)
+static int ov13b10_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov13b10 *ov13b = to_ov13b10(sd);
@@ -1153,26 +1204,35 @@ static int __maybe_unused ov13b10_suspend(struct device *dev)
if (ov13b->streaming)
ov13b10_stop_streaming(ov13b);
+ ov13b10_power_off(dev);
+
return 0;
}
-static int __maybe_unused ov13b10_resume(struct device *dev)
+static int ov13b10_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov13b10 *ov13b = to_ov13b10(sd);
int ret;
+ ret = ov13b10_power_on(dev);
+ if (ret)
+ goto pm_fail;
+
if (ov13b->streaming) {
ret = ov13b10_start_streaming(ov13b);
if (ret)
- goto error;
+ goto stop_streaming;
}
return 0;
-error:
+stop_streaming:
ov13b10_stop_streaming(ov13b);
+ ov13b10_power_off(dev);
+pm_fail:
ov13b->streaming = false;
+
return ret;
}
@@ -1317,6 +1377,34 @@ static void ov13b10_free_controls(struct ov13b10 *ov13b)
mutex_destroy(&ov13b->mutex);
}
+static int ov13b10_get_pm_resources(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov13b10 *ov13b = to_ov13b10(sd);
+ int ret;
+
+ ov13b->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov13b->reset))
+ return dev_err_probe(dev, PTR_ERR(ov13b->reset),
+ "failed to get reset gpio\n");
+
+ ov13b->img_clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ov13b->img_clk))
+ return dev_err_probe(dev, PTR_ERR(ov13b->img_clk),
+ "failed to get imaging clock\n");
+
+ ov13b->avdd = devm_regulator_get_optional(dev, "avdd");
+ if (IS_ERR(ov13b->avdd)) {
+ ret = PTR_ERR(ov13b->avdd);
+ ov13b->avdd = NULL;
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "failed to get avdd regulator\n");
+ }
+
+ return 0;
+}
+
static int ov13b10_check_hwcfg(struct device *dev)
{
struct v4l2_fwnode_endpoint bus_cfg = {
@@ -1331,6 +1419,10 @@ static int ov13b10_check_hwcfg(struct device *dev)
if (!fwnode)
return -ENXIO;
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep)
+ return -EPROBE_DEFER;
+
ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
&ext_clk);
if (ret) {
@@ -1344,10 +1436,6 @@ static int ov13b10_check_hwcfg(struct device *dev)
return -EINVAL;
}
- ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
- if (!ep)
- return -ENXIO;
-
ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
fwnode_handle_put(ep);
if (ret)
@@ -1407,13 +1495,23 @@ static int ov13b10_probe(struct i2c_client *client)
/* Initialize subdev */
v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops);
+ ret = ov13b10_get_pm_resources(&client->dev);
+ if (ret)
+ return ret;
+
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
+ ov13b10_power_on(&client->dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to power on\n");
+ return ret;
+ }
+
/* Check module identity */
ret = ov13b10_identify_module(ov13b);
if (ret) {
dev_err(&client->dev, "failed to find sensor: %d\n", ret);
- return ret;
+ goto error_power_off;
}
}
@@ -1422,7 +1520,7 @@ static int ov13b10_probe(struct i2c_client *client)
ret = ov13b10_init_controls(ov13b);
if (ret)
- return ret;
+ goto error_power_off;
/* Initialize subdev */
ov13b->sd.internal_ops = &ov13b10_internal_ops;
@@ -1462,6 +1560,9 @@ error_handler_free:
ov13b10_free_controls(ov13b);
dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+error_power_off:
+ ov13b10_power_off(&client->dev);
+
return ret;
}
@@ -1477,13 +1578,13 @@ static void ov13b10_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev);
}
-static const struct dev_pm_ops ov13b10_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume)
-};
+static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend,
+ ov13b10_resume, NULL);
#ifdef CONFIG_ACPI
static const struct acpi_device_id ov13b10_acpi_ids[] = {
{"OVTIDB10"},
+ {"OVTI13B1"},
{ /* sentinel */ }
};
@@ -1493,7 +1594,7 @@ MODULE_DEVICE_TABLE(acpi, ov13b10_acpi_ids);
static struct i2c_driver ov13b10_i2c_driver = {
.driver = {
.name = "ov13b10",
- .pm = &ov13b10_pm_ops,
+ .pm = pm_ptr(&ov13b10_pm_ops),
.acpi_match_table = ACPI_PTR(ov13b10_acpi_ids),
},
.probe = ov13b10_probe,