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);
openSUSE Build Service is sponsored by