From 01732af7d1a36410ecc9309658ffcce21e06bde9 Mon Sep 17 00:00:00 2001
From: Omer Faruk Edemen <ofedemen@lectrontech.com>
Date: Mon, 8 Sep 2025 10:06:23 +0300
Subject: [PATCH] media: i2c: ov9282: Add external FSIN trigger snapshot mode

This patch adds support for external FSIN-triggered snapshot mode
to the OmniVision OV9282 sensor driver. It enables frame capture
synchronized with an external hardware trigger signal.

Signed-off-by: Omer Faruk Edemen <ofedemen@lectrontech.com>
---
 drivers/media/i2c/ov9282.c | 85 ++++++++++++++++++++++++++++++++++----
 1 file changed, 78 insertions(+), 7 deletions(-)

--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -74,6 +74,14 @@
 #define OV9282_REG_MIPI_CTRL00	0x4800
 #define OV9282_GATED_CLOCK	BIT(5)
 
+/* Trigger mode registers */
+#define OV9282_REG_POWER_CTRL 0x4F00
+#define OV9282_REG_LOW_POWER_MODE_CTRL 0x3030
+#define OV9282_REG_NUM_FRAME_ON_TRIG 0x303F
+#define OV9282_REG_SLEEP_PERIOD_CTRL0 0x302C
+#define OV9282_REG_SLEEP_PERIOD_CTRL3 0x302F
+#define OV9282_REG_TIMING_23 0x3823
+
 /* Input clock rate */
 #define OV9282_INCLK_RATE	24000000
 
@@ -196,6 +204,7 @@ struct ov9282 {
 	const struct ov9282_mode *cur_mode;
 	u32 code;
 	struct mutex mutex;
+	int trigger_mode;
 };
 
 static const s64 link_freq[] = {
@@ -957,6 +966,52 @@ static int ov9282_get_selection(struct v
 }
 
 /**
+ * ov9282_apply_trigger_config() - Configure sensor for FSIN external trigger mode
+ * @ov9282: pointer to ov9282 device
+ *
+ * Return: 0 on success, error code otherwise.
+ */
+static int ov9282_apply_trigger_config(struct ov9282 *ov9282)
+{
+	int ret;
+
+	ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
+			1, OV9282_MODE_STANDBY);
+	if (ret)
+		return ret;
+
+	/* Low power mode */
+	ret = ov9282_write_reg(ov9282, OV9282_REG_POWER_CTRL, 1, 0x01);
+	if (ret)
+		return ret;
+
+	/* External trigger snapshot */
+	ret = ov9282_write_reg(ov9282, OV9282_REG_LOW_POWER_MODE_CTRL, 1, 0x04);
+	if (ret)
+		return ret;
+
+	/* 1 frame per trigger */
+	ret = ov9282_write_reg(ov9282, OV9282_REG_NUM_FRAME_ON_TRIG, 1, 0x01);
+	if (ret)
+		return ret;
+
+	ret = ov9282_write_reg(ov9282, OV9282_REG_SLEEP_PERIOD_CTRL0, 1, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov9282_write_reg(ov9282, OV9282_REG_SLEEP_PERIOD_CTRL3, 1, 0x7F);
+	if (ret)
+		return ret;
+
+	/* No auto wake */
+	ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_23, 1, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/**
  * ov9282_start_streaming() - Start sensor stream
  * @ov9282: pointer to ov9282 device
  *
@@ -1007,15 +1062,26 @@ static int ov9282_start_streaming(struct
 		return ret;
 	}
 
-	/* Start streaming */
-	ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
-			       1, OV9282_MODE_STREAMING);
-	if (ret) {
-		dev_err(ov9282->dev, "fail to start streaming");
-		return ret;
+	/* Configure FSIN external trigger mode */
+	if (ov9282->trigger_mode > 0) {
+		ret = ov9282_apply_trigger_config(ov9282);
+		if (ret) {
+			dev_err(ov9282->dev, "failed to config external trigger mode");
+			return ret;
+		}
+		/* stay in standby mode and wait for trigger signal */
+		ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
+				       1, OV9282_MODE_STANDBY);
+	} else {
+		/* Start streaming */
+		ret = ov9282_write_reg(ov9282, OV9282_REG_MODE_SELECT,
+				       1, OV9282_MODE_STREAMING);
 	}
 
-	return 0;
+	if (ret)
+		dev_err(ov9282->dev, "fail to start streaming");
+
+	return ret;
 }
 
 /**
@@ -1400,6 +1466,7 @@ static int ov9282_probe(struct i2c_clien
 {
 	struct ov9282 *ov9282;
 	int ret;
+	u32 trig_mod;
 
 	ov9282 = devm_kzalloc(&client->dev, sizeof(*ov9282), GFP_KERNEL);
 	if (!ov9282)
@@ -1439,6 +1506,10 @@ static int ov9282_probe(struct i2c_clien
 	ov9282->code = MEDIA_BUS_FMT_Y10_1X10;
 	ov9282->vblank = ov9282->cur_mode->vblank;
 
+	ret = of_property_read_u32(client->dev.of_node,
+			"trigger-mode", &trig_mod);
+	ov9282->trigger_mode = (ret == 0) ? trig_mod : -1;
+
 	ret = ov9282_init_controls(ov9282);
 	if (ret) {
 		dev_err(ov9282->dev, "failed to init controls: %d", ret);
