linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
@ 2025-07-14 20:51 Pratap Nirujogi
  2025-07-15 11:54 ` Bryan O'Donoghue
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Pratap Nirujogi @ 2025-07-14 20:51 UTC (permalink / raw)
  To: mchehab, sakari.ailus, laurent.pinchart, kieran.bingham,
	bryan.odonoghue, hao.yao, mehdi.djait, dongcheng.yan, hverkuil,
	krzk, dave.stevenson, hdegoede, jai.luthra, tomi.valkeinen
  Cc: linux-media, linux-kernel, benjamin.chan, bin.du, grosikop,
	king.li, dantony, vengutta, Phil.Jawich, Pratap Nirujogi

Add driver for OmniVision 5.2M OV05C10 sensor. This driver
supports only the full size normal 2888x1808@30fps 2-lane
sensor profile.

Co-developed-by: Venkata Narendra Kumar Gutta <vengutta@amd.com>
Signed-off-by: Venkata Narendra Kumar Gutta <vengutta@amd.com>
Co-developed-by: Bin Du <bin.du@amd.com>
Signed-off-by: Bin Du <bin.du@amd.com>
Signed-off-by: Pratap Nirujogi <pratap.nirujogi@amd.com>
---
Changes v3 -> v4:

* Use link frequency@900MHz and remove redundant ov05c10_link_frequencies[] definition
* Remove ov05c10_switch_page(), include page switch logic in ov05c10_reg_read() and ov05c10_reg_write()
* Fix VTS and HTS related config issues
* Add CCI helper macros to embed page number in the CCI private bits 28-31
* Remove ov05c10_switch_page(), include page switch logic in ov05c10_reg_read() and ov05c10_reg_write()
* Inline single-use functions - ov05c10_update_pad_format(), ov05c10_start_streaming(), ov05c10_stop_streaming()
* Move ov05c10_test_pattern_menu just above ov05c10_init_controls()
* Move ov05c10_init_state() to the other subdev ops
* Move v4l2_i2c_subdev_init() just before ov05c10_init_controls() to keep all subdev initialization grouped
* Use cached cur_mode instead of accessing global const array ov05c10_supported_modes in ov05c10_update_vblank(), ov05c10_init_controls() and ov05c10_set_ctrl()
* Rename macro OV05C10_REG_TEST_PATTERN_XXX to OV05C10_REG_TEST_PATTERN_CTL
* Change val type from u64 to u32 in ov05c10_update_vblank() and ov05c10_update_digital_gain()
* Call OV05C10_REG_TRIGGER once in ov05c10_set_ctrl() instead of calling in every update control function
* Store a struct device *dev in struct ov05c10, and access it instead of client->dev all the time
* Remove unnecessary if conditions and just do the action unconditionally
* Add .colorspace = V4L2_COLORSPACE_RAW to define the colorspace used by the format
* Remove ov05c10_start_streaming() and ov05c10_stop_streaming(), and include the respective code in ov05c10_enable_streams() and ov05c10_disable_streams()
* Drop the supported_mode.lanes field and use OV05C10_DATA_LANES
* Use ov05c10->link_freq_bitmap to select which link frequencies to expose to userspace
* Use devm_v4l2_sensor_clk_get() to get clock-frequency
* In ov05c10_probe, modify runtime PM implementation to make sensor work normally in both CONFIG_PM enabled and disabled cases
* Grouped macro definitions with other related macros at top of file
* Couldn't update hardcoded register offset values with valid register names due to lack of info from sensor vendor
* Fix typo, naming, indentations and other cosmetic errors

Dependencies:

link: https://lore.kernel.org/linux-media/8ecbcafbd91b25ad5e188dbe127b921a1643027e.1750942967.git.mehdi.djait@linux.intel.com/

 MAINTAINERS                 |    8 +
 drivers/media/i2c/Kconfig   |   10 +
 drivers/media/i2c/Makefile  |    1 +
 drivers/media/i2c/ov05c10.c | 1105 +++++++++++++++++++++++++++++++++++
 4 files changed, 1124 insertions(+)
 create mode 100644 drivers/media/i2c/ov05c10.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 60bba48f5479..6aa6dcc78c07 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18344,6 +18344,14 @@ T:	git git://linuxtv.org/media.git
 F:	Documentation/devicetree/bindings/media/i2c/ovti,ov02e10.yaml
 F:	drivers/media/i2c/ov02e10.c
 
+OMNIVISION OV05C10 SENSOR DRIVER
+M:	Nirujogi Pratap <pratap.nirujogi@amd.com>
+M:	Bin Du <bin.du@amd.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media.git
+F:	drivers/media/i2c/ov05c10.c
+
 OMNIVISION OV08D10 SENSOR DRIVER
 M:	Jimmy Su <jimmy.su@intel.com>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e68202954a8f..1662fb29d75c 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -377,6 +377,16 @@ config VIDEO_OV02C10
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov02c10.
 
+config VIDEO_OV05C10
+	tristate "OmniVision OV05C10 sensor support"
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the OmniVision
+	  OV05C10 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called OV05C10.
+
 config VIDEO_OV08D10
         tristate "OmniVision OV08D10 sensor support"
         help
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 5873d29433ee..b4a1d721a7f2 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
 obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
 obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o
 obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o
+obj-$(CONFIG_VIDEO_OV05C10) += ov05c10.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/ov05c10.c b/drivers/media/i2c/ov05c10.c
new file mode 100644
index 000000000000..c6385f4525bc
--- /dev/null
+++ b/drivers/media/i2c/ov05c10.c
@@ -0,0 +1,1105 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (C) 2025 Advanced Micro Devices, Inc.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/units.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define DRV_NAME			"ov05c10"
+#define OV05C10_REF_CLK			(24 * HZ_PER_MHZ)
+
+#define MODE_WIDTH  2888
+#define MODE_HEIGHT 1808
+
+/*
+ * Macros to define pages of register address with encoded
+ * into the higher private bits.
+ */
+#define OV05C10_REG_PAGE_SHIFT		(CCI_REG_PRIVATE_SHIFT)
+#define OV05C10_REG_PAGE_MASK		(CCI_REG_PRIVATE_MASK)
+#define OV05C10_PAGE_REG8(p, x)		(((p) << OV05C10_REG_PAGE_SHIFT) | (1 << CCI_REG_WIDTH_SHIFT) | (x))
+#define OV05C10_PAGE_REG16(p, x)	(((p) << OV05C10_REG_PAGE_SHIFT) | (2 << CCI_REG_WIDTH_SHIFT) | (x))
+#define OV05C10_PAGE_REG24(p, x)	(((p) << OV05C10_REG_PAGE_SHIFT) | (3 << CCI_REG_WIDTH_SHIFT) | (x))
+#define OV05C10_PAGE_GET(x)		FIELD_GET(OV05C10_REG_PAGE_MASK, x)
+
+/* Page selection */
+#define OV05C10_REG_PAGE_CTL		CCI_REG8(0xfd)
+
+/* Chip ID */
+#define OV05C10_REG_CHIP_ID		OV05C10_PAGE_REG24(0x00, 0x00)
+#define OV05C10_CHIP_ID			0x43055610
+
+/* Control registers */
+#define OV05C10_REG_TRIGGER		OV05C10_PAGE_REG8(0x01, 0x01)
+#define OV05C_REG_TRIGGER_START		BIT(0)
+
+/* Exposure control */
+#define OV05C10_REG_EXPOSURE		OV05C10_PAGE_REG24(0x01, 0x02)
+#define OV05C10_EXPOSURE_MAX_MARGIN	33
+#define OV05C10_EXPOSURE_MIN		4
+#define OV05C10_EXPOSURE_STEP		1
+#define OV05C10_EXPOSURE_DEFAULT	0x40
+
+/* V_TIMING internal */
+#define OV05C10_REG_VTS			OV05C10_PAGE_REG16(0x01, 0x05)
+#define OV05C10_REG_TIMING_VTS		OV05C10_PAGE_REG16(0x01, 0x35)
+#define OV05C10_VTS_30FPS		1860
+#define OV05C10_VTS_MAX			0x7fff
+
+/* Test Pattern Control */
+#define OV05C10_REG_TEST_PATTERN	OV05C10_PAGE_REG8(0x04, 0x12)
+#define OV05C10_TEST_PATTERN_ENABLE	BIT(0)
+#define OV05C10_REG_TEST_PATTERN_CTL	OV05C10_PAGE_REG8(0x04, 0xf3)
+#define OV05C10_REG_TEST_PATTERN_CTL_COLOR_BAR	BIT(0)
+
+/* Digital gain control */
+#define OV05C10_REG_DGTL_GAIN		OV05C10_PAGE_REG8(0x01, 0x21)
+
+#define OV05C10_DGTL_GAIN_MIN		0x40
+#define OV05C10_DGTL_GAIN_MAX		0xff
+#define OV05C10_DGTL_GAIN_DEFAULT	0x40
+#define OV05C10_DGTL_GAIN_STEP		1
+
+/* Analog gain control */
+#define OV05C10_REG_ANALOG_GAIN		OV05C10_PAGE_REG8(0x01, 0x24)
+#define OV05C10_ANA_GAIN_MIN		0x80
+#define OV05C10_ANA_GAIN_MAX		0x07c0
+#define OV05C10_ANA_GAIN_STEP		1
+#define OV05C10_ANA_GAIN_DEFAULT	0x80
+
+/* H TIMING internal */
+#define OV05C10_REG_HTS			OV05C10_PAGE_REG16(0x01, 0x37)
+#define OV05C10_HTS_30FPS		0x0280
+
+/* Page selection */
+#define OV05C10_REG_PAGE_CTL		CCI_REG8(0xfd)
+
+#define OV05C10_NUM_OF_PADS 1
+
+/* Configurations for supported link frequencies */
+#define OV05C10_LINK_FREQ_900MHZ	(900 * HZ_PER_MHZ)
+
+/* Number of lanes supported */
+#define OV05C10_DATA_LANES		2
+
+/* Bits per sample of sensor output */
+#define OV05C10_BITS_PER_SAMPLE		10
+
+enum {
+	OV05C10_LINK_FREQ_900MHZ_INDEX,
+};
+
+struct ov05c10_reg_list {
+	u32 num_of_regs;
+	const struct cci_reg_sequence *regs;
+};
+
+/* Mode : resolution and related config&values */
+struct ov05c10_mode {
+	/* Frame width */
+	u32 width;
+	/* Frame height */
+	u32 height;
+	/* number of lanes */
+	u32 lanes;
+
+	/* V-timing */
+	u32 vts_def;
+	u32 vts_min;
+
+	/* HTS */
+	u32 hts;
+
+	/* Index of Link frequency config to be used */
+	u32 link_freq_index;
+
+	/* Default register values */
+	struct ov05c10_reg_list reg_list;
+};
+
+/* 2888x1808 30fps, 1800mbps, 2lane, 24mhz */
+static const struct cci_reg_sequence ov05c10_2888x1808_regs[] = {
+	{ OV05C10_PAGE_REG8(0x00, 0x20),  0x00 },
+	{ OV05C10_PAGE_REG8(0x00, 0x20),  0x0b },
+	{ OV05C10_PAGE_REG8(0x00, 0xc1),  0x09 },
+	{ OV05C10_PAGE_REG8(0x00, 0x21),  0x06 },
+	{ OV05C10_PAGE_REG8(0x00, 0x14),  0x78 },
+	{ OV05C10_PAGE_REG8(0x00, 0xe7),  0x03 },
+	{ OV05C10_PAGE_REG8(0x00, 0xe7),  0x00 },
+	{ OV05C10_PAGE_REG8(0x00, 0x21),  0x00 },
+	{ OV05C10_PAGE_REG8(0x01, 0x03),  0x00 },
+	{ OV05C10_PAGE_REG8(0x01, 0x04),  0x06 },
+	{ OV05C10_PAGE_REG8(0x01, 0x05),  0x07 },
+	{ OV05C10_PAGE_REG8(0x01, 0x06),  0x44 },
+	{ OV05C10_PAGE_REG8(0x01, 0x07),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x1b),  0x01 },
+	{ OV05C10_PAGE_REG8(0x01, 0x24),  0xff },
+	{ OV05C10_PAGE_REG8(0x01, 0x32),  0x03 },
+	{ OV05C10_PAGE_REG8(0x01, 0x42),  0x5d },
+	{ OV05C10_PAGE_REG8(0x01, 0x43),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x44),  0x81 },
+	{ OV05C10_PAGE_REG8(0x01, 0x46),  0x5f },
+	{ OV05C10_PAGE_REG8(0x01, 0x48),  0x18 },
+	{ OV05C10_PAGE_REG8(0x01, 0x49),  0x04 },
+	{ OV05C10_PAGE_REG8(0x01, 0x5c),  0x18 },
+	{ OV05C10_PAGE_REG8(0x01, 0x5e),  0x13 },
+	{ OV05C10_PAGE_REG8(0x01, 0x70),  0x15 },
+	{ OV05C10_PAGE_REG8(0x01, 0x77),  0x35 },
+	{ OV05C10_PAGE_REG8(0x01, 0x79),  0x00 },
+	{ OV05C10_PAGE_REG8(0x01, 0x7b),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x7d),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x7e),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x7f),  0x08 },
+	{ OV05C10_PAGE_REG8(0x01, 0x90),  0x37 },
+	{ OV05C10_PAGE_REG8(0x01, 0x91),  0x05 },
+	{ OV05C10_PAGE_REG8(0x01, 0x92),  0x18 },
+	{ OV05C10_PAGE_REG8(0x01, 0x93),  0x27 },
+	{ OV05C10_PAGE_REG8(0x01, 0x94),  0x05 },
+	{ OV05C10_PAGE_REG8(0x01, 0x95),  0x38 },
+	{ OV05C10_PAGE_REG8(0x01, 0x9b),  0x00 },
+	{ OV05C10_PAGE_REG8(0x01, 0x9c),  0x06 },
+	{ OV05C10_PAGE_REG8(0x01, 0x9d),  0x28 },
+	{ OV05C10_PAGE_REG8(0x01, 0x9e),  0x06 },
+	{ OV05C10_PAGE_REG8(0x01, 0xb2),  0x0f },
+	{ OV05C10_PAGE_REG8(0x01, 0xb3),  0x29 },
+	{ OV05C10_PAGE_REG8(0x01, 0xbf),  0x3c },
+	{ OV05C10_PAGE_REG8(0x01, 0xc2),  0x04 },
+	{ OV05C10_PAGE_REG8(0x01, 0xc4),  0x00 },
+	{ OV05C10_PAGE_REG8(0x01, 0xca),  0x20 },
+	{ OV05C10_PAGE_REG8(0x01, 0xcb),  0x20 },
+	{ OV05C10_PAGE_REG8(0x01, 0xcc),  0x28 },
+	{ OV05C10_PAGE_REG8(0x01, 0xcd),  0x28 },
+	{ OV05C10_PAGE_REG8(0x01, 0xce),  0x20 },
+	{ OV05C10_PAGE_REG8(0x01, 0xcf),  0x20 },
+	{ OV05C10_PAGE_REG8(0x01, 0xd0),  0x2a },
+	{ OV05C10_PAGE_REG8(0x01, 0xd1),  0x2a },
+	{ OV05C10_PAGE_REG8(0x0f, 0x00),  0x00 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x01),  0xa0 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x02),  0x48 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x07),  0x8f },
+	{ OV05C10_PAGE_REG8(0x0f, 0x08),  0x70 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x09),  0x01 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x0b),  0x40 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x0d),  0x07 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x11),  0x33 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x12),  0x77 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x13),  0x66 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x14),  0x65 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x15),  0x37 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x16),  0xbf },
+	{ OV05C10_PAGE_REG8(0x0f, 0x17),  0xff },
+	{ OV05C10_PAGE_REG8(0x0f, 0x18),  0xff },
+	{ OV05C10_PAGE_REG8(0x0f, 0x19),  0x12 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x1a),  0x10 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x1c),  0x77 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x1d),  0x77 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x20),  0x0f },
+	{ OV05C10_PAGE_REG8(0x0f, 0x21),  0x0f },
+	{ OV05C10_PAGE_REG8(0x0f, 0x22),  0x0f },
+	{ OV05C10_PAGE_REG8(0x0f, 0x23),  0x0f },
+	{ OV05C10_PAGE_REG8(0x0f, 0x2b),  0x20 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x2c),  0x20 },
+	{ OV05C10_PAGE_REG8(0x0f, 0x2d),  0x04 },
+	{ OV05C10_PAGE_REG8(0x03, 0x9d),  0x0f },
+	{ OV05C10_PAGE_REG8(0x03, 0x9f),  0x40 },
+	{ OV05C10_PAGE_REG8(0x00, 0x20),  0x1b },
+	{ OV05C10_PAGE_REG8(0x04, 0x19),  0x60 },
+	{ OV05C10_PAGE_REG8(0x02, 0x75),  0x05 },
+	{ OV05C10_PAGE_REG8(0x02, 0x7f),  0x06 },
+	{ OV05C10_PAGE_REG8(0x02, 0x9a),  0x03 },
+	{ OV05C10_PAGE_REG8(0x02, 0xa2),  0x07 },
+	{ OV05C10_PAGE_REG8(0x02, 0xa3),  0x10 },
+	{ OV05C10_PAGE_REG8(0x02, 0xa5),  0x02 },
+	{ OV05C10_PAGE_REG8(0x02, 0xa6),  0x0b },
+	{ OV05C10_PAGE_REG8(0x02, 0xa7),  0x48 },
+	{ OV05C10_PAGE_REG8(0x07, 0x42),  0x00 },
+	{ OV05C10_PAGE_REG8(0x07, 0x43),  0x80 },
+	{ OV05C10_PAGE_REG8(0x07, 0x44),  0x00 },
+	{ OV05C10_PAGE_REG8(0x07, 0x45),  0x80 },
+	{ OV05C10_PAGE_REG8(0x07, 0x46),  0x00 },
+	{ OV05C10_PAGE_REG8(0x07, 0x47),  0x80 },
+	{ OV05C10_PAGE_REG8(0x07, 0x48),  0x00 },
+	{ OV05C10_PAGE_REG8(0x07, 0x49),  0x80 },
+	{ OV05C10_PAGE_REG8(0x07, 0x00),  0xf7 },
+	{ OV05C10_PAGE_REG8(0x00, 0xe7),  0x03 },
+	{ OV05C10_PAGE_REG8(0x00, 0xe7),  0x00 },
+	{ OV05C10_PAGE_REG8(0x00, 0x93),  0x18 },
+	{ OV05C10_PAGE_REG8(0x00, 0x94),  0xff },
+	{ OV05C10_PAGE_REG8(0x00, 0x95),  0xbd },
+	{ OV05C10_PAGE_REG8(0x00, 0x96),  0x1a },
+	{ OV05C10_PAGE_REG8(0x00, 0x98),  0x04 },
+	{ OV05C10_PAGE_REG8(0x00, 0x99),  0x08 },
+	{ OV05C10_PAGE_REG8(0x00, 0x9b),  0x10 },
+	{ OV05C10_PAGE_REG8(0x00, 0x9c),  0x3f },
+	{ OV05C10_PAGE_REG8(0x00, 0xa1),  0x05 },
+	{ OV05C10_PAGE_REG8(0x00, 0xa4),  0x2f },
+	{ OV05C10_PAGE_REG8(0x00, 0xc0),  0x0c },
+	{ OV05C10_PAGE_REG8(0x00, 0xc1),  0x08 },
+	{ OV05C10_PAGE_REG8(0x00, 0xc2),  0x00 },
+	{ OV05C10_PAGE_REG8(0x00, 0xb6),  0x20 },
+	{ OV05C10_PAGE_REG8(0x00, 0xbb),  0x80 },
+	{ OV05C10_PAGE_REG8(0x00, 0xa0),  0x01 },
+};
+
+static const struct cci_reg_sequence mode_OV05C10_stream_on_regs[] = {
+	{ OV05C10_PAGE_REG8(0x01, 0x33), 0x03 },
+	{ OV05C10_PAGE_REG8(0x01, 0x01), 0x02 },
+	{ OV05C10_PAGE_REG8(0x00, 0x20), 0x1f },
+	{ OV05C10_PAGE_REG8(0x00, 0xfd), 0x01 },
+};
+
+static const struct cci_reg_sequence mode_OV05C10_stream_off_regs[] = {
+	{ OV05C10_PAGE_REG8(0x00, 0x20), 0x5b },
+	{ OV05C10_PAGE_REG8(0x01, 0x33), 0x02 },
+	{ OV05C10_PAGE_REG8(0x01, 0x01), 0x02 },
+};
+
+/*
+ * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
+ * data rate => double data rate; number of lanes => 2; bits per pixel => 10
+ */
+static u64 link_freq_to_pixel_rate(u64 f, u32 lane_nr)
+{
+	f *= 2 * lane_nr;
+	do_div(f, OV05C10_BITS_PER_SAMPLE);
+
+	return f;
+}
+
+/* Menu items for LINK_FREQ V4L2 control */
+static const s64 ov05c10_link_freq_menu_items[] = {
+	OV05C10_LINK_FREQ_900MHZ,
+};
+
+/* Mode configs, currently, only support 1 mode */
+static const struct ov05c10_mode ov05c10_supported_modes[] = {
+	{
+		.width = MODE_WIDTH,
+		.height = MODE_HEIGHT,
+		.vts_def = OV05C10_VTS_30FPS,
+		.vts_min = OV05C10_VTS_30FPS,
+		.hts = 640,
+		.lanes = 2,
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(ov05c10_2888x1808_regs),
+			.regs = ov05c10_2888x1808_regs,
+		},
+		.link_freq_index = OV05C10_LINK_FREQ_900MHZ_INDEX,
+	},
+};
+
+struct ov05c10 {
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+
+	/* V4L2 control handler */
+	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 regmap *regmap;
+
+	/* gpio descriptor */
+	struct gpio_desc *enable_gpio;
+
+	/* Pointer to current selected sensor mode */
+	const struct ov05c10_mode *cur_mode;
+
+	/* Menu bitmap for link_freq_ctrl */
+	unsigned long link_freq_bitmap;
+	struct clk *clk;
+
+	/* Current page for sensor register control */
+	int cur_page;
+	u32 page_ctrl_reg;
+};
+
+static inline struct ov05c10 *to_ov05c10(struct v4l2_subdev *_sd)
+{
+	return container_of(_sd, struct ov05c10, sd);
+}
+
+static int ov05c10_reg_read(struct ov05c10 *ov05c10,
+			    u32 reg, u64 *val, int *err)
+{
+	int ret;
+	u8 page;
+
+	page = OV05C10_PAGE_GET(reg);
+	if (page == ov05c10->cur_page)
+		goto read_reg;
+
+	/* switch to register page */
+	ret = cci_write(ov05c10->regmap, ov05c10->page_ctrl_reg, page, err);
+	if (ret) {
+		dev_err(ov05c10->sd.dev, "Error switch page reg 0x%04x: %d\n",
+			reg, ret);
+		ov05c10->cur_page = -1;
+		goto out;
+	}
+
+	ov05c10->cur_page = page;
+read_reg:
+	ret = cci_read(ov05c10->regmap, reg, val, err);
+out:
+	return ret;
+}
+
+static int ov05c10_reg_write(struct ov05c10 *ov05c10, u32 reg, u64 val, int *err)
+{
+	int ret;
+	u8 page;
+
+	page = OV05C10_PAGE_GET(reg);
+	if (page == ov05c10->cur_page)
+		goto write_reg;
+
+	/* switch to register page */
+	ret = cci_write(ov05c10->regmap, ov05c10->page_ctrl_reg, page, err);
+	if (ret) {
+		dev_err(ov05c10->sd.dev, "Error switch page reg 0x%04x: %d\n",
+			reg, ret);
+		ov05c10->cur_page = -1;
+		goto out;
+	}
+
+	ov05c10->cur_page = page;
+write_reg:
+	ret = cci_write(ov05c10->regmap, reg, val, err);
+out:
+	return ret;
+}
+
+static int ov05c10_cci_multi_reg_pwrite(struct ov05c10 *ov05c10,
+					const struct cci_reg_sequence *regs,
+					unsigned int num_regs, int *err)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < num_regs; i++) {
+		ret = ov05c10_reg_write(ov05c10, regs[i].reg, regs[i].val, err);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ov05c10_cci_update_pbits(struct ov05c10 *ov05c10, u32 reg, u64 mask,
+				    u64 val, int *err)
+{
+	int ret = 0;
+	u8 page;
+
+	page = OV05C10_PAGE_GET(reg);
+	if (page == ov05c10->cur_page)
+		goto update_bits;
+
+	/* switch to register page */
+	ret = cci_write(ov05c10->regmap, ov05c10->page_ctrl_reg, page, err);
+	if (ret) {
+		dev_err(ov05c10->sd.dev, "Error switch page reg 0x%04x: %d\n",
+			reg, ret);
+		ov05c10->cur_page = -1;
+		goto out;
+	}
+
+	ret = cci_update_bits(ov05c10->regmap, reg, mask, val, err);
+update_bits:
+	ov05c10->cur_page = page;
+out:
+	return ret;
+}
+
+static int ov05c10_update_vblank(struct ov05c10 *ov05c10, u32 vblank)
+{
+	const struct ov05c10_mode *mode = ov05c10->cur_mode;
+	u64 vts;
+	u32 val;
+	int ret = 0;
+
+	val = mode->height + vblank;
+	ov05c10_reg_read(ov05c10, OV05C10_REG_TIMING_VTS, &vts, &ret);
+	if (ret)
+		goto out;
+
+	val = (val > vts) ? val - vts : 0;
+	ov05c10_reg_write(ov05c10, OV05C10_REG_VTS, val, &ret);
+out:
+	return ret;
+}
+
+static int ov05c10_update_exposure(struct ov05c10 *ov05c10, u32 exposure)
+{
+	int ret = 0;
+
+	ov05c10_reg_write(ov05c10, OV05C10_REG_EXPOSURE, exposure, &ret);
+
+	return ret;
+}
+
+static int ov05c10_update_analog_gain(struct ov05c10 *ov05c10, u32 a_gain)
+{
+	int ret = 0;
+
+	ov05c10_reg_write(ov05c10, OV05C10_REG_ANALOG_GAIN, a_gain, &ret);
+
+	return ret;
+}
+
+static int ov05c10_update_digital_gain(struct ov05c10 *ov05c10, u32 d_gain)
+{
+	int ret = 0;
+
+	ov05c10_reg_write(ov05c10, OV05C10_REG_DGTL_GAIN, d_gain, &ret);
+
+	return ret;
+}
+
+static int ov05c10_enable_test_pattern(struct ov05c10 *ov05c10, u32 pattern)
+{
+	int ret = 0;
+
+	if (pattern) {
+		ov05c10_cci_update_pbits(ov05c10,
+					 OV05C10_REG_TEST_PATTERN_CTL,
+					 OV05C10_REG_TEST_PATTERN_CTL_COLOR_BAR,
+					 1,
+					 &ret);
+		if (ret)
+			goto out;
+	}
+
+	ov05c10_cci_update_pbits(ov05c10,
+				 OV05C10_REG_TEST_PATTERN,
+				 OV05C10_TEST_PATTERN_ENABLE,
+				 pattern ? 1 : 0,
+				 &ret);
+	if (ret)
+		goto out;
+
+	ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER,
+			  OV05C_REG_TRIGGER_START, &ret);
+
+out:
+	return ret;
+}
+
+static int ov05c10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov05c10 *ov05c10 = container_of(ctrl->handler,
+					       struct ov05c10, ctrl_handler);
+	int ret = 0;
+	s64 max;
+
+	/* Propagate change of current control to all related controls */
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		s64 cur_exp = ov05c10->exposure->cur.val;
+
+		/* Update max exposure while meeting expected vblanking */
+		max = ov05c10->cur_mode->height + ctrl->val - OV05C10_EXPOSURE_MAX_MARGIN;
+		cur_exp = clamp(cur_exp, ov05c10->exposure->minimum, max);
+		ret = __v4l2_ctrl_modify_range(ov05c10->exposure,
+					       ov05c10->exposure->minimum,
+					       max, ov05c10->exposure->step,
+					       cur_exp);
+		if (!ret)
+			return ret;
+	}
+
+	/*
+	 * Applying V4L2 control value only happens
+	 * when power is up for streaming
+	 */
+	if (!pm_runtime_get_if_in_use(ov05c10->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = ov05c10_update_analog_gain(ov05c10, ctrl->val);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ret = ov05c10_update_digital_gain(ov05c10, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = ov05c10_update_exposure(ov05c10, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = ov05c10_update_vblank(ov05c10, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = ov05c10_enable_test_pattern(ov05c10, ctrl->val);
+		break;
+	default:
+		ret = -ENOTTY;
+		dev_err(ov05c10->dev,
+			"ctrl(id:0x%x,val:0x%x) is not handled\n",
+			ctrl->id, ctrl->val);
+		break;
+	}
+
+	ov05c10_reg_write(ov05c10, OV05C10_REG_TRIGGER,
+			  OV05C_REG_TRIGGER_START, &ret);
+
+	pm_runtime_mark_last_busy(ov05c10->dev);
+	pm_runtime_put_autosuspend(ov05c10->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov05c10_ctrl_ops = {
+	.s_ctrl = ov05c10_set_ctrl,
+};
+
+static int ov05c10_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	/* Only one bayer order(GRBG) is supported */
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+	return 0;
+}
+
+static int ov05c10_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(ov05c10_supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+		return -EINVAL;
+
+	fse->min_width = ov05c10_supported_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov05c10_supported_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov05c10_set_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_format *fmt)
+{
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+	struct v4l2_mbus_framefmt *framefmt;
+	const struct ov05c10_mode *mode;
+	s32 vblank_def;
+	s32 vblank_min;
+	s64 pixel_rate;
+	s64 link_freq;
+	s64 h_blank;
+
+	/* Only one raw bayer(GRBG) order is supported */
+	fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+	mode = v4l2_find_nearest_size(ov05c10_supported_modes,
+				      ARRAY_SIZE(ov05c10_supported_modes),
+				      width, height,
+				      fmt->format.width, fmt->format.height);
+
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+
+	framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
+	*framefmt = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		__v4l2_ctrl_s_ctrl(ov05c10->link_freq, mode->link_freq_index);
+		link_freq = ov05c10_link_freq_menu_items[mode->link_freq_index];
+		pixel_rate = link_freq_to_pixel_rate(link_freq,
+						     mode->lanes);
+		__v4l2_ctrl_s_ctrl_int64(ov05c10->pixel_rate, pixel_rate);
+
+		/* Update limits and set FPS to default */
+		vblank_def = mode->vts_def - mode->height;
+		vblank_min = mode->vts_min - mode->height;
+		__v4l2_ctrl_modify_range(ov05c10->vblank, vblank_min,
+					 OV05C10_VTS_MAX - mode->height,
+					 1, vblank_def);
+		__v4l2_ctrl_s_ctrl(ov05c10->vblank, vblank_def);
+		h_blank = mode->hts;
+		__v4l2_ctrl_modify_range(ov05c10->hblank, h_blank,
+					 h_blank, 1, h_blank);
+
+		ov05c10->cur_mode = mode;
+	}
+
+	return 0;
+}
+
+static void ov05c10_sensor_power_set(struct ov05c10 *ov05c10, bool on)
+{
+	if (on) {
+		gpiod_set_value(ov05c10->enable_gpio, 0);
+		usleep_range(10, 20);
+
+		gpiod_set_value(ov05c10->enable_gpio, 1);
+		usleep_range(1000, 2000);
+	} else {
+		gpiod_set_value(ov05c10->enable_gpio, 0);
+		usleep_range(10, 20);
+	}
+}
+
+static int ov05c10_enable_streams(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state, u32 pad,
+				  u64 streams_mask)
+{
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+	const struct ov05c10_reg_list *reg_list;
+	int ret = 0;
+
+	ret = pm_runtime_resume_and_get(ov05c10->dev);
+	if (ret < 0)
+		return ret;
+
+	/* Apply default values of current mode */
+	reg_list =  &ov05c10->cur_mode->reg_list;
+	ov05c10_cci_multi_reg_pwrite(ov05c10, reg_list->regs,
+				     reg_list->num_of_regs, &ret);
+	if (ret) {
+		dev_err(ov05c10->dev, "fail to set mode, ret: %d\n", ret);
+		goto err_rpm_put;
+	}
+
+	/* Apply customized values from user */
+	ret =  __v4l2_ctrl_handler_setup(ov05c10->sd.ctrl_handler);
+	if (ret) {
+		dev_err(ov05c10->dev, "failed to setup v4l2 handler %d\n", ret);
+		goto err_rpm_put;
+	}
+
+	ov05c10_cci_multi_reg_pwrite(ov05c10, mode_OV05C10_stream_on_regs,
+				     ARRAY_SIZE(mode_OV05C10_stream_on_regs), &ret);
+	if (ret) {
+		dev_err(ov05c10->dev, "fail to start the streaming\n");
+		goto err_rpm_put;
+	}
+
+	return 0;
+
+err_rpm_put:
+	pm_runtime_put(ov05c10->dev);
+	return ret;
+}
+
+static int ov05c10_disable_streams(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *state, u32 pad,
+				   u64 streams_mask)
+{
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+	int ret = 0;
+
+	ov05c10_cci_multi_reg_pwrite(ov05c10, mode_OV05C10_stream_off_regs,
+				     ARRAY_SIZE(mode_OV05C10_stream_off_regs), &ret);
+	if (ret)
+		dev_err(ov05c10->dev, "fail to stop the streaming\n");
+
+	pm_runtime_put(ov05c10->dev);
+
+	return 0;
+}
+
+static int ov05c10_init_state(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *sd_state)
+{
+	struct v4l2_mbus_framefmt *frame_fmt;
+	static const struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.format = {
+			.width = MODE_WIDTH,
+			.height = MODE_HEIGHT,
+			.code = MEDIA_BUS_FMT_SGRBG10_1X10,
+			.field = V4L2_FIELD_NONE,
+			.colorspace = V4L2_COLORSPACE_RAW,
+		}
+	};
+
+	frame_fmt = v4l2_subdev_state_get_format(sd_state, 0);
+	*frame_fmt = fmt.format;
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov05c10_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops ov05c10_pad_ops = {
+	.enum_mbus_code = ov05c10_enum_mbus_code,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = ov05c10_set_pad_format,
+	.enum_frame_size = ov05c10_enum_frame_size,
+	.enable_streams = ov05c10_enable_streams,
+	.disable_streams = ov05c10_disable_streams,
+};
+
+static const struct v4l2_subdev_ops ov05c10_subdev_ops = {
+	.video = &ov05c10_video_ops,
+	.pad = &ov05c10_pad_ops,
+};
+
+static const struct media_entity_operations ov05c10_subdev_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov05c10_internal_ops = {
+	.init_state = ov05c10_init_state,
+};
+
+static const char * const ov05c10_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+static int ov05c10_init_controls(struct ov05c10 *ov05c10)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr = &ov05c10->ctrl_handler;
+	const struct ov05c10_mode *mode = ov05c10->cur_mode;
+	struct v4l2_fwnode_device_properties props;
+	s64 pixel_rate_max;
+	s64 exposure_max;
+	s64 vblank_def;
+	s64 vblank_min;
+	u32 max_items;
+	s64 hblank;
+	int ret;
+
+	ret = v4l2_fwnode_device_parse(ov05c10->dev, &props);
+	if (ret)
+		goto err_hdl_free;
+
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+	if (ret)
+		return ret;
+
+	max_items = ARRAY_SIZE(ov05c10_link_freq_menu_items) - 1;
+	ov05c10->link_freq =
+		v4l2_ctrl_new_int_menu(ctrl_hdlr,
+				       NULL,
+				       V4L2_CID_LINK_FREQ,
+				       __fls(ov05c10->link_freq_bitmap),
+				       __ffs(ov05c10->link_freq_bitmap),
+				       ov05c10_link_freq_menu_items);
+	if (ov05c10->link_freq)
+		ov05c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	pixel_rate_max =
+		link_freq_to_pixel_rate(ov05c10_link_freq_menu_items[0],
+					mode->lanes);
+	ov05c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
+						V4L2_CID_PIXEL_RATE,
+						0, pixel_rate_max,
+						1, pixel_rate_max);
+
+	vblank_def = mode->vts_def - mode->height;
+	vblank_min = mode->vts_min - mode->height;
+	ov05c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
+					    V4L2_CID_VBLANK,
+					    vblank_min,
+					    OV05C10_VTS_MAX - mode->height,
+					    1, vblank_def);
+
+	hblank = mode->hts - mode->width;
+	ov05c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
+					    V4L2_CID_HBLANK,
+					    hblank, hblank, 1, hblank);
+	if (ov05c10->hblank)
+		ov05c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	exposure_max = mode->vts_def - OV05C10_EXPOSURE_MAX_MARGIN;
+	ov05c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
+					      V4L2_CID_EXPOSURE,
+					      OV05C10_EXPOSURE_MIN,
+					      exposure_max,
+					      OV05C10_EXPOSURE_STEP,
+					      exposure_max);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+			  OV05C10_ANA_GAIN_MIN, OV05C10_ANA_GAIN_MAX,
+			  OV05C10_ANA_GAIN_STEP, OV05C10_ANA_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+			  OV05C10_DGTL_GAIN_MIN, OV05C10_DGTL_GAIN_MAX,
+			  OV05C10_DGTL_GAIN_STEP, OV05C10_DGTL_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov05c10_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(ov05c10_test_pattern_menu) - 1,
+				     0, 0, ov05c10_test_pattern_menu);
+
+	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov05c10_ctrl_ops,
+					      &props);
+	if (ret)
+		goto err_hdl_free;
+
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		dev_err(ov05c10->dev, "V4L2 control init failed (%d)\n", ret);
+		goto err_hdl_free;
+	}
+
+	ov05c10->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+
+err_hdl_free:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+
+	return ret;
+}
+
+static int ov05c10_parse_endpoint(struct ov05c10 *ov05c10,
+				  struct fwnode_handle *fwnode)
+{
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	struct device *dev = ov05c10->dev;
+	struct fwnode_handle *ep;
+	int ret;
+
+	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+	if (!ep) {
+		dev_err(dev, "Failed to get next endpoint\n");
+		return -ENXIO;
+	}
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+	fwnode_handle_put(ep);
+	if (ret)
+		return ret;
+
+	if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV05C10_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 err_endpoint_free;
+	}
+
+	ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+				       bus_cfg.nr_of_link_frequencies,
+				       ov05c10_link_freq_menu_items,
+				       ARRAY_SIZE(ov05c10_link_freq_menu_items),
+				       &ov05c10->link_freq_bitmap);
+	if (ret)
+		dev_err(dev, "v4l2_link_freq_to_bitmap fail with %d\n", ret);
+err_endpoint_free:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+
+	return ret;
+}
+
+static int ov05c10_runtime_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+	int ret;
+
+	ret = clk_prepare_enable(ov05c10->clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clock %d\n", ret);
+		goto error;
+	}
+
+	ov05c10_sensor_power_set(ov05c10, true);
+
+error:
+	return ret;
+}
+
+static int ov05c10_runtime_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+
+	ov05c10_sensor_power_set(ov05c10, false);
+	clk_disable_unprepare(ov05c10->clk);
+
+	return 0;
+}
+
+static int ov05c10_probe(struct i2c_client *client)
+{
+	struct ov05c10 *ov05c10;
+	u32 clkfreq;
+	int ret;
+
+	ov05c10 = devm_kzalloc(&client->dev, sizeof(*ov05c10), GFP_KERNEL);
+	if (!ov05c10)
+		return -ENOMEM;
+
+	ov05c10->dev = &client->dev;
+	ov05c10->cur_mode = &ov05c10_supported_modes[0];
+
+	struct fwnode_handle *fwnode = dev_fwnode(ov05c10->dev);
+
+	ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
+	if (IS_ERR(ov05c10->clk))
+		return dev_err_probe(&client->dev, PTR_ERR(ov05c10->clk),
+				     "failed to get clk\n");
+
+	clkfreq = clk_get_rate(ov05c10->clk);
+	if (clkfreq != OV05C10_REF_CLK)
+		return dev_err_probe(ov05c10->dev, -EINVAL,
+				     "fail invalid clock freq %u, %lu expected\n",
+				     clkfreq, OV05C10_REF_CLK);
+
+	ret = ov05c10_parse_endpoint(ov05c10, fwnode);
+	if (ret)
+		return dev_err_probe(ov05c10->dev, -EINVAL,
+				     "fail to parse endpoint\n");
+
+	ov05c10->enable_gpio = devm_gpiod_get(ov05c10->dev, "enable",
+					      GPIOD_OUT_LOW);
+	if (IS_ERR(ov05c10->enable_gpio))
+		return dev_err_probe(ov05c10->dev,
+				     PTR_ERR(ov05c10->enable_gpio),
+				     "fail to get enable gpio\n");
+
+	ov05c10->regmap = devm_cci_regmap_init_i2c(client, 8);
+	if (IS_ERR(ov05c10->regmap))
+		return dev_err_probe(ov05c10->dev, PTR_ERR(ov05c10->regmap),
+				     "fail to init cci\n");
+
+	ov05c10->cur_page = -1;
+	ov05c10->page_ctrl_reg = OV05C10_REG_PAGE_CTL;
+
+	/*
+	 * Enable power management. The driver supports runtime PM, but needs to
+	 * work when runtime PM is disabled in the kernel. To that end, power
+	 * the sensor on manually here.
+	 */
+	ov05c10_sensor_power_set(ov05c10, true);
+
+	/*
+	 * Enable runtime PM with autosuspend. As the device has been powered
+	 * manually, mark it as active, and increase the usage count without
+	 * resuming the device.
+	 */
+	pm_runtime_set_active(ov05c10->dev);
+	pm_runtime_get_noresume(ov05c10->dev);
+	pm_runtime_enable(ov05c10->dev);
+	pm_runtime_set_autosuspend_delay(ov05c10->dev, 1000);
+	pm_runtime_use_autosuspend(ov05c10->dev);
+
+	v4l2_i2c_subdev_init(&ov05c10->sd, client, &ov05c10_subdev_ops);
+
+	ret = ov05c10_runtime_resume(&client->dev);
+	if (ret)
+		return dev_err_probe(&client->dev, ret,
+				     "failed to power-on the sensor");
+
+	ret = ov05c10_init_controls(ov05c10);
+	if (ret) {
+		dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
+		goto err_pm;
+	}
+
+	ov05c10->sd.internal_ops = &ov05c10_internal_ops;
+	ov05c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	ov05c10->sd.entity.ops = &ov05c10_subdev_entity_ops;
+	ov05c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	ov05c10->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&ov05c10->sd.entity, OV05C10_NUM_OF_PADS,
+				     &ov05c10->pad);
+	if (ret) {
+		dev_err(ov05c10->dev, "fail to init ov05c10 pads %d\n", ret);
+		goto err_hdl_free;
+	}
+
+	ret = v4l2_subdev_init_finalize(&ov05c10->sd);
+	if (ret < 0) {
+		dev_err(ov05c10->dev, "fail to finalize ov05c10 subdev init %d\n", ret);
+		goto err_media_entity_cleanup;
+	}
+
+	ret = v4l2_async_register_subdev_sensor(&ov05c10->sd);
+	if (ret) {
+		dev_err(ov05c10->dev, "fail to register ov05c10 subdev %d\n", ret);
+		goto err_media_entity_cleanup;
+	}
+
+	return 0;
+
+err_media_entity_cleanup:
+	media_entity_cleanup(&ov05c10->sd.entity);
+err_hdl_free:
+	v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler);
+err_pm:
+	pm_runtime_disable(ov05c10->dev);
+	pm_runtime_put_noidle(ov05c10->dev);
+	ov05c10_sensor_power_set(ov05c10, false);
+	return ret;
+}
+
+static void ov05c10_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov05c10 *ov05c10 = to_ov05c10(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		ov05c10_runtime_suspend(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ov05c10_pm_ops, ov05c10_runtime_suspend,
+				 ov05c10_runtime_resume, NULL);
+
+static const struct i2c_device_id ov05c10_i2c_ids[] = {
+	{"ov05c10", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov05c10_i2c_ids);
+
+static struct i2c_driver ov05c10_i2c_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.pm = pm_ptr(&ov05c10_pm_ops),
+	},
+	.id_table = ov05c10_i2c_ids,
+	.probe = ov05c10_probe,
+	.remove = ov05c10_remove,
+};
+
+module_i2c_driver(ov05c10_i2c_driver);
+
+MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
+MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vengutta@amd.com>");
+MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
+MODULE_DESCRIPTION("OmniVision OV05C10 sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.43.0


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

* Re: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
  2025-07-14 20:51 [PATCH v4] media: i2c: Add OV05C10 camera sensor driver Pratap Nirujogi
@ 2025-07-15 11:54 ` Bryan O'Donoghue
  2025-07-15 18:13   ` Laurent Pinchart
  2025-07-15 14:19 ` kernel test robot
  2025-07-15 16:45 ` kernel test robot
  2 siblings, 1 reply; 6+ messages in thread
From: Bryan O'Donoghue @ 2025-07-15 11:54 UTC (permalink / raw)
  To: Pratap Nirujogi, mchehab, sakari.ailus, laurent.pinchart,
	kieran.bingham, hao.yao, mehdi.djait, dongcheng.yan, hverkuil,
	krzk, dave.stevenson, hdegoede, jai.luthra, tomi.valkeinen
  Cc: linux-media, linux-kernel, benjamin.chan, bin.du, grosikop,
	king.li, dantony, vengutta, Phil.Jawich

On 14/07/2025 21:51, Pratap Nirujogi wrote:

> +	ret = ov05c10_init_controls(ov05c10);
> +	if (ret) {
> +		dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
> +		goto err_pm;
> +	}

I would expect to see an "identify_module()" function here, something 
similar to ov02c10.

ret = ov02c10_power_on(&client->dev);
if (ret) {
         dev_err_probe(&client->dev, ret, "failed to power on\n");
         return ret;
}

ret = ov02c10_identify_module(ov02c10);
if (ret) {
        dev_err(&client->dev, "failed to find sensor: %d", ret);
         goto probe_error_power_off;
}

ret = ov02c10_init_controls(ov02c10);
if (ret) {
        dev_err(&client->dev, "failed to init controls: %d", ret);
         goto probe_error_v4l2_ctrl_handler_free;
}

Standard practice is to try to talk to the sensor in probe() and bug out 
if you can't.

With your current logic, the first time you'd realise no sensor was 
present or is in reset etc is the first time you try to stream I think..

Definitely a good idea to probe for your sensor in probe failing the 
probe if you can't find the hardware.

---
bod

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

* Re: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
  2025-07-14 20:51 [PATCH v4] media: i2c: Add OV05C10 camera sensor driver Pratap Nirujogi
  2025-07-15 11:54 ` Bryan O'Donoghue
@ 2025-07-15 14:19 ` kernel test robot
  2025-07-15 16:45 ` kernel test robot
  2 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2025-07-15 14:19 UTC (permalink / raw)
  To: Pratap Nirujogi, mchehab, sakari.ailus, laurent.pinchart,
	kieran.bingham, bryan.odonoghue, hao.yao, mehdi.djait,
	dongcheng.yan, hverkuil, krzk, dave.stevenson, hdegoede,
	jai.luthra, tomi.valkeinen
  Cc: oe-kbuild-all, linux-media, linux-kernel, benjamin.chan, bin.du,
	grosikop, king.li, dantony, vengutta, Phil.Jawich,
	Pratap Nirujogi

Hi Pratap,

kernel test robot noticed the following build errors:

[auto build test ERROR on linuxtv-media-pending/master]
[also build test ERROR on sailus-media-tree/master linus/master v6.16-rc6 next-20250715]
[cannot apply to sailus-media-tree/streams media-tree/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Pratap-Nirujogi/media-i2c-Add-OV05C10-camera-sensor-driver/20250715-050130
base:   https://git.linuxtv.org/media-ci/media-pending.git master
patch link:    https://lore.kernel.org/r/20250714205805.1329403-1-pratap.nirujogi%40amd.com
patch subject: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250715/202507152257.Px9xxVPL-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250715/202507152257.Px9xxVPL-lkp@intel.com/reproduce)

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>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507152257.Px9xxVPL-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   drivers/media/i2c/ov05c10.c: In function 'ov05c10_init_controls':
>> drivers/media/i2c/ov05c10.c:790:13: warning: variable 'max_items' set but not used [-Wunused-but-set-variable]
     790 |         u32 max_items;
         |             ^~~~~~~~~
   drivers/media/i2c/ov05c10.c: In function 'ov05c10_probe':
>> drivers/media/i2c/ov05c10.c:964:24: error: implicit declaration of function 'devm_v4l2_sensor_clk_get' [-Wimplicit-function-declaration]
     964 |         ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
         |                        ^~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/media/i2c/ov05c10.c:964:22: error: assignment to 'struct clk *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     964 |         ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
         |                      ^


vim +/devm_v4l2_sensor_clk_get +964 drivers/media/i2c/ov05c10.c

   780	
   781	static int ov05c10_init_controls(struct ov05c10 *ov05c10)
   782	{
   783		struct v4l2_ctrl_handler *ctrl_hdlr = &ov05c10->ctrl_handler;
   784		const struct ov05c10_mode *mode = ov05c10->cur_mode;
   785		struct v4l2_fwnode_device_properties props;
   786		s64 pixel_rate_max;
   787		s64 exposure_max;
   788		s64 vblank_def;
   789		s64 vblank_min;
 > 790		u32 max_items;
   791		s64 hblank;
   792		int ret;
   793	
   794		ret = v4l2_fwnode_device_parse(ov05c10->dev, &props);
   795		if (ret)
   796			goto err_hdl_free;
   797	
   798		ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
   799		if (ret)
   800			return ret;
   801	
   802		max_items = ARRAY_SIZE(ov05c10_link_freq_menu_items) - 1;
   803		ov05c10->link_freq =
   804			v4l2_ctrl_new_int_menu(ctrl_hdlr,
   805					       NULL,
   806					       V4L2_CID_LINK_FREQ,
   807					       __fls(ov05c10->link_freq_bitmap),
   808					       __ffs(ov05c10->link_freq_bitmap),
   809					       ov05c10_link_freq_menu_items);
   810		if (ov05c10->link_freq)
   811			ov05c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
   812	
   813		pixel_rate_max =
   814			link_freq_to_pixel_rate(ov05c10_link_freq_menu_items[0],
   815						mode->lanes);
   816		ov05c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
   817							V4L2_CID_PIXEL_RATE,
   818							0, pixel_rate_max,
   819							1, pixel_rate_max);
   820	
   821		vblank_def = mode->vts_def - mode->height;
   822		vblank_min = mode->vts_min - mode->height;
   823		ov05c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
   824						    V4L2_CID_VBLANK,
   825						    vblank_min,
   826						    OV05C10_VTS_MAX - mode->height,
   827						    1, vblank_def);
   828	
   829		hblank = mode->hts - mode->width;
   830		ov05c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
   831						    V4L2_CID_HBLANK,
   832						    hblank, hblank, 1, hblank);
   833		if (ov05c10->hblank)
   834			ov05c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
   835	
   836		exposure_max = mode->vts_def - OV05C10_EXPOSURE_MAX_MARGIN;
   837		ov05c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
   838						      V4L2_CID_EXPOSURE,
   839						      OV05C10_EXPOSURE_MIN,
   840						      exposure_max,
   841						      OV05C10_EXPOSURE_STEP,
   842						      exposure_max);
   843	
   844		v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
   845				  OV05C10_ANA_GAIN_MIN, OV05C10_ANA_GAIN_MAX,
   846				  OV05C10_ANA_GAIN_STEP, OV05C10_ANA_GAIN_DEFAULT);
   847	
   848		v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
   849				  OV05C10_DGTL_GAIN_MIN, OV05C10_DGTL_GAIN_MAX,
   850				  OV05C10_DGTL_GAIN_STEP, OV05C10_DGTL_GAIN_DEFAULT);
   851	
   852		v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov05c10_ctrl_ops,
   853					     V4L2_CID_TEST_PATTERN,
   854					     ARRAY_SIZE(ov05c10_test_pattern_menu) - 1,
   855					     0, 0, ov05c10_test_pattern_menu);
   856	
   857		ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov05c10_ctrl_ops,
   858						      &props);
   859		if (ret)
   860			goto err_hdl_free;
   861	
   862		if (ctrl_hdlr->error) {
   863			ret = ctrl_hdlr->error;
   864			dev_err(ov05c10->dev, "V4L2 control init failed (%d)\n", ret);
   865			goto err_hdl_free;
   866		}
   867	
   868		ov05c10->sd.ctrl_handler = ctrl_hdlr;
   869	
   870		return 0;
   871	
   872	err_hdl_free:
   873		v4l2_ctrl_handler_free(ctrl_hdlr);
   874	
   875		return ret;
   876	}
   877	
   878	static int ov05c10_parse_endpoint(struct ov05c10 *ov05c10,
   879					  struct fwnode_handle *fwnode)
   880	{
   881		struct v4l2_fwnode_endpoint bus_cfg = {
   882			.bus_type = V4L2_MBUS_CSI2_DPHY
   883		};
   884		struct device *dev = ov05c10->dev;
   885		struct fwnode_handle *ep;
   886		int ret;
   887	
   888		ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
   889		if (!ep) {
   890			dev_err(dev, "Failed to get next endpoint\n");
   891			return -ENXIO;
   892		}
   893	
   894		ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
   895		fwnode_handle_put(ep);
   896		if (ret)
   897			return ret;
   898	
   899		if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV05C10_DATA_LANES) {
   900			dev_err(dev,
   901				"number of CSI2 data lanes %d is not supported\n",
   902				bus_cfg.bus.mipi_csi2.num_data_lanes);
   903			ret = -EINVAL;
   904			goto err_endpoint_free;
   905		}
   906	
   907		ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
   908					       bus_cfg.nr_of_link_frequencies,
   909					       ov05c10_link_freq_menu_items,
   910					       ARRAY_SIZE(ov05c10_link_freq_menu_items),
   911					       &ov05c10->link_freq_bitmap);
   912		if (ret)
   913			dev_err(dev, "v4l2_link_freq_to_bitmap fail with %d\n", ret);
   914	err_endpoint_free:
   915		v4l2_fwnode_endpoint_free(&bus_cfg);
   916	
   917		return ret;
   918	}
   919	
   920	static int ov05c10_runtime_resume(struct device *dev)
   921	{
   922		struct v4l2_subdev *sd = dev_get_drvdata(dev);
   923		struct ov05c10 *ov05c10 = to_ov05c10(sd);
   924		int ret;
   925	
   926		ret = clk_prepare_enable(ov05c10->clk);
   927		if (ret) {
   928			dev_err(dev, "failed to enable clock %d\n", ret);
   929			goto error;
   930		}
   931	
   932		ov05c10_sensor_power_set(ov05c10, true);
   933	
   934	error:
   935		return ret;
   936	}
   937	
   938	static int ov05c10_runtime_suspend(struct device *dev)
   939	{
   940		struct v4l2_subdev *sd = dev_get_drvdata(dev);
   941		struct ov05c10 *ov05c10 = to_ov05c10(sd);
   942	
   943		ov05c10_sensor_power_set(ov05c10, false);
   944		clk_disable_unprepare(ov05c10->clk);
   945	
   946		return 0;
   947	}
   948	
   949	static int ov05c10_probe(struct i2c_client *client)
   950	{
   951		struct ov05c10 *ov05c10;
   952		u32 clkfreq;
   953		int ret;
   954	
   955		ov05c10 = devm_kzalloc(&client->dev, sizeof(*ov05c10), GFP_KERNEL);
   956		if (!ov05c10)
   957			return -ENOMEM;
   958	
   959		ov05c10->dev = &client->dev;
   960		ov05c10->cur_mode = &ov05c10_supported_modes[0];
   961	
   962		struct fwnode_handle *fwnode = dev_fwnode(ov05c10->dev);
   963	
 > 964		ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
   965		if (IS_ERR(ov05c10->clk))
   966			return dev_err_probe(&client->dev, PTR_ERR(ov05c10->clk),
   967					     "failed to get clk\n");
   968	
   969		clkfreq = clk_get_rate(ov05c10->clk);
   970		if (clkfreq != OV05C10_REF_CLK)
   971			return dev_err_probe(ov05c10->dev, -EINVAL,
   972					     "fail invalid clock freq %u, %lu expected\n",
   973					     clkfreq, OV05C10_REF_CLK);
   974	
   975		ret = ov05c10_parse_endpoint(ov05c10, fwnode);
   976		if (ret)
   977			return dev_err_probe(ov05c10->dev, -EINVAL,
   978					     "fail to parse endpoint\n");
   979	
   980		ov05c10->enable_gpio = devm_gpiod_get(ov05c10->dev, "enable",
   981						      GPIOD_OUT_LOW);
   982		if (IS_ERR(ov05c10->enable_gpio))
   983			return dev_err_probe(ov05c10->dev,
   984					     PTR_ERR(ov05c10->enable_gpio),
   985					     "fail to get enable gpio\n");
   986	
   987		ov05c10->regmap = devm_cci_regmap_init_i2c(client, 8);
   988		if (IS_ERR(ov05c10->regmap))
   989			return dev_err_probe(ov05c10->dev, PTR_ERR(ov05c10->regmap),
   990					     "fail to init cci\n");
   991	
   992		ov05c10->cur_page = -1;
   993		ov05c10->page_ctrl_reg = OV05C10_REG_PAGE_CTL;
   994	
   995		/*
   996		 * Enable power management. The driver supports runtime PM, but needs to
   997		 * work when runtime PM is disabled in the kernel. To that end, power
   998		 * the sensor on manually here.
   999		 */
  1000		ov05c10_sensor_power_set(ov05c10, true);
  1001	
  1002		/*
  1003		 * Enable runtime PM with autosuspend. As the device has been powered
  1004		 * manually, mark it as active, and increase the usage count without
  1005		 * resuming the device.
  1006		 */
  1007		pm_runtime_set_active(ov05c10->dev);
  1008		pm_runtime_get_noresume(ov05c10->dev);
  1009		pm_runtime_enable(ov05c10->dev);
  1010		pm_runtime_set_autosuspend_delay(ov05c10->dev, 1000);
  1011		pm_runtime_use_autosuspend(ov05c10->dev);
  1012	
  1013		v4l2_i2c_subdev_init(&ov05c10->sd, client, &ov05c10_subdev_ops);
  1014	
  1015		ret = ov05c10_runtime_resume(&client->dev);
  1016		if (ret)
  1017			return dev_err_probe(&client->dev, ret,
  1018					     "failed to power-on the sensor");
  1019	
  1020		ret = ov05c10_init_controls(ov05c10);
  1021		if (ret) {
  1022			dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
  1023			goto err_pm;
  1024		}
  1025	
  1026		ov05c10->sd.internal_ops = &ov05c10_internal_ops;
  1027		ov05c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
  1028		ov05c10->sd.entity.ops = &ov05c10_subdev_entity_ops;
  1029		ov05c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
  1030	
  1031		ov05c10->pad.flags = MEDIA_PAD_FL_SOURCE;
  1032	
  1033		ret = media_entity_pads_init(&ov05c10->sd.entity, OV05C10_NUM_OF_PADS,
  1034					     &ov05c10->pad);
  1035		if (ret) {
  1036			dev_err(ov05c10->dev, "fail to init ov05c10 pads %d\n", ret);
  1037			goto err_hdl_free;
  1038		}
  1039	
  1040		ret = v4l2_subdev_init_finalize(&ov05c10->sd);
  1041		if (ret < 0) {
  1042			dev_err(ov05c10->dev, "fail to finalize ov05c10 subdev init %d\n", ret);
  1043			goto err_media_entity_cleanup;
  1044		}
  1045	
  1046		ret = v4l2_async_register_subdev_sensor(&ov05c10->sd);
  1047		if (ret) {
  1048			dev_err(ov05c10->dev, "fail to register ov05c10 subdev %d\n", ret);
  1049			goto err_media_entity_cleanup;
  1050		}
  1051	
  1052		return 0;
  1053	
  1054	err_media_entity_cleanup:
  1055		media_entity_cleanup(&ov05c10->sd.entity);
  1056	err_hdl_free:
  1057		v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler);
  1058	err_pm:
  1059		pm_runtime_disable(ov05c10->dev);
  1060		pm_runtime_put_noidle(ov05c10->dev);
  1061		ov05c10_sensor_power_set(ov05c10, false);
  1062		return ret;
  1063	}
  1064	

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

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

* Re: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
  2025-07-14 20:51 [PATCH v4] media: i2c: Add OV05C10 camera sensor driver Pratap Nirujogi
  2025-07-15 11:54 ` Bryan O'Donoghue
  2025-07-15 14:19 ` kernel test robot
@ 2025-07-15 16:45 ` kernel test robot
  2 siblings, 0 replies; 6+ messages in thread
From: kernel test robot @ 2025-07-15 16:45 UTC (permalink / raw)
  To: Pratap Nirujogi, mchehab, sakari.ailus, laurent.pinchart,
	kieran.bingham, bryan.odonoghue, hao.yao, mehdi.djait,
	dongcheng.yan, hverkuil, krzk, dave.stevenson, hdegoede,
	jai.luthra, tomi.valkeinen
  Cc: oe-kbuild-all, linux-media, linux-kernel, benjamin.chan, bin.du,
	grosikop, king.li, dantony, vengutta, Phil.Jawich,
	Pratap Nirujogi

Hi Pratap,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linuxtv-media-pending/master]
[also build test WARNING on sailus-media-tree/master linus/master v6.16-rc6 next-20250715]
[cannot apply to sailus-media-tree/streams media-tree/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Pratap-Nirujogi/media-i2c-Add-OV05C10-camera-sensor-driver/20250715-050130
base:   https://git.linuxtv.org/media-ci/media-pending.git master
patch link:    https://lore.kernel.org/r/20250714205805.1329403-1-pratap.nirujogi%40amd.com
patch subject: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
config: um-allyesconfig (https://download.01.org/0day-ci/archive/20250716/202507160002.wqQBk380-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250716/202507160002.wqQBk380-lkp@intel.com/reproduce)

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>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507160002.wqQBk380-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/media/i2c/ov05c10.c: In function 'ov05c10_init_controls':
   drivers/media/i2c/ov05c10.c:790:13: warning: variable 'max_items' set but not used [-Wunused-but-set-variable]
     790 |         u32 max_items;
         |             ^~~~~~~~~
   drivers/media/i2c/ov05c10.c: In function 'ov05c10_probe':
   drivers/media/i2c/ov05c10.c:964:24: error: implicit declaration of function 'devm_v4l2_sensor_clk_get' [-Werror=implicit-function-declaration]
     964 |         ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
         |                        ^~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/media/i2c/ov05c10.c:964:22: warning: assignment to 'struct clk *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     964 |         ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
         |                      ^
   cc1: some warnings being treated as errors


vim +964 drivers/media/i2c/ov05c10.c

   780	
   781	static int ov05c10_init_controls(struct ov05c10 *ov05c10)
   782	{
   783		struct v4l2_ctrl_handler *ctrl_hdlr = &ov05c10->ctrl_handler;
   784		const struct ov05c10_mode *mode = ov05c10->cur_mode;
   785		struct v4l2_fwnode_device_properties props;
   786		s64 pixel_rate_max;
   787		s64 exposure_max;
   788		s64 vblank_def;
   789		s64 vblank_min;
 > 790		u32 max_items;
   791		s64 hblank;
   792		int ret;
   793	
   794		ret = v4l2_fwnode_device_parse(ov05c10->dev, &props);
   795		if (ret)
   796			goto err_hdl_free;
   797	
   798		ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
   799		if (ret)
   800			return ret;
   801	
   802		max_items = ARRAY_SIZE(ov05c10_link_freq_menu_items) - 1;
   803		ov05c10->link_freq =
   804			v4l2_ctrl_new_int_menu(ctrl_hdlr,
   805					       NULL,
   806					       V4L2_CID_LINK_FREQ,
   807					       __fls(ov05c10->link_freq_bitmap),
   808					       __ffs(ov05c10->link_freq_bitmap),
   809					       ov05c10_link_freq_menu_items);
   810		if (ov05c10->link_freq)
   811			ov05c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
   812	
   813		pixel_rate_max =
   814			link_freq_to_pixel_rate(ov05c10_link_freq_menu_items[0],
   815						mode->lanes);
   816		ov05c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
   817							V4L2_CID_PIXEL_RATE,
   818							0, pixel_rate_max,
   819							1, pixel_rate_max);
   820	
   821		vblank_def = mode->vts_def - mode->height;
   822		vblank_min = mode->vts_min - mode->height;
   823		ov05c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
   824						    V4L2_CID_VBLANK,
   825						    vblank_min,
   826						    OV05C10_VTS_MAX - mode->height,
   827						    1, vblank_def);
   828	
   829		hblank = mode->hts - mode->width;
   830		ov05c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
   831						    V4L2_CID_HBLANK,
   832						    hblank, hblank, 1, hblank);
   833		if (ov05c10->hblank)
   834			ov05c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
   835	
   836		exposure_max = mode->vts_def - OV05C10_EXPOSURE_MAX_MARGIN;
   837		ov05c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops,
   838						      V4L2_CID_EXPOSURE,
   839						      OV05C10_EXPOSURE_MIN,
   840						      exposure_max,
   841						      OV05C10_EXPOSURE_STEP,
   842						      exposure_max);
   843	
   844		v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
   845				  OV05C10_ANA_GAIN_MIN, OV05C10_ANA_GAIN_MAX,
   846				  OV05C10_ANA_GAIN_STEP, OV05C10_ANA_GAIN_DEFAULT);
   847	
   848		v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
   849				  OV05C10_DGTL_GAIN_MIN, OV05C10_DGTL_GAIN_MAX,
   850				  OV05C10_DGTL_GAIN_STEP, OV05C10_DGTL_GAIN_DEFAULT);
   851	
   852		v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov05c10_ctrl_ops,
   853					     V4L2_CID_TEST_PATTERN,
   854					     ARRAY_SIZE(ov05c10_test_pattern_menu) - 1,
   855					     0, 0, ov05c10_test_pattern_menu);
   856	
   857		ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov05c10_ctrl_ops,
   858						      &props);
   859		if (ret)
   860			goto err_hdl_free;
   861	
   862		if (ctrl_hdlr->error) {
   863			ret = ctrl_hdlr->error;
   864			dev_err(ov05c10->dev, "V4L2 control init failed (%d)\n", ret);
   865			goto err_hdl_free;
   866		}
   867	
   868		ov05c10->sd.ctrl_handler = ctrl_hdlr;
   869	
   870		return 0;
   871	
   872	err_hdl_free:
   873		v4l2_ctrl_handler_free(ctrl_hdlr);
   874	
   875		return ret;
   876	}
   877	
   878	static int ov05c10_parse_endpoint(struct ov05c10 *ov05c10,
   879					  struct fwnode_handle *fwnode)
   880	{
   881		struct v4l2_fwnode_endpoint bus_cfg = {
   882			.bus_type = V4L2_MBUS_CSI2_DPHY
   883		};
   884		struct device *dev = ov05c10->dev;
   885		struct fwnode_handle *ep;
   886		int ret;
   887	
   888		ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
   889		if (!ep) {
   890			dev_err(dev, "Failed to get next endpoint\n");
   891			return -ENXIO;
   892		}
   893	
   894		ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
   895		fwnode_handle_put(ep);
   896		if (ret)
   897			return ret;
   898	
   899		if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV05C10_DATA_LANES) {
   900			dev_err(dev,
   901				"number of CSI2 data lanes %d is not supported\n",
   902				bus_cfg.bus.mipi_csi2.num_data_lanes);
   903			ret = -EINVAL;
   904			goto err_endpoint_free;
   905		}
   906	
   907		ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
   908					       bus_cfg.nr_of_link_frequencies,
   909					       ov05c10_link_freq_menu_items,
   910					       ARRAY_SIZE(ov05c10_link_freq_menu_items),
   911					       &ov05c10->link_freq_bitmap);
   912		if (ret)
   913			dev_err(dev, "v4l2_link_freq_to_bitmap fail with %d\n", ret);
   914	err_endpoint_free:
   915		v4l2_fwnode_endpoint_free(&bus_cfg);
   916	
   917		return ret;
   918	}
   919	
   920	static int ov05c10_runtime_resume(struct device *dev)
   921	{
   922		struct v4l2_subdev *sd = dev_get_drvdata(dev);
   923		struct ov05c10 *ov05c10 = to_ov05c10(sd);
   924		int ret;
   925	
   926		ret = clk_prepare_enable(ov05c10->clk);
   927		if (ret) {
   928			dev_err(dev, "failed to enable clock %d\n", ret);
   929			goto error;
   930		}
   931	
   932		ov05c10_sensor_power_set(ov05c10, true);
   933	
   934	error:
   935		return ret;
   936	}
   937	
   938	static int ov05c10_runtime_suspend(struct device *dev)
   939	{
   940		struct v4l2_subdev *sd = dev_get_drvdata(dev);
   941		struct ov05c10 *ov05c10 = to_ov05c10(sd);
   942	
   943		ov05c10_sensor_power_set(ov05c10, false);
   944		clk_disable_unprepare(ov05c10->clk);
   945	
   946		return 0;
   947	}
   948	
   949	static int ov05c10_probe(struct i2c_client *client)
   950	{
   951		struct ov05c10 *ov05c10;
   952		u32 clkfreq;
   953		int ret;
   954	
   955		ov05c10 = devm_kzalloc(&client->dev, sizeof(*ov05c10), GFP_KERNEL);
   956		if (!ov05c10)
   957			return -ENOMEM;
   958	
   959		ov05c10->dev = &client->dev;
   960		ov05c10->cur_mode = &ov05c10_supported_modes[0];
   961	
   962		struct fwnode_handle *fwnode = dev_fwnode(ov05c10->dev);
   963	
 > 964		ov05c10->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL);
   965		if (IS_ERR(ov05c10->clk))
   966			return dev_err_probe(&client->dev, PTR_ERR(ov05c10->clk),
   967					     "failed to get clk\n");
   968	
   969		clkfreq = clk_get_rate(ov05c10->clk);
   970		if (clkfreq != OV05C10_REF_CLK)
   971			return dev_err_probe(ov05c10->dev, -EINVAL,
   972					     "fail invalid clock freq %u, %lu expected\n",
   973					     clkfreq, OV05C10_REF_CLK);
   974	
   975		ret = ov05c10_parse_endpoint(ov05c10, fwnode);
   976		if (ret)
   977			return dev_err_probe(ov05c10->dev, -EINVAL,
   978					     "fail to parse endpoint\n");
   979	
   980		ov05c10->enable_gpio = devm_gpiod_get(ov05c10->dev, "enable",
   981						      GPIOD_OUT_LOW);
   982		if (IS_ERR(ov05c10->enable_gpio))
   983			return dev_err_probe(ov05c10->dev,
   984					     PTR_ERR(ov05c10->enable_gpio),
   985					     "fail to get enable gpio\n");
   986	
   987		ov05c10->regmap = devm_cci_regmap_init_i2c(client, 8);
   988		if (IS_ERR(ov05c10->regmap))
   989			return dev_err_probe(ov05c10->dev, PTR_ERR(ov05c10->regmap),
   990					     "fail to init cci\n");
   991	
   992		ov05c10->cur_page = -1;
   993		ov05c10->page_ctrl_reg = OV05C10_REG_PAGE_CTL;
   994	
   995		/*
   996		 * Enable power management. The driver supports runtime PM, but needs to
   997		 * work when runtime PM is disabled in the kernel. To that end, power
   998		 * the sensor on manually here.
   999		 */
  1000		ov05c10_sensor_power_set(ov05c10, true);
  1001	
  1002		/*
  1003		 * Enable runtime PM with autosuspend. As the device has been powered
  1004		 * manually, mark it as active, and increase the usage count without
  1005		 * resuming the device.
  1006		 */
  1007		pm_runtime_set_active(ov05c10->dev);
  1008		pm_runtime_get_noresume(ov05c10->dev);
  1009		pm_runtime_enable(ov05c10->dev);
  1010		pm_runtime_set_autosuspend_delay(ov05c10->dev, 1000);
  1011		pm_runtime_use_autosuspend(ov05c10->dev);
  1012	
  1013		v4l2_i2c_subdev_init(&ov05c10->sd, client, &ov05c10_subdev_ops);
  1014	
  1015		ret = ov05c10_runtime_resume(&client->dev);
  1016		if (ret)
  1017			return dev_err_probe(&client->dev, ret,
  1018					     "failed to power-on the sensor");
  1019	
  1020		ret = ov05c10_init_controls(ov05c10);
  1021		if (ret) {
  1022			dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
  1023			goto err_pm;
  1024		}
  1025	
  1026		ov05c10->sd.internal_ops = &ov05c10_internal_ops;
  1027		ov05c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
  1028		ov05c10->sd.entity.ops = &ov05c10_subdev_entity_ops;
  1029		ov05c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
  1030	
  1031		ov05c10->pad.flags = MEDIA_PAD_FL_SOURCE;
  1032	
  1033		ret = media_entity_pads_init(&ov05c10->sd.entity, OV05C10_NUM_OF_PADS,
  1034					     &ov05c10->pad);
  1035		if (ret) {
  1036			dev_err(ov05c10->dev, "fail to init ov05c10 pads %d\n", ret);
  1037			goto err_hdl_free;
  1038		}
  1039	
  1040		ret = v4l2_subdev_init_finalize(&ov05c10->sd);
  1041		if (ret < 0) {
  1042			dev_err(ov05c10->dev, "fail to finalize ov05c10 subdev init %d\n", ret);
  1043			goto err_media_entity_cleanup;
  1044		}
  1045	
  1046		ret = v4l2_async_register_subdev_sensor(&ov05c10->sd);
  1047		if (ret) {
  1048			dev_err(ov05c10->dev, "fail to register ov05c10 subdev %d\n", ret);
  1049			goto err_media_entity_cleanup;
  1050		}
  1051	
  1052		return 0;
  1053	
  1054	err_media_entity_cleanup:
  1055		media_entity_cleanup(&ov05c10->sd.entity);
  1056	err_hdl_free:
  1057		v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler);
  1058	err_pm:
  1059		pm_runtime_disable(ov05c10->dev);
  1060		pm_runtime_put_noidle(ov05c10->dev);
  1061		ov05c10_sensor_power_set(ov05c10, false);
  1062		return ret;
  1063	}
  1064	

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

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

* Re: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
  2025-07-15 11:54 ` Bryan O'Donoghue
@ 2025-07-15 18:13   ` Laurent Pinchart
  2025-07-16 18:00     ` Nirujogi, Pratap
  0 siblings, 1 reply; 6+ messages in thread
From: Laurent Pinchart @ 2025-07-15 18:13 UTC (permalink / raw)
  To: Bryan O'Donoghue
  Cc: Pratap Nirujogi, mchehab, sakari.ailus, kieran.bingham, hao.yao,
	mehdi.djait, dongcheng.yan, hverkuil, krzk, dave.stevenson,
	hdegoede, jai.luthra, tomi.valkeinen, linux-media, linux-kernel,
	benjamin.chan, bin.du, grosikop, king.li, dantony, vengutta,
	Phil.Jawich

On Tue, Jul 15, 2025 at 12:54:30PM +0100, Bryan O'Donoghue wrote:
> On 14/07/2025 21:51, Pratap Nirujogi wrote:
> 
> > +	ret = ov05c10_init_controls(ov05c10);
> > +	if (ret) {
> > +		dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
> > +		goto err_pm;
> > +	}
> 
> I would expect to see an "identify_module()" function here, something 
> similar to ov02c10.
> 
> ret = ov02c10_power_on(&client->dev);
> if (ret) {
>          dev_err_probe(&client->dev, ret, "failed to power on\n");
>          return ret;
> }
> 
> ret = ov02c10_identify_module(ov02c10);
> if (ret) {
>         dev_err(&client->dev, "failed to find sensor: %d", ret);
>          goto probe_error_power_off;
> }
> 
> ret = ov02c10_init_controls(ov02c10);
> if (ret) {
>         dev_err(&client->dev, "failed to init controls: %d", ret);
>          goto probe_error_v4l2_ctrl_handler_free;
> }
> 
> Standard practice is to try to talk to the sensor in probe() and bug out 
> if you can't.

It's actually not that standard, and is a frowned upon behaviour when
the sensor has a privacy LED GPIO connected to the power rail instead of
a hardware streaming signal. It would cause the privacy GPIO to flash at
boot time, which is considered a worrying behaviour for users. That's
why a few sensor drivers make runtime identification optional. We should
try to handle that in a standard way across all drivers, likely based on
a device property..

> With your current logic, the first time you'd realise no sensor was 
> present or is in reset etc is the first time you try to stream I think..
> 
> Definitely a good idea to probe for your sensor in probe failing the 
> probe if you can't find the hardware.

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v4] media: i2c: Add OV05C10 camera sensor driver
  2025-07-15 18:13   ` Laurent Pinchart
@ 2025-07-16 18:00     ` Nirujogi, Pratap
  0 siblings, 0 replies; 6+ messages in thread
From: Nirujogi, Pratap @ 2025-07-16 18:00 UTC (permalink / raw)
  To: Laurent Pinchart, Bryan O'Donoghue
  Cc: Pratap Nirujogi, mchehab, sakari.ailus, kieran.bingham, hao.yao,
	mehdi.djait, dongcheng.yan, hverkuil, krzk, dave.stevenson,
	hdegoede, jai.luthra, tomi.valkeinen, linux-media, linux-kernel,
	benjamin.chan, bin.du, grosikop, king.li, dantony, vengutta,
	Phil.Jawich

Hi Laurent, Hi Bryan,

On 7/15/2025 2:13 PM, Laurent Pinchart wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
> 
> 
> On Tue, Jul 15, 2025 at 12:54:30PM +0100, Bryan O'Donoghue wrote:
>> On 14/07/2025 21:51, Pratap Nirujogi wrote:
>>
>>> +   ret = ov05c10_init_controls(ov05c10);
>>> +   if (ret) {
>>> +           dev_err(ov05c10->dev, "fail to init ov05c10 ctl %d\n", ret);
>>> +           goto err_pm;
>>> +   }
>>
>> I would expect to see an "identify_module()" function here, something
>> similar to ov02c10.
>>
>> ret = ov02c10_power_on(&client->dev);
>> if (ret) {
>>           dev_err_probe(&client->dev, ret, "failed to power on\n");
>>           return ret;
>> }
>>
>> ret = ov02c10_identify_module(ov02c10);
>> if (ret) {
>>          dev_err(&client->dev, "failed to find sensor: %d", ret);
>>           goto probe_error_power_off;
>> }
>>
>> ret = ov02c10_init_controls(ov02c10);
>> if (ret) {
>>          dev_err(&client->dev, "failed to init controls: %d", ret);
>>           goto probe_error_v4l2_ctrl_handler_free;
>> }
>>
>> Standard practice is to try to talk to the sensor in probe() and bug out
>> if you can't.
> 
> It's actually not that standard, and is a frowned upon behaviour when
> the sensor has a privacy LED GPIO connected to the power rail instead of
> a hardware streaming signal. It would cause the privacy GPIO to flash at
> boot time, which is considered a worrying behaviour for users. That's
> why a few sensor drivers make runtime identification optional. We should
> try to handle that in a standard way across all drivers, likely based on
> a device property..
> 
Shall I add chip_id as the device property variable in the x86/platform 
driver to address this comment? If this is okay, I will add a chip_id 
check reading the property variable in sensor probe(). Please share your 
thoughts.

Thanks,
Pratap


>> With your current logic, the first time you'd realise no sensor was
>> present or is in reset etc is the first time you try to stream I think..
>>
>> Definitely a good idea to probe for your sensor in probe failing the
>> probe if you can't find the hardware.
> 
> --
> Regards,
> 
> Laurent Pinchart


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

end of thread, other threads:[~2025-07-16 18:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-14 20:51 [PATCH v4] media: i2c: Add OV05C10 camera sensor driver Pratap Nirujogi
2025-07-15 11:54 ` Bryan O'Donoghue
2025-07-15 18:13   ` Laurent Pinchart
2025-07-16 18:00     ` Nirujogi, Pratap
2025-07-15 14:19 ` kernel test robot
2025-07-15 16:45 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).