File 0026-ov08x40-add-handshake-pin-support.patch of Package intel-ipu6
From: Serin Yeh <serin.yeh@intel.com>
Date: Tue, 19 Mar 2024 17:43:56 +0800
Subject: ov08x40: add handshake pin support
BugLink: https://bugs.launchpad.net/bugs/2060101
Signed-off-by: You-Sheng Yang <vicamo.yang@canonical.com>
---
drivers/media/i2c/ov08x40.c | 114 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 107 insertions(+), 7 deletions(-)
diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c
index 48df077..14bb46b 100644
--- a/drivers/media/i2c/ov08x40.c
+++ b/drivers/media/i2c/ov08x40.c
@@ -6,6 +6,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -1291,6 +1292,15 @@ struct ov08x40 {
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ /* GPIO for reset */
+ struct gpio_desc *reset;
+ /* GPIO for Lattice handshake */
+ struct gpio_desc *handshake;
+ /* regulator */
+ struct regulator *avdd;
+ /* Clock provider */
+ struct clk *img_clk;
+
/* Current mode */
const struct ov08x40_mode *cur_mode;
@@ -1883,6 +1893,51 @@ err_unlock:
return ret;
}
+static int ov08x40_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov08x40 *ov08x40 = to_ov08x40(sd);
+ int ret = 0;
+
+ gpiod_set_value_cansleep(ov08x40->reset, 1);
+ gpiod_set_value_cansleep(ov08x40->handshake, 0);
+ if (ov08x40->avdd)
+ ret = regulator_disable(ov08x40->avdd);
+ clk_disable_unprepare(ov08x40->img_clk);
+
+ return ret;
+}
+
+static int ov08x40_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov08x40 *ov08x40 = to_ov08x40(sd);
+ int ret;
+
+ ret = clk_prepare_enable(ov08x40->img_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable imaging clock: %d", ret);
+ return ret;
+ }
+ if (ov08x40->avdd) {
+ ret = regulator_enable(ov08x40->avdd);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable avdd: %d", ret);
+ clk_disable_unprepare(ov08x40->img_clk);
+ return ret;
+ }
+ }
+ gpiod_set_value_cansleep(ov08x40->handshake, 1);
+ gpiod_set_value_cansleep(ov08x40->reset, 0);
+
+ /* Lattice MIPI aggregator with some version FW needs longer delay
+ after handshake triggered. We set 25ms as a safe value and wait
+ for a stable version FW. */
+ msleep_interruptible(25);
+
+ return ret;
+}
+
/* Verify chip ID */
static int ov08x40_identify_module(struct ov08x40 *ov08x)
{
@@ -2049,6 +2104,41 @@ static void ov08x40_free_controls(struct ov08x40 *ov08x)
mutex_destroy(&ov08x->mutex);
}
+
+static int ov08x40_get_pm_resources(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct ov08x40 *ov08x40 = to_ov08x40(sd);
+ int ret;
+
+ ov08x40->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov08x40->reset))
+ return dev_err_probe(dev, PTR_ERR(ov08x40->reset),
+ "failed to get reset gpio\n");
+
+ ov08x40->handshake = devm_gpiod_get_optional(dev, "handshake",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov08x40->handshake))
+ return dev_err_probe(dev, PTR_ERR(ov08x40->handshake),
+ "failed to get handshake gpio\n");
+
+ ov08x40->img_clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(ov08x40->img_clk))
+ return dev_err_probe(dev, PTR_ERR(ov08x40->img_clk),
+ "failed to get imaging clock\n");
+
+ ov08x40->avdd = devm_regulator_get_optional(dev, "avdd");
+ if (IS_ERR(ov08x40->avdd)) {
+ ret = PTR_ERR(ov08x40->avdd);
+ ov08x40->avdd = NULL;
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "failed to get avdd regulator\n");
+ }
+
+ return 0;
+}
+
static int ov08x40_check_hwcfg(struct device *dev)
{
struct v4l2_fwnode_endpoint bus_cfg = {
@@ -2060,8 +2150,10 @@ static int ov08x40_check_hwcfg(struct device *dev)
int ret;
u32 ext_clk;
- if (!fwnode)
- return -ENXIO;
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ dev_dbg(dev, "fwnode_graph_get_next_endpoint = %d\n", ep);
+ if (!ep)
+ return -EPROBE_DEFER;
ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
&ext_clk);
@@ -2076,10 +2168,6 @@ static int ov08x40_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)
@@ -2141,6 +2229,17 @@ static int ov08x40_probe(struct i2c_client *client)
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
+ dev_err(&client->dev, "start full_power\n");
+ ret = ov08x40_get_pm_resources(&client->dev);
+ if (ret)
+ return ret;
+ ret = ov08x40_power_on(&client->dev);
+ if (ret) {
+ dev_err_probe(&client->dev, ret,
+ "failed to power on\n");
+ goto probe_error_ret;
+ }
+
/* Check module identity */
ret = ov08x40_identify_module(ov08x);
if (ret) {
@@ -2187,6 +2286,8 @@ error_media_entity:
error_handler_free:
ov08x40_free_controls(ov08x);
+probe_error_ret:
+ ov08x40_power_off(&client->dev);
return ret;
}
@@ -2219,7 +2320,6 @@ static struct i2c_driver ov08x40_i2c_driver = {
},
.probe = ov08x40_probe,
.remove = ov08x40_remove,
- .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE,
};
module_i2c_driver(ov08x40_i2c_driver);