mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it
@ 2025-03-17  0:39 Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 1/8] media: i2c: add OV02E10 image sensor driver Bryan O'Donoghue
                   ` (8 more replies)
  0 siblings, 9 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Similar to Hans' progressive series on ov02c10 I've picked up via Hans the
IPU6 driver with some additional fixes from Alan Stern.

https://lore.kernel.org/linux-media/20250313184314.91410-1-hdegoede@redhat.com

I've made a number of initial changes to this driver sufficient to get it
working on the Dell Inspiron 14 Plus 7441 with the Qualcomm X Elite
x1e80100 SoC and its CAMSS stack.

link: https://git.codelinaro.org/bryan.odonoghue/kernel/-/tree/x1e80100-6.14-rc6-inspirion14-slim7x-camss?ref_type=heads

This is a first pass which adds in the minimum to get the sensor going on
the Qualcomm platform. What would be nice would be someone on the IPU6 side
giving it a test.

A big TBD here is the YAML for this file but, I'd like to make sure nothing
has broken for IPU6/IPU7 with the modifications before diving into
Documentation.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
Bryan O'Donoghue (7):
      media: i2c: ov02e10: Fix initial indentation and spacing
      media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO
      media: i2c: ov02e10: Convert to regulator_bulk API
      media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF
      media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up
      media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err()
      media: i2c: ov02e10: Add OF probe support

Jingjing Xiong (1):
      media: i2c: add OV02E10 image sensor driver

 drivers/media/i2c/Kconfig   |  10 +
 drivers/media/i2c/Makefile  |   1 +
 drivers/media/i2c/ov02e10.c | 978 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 989 insertions(+)
---
base-commit: f2151613e040973c868d28c8b00885dfab69eb75
change-id: 20250317-b4-media-comitters-next-25-03-13-ov02e10-23e68307d589

Best regards,
-- 
Bryan O'Donoghue <bryan.odonoghue@linaro.org>


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/8] media: i2c: add OV02E10 image sensor driver
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-21 16:38   ` Dan Carpenter
  2025-03-17  0:39 ` [PATCH 2/8] media: i2c: ov02e10: Fix initial indentation and spacing Bryan O'Donoghue
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

From: Jingjing Xiong <jingjing.xiong@intel.com>

Add in the ov02e10 driver from the Intel IPU6 repository.

Signed-off-by: Jingjing Xiong <jingjing.xiong@intel.com>
Co-developed-by: Hao Yao <hao.yao@intel.com>
Signed-off-by: Hao Yao <hao.yao@intel.com>
Co-developed-by: Jim Lai <jim.lai@intel.com>
Signed-off-by: Jim Lai <jim.lai@intel.com>
Co-developed-by: You-Sheng Yang <vicamo.yang@canonical.com>
Signed-off-by: You-Sheng Yang <vicamo.yang@canonical.com>
Co-developed-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Co-developed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/Kconfig   |  10 +
 drivers/media/i2c/Makefile  |   1 +
 drivers/media/i2c/ov02e10.c | 975 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 986 insertions(+)

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e576b213084d232e90b7e556a7a855a3bb95544c..7b8af1c87a0e40c19ef8ddb9a8603a1962877163 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -356,6 +356,16 @@ config VIDEO_OV02A10
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov02a10.
 
+config VIDEO_OV02E10
+	tristate "OmniVision OV02E10 sensor support"
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the OmniVision
+	  OV02E10 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov02e10.
+
 config VIDEO_OV08D10
         tristate "OmniVision OV08D10 sensor support"
         help
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 6c23a4463527cf762032df663bbfe26be29018c8..ed5e62fd6199d8c0ab0d3104fd353243021a11be 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
 obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
 obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
 obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
+obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o
 obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o
 obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o
 obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
new file mode 100644
index 0000000000000000000000000000000000000000..8c4455b3643307be5b1e8110979740a7e843c146
--- /dev/null
+++ b/drivers/media/i2c/ov02e10.c
@@ -0,0 +1,975 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2023 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>
+#include <linux/regmap.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define OV02E10_LINK_FREQ_360MHZ	360000000ULL
+#define OV02E10_SCLK			36000000LL
+#define OV02E10_MCLK			19200000
+#define OV02E10_DATA_LANES		2
+#define OV02E10_RGB_DEPTH		10
+
+#define OV02E10_REG_PAGE_FLAG		CCI_REG8(0xfd)
+#define OV02E10_PAGE_0			0x0
+#define OV02E10_PAGE_1			0x1
+#define OV02E10_PAGE_2			0x2
+#define OV02E10_PAGE_3			0x3
+#define OV02E10_PAGE_5			0x4
+#define OV02E10_PAGE_7			0x5
+#define OV02E10_PAGE_8			0x6
+#define OV02E10_PAGE_9			0xF
+#define OV02E10_PAGE_D			0x8
+#define OV02E10_PAGE_E			0x9
+#define OV02E10_PAGE_F			0xA
+
+#define OV02E10_REG_CHIP_ID		CCI_REG32(0x00)
+#define OV02E10_CHIP_ID			0x45025610
+
+/* vertical-timings from sensor */
+#define OV02E10_REG_VTS			CCI_REG16(0x35)
+#define OV02E10_VTS_DEF			2244
+#define OV02E10_VTS_MIN			2244
+#define OV02E10_VTS_MAX			0x7fff
+
+/* horizontal-timings from sensor */
+#define OV02E10_REG_HTS			CCI_REG16(0x37)
+
+/* Exposure controls from sensor */
+#define OV02E10_REG_EXPOSURE		CCI_REG16(0x03)
+#define OV02E10_EXPOSURE_MIN		1
+#define OV02E10_EXPOSURE_MAX_MARGIN	2
+#define OV02E10_EXPOSURE_STEP		1
+
+/* Analog gain controls from sensor */
+#define OV02E10_REG_ANALOG_GAIN		CCI_REG8(0x24)
+#define OV02E10_ANAL_GAIN_MIN		0x10
+#define OV02E10_ANAL_GAIN_MAX		0xf8
+#define OV02E10_ANAL_GAIN_STEP		1
+
+/* Digital gain controls from sensor */
+#define OV02E10_REG_DIGITAL_GAIN	CCI_REG16(0x21)
+#define OV02E10_DGTL_GAIN_MIN		256
+#define OV02E10_DGTL_GAIN_MAX		1020
+#define OV02E10_DGTL_GAIN_STEP		1
+#define OV02E10_DGTL_GAIN_DEFAULT	256
+
+/* Register update control */
+#define OV02E10_REG_COMMAND_UPDATE	CCI_REG8(0xE7)
+#define OV02E10_COMMAND_UPDATE		0x00
+#define OV02E10_COMMAND_HOLD		0x01
+
+/* Test Pattern Control */
+#define OV02E10_REG_TEST_PATTERN	CCI_REG8(0x12)
+#define OV02E10_TEST_PATTERN_ENABLE	BIT(0)
+#define OV02E10_TEST_PATTERN_BAR_SHIFT	1
+
+enum {
+	OV02E10_LINK_FREQ_360MHZ_INDEX,
+};
+
+struct reg_sequence_list {
+	u32 num_regs;
+	const struct reg_sequence *regs;
+};
+
+struct ov02e10_mode {
+	/* Frame width in pixels */
+	u32 width;
+
+	/* Frame height in pixels */
+	u32 height;
+
+	/* Horizontal timining size */
+	u32 hts;
+
+	/* Default vertical timining size */
+	u32 vts_def;
+
+	/* Min vertical timining size */
+	u32 vts_min;
+
+	/* Link frequency needed for this resolution */
+	u32 link_freq_index;
+
+	/* Sensor register settings for this resolution */
+	const struct reg_sequence_list reg_list;
+};
+
+static const struct reg_sequence mode_1928x1088_30fps_2lane[] = {
+	{ 0xfd, 0x00 },
+	{ 0x20, 0x00 },
+	{ 0x20, 0x0b },
+	{ 0x21, 0x02 },
+	{ 0x10, 0x23 },
+	{ 0xc5, 0x04 },
+	{ 0x21, 0x00 },
+	{ 0x14, 0x96 },
+	{ 0x17, 0x01 },
+	{ 0xfd, 0x01 },
+	{ 0x03, 0x00 },
+	{ 0x04, 0x04 },
+	{ 0x05, 0x04 },
+	{ 0x06, 0x62 },
+	{ 0x07, 0x01 },
+	{ 0x22, 0x80 },
+	{ 0x24, 0xff },
+	{ 0x40, 0xc6 },
+	{ 0x41, 0x18 },
+	{ 0x45, 0x3f },
+	{ 0x48, 0x0c },
+	{ 0x4c, 0x08 },
+	{ 0x51, 0x12 },
+	{ 0x52, 0x10 },
+	{ 0x57, 0x98 },
+	{ 0x59, 0x06 },
+	{ 0x5a, 0x04 },
+	{ 0x5c, 0x38 },
+	{ 0x5e, 0x10 },
+	{ 0x67, 0x11 },
+	{ 0x7b, 0x04 },
+	{ 0x81, 0x12 },
+	{ 0x90, 0x51 },
+	{ 0x91, 0x09 },
+	{ 0x92, 0x21 },
+	{ 0x93, 0x28 },
+	{ 0x95, 0x54 },
+	{ 0x9d, 0x20 },
+	{ 0x9e, 0x04 },
+	{ 0xb1, 0x9a },
+	{ 0xb2, 0x86 },
+	{ 0xb6, 0x3f },
+	{ 0xb9, 0x30 },
+	{ 0xc1, 0x01 },
+	{ 0xc5, 0xa0 },
+	{ 0xc6, 0x73 },
+	{ 0xc7, 0x04 },
+	{ 0xc8, 0x25 },
+	{ 0xc9, 0x05 },
+	{ 0xca, 0x28 },
+	{ 0xcb, 0x00 },
+	{ 0xcf, 0x16 },
+	{ 0xd2, 0xd0 },
+	{ 0xd7, 0x3f },
+	{ 0xd8, 0x40 },
+	{ 0xd9, 0x40 },
+	{ 0xda, 0x44 },
+	{ 0xdb, 0x3d },
+	{ 0xdc, 0x3d },
+	{ 0xdd, 0x3d },
+	{ 0xde, 0x3d },
+	{ 0xdf, 0xf0 },
+	{ 0xea, 0x0f },
+	{ 0xeb, 0x04 },
+	{ 0xec, 0x29 },
+	{ 0xee, 0x47 },
+	{ 0xfd, 0x01 },
+	{ 0x31, 0x01 },
+	{ 0x27, 0x00 },
+	{ 0x2f, 0x41 },
+	{ 0xfd, 0x02 },
+	{ 0xa1, 0x01 },
+	{ 0xfd, 0x02 },
+	{ 0x9a, 0x03 },
+	{ 0xfd, 0x03 },
+	{ 0x9d, 0x0f },
+	{ 0xfd, 0x07 },
+	{ 0x42, 0x00 },
+	{ 0x43, 0xad },
+	{ 0x44, 0x00 },
+	{ 0x45, 0xa8 },
+	{ 0x46, 0x00 },
+	{ 0x47, 0xa8 },
+	{ 0x48, 0x00 },
+	{ 0x49, 0xad },
+	{ 0xfd, 0x00 },
+	{ 0xc4, 0x01 },
+	{ 0xfd, 0x01 },
+	{ 0x33, 0x03 },
+	{ 0xfd, 0x00 },
+	{ 0x20, 0x1f },
+};
+
+static const char *const ov02e10_test_pattern_menu[] = {
+	"Disabled",
+	"Color Bar",
+};
+
+static const s64 link_freq_menu_items[] = {
+	OV02E10_LINK_FREQ_360MHZ,
+};
+
+static const struct ov02e10_mode supported_modes[] = {
+	{
+		.width = 1928,
+		.height = 1088,
+		.hts = 534,
+		.vts_def = 2244,
+		.vts_min = 2244,
+		.reg_list = {
+			.num_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane),
+			.regs = mode_1928x1088_30fps_2lane,
+		},
+
+		.link_freq_index = OV02E10_LINK_FREQ_360MHZ_INDEX,
+	},
+};
+
+struct ov02e10 {
+	struct regmap *regmap;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+
+	/* V4L2 Controls */
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *exposure;
+
+	struct clk *img_clk;
+	struct regulator *avdd;
+	struct gpio_desc *reset;
+	struct gpio_desc *handshake;
+
+	/* Current mode */
+	const struct ov02e10_mode *cur_mode;
+
+	/* To serialize asynchronus callbacks */
+	struct mutex mutex;
+
+	/* Streaming on/off */
+	bool streaming;
+};
+
+static inline struct ov02e10 *to_ov02e10(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct ov02e10, sd);
+}
+
+static u64 to_pixel_rate(u32 f_index)
+{
+	u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV02E10_DATA_LANES;
+
+	do_div(pixel_rate, OV02E10_RGB_DEPTH);
+
+	return pixel_rate;
+}
+
+static u64 to_pixels_per_line(u32 hts, u32 f_index)
+{
+	u64 ppl = hts * to_pixel_rate(f_index);
+
+	do_div(ppl, OV02E10_SCLK);
+
+	return ppl;
+}
+
+static void ov02e10_test_pattern(struct ov02e10 *ov02e10, u32 pattern, int *pret)
+{
+	if (pattern)
+		pattern = pattern << OV02E10_TEST_PATTERN_BAR_SHIFT |
+		    OV02E10_TEST_PATTERN_ENABLE;
+
+	cci_write(ov02e10->regmap, OV02E10_REG_TEST_PATTERN, pattern, pret);
+}
+
+static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov02e10 *ov02e10 = container_of(ctrl->handler,
+					       struct ov02e10, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd);
+	s64 exposure_max;
+	int ret;
+
+	/* Propagate change of current control to all related controls */
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		/* Update max exposure while meeting expected vblanking */
+		exposure_max = ov02e10->cur_mode->height + ctrl->val -
+		    OV02E10_EXPOSURE_MAX_MARGIN;
+		__v4l2_ctrl_modify_range(ov02e10->exposure,
+					 ov02e10->exposure->minimum,
+					 exposure_max, ov02e10->exposure->step,
+					 exposure_max);
+	}
+
+	/* V4L2 controls values will be applied only when power is already up */
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+	ret = cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE,
+			OV02E10_COMMAND_HOLD, NULL);
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		dev_dbg(&client->dev, "set analog gain\n");
+		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			  OV02E10_PAGE_1, &ret);
+		cci_write(ov02e10->regmap, OV02E10_REG_ANALOG_GAIN,
+			  ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_DIGITAL_GAIN:
+		dev_dbg(&client->dev, "set digital gain\n");
+		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			  OV02E10_PAGE_1, &ret);
+		cci_write(ov02e10->regmap, OV02E10_REG_DIGITAL_GAIN,
+			  ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		dev_dbg(&client->dev, "set exposure\n");
+		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			  OV02E10_PAGE_1, &ret);
+		cci_write(ov02e10->regmap, OV02E10_REG_EXPOSURE,
+			  ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_VBLANK:
+		dev_dbg(&client->dev, "set vblank\n");
+		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			  OV02E10_PAGE_1, &ret);
+		cci_write(ov02e10->regmap, OV02E10_REG_VTS,
+			  ov02e10->cur_mode->height + ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_TEST_PATTERN:
+		dev_dbg(&client->dev, "set test pattern\n");
+		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			  OV02E10_PAGE_1, &ret);
+		ov02e10_test_pattern(ov02e10, ctrl->val, &ret);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	dev_dbg(&client->dev, "will update cmd\n");
+	cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE,
+		  OV02E10_COMMAND_UPDATE, &ret);
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov02e10_ctrl_ops = {
+	.s_ctrl = ov02e10_set_ctrl,
+};
+
+static int ov02e10_init_controls(struct ov02e10 *ov02e10)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	const struct ov02e10_mode *cur_mode;
+	s64 exposure_max, h_blank, pixel_rate;
+	u32 vblank_min, vblank_max, vblank_default;
+	int size;
+	int ret;
+
+	ctrl_hdlr = &ov02e10->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+
+	if (ret)
+		return ret;
+
+	ctrl_hdlr->lock = &ov02e10->mutex;
+	cur_mode = ov02e10->cur_mode;
+	size = ARRAY_SIZE(link_freq_menu_items);
+
+	ov02e10->link_freq =
+	    v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov02e10_ctrl_ops,
+				   V4L2_CID_LINK_FREQ, size - 1, 0,
+				   link_freq_menu_items);
+	if (ov02e10->link_freq)
+		ov02e10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	pixel_rate = to_pixel_rate(OV02E10_LINK_FREQ_360MHZ_INDEX);
+	ov02e10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops,
+						V4L2_CID_PIXEL_RATE, 0,
+						pixel_rate, 1, pixel_rate);
+
+	vblank_min = cur_mode->vts_min - cur_mode->height;
+	vblank_max = OV02E10_VTS_MAX - cur_mode->height;
+	vblank_default = cur_mode->vts_def - cur_mode->height;
+	ov02e10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_min,
+					    vblank_max, 1, vblank_default);
+
+	h_blank = to_pixels_per_line(cur_mode->hts, cur_mode->link_freq_index);
+	h_blank -= cur_mode->width;
+	ov02e10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops,
+					    V4L2_CID_HBLANK, h_blank, h_blank,
+					    1, h_blank);
+	if (ov02e10->hblank)
+		ov02e10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+			  OV02E10_ANAL_GAIN_MIN, OV02E10_ANAL_GAIN_MAX,
+			  OV02E10_ANAL_GAIN_STEP, OV02E10_ANAL_GAIN_MIN);
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+			  OV02E10_DGTL_GAIN_MIN, OV02E10_DGTL_GAIN_MAX,
+			  OV02E10_DGTL_GAIN_STEP, OV02E10_DGTL_GAIN_DEFAULT);
+	exposure_max = cur_mode->vts_def - OV02E10_EXPOSURE_MAX_MARGIN;
+	ov02e10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov02e10_ctrl_ops,
+					      V4L2_CID_EXPOSURE,
+					      OV02E10_EXPOSURE_MIN,
+					      exposure_max,
+					      OV02E10_EXPOSURE_STEP,
+					      exposure_max);
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02e10_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(ov02e10_test_pattern_menu) - 1,
+				     0, 0, ov02e10_test_pattern_menu);
+	if (ctrl_hdlr->error)
+		return ctrl_hdlr->error;
+
+	ov02e10->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+}
+
+static void ov02e10_update_pad_format(const struct ov02e10_mode *mode,
+				      struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov02e10_set_stream_mode(struct ov02e10 *ov02e10, u8 val)
+{
+	int ret = 0;
+
+	cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_0, &ret);
+	cci_write(ov02e10->regmap, CCI_REG8(0xa0), val, &ret);
+	cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG, OV02E10_PAGE_1, &ret);
+	cci_write(ov02e10->regmap, CCI_REG8(0x01), 0x02, &ret);
+
+	return ret;
+}
+
+static int ov02e10_start_streaming(struct ov02e10 *ov02e10)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd);
+	const struct reg_sequence_list *reg_list;
+	int ret;
+
+	reg_list = &ov02e10->cur_mode->reg_list;
+	ret = regmap_multi_reg_write(ov02e10->regmap, reg_list->regs,
+				     reg_list->num_regs);
+	if (ret) {
+		dev_err(&client->dev, "failed to set mode\n");
+		return ret;
+	}
+	ret = __v4l2_ctrl_handler_setup(ov02e10->sd.ctrl_handler);
+	if (ret) {
+		dev_err(&client->dev, "setup V4L2 ctrl handler fail\n");
+		return ret;
+	}
+
+	return ov02e10_set_stream_mode(ov02e10, 1);
+}
+
+static inline void ov02e10_stop_streaming(struct ov02e10 *ov02e10)
+{
+	ov02e10_set_stream_mode(ov02e10, 0);
+}
+
+static int ov02e10_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	if (ov02e10->streaming == enable)
+		return 0;
+
+	mutex_lock(&ov02e10->mutex);
+	if (enable) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			mutex_unlock(&ov02e10->mutex);
+			return ret;
+		}
+
+		ret = ov02e10_start_streaming(ov02e10);
+		if (ret) {
+			dev_dbg(&client->dev, "start streaming failed\n");
+			enable = 0;
+			ov02e10_stop_streaming(ov02e10);
+			pm_runtime_put(&client->dev);
+		}
+	} else {
+		ov02e10_stop_streaming(ov02e10);
+		pm_runtime_put(&client->dev);
+	}
+
+	ov02e10->streaming = enable;
+	mutex_unlock(&ov02e10->mutex);
+
+	return ret;
+}
+
+/* This function tries to get power control resources */
+static int ov02e10_get_pm_resources(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	int ret;
+
+	ov02e10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ov02e10->reset))
+		return dev_err_probe(dev, PTR_ERR(ov02e10->reset),
+				     "failed to get reset gpio\n");
+
+	ov02e10->handshake = devm_gpiod_get_optional(dev, "handshake",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(ov02e10->handshake))
+		return dev_err_probe(dev, PTR_ERR(ov02e10->handshake),
+				     "failed to get handshake gpio\n");
+
+	ov02e10->img_clk = devm_clk_get_optional(dev, NULL);
+	if (IS_ERR(ov02e10->img_clk))
+		return dev_err_probe(dev, PTR_ERR(ov02e10->img_clk),
+				     "failed to get imaging clock\n");
+
+	ov02e10->avdd = devm_regulator_get_optional(dev, "avdd");
+	if (IS_ERR(ov02e10->avdd)) {
+		ret = PTR_ERR(ov02e10->avdd);
+		ov02e10->avdd = NULL;
+		if (ret != -ENODEV)
+			return dev_err_probe(dev, ret,
+					     "failed to get avdd regulator\n");
+	}
+
+	return 0;
+}
+
+static int ov02e10_power_off(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	int ret = 0;
+
+	gpiod_set_value_cansleep(ov02e10->reset, 1);
+	gpiod_set_value_cansleep(ov02e10->handshake, 0);
+
+	if (ov02e10->avdd)
+		ret = regulator_disable(ov02e10->avdd);
+
+	clk_disable_unprepare(ov02e10->img_clk);
+
+	return ret;
+}
+
+static int ov02e10_power_on(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	int ret;
+
+	ret = clk_prepare_enable(ov02e10->img_clk);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable imaging clock: %d\n", ret);
+		return ret;
+	}
+
+	if (ov02e10->avdd) {
+		ret = regulator_enable(ov02e10->avdd);
+		if (ret < 0) {
+			dev_err(dev, "failed to enable avdd: %d\n", ret);
+			clk_disable_unprepare(ov02e10->img_clk);
+			return ret;
+		}
+	}
+	gpiod_set_value_cansleep(ov02e10->handshake, 1);
+	gpiod_set_value_cansleep(ov02e10->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;
+}
+
+static int __maybe_unused ov02e10_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+
+	mutex_lock(&ov02e10->mutex);
+	if (ov02e10->streaming)
+		ov02e10_stop_streaming(ov02e10);
+
+	mutex_unlock(&ov02e10->mutex);
+
+	return 0;
+}
+
+static int __maybe_unused ov02e10_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	int ret = 0;
+
+	mutex_lock(&ov02e10->mutex);
+	if (!ov02e10->streaming)
+		goto exit;
+
+	ret = ov02e10_start_streaming(ov02e10);
+	if (ret) {
+		ov02e10->streaming = false;
+		ov02e10_stop_streaming(ov02e10);
+	}
+
+exit:
+	mutex_unlock(&ov02e10->mutex);
+	return ret;
+}
+
+static int ov02e10_set_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *sd_state,
+			      struct v4l2_subdev_format *fmt)
+{
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+	const struct ov02e10_mode *mode;
+	s32 vblank_def, h_blank;
+
+	mode = v4l2_find_nearest_size(supported_modes,
+				      ARRAY_SIZE(supported_modes),
+				      width, height, fmt->format.width,
+				      fmt->format.height);
+
+	mutex_lock(&ov02e10->mutex);
+	ov02e10_update_pad_format(mode, &fmt->format);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		*v4l2_subdev_state_get_format(sd_state, fmt->pad) =
+		    fmt->format;
+	} else {
+		ov02e10->cur_mode = mode;
+		__v4l2_ctrl_s_ctrl(ov02e10->link_freq, mode->link_freq_index);
+		__v4l2_ctrl_s_ctrl_int64(ov02e10->pixel_rate,
+					 to_pixel_rate(mode->link_freq_index));
+
+		/* Update limits and set FPS to default */
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(ov02e10->vblank,
+					 mode->vts_min - mode->height,
+					 OV02E10_VTS_MAX - mode->height, 1,
+					 vblank_def);
+		__v4l2_ctrl_s_ctrl(ov02e10->vblank, vblank_def);
+		h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) -
+		    mode->width;
+		__v4l2_ctrl_modify_range(ov02e10->hblank, h_blank, h_blank, 1,
+					 h_blank);
+	}
+	mutex_unlock(&ov02e10->mutex);
+
+	return 0;
+}
+
+static int ov02e10_get_format(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *sd_state,
+			      struct v4l2_subdev_format *fmt)
+{
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+
+	mutex_lock(&ov02e10->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+		fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
+	else
+		ov02e10_update_pad_format(ov02e10->cur_mode, &fmt->format);
+
+	mutex_unlock(&ov02e10->mutex);
+
+	return 0;
+}
+
+static int ov02e10_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+	return 0;
+}
+
+static int ov02e10_enum_frame_size(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *sd_state,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+		return -EINVAL;
+
+	fse->min_width = supported_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = supported_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov02e10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+
+	mutex_lock(&ov02e10->mutex);
+	ov02e10_update_pad_format(&supported_modes[0],
+				  v4l2_subdev_state_get_format(fh->state, 0));
+	mutex_unlock(&ov02e10->mutex);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov02e10_video_ops = {
+	.s_stream = ov02e10_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov02e10_pad_ops = {
+	.set_fmt = ov02e10_set_format,
+	.get_fmt = ov02e10_get_format,
+	.enum_mbus_code = ov02e10_enum_mbus_code,
+	.enum_frame_size = ov02e10_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops ov02e10_subdev_ops = {
+	.video = &ov02e10_video_ops,
+	.pad = &ov02e10_pad_ops,
+};
+
+static const struct media_entity_operations ov02e10_subdev_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov02e10_internal_ops = {
+	.open = ov02e10_open,
+};
+
+static int ov02e10_identify_module(struct ov02e10 *ov02e10)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&ov02e10->sd);
+	int ret;
+	u64 val;
+
+	ret = cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
+			   OV02E10_PAGE_0, NULL);
+	cci_read(ov02e10->regmap, OV02E10_REG_CHIP_ID, &val, &ret);
+	if (ret)
+		return ret;
+
+	if (val != OV02E10_CHIP_ID) {
+		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+			OV02E10_CHIP_ID, (u32) val);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int ov02e10_check_hwcfg(struct device *dev)
+{
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	struct fwnode_handle *ep;
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+	unsigned int i, j;
+	int ret;
+	u32 ext_clk;
+
+	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+	if (!ep)
+		return dev_err_probe(dev, -EPROBE_DEFER,
+				     "waiting for fwnode graph endpoint\n");
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+	fwnode_handle_put(ep);
+	if (ret)
+		return dev_err_probe(dev, ret, "parsing endpoint failed\n");
+
+	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+				       &ext_clk);
+	if (ret) {
+		dev_err(dev, "can't get clock frequency\n");
+		goto out_err;
+	}
+
+	if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV02E10_DATA_LANES) {
+		dev_err(dev, "number of CSI2 data lanes %d is not supported\n",
+			bus_cfg.bus.mipi_csi2.num_data_lanes);
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	if (!bus_cfg.nr_of_link_frequencies) {
+		dev_err(dev, "no link frequencies defined\n");
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
+		for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) {
+			if (link_freq_menu_items[i] ==
+				bus_cfg.link_frequencies[j])
+				break;
+		}
+
+		if (j == bus_cfg.nr_of_link_frequencies) {
+			dev_err(dev, "no link frequency %lld supported\n",
+				link_freq_menu_items[i]);
+			ret = -EINVAL;
+			goto out_err;
+		}
+	}
+
+out_err:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+
+	return ret;
+}
+
+static void ov02e10_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov02e10 *ov02e10 = to_ov02e10(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	pm_runtime_disable(&client->dev);
+	mutex_destroy(&ov02e10->mutex);
+}
+
+static int ov02e10_probe(struct i2c_client *client)
+{
+	struct ov02e10 *ov02e;
+	int ret;
+
+	/* Check HW config */
+	ret = ov02e10_check_hwcfg(&client->dev);
+	if (ret)
+		return ret;
+
+	ov02e = devm_kzalloc(&client->dev, sizeof(*ov02e), GFP_KERNEL);
+	if (!ov02e)
+		return -ENOMEM;
+
+	/* Initialize subdev */
+	ov02e->regmap = devm_cci_regmap_init_i2c(client, 8);
+	if (IS_ERR(ov02e->regmap))
+		return PTR_ERR(ov02e->regmap);
+
+	v4l2_i2c_subdev_init(&ov02e->sd, client, &ov02e10_subdev_ops);
+	ov02e10_get_pm_resources(&client->dev);
+
+	ret = ov02e10_power_on(&client->dev);
+	if (ret) {
+		dev_err_probe(&client->dev, ret, "failed to power on\n");
+		goto error_power_off;
+	}
+
+	/* Check module identity */
+	ret = ov02e10_identify_module(ov02e);
+	if (ret) {
+		dev_err(&client->dev, "failed to find sensor: %d\n", ret);
+		goto error_power_off;
+	}
+
+	/* Set default mode to max resolution */
+	ov02e->cur_mode = &supported_modes[0];
+
+	dev_dbg(&client->dev, "will Init controls\n");
+	ret = ov02e10_init_controls(ov02e);
+	if (ret)
+		return ret;
+
+	/* Initialize subdev */
+	ov02e->sd.internal_ops = &ov02e10_internal_ops;
+	ov02e->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	ov02e->sd.entity.ops = &ov02e10_subdev_entity_ops;
+	ov02e->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	/* Initialize source pad */
+	ov02e->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&ov02e->sd.entity, 1, &ov02e->pad);
+	if (ret) {
+		dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+		goto error_handler_free;
+	}
+
+	ret = v4l2_async_register_subdev_sensor(&ov02e->sd);
+	if (ret < 0) {
+		dev_err(&client->dev, "async reg subdev error\n");
+		goto error_media_entity;
+	}
+
+	/*
+	 * Device is already turned on by i2c-core with ACPI domain PM.
+	 * Enable runtime PM and turn off the device.
+	 */
+	pm_runtime_set_active(&client->dev);
+	pm_runtime_enable(&client->dev);
+	pm_runtime_idle(&client->dev);
+
+	return 0;
+
+error_media_entity:
+	media_entity_cleanup(&ov02e->sd.entity);
+
+error_handler_free:
+	v4l2_ctrl_handler_free(ov02e->sd.ctrl_handler);
+	mutex_destroy(&ov02e->mutex);
+	dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+error_power_off:
+	ov02e10_power_off(&client->dev);
+
+	dev_dbg(&client->dev, "probe done\n");
+	return ret;
+}
+
+static const struct dev_pm_ops ov02e10_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ov02e10_suspend, ov02e10_resume)
+	    SET_RUNTIME_PM_OPS(ov02e10_power_off, ov02e10_power_on, NULL)
+};
+
+static const struct acpi_device_id ov02e10_acpi_ids[] = {
+	{ "OVTI02E1" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids);
+
+static struct i2c_driver ov02e10_i2c_driver = {
+	.driver = {
+		   .name = "ov02e10",
+		   .pm = &ov02e10_pm_ops,
+		   .acpi_match_table = ov02e10_acpi_ids,
+		    },
+	.probe = ov02e10_probe,
+	.remove = ov02e10_remove,
+};
+
+module_i2c_driver(ov02e10_i2c_driver);
+
+MODULE_AUTHOR("Jingjing Xiong <jingjing.xiong@intel.com>");
+MODULE_DESCRIPTION("OmniVision OV02E10 sensor driver");
+MODULE_LICENSE("GPL v2");

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 2/8] media: i2c: ov02e10: Fix initial indentation and spacing
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 1/8] media: i2c: add OV02E10 image sensor driver Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 3/8] media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO Bryan O'Donoghue
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 8c4455b3643307be5b1e8110979740a7e843c146..db27d8c9f3f0b8dd6df6f9757cc674075e419075 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -220,7 +220,6 @@ static const struct ov02e10_mode supported_modes[] = {
 			.num_regs = ARRAY_SIZE(mode_1928x1088_30fps_2lane),
 			.regs = mode_1928x1088_30fps_2lane,
 		},
-
 		.link_freq_index = OV02E10_LINK_FREQ_360MHZ_INDEX,
 	},
 };
@@ -771,14 +770,14 @@ static int ov02e10_identify_module(struct ov02e10 *ov02e10)
 	u64 val;
 
 	ret = cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
-			   OV02E10_PAGE_0, NULL);
+			OV02E10_PAGE_0, NULL);
 	cci_read(ov02e10->regmap, OV02E10_REG_CHIP_ID, &val, &ret);
 	if (ret)
 		return ret;
 
 	if (val != OV02E10_CHIP_ID) {
 		dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
-			OV02E10_CHIP_ID, (u32) val);
+			OV02E10_CHIP_ID, (u32)val);
 		return -ENXIO;
 	}
 
@@ -948,7 +947,7 @@ static int ov02e10_probe(struct i2c_client *client)
 
 static const struct dev_pm_ops ov02e10_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(ov02e10_suspend, ov02e10_resume)
-	    SET_RUNTIME_PM_OPS(ov02e10_power_off, ov02e10_power_on, NULL)
+	SET_RUNTIME_PM_OPS(ov02e10_power_off, ov02e10_power_on, NULL)
 };
 
 static const struct acpi_device_id ov02e10_acpi_ids[] = {
@@ -960,10 +959,10 @@ MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids);
 
 static struct i2c_driver ov02e10_i2c_driver = {
 	.driver = {
-		   .name = "ov02e10",
-		   .pm = &ov02e10_pm_ops,
-		   .acpi_match_table = ov02e10_acpi_ids,
-		    },
+		.name = "ov02e10",
+		.pm = &ov02e10_pm_ops,
+		.acpi_match_table = ov02e10_acpi_ids,
+	},
 	.probe = ov02e10_probe,
 	.remove = ov02e10_remove,
 };

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 3/8] media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 1/8] media: i2c: add OV02E10 image sensor driver Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 2/8] media: i2c: ov02e10: Fix initial indentation and spacing Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 4/8] media: i2c: ov02e10: Convert to regulator_bulk API Bryan O'Donoghue
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

The IPU6 handshake GPIO signals to the FPGA on IPU6/IPU7 laptops and as
such is not a sensor signal.

Hans has a proposal for handling those GPIOs below. Dropf from the incoming
sensor driver.

link: https://lore.kernel.org/linux-media/4b87a956-a767-48dc-b98b-f80d9a44adc8@redhat.com
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index db27d8c9f3f0b8dd6df6f9757cc674075e419075..29f9eec927b4660f941fa8e32249b321d25b7735 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -240,7 +240,6 @@ struct ov02e10 {
 	struct clk *img_clk;
 	struct regulator *avdd;
 	struct gpio_desc *reset;
-	struct gpio_desc *handshake;
 
 	/* Current mode */
 	const struct ov02e10_mode *cur_mode;
@@ -533,12 +532,6 @@ static int ov02e10_get_pm_resources(struct device *dev)
 		return dev_err_probe(dev, PTR_ERR(ov02e10->reset),
 				     "failed to get reset gpio\n");
 
-	ov02e10->handshake = devm_gpiod_get_optional(dev, "handshake",
-						   GPIOD_OUT_LOW);
-	if (IS_ERR(ov02e10->handshake))
-		return dev_err_probe(dev, PTR_ERR(ov02e10->handshake),
-				     "failed to get handshake gpio\n");
-
 	ov02e10->img_clk = devm_clk_get_optional(dev, NULL);
 	if (IS_ERR(ov02e10->img_clk))
 		return dev_err_probe(dev, PTR_ERR(ov02e10->img_clk),
@@ -563,7 +556,6 @@ static int ov02e10_power_off(struct device *dev)
 	int ret = 0;
 
 	gpiod_set_value_cansleep(ov02e10->reset, 1);
-	gpiod_set_value_cansleep(ov02e10->handshake, 0);
 
 	if (ov02e10->avdd)
 		ret = regulator_disable(ov02e10->avdd);
@@ -593,14 +585,8 @@ static int ov02e10_power_on(struct device *dev)
 			return ret;
 		}
 	}
-	gpiod_set_value_cansleep(ov02e10->handshake, 1);
 	gpiod_set_value_cansleep(ov02e10->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;
 }
 

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 4/8] media: i2c: ov02e10: Convert to regulator_bulk API
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (2 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 3/8] media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 5/8] media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF Bryan O'Donoghue
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Change to regulator_bulk API to capture the full range of rails to the
o02e10 sensor.

This will work for ACPI systems which only have the avdd rail available as
missing rails won't fail the regulator_bulk_* function calls.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 48 ++++++++++++++++++++++++---------------------
 1 file changed, 26 insertions(+), 22 deletions(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 29f9eec927b4660f941fa8e32249b321d25b7735..0a016cb23cbe98d8af371c6a35db2f1d8c699d68 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -224,6 +224,12 @@ static const struct ov02e10_mode supported_modes[] = {
 	},
 };
 
+static const char * const ov02e10_supply_names[] = {
+	"dovdd",	/* Digital I/O power */
+	"avdd",		/* Analog power */
+	"dvdd",		/* Digital core power */
+};
+
 struct ov02e10 {
 	struct regmap *regmap;
 	struct v4l2_subdev sd;
@@ -238,7 +244,7 @@ struct ov02e10 {
 	struct v4l2_ctrl *exposure;
 
 	struct clk *img_clk;
-	struct regulator *avdd;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(ov02e10_supply_names)];
 	struct gpio_desc *reset;
 
 	/* Current mode */
@@ -525,7 +531,7 @@ static int ov02e10_get_pm_resources(struct device *dev)
 {
 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
 	struct ov02e10 *ov02e10 = to_ov02e10(sd);
-	int ret;
+	int i;
 
 	ov02e10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(ov02e10->reset))
@@ -537,32 +543,26 @@ static int ov02e10_get_pm_resources(struct device *dev)
 		return dev_err_probe(dev, PTR_ERR(ov02e10->img_clk),
 				     "failed to get imaging clock\n");
 
-	ov02e10->avdd = devm_regulator_get_optional(dev, "avdd");
-	if (IS_ERR(ov02e10->avdd)) {
-		ret = PTR_ERR(ov02e10->avdd);
-		ov02e10->avdd = NULL;
-		if (ret != -ENODEV)
-			return dev_err_probe(dev, ret,
-					     "failed to get avdd regulator\n");
-	}
+	for (i = 0; i < ARRAY_SIZE(ov02e10_supply_names); i++)
+		ov02e10->supplies[i].supply = ov02e10_supply_names[i];
 
-	return 0;
+	return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02e10_supply_names),
+				       ov02e10->supplies);
 }
 
 static int ov02e10_power_off(struct device *dev)
 {
 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
 	struct ov02e10 *ov02e10 = to_ov02e10(sd);
-	int ret = 0;
 
 	gpiod_set_value_cansleep(ov02e10->reset, 1);
 
-	if (ov02e10->avdd)
-		ret = regulator_disable(ov02e10->avdd);
+	regulator_bulk_disable(ARRAY_SIZE(ov02e10_supply_names),
+			       ov02e10->supplies);
 
 	clk_disable_unprepare(ov02e10->img_clk);
 
-	return ret;
+	return 0;
 }
 
 static int ov02e10_power_on(struct device *dev)
@@ -577,16 +577,20 @@ static int ov02e10_power_on(struct device *dev)
 		return ret;
 	}
 
-	if (ov02e10->avdd) {
-		ret = regulator_enable(ov02e10->avdd);
-		if (ret < 0) {
-			dev_err(dev, "failed to enable avdd: %d\n", ret);
-			clk_disable_unprepare(ov02e10->img_clk);
-			return ret;
-		}
+	ret = regulator_bulk_enable(ARRAY_SIZE(ov02e10_supply_names),
+				    ov02e10->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable regulators\n");
+		goto disable_clk;
 	}
+
 	gpiod_set_value_cansleep(ov02e10->reset, 0);
 
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(ov02e10->img_clk);
+
 	return ret;
 }
 

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 5/8] media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (3 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 4/8] media: i2c: ov02e10: Convert to regulator_bulk API Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up Bryan O'Donoghue
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Move the MCLK validation logic to check_hwcfg() and move check_hwcfg() to
after devm_kzalloc() so that we can pass a struct ov02e pointer to
check_hwcfg() to begin to enumerate that structure.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 43 ++++++++++++++++++++++++++++---------------
 1 file changed, 28 insertions(+), 15 deletions(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 0a016cb23cbe98d8af371c6a35db2f1d8c699d68..9ad70671a718ea0aaf80ad3adcc5738ee57a7ff6 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -538,11 +538,6 @@ static int ov02e10_get_pm_resources(struct device *dev)
 		return dev_err_probe(dev, PTR_ERR(ov02e10->reset),
 				     "failed to get reset gpio\n");
 
-	ov02e10->img_clk = devm_clk_get_optional(dev, NULL);
-	if (IS_ERR(ov02e10->img_clk))
-		return dev_err_probe(dev, PTR_ERR(ov02e10->img_clk),
-				     "failed to get imaging clock\n");
-
 	for (i = 0; i < ARRAY_SIZE(ov02e10_supply_names); i++)
 		ov02e10->supplies[i].supply = ov02e10_supply_names[i];
 
@@ -774,7 +769,7 @@ static int ov02e10_identify_module(struct ov02e10 *ov02e10)
 	return 0;
 }
 
-static int ov02e10_check_hwcfg(struct device *dev)
+static int ov02e10_check_hwcfg(struct device *dev, struct ov02e10 *ov02e10)
 {
 	struct v4l2_fwnode_endpoint bus_cfg = {
 		.bus_type = V4L2_MBUS_CSI2_DPHY
@@ -795,10 +790,28 @@ static int ov02e10_check_hwcfg(struct device *dev)
 	if (ret)
 		return dev_err_probe(dev, ret, "parsing endpoint failed\n");
 
-	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
-				       &ext_clk);
-	if (ret) {
-		dev_err(dev, "can't get clock frequency\n");
+	ov02e10->img_clk = devm_clk_get_optional(dev, NULL);
+	if (IS_ERR(ov02e10->img_clk)) {
+		ret = dev_err_probe(dev, PTR_ERR(ov02e10->img_clk),
+				    "failed to get imaging clock\n");
+		goto out_err;
+	}
+
+	if (ov02e10->img_clk) {
+		ext_clk = clk_get_rate(ov02e10->img_clk);
+	} else {
+		ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+					       &ext_clk);
+		if (ret) {
+			dev_err(dev, "can't get clock frequency\n");
+			goto out_err;
+		}
+	}
+
+	if (ext_clk != OV02E10_MCLK) {
+		dev_err(dev, "external clock %d is not supported\n",
+			ext_clk);
+		ret = -EINVAL;
 		goto out_err;
 	}
 
@@ -853,15 +866,15 @@ static int ov02e10_probe(struct i2c_client *client)
 	struct ov02e10 *ov02e;
 	int ret;
 
-	/* Check HW config */
-	ret = ov02e10_check_hwcfg(&client->dev);
-	if (ret)
-		return ret;
-
 	ov02e = devm_kzalloc(&client->dev, sizeof(*ov02e), GFP_KERNEL);
 	if (!ov02e)
 		return -ENOMEM;
 
+	/* Check HW config */
+	ret = ov02e10_check_hwcfg(&client->dev, ov02e);
+	if (ret)
+		return ret;
+
 	/* Initialize subdev */
 	ov02e->regmap = devm_cci_regmap_init_i2c(client, 8);
 	if (IS_ERR(ov02e->regmap))

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (4 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 5/8] media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  9:04   ` Hans de Goede
  2025-03-17  0:39 ` [PATCH 7/8] media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err() Bryan O'Donoghue
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

The ov02e10 specification says for power-on:

t3 = the time between dvdd stable and XSHUTDOWN deassert
t5 = the time between XSHUTDOWN deassert and SCCB ready

The power-off path in the spec shows no required delays between XSHUTDONW
and power-rail shut off so power-off is left alone.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 9ad70671a718ea0aaf80ad3adcc5738ee57a7ff6..40c4d3ee36e3e2a0bb8be3ff10d016e2bb9bbc9d 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -579,7 +579,11 @@ static int ov02e10_power_on(struct device *dev)
 		goto disable_clk;
 	}
 
-	gpiod_set_value_cansleep(ov02e10->reset, 0);
+	if (ov02e10->reset) {
+		usleep_range(5000, 5100);
+		gpiod_set_value_cansleep(ov02e10->reset, 0);
+		usleep_range(8000, 8100);
+	}
 
 	return 0;
 

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 7/8] media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err()
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (5 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  0:39 ` [PATCH 8/8] media: i2c: ov02e10: Add OF probe support Bryan O'Donoghue
  2025-03-17  9:02 ` [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Hans de Goede
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 40c4d3ee36e3e2a0bb8be3ff10d016e2bb9bbc9d..2c15df8ca733aa4f37e125ac3bfb51a319e34c6e 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -316,7 +316,6 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 
 	switch (ctrl->id) {
 	case V4L2_CID_ANALOGUE_GAIN:
-		dev_dbg(&client->dev, "set analog gain\n");
 		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
 			  OV02E10_PAGE_1, &ret);
 		cci_write(ov02e10->regmap, OV02E10_REG_ANALOG_GAIN,
@@ -324,7 +323,6 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 
 	case V4L2_CID_DIGITAL_GAIN:
-		dev_dbg(&client->dev, "set digital gain\n");
 		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
 			  OV02E10_PAGE_1, &ret);
 		cci_write(ov02e10->regmap, OV02E10_REG_DIGITAL_GAIN,
@@ -332,7 +330,6 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 
 	case V4L2_CID_EXPOSURE:
-		dev_dbg(&client->dev, "set exposure\n");
 		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
 			  OV02E10_PAGE_1, &ret);
 		cci_write(ov02e10->regmap, OV02E10_REG_EXPOSURE,
@@ -340,7 +337,6 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 
 	case V4L2_CID_VBLANK:
-		dev_dbg(&client->dev, "set vblank\n");
 		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
 			  OV02E10_PAGE_1, &ret);
 		cci_write(ov02e10->regmap, OV02E10_REG_VTS,
@@ -348,7 +344,6 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 		break;
 
 	case V4L2_CID_TEST_PATTERN:
-		dev_dbg(&client->dev, "set test pattern\n");
 		cci_write(ov02e10->regmap, OV02E10_REG_PAGE_FLAG,
 			  OV02E10_PAGE_1, &ret);
 		ov02e10_test_pattern(ov02e10, ctrl->val, &ret);
@@ -358,7 +353,7 @@ static int ov02e10_set_ctrl(struct v4l2_ctrl *ctrl)
 		ret = -EINVAL;
 		break;
 	}
-	dev_dbg(&client->dev, "will update cmd\n");
+
 	cci_write(ov02e10->regmap, OV02E10_REG_COMMAND_UPDATE,
 		  OV02E10_COMMAND_UPDATE, &ret);
 
@@ -510,7 +505,6 @@ static int ov02e10_set_stream(struct v4l2_subdev *sd, int enable)
 
 		ret = ov02e10_start_streaming(ov02e10);
 		if (ret) {
-			dev_dbg(&client->dev, "start streaming failed\n");
 			enable = 0;
 			ov02e10_stop_streaming(ov02e10);
 			pm_runtime_put(&client->dev);
@@ -903,7 +897,6 @@ static int ov02e10_probe(struct i2c_client *client)
 	/* Set default mode to max resolution */
 	ov02e->cur_mode = &supported_modes[0];
 
-	dev_dbg(&client->dev, "will Init controls\n");
 	ret = ov02e10_init_controls(ov02e);
 	if (ret)
 		return ret;
@@ -917,10 +910,8 @@ static int ov02e10_probe(struct i2c_client *client)
 	/* Initialize source pad */
 	ov02e->pad.flags = MEDIA_PAD_FL_SOURCE;
 	ret = media_entity_pads_init(&ov02e->sd.entity, 1, &ov02e->pad);
-	if (ret) {
-		dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+	if (ret)
 		goto error_handler_free;
-	}
 
 	ret = v4l2_async_register_subdev_sensor(&ov02e->sd);
 	if (ret < 0) {
@@ -944,11 +935,10 @@ static int ov02e10_probe(struct i2c_client *client)
 error_handler_free:
 	v4l2_ctrl_handler_free(ov02e->sd.ctrl_handler);
 	mutex_destroy(&ov02e->mutex);
-	dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
+
 error_power_off:
 	ov02e10_power_off(&client->dev);
 
-	dev_dbg(&client->dev, "probe done\n");
 	return ret;
 }
 

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 8/8] media: i2c: ov02e10: Add OF probe support
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (6 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 7/8] media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err() Bryan O'Donoghue
@ 2025-03-17  0:39 ` Bryan O'Donoghue
  2025-03-17  9:02 ` [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Hans de Goede
  8 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17  0:39 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	Hans de Goede, linux-kernel, linux-media, Bryan O'Donoghue

Supply OF probe matching table and enumeration structure hook.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
 drivers/media/i2c/ov02e10.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 2c15df8ca733aa4f37e125ac3bfb51a319e34c6e..10cac199b23f14ed79f4fb7e84d2c685c6d70399 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -954,11 +954,18 @@ static const struct acpi_device_id ov02e10_acpi_ids[] = {
 
 MODULE_DEVICE_TABLE(acpi, ov02e10_acpi_ids);
 
+static const struct of_device_id ov02e10_of_match[] = {
+	{ .compatible = "ovti,ov02e10" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov02e10_of_match);
+
 static struct i2c_driver ov02e10_i2c_driver = {
 	.driver = {
 		.name = "ov02e10",
 		.pm = &ov02e10_pm_ops,
 		.acpi_match_table = ov02e10_acpi_ids,
+		.of_match_table = ov02e10_of_match,
 	},
 	.probe = ov02e10_probe,
 	.remove = ov02e10_remove,

-- 
2.48.1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it
  2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
                   ` (7 preceding siblings ...)
  2025-03-17  0:39 ` [PATCH 8/8] media: i2c: ov02e10: Add OF probe support Bryan O'Donoghue
@ 2025-03-17  9:02 ` Hans de Goede
  2025-03-17 17:33   ` Alan Stern
  8 siblings, 1 reply; 14+ messages in thread
From: Hans de Goede @ 2025-03-17  9:02 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab, Sakari Ailus,
	Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	linux-kernel, linux-media

Hi Bryan, Alan,

On 17-Mar-25 01:39, Bryan O'Donoghue wrote:
> Similar to Hans' progressive series on ov02c10 I've picked up via Hans the
> IPU6 driver with some additional fixes from Alan Stern.
> 
> https://lore.kernel.org/linux-media/20250313184314.91410-1-hdegoede@redhat.com
> 
> I've made a number of initial changes to this driver sufficient to get it
> working on the Dell Inspiron 14 Plus 7441 with the Qualcomm X Elite
> x1e80100 SoC and its CAMSS stack.
> 
> link: https://git.codelinaro.org/bryan.odonoghue/kernel/-/tree/x1e80100-6.14-rc6-inspirion14-slim7x-camss?ref_type=heads
> 
> This is a first pass which adds in the minimum to get the sensor going on
> the Qualcomm platform. What would be nice would be someone on the IPU6 side
> giving it a test.
> 
> A big TBD here is the YAML for this file but, I'd like to make sure nothing
> has broken for IPU6/IPU7 with the modifications before diving into
> Documentation.
> 
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>

Bryan, thank you for your work on this.

Alan, can you give this version of the driver a test run on your laptop?

Note you need to skip patch 3/8 for now until I've implemented the
solution for handshake handling in the INT3472 driver. Also you need
keep the sleep of at least 25 ms after setting the handshake GPIO.

Regards,

Hans





> ---
> Bryan O'Donoghue (7):
>       media: i2c: ov02e10: Fix initial indentation and spacing
>       media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO
>       media: i2c: ov02e10: Convert to regulator_bulk API
>       media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF
>       media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up
>       media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err()
>       media: i2c: ov02e10: Add OF probe support
> 
> Jingjing Xiong (1):
>       media: i2c: add OV02E10 image sensor driver
> 
>  drivers/media/i2c/Kconfig   |  10 +
>  drivers/media/i2c/Makefile  |   1 +
>  drivers/media/i2c/ov02e10.c | 978 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 989 insertions(+)
> ---
> base-commit: f2151613e040973c868d28c8b00885dfab69eb75
> change-id: 20250317-b4-media-comitters-next-25-03-13-ov02e10-23e68307d589
> 
> Best regards,


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up
  2025-03-17  0:39 ` [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up Bryan O'Donoghue
@ 2025-03-17  9:04   ` Hans de Goede
  0 siblings, 0 replies; 14+ messages in thread
From: Hans de Goede @ 2025-03-17  9:04 UTC (permalink / raw)
  To: Bryan O'Donoghue, Mauro Carvalho Chehab, Sakari Ailus,
	Liam Girdwood, Mark Brown
  Cc: Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, Alan Stern,
	linux-kernel, linux-media

Hi,

On 17-Mar-25 01:39, Bryan O'Donoghue wrote:
> The ov02e10 specification says for power-on:
> 
> t3 = the time between dvdd stable and XSHUTDOWN deassert
> t5 = the time between XSHUTDOWN deassert and SCCB ready
> 
> The power-off path in the spec shows no required delays between XSHUTDONW
> and power-rail shut off so power-off is left alone.
> 
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
> ---
>  drivers/media/i2c/ov02e10.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
> index 9ad70671a718ea0aaf80ad3adcc5738ee57a7ff6..40c4d3ee36e3e2a0bb8be3ff10d016e2bb9bbc9d 100644
> --- a/drivers/media/i2c/ov02e10.c
> +++ b/drivers/media/i2c/ov02e10.c
> @@ -579,7 +579,11 @@ static int ov02e10_power_on(struct device *dev)
>  		goto disable_clk;
>  	}
>  
> -	gpiod_set_value_cansleep(ov02e10->reset, 0);
> +	if (ov02e10->reset) {
> +		usleep_range(5000, 5100);
> +		gpiod_set_value_cansleep(ov02e10->reset, 0);
> +		usleep_range(8000, 8100);
> +	}
>  
>  	return 0;
>  


Note ATM ov02e10->reset is requested with GPIOD_OUT_LOW and it is not
guaranteed that it was high before that. It really should be requested
with GPIOD_OUT_HIGH so that it is guaranteed to be high before
ov02e10_power_on() gets called as the code expects here.

Regards,

Hans



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it
  2025-03-17  9:02 ` [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Hans de Goede
@ 2025-03-17 17:33   ` Alan Stern
  2025-03-17 20:56     ` Bryan O'Donoghue
  0 siblings, 1 reply; 14+ messages in thread
From: Alan Stern @ 2025-03-17 17:33 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Bryan O'Donoghue, Mauro Carvalho Chehab, Sakari Ailus,
	Liam Girdwood, Mark Brown, Jingjing Xiong, Hao Yao, Jim Lai,
	You-Sheng Yang, linux-kernel, linux-media

On Mon, Mar 17, 2025 at 10:02:41AM +0100, Hans de Goede wrote:
> Bryan, thank you for your work on this.
> 
> Alan, can you give this version of the driver a test run on your laptop?
> 
> Note you need to skip patch 3/8 for now until I've implemented the
> solution for handshake handling in the INT3472 driver. Also you need
> keep the sleep of at least 25 ms after setting the handshake GPIO.

After installing all the patches except for 3/8 (and adjusting some of 
the later ones to account for its lack), I can confirm that the camera 
on my laptop still works.

Is there any particular aspect you want me to test or is basic 
functionality good enough?

Alan

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it
  2025-03-17 17:33   ` Alan Stern
@ 2025-03-17 20:56     ` Bryan O'Donoghue
  0 siblings, 0 replies; 14+ messages in thread
From: Bryan O'Donoghue @ 2025-03-17 20:56 UTC (permalink / raw)
  To: Alan Stern, Hans de Goede
  Cc: Mauro Carvalho Chehab, Sakari Ailus, Liam Girdwood, Mark Brown,
	Jingjing Xiong, Hao Yao, Jim Lai, You-Sheng Yang, linux-kernel,
	linux-media

On 17/03/2025 17:33, Alan Stern wrote:
> After installing all the patches except for 3/8 (and adjusting some of
> the later ones to account for its lack), I can confirm that the camera
> on my laptop still works.
> 
> Is there any particular aspect you want me to test or is basic
> functionality good enough?
> 
> Alan

No not really thank you, just a smoke test to make sure nothing broke.

---
bod

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/8] media: i2c: add OV02E10 image sensor driver
  2025-03-17  0:39 ` [PATCH 1/8] media: i2c: add OV02E10 image sensor driver Bryan O'Donoghue
@ 2025-03-21 16:38   ` Dan Carpenter
  0 siblings, 0 replies; 14+ messages in thread
From: Dan Carpenter @ 2025-03-21 16:38 UTC (permalink / raw)
  To: oe-kbuild, Bryan O'Donoghue, Mauro Carvalho Chehab,
	Sakari Ailus, Liam Girdwood, Mark Brown
  Cc: lkp, oe-kbuild-all, linux-media, Jingjing Xiong, Hao Yao,
	Jim Lai, You-Sheng Yang, Alan Stern, Hans de Goede, linux-kernel,
	Bryan O'Donoghue

Hi Bryan,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Bryan-O-Donoghue/media-i2c-add-OV02E10-image-sensor-driver/20250317-084316
base:   f2151613e040973c868d28c8b00885dfab69eb75
patch link:    https://lore.kernel.org/r/20250317-b4-media-comitters-next-25-03-13-ov02e10-v1-1-bd924634b889%40linaro.org
patch subject: [PATCH 1/8] media: i2c: add OV02E10 image sensor driver
config: openrisc-randconfig-r073-20250321 (https://download.01.org/0day-ci/archive/20250322/202503220055.pt1r9P7M-lkp@intel.com/config)
compiler: or1k-linux-gcc (GCC) 11.5.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202503220055.pt1r9P7M-lkp@intel.com/

smatch warnings:
drivers/media/i2c/ov02e10.c:903 ov02e10_probe() warn: missing unwind goto?

vim +903 drivers/media/i2c/ov02e10.c

62323d3b3c4dc1 Jingjing Xiong 2025-03-17  862  static int ov02e10_probe(struct i2c_client *client)
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  863  {
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  864  	struct ov02e10 *ov02e;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  865  	int ret;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  866  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  867  	/* Check HW config */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  868  	ret = ov02e10_check_hwcfg(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  869  	if (ret)
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  870  		return ret;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  871  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  872  	ov02e = devm_kzalloc(&client->dev, sizeof(*ov02e), GFP_KERNEL);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  873  	if (!ov02e)
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  874  		return -ENOMEM;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  875  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  876  	/* Initialize subdev */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  877  	ov02e->regmap = devm_cci_regmap_init_i2c(client, 8);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  878  	if (IS_ERR(ov02e->regmap))
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  879  		return PTR_ERR(ov02e->regmap);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  880  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  881  	v4l2_i2c_subdev_init(&ov02e->sd, client, &ov02e10_subdev_ops);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  882  	ov02e10_get_pm_resources(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  883  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  884  	ret = ov02e10_power_on(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  885  	if (ret) {
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  886  		dev_err_probe(&client->dev, ret, "failed to power on\n");
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  887  		goto error_power_off;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  888  	}
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  889  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  890  	/* Check module identity */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  891  	ret = ov02e10_identify_module(ov02e);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  892  	if (ret) {
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  893  		dev_err(&client->dev, "failed to find sensor: %d\n", ret);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  894  		goto error_power_off;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  895  	}
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  896  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  897  	/* Set default mode to max resolution */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  898  	ov02e->cur_mode = &supported_modes[0];
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  899  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  900  	dev_dbg(&client->dev, "will Init controls\n");
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  901  	ret = ov02e10_init_controls(ov02e);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  902  	if (ret)
62323d3b3c4dc1 Jingjing Xiong 2025-03-17 @903  		return ret;

goto error_power_off;?

62323d3b3c4dc1 Jingjing Xiong 2025-03-17  904  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  905  	/* Initialize subdev */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  906  	ov02e->sd.internal_ops = &ov02e10_internal_ops;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  907  	ov02e->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  908  	ov02e->sd.entity.ops = &ov02e10_subdev_entity_ops;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  909  	ov02e->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  910  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  911  	/* Initialize source pad */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  912  	ov02e->pad.flags = MEDIA_PAD_FL_SOURCE;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  913  	ret = media_entity_pads_init(&ov02e->sd.entity, 1, &ov02e->pad);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  914  	if (ret) {
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  915  		dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  916  		goto error_handler_free;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  917  	}
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  918  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  919  	ret = v4l2_async_register_subdev_sensor(&ov02e->sd);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  920  	if (ret < 0) {
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  921  		dev_err(&client->dev, "async reg subdev error\n");
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  922  		goto error_media_entity;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  923  	}
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  924  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  925  	/*
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  926  	 * Device is already turned on by i2c-core with ACPI domain PM.
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  927  	 * Enable runtime PM and turn off the device.
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  928  	 */
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  929  	pm_runtime_set_active(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  930  	pm_runtime_enable(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  931  	pm_runtime_idle(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  932  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  933  	return 0;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  934  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  935  error_media_entity:
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  936  	media_entity_cleanup(&ov02e->sd.entity);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  937  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  938  error_handler_free:
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  939  	v4l2_ctrl_handler_free(ov02e->sd.ctrl_handler);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  940  	mutex_destroy(&ov02e->mutex);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  941  	dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  942  error_power_off:
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  943  	ov02e10_power_off(&client->dev);
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  944  
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  945  	dev_dbg(&client->dev, "probe done\n");
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  946  	return ret;
62323d3b3c4dc1 Jingjing Xiong 2025-03-17  947  }

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2025-03-21 16:38 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-17  0:39 [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 1/8] media: i2c: add OV02E10 image sensor driver Bryan O'Donoghue
2025-03-21 16:38   ` Dan Carpenter
2025-03-17  0:39 ` [PATCH 2/8] media: i2c: ov02e10: Fix initial indentation and spacing Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 3/8] media: i2c: ov02e10: Drop IPU6 FPGA specific handshake GPIO Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 4/8] media: i2c: ov02e10: Convert to regulator_bulk API Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 5/8] media: i2c: ov02e10: Rework MCLK clock get logic to facilitate OF Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 6/8] media: i2c: ov02e10: Implement specification t3 and t5 delays on power-up Bryan O'Donoghue
2025-03-17  9:04   ` Hans de Goede
2025-03-17  0:39 ` [PATCH 7/8] media: i2c: ov02e10: Remove redundant dev_dbg() and some extra dev_err() Bryan O'Donoghue
2025-03-17  0:39 ` [PATCH 8/8] media: i2c: ov02e10: Add OF probe support Bryan O'Donoghue
2025-03-17  9:02 ` [PATCH 0/8] RFT/RFC: Import IPU6 ov02e10 sensor driver and enable OF usage of it Hans de Goede
2025-03-17 17:33   ` Alan Stern
2025-03-17 20:56     ` Bryan O'Donoghue

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®