Devicetree
 help / color / mirror / Atom feed
* [PATCH 0/3] media: i2c: Add imx576 camera sensor driver
@ 2026-05-20 11:56 Himanshu Bhavani
  2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Himanshu Bhavani @ 2026-05-20 11:56 UTC (permalink / raw)
  To: sakari.ailus
  Cc: Himanshu Bhavani, Himanshu Bhavani, Hardevsinh Palaniya,
	Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Hans Verkuil,
	Hans de Goede, Vladimir Zapolskiy, Elgin Perumbilly,
	Laurent Pinchart, Walter Werner Schneider, Kate Hsuan,
	Svyatoslav Ryhel, linux-media, devicetree, linux-kernel,
	linux-arm-msm

From: Himanshu Bhavani <Himanshu Bhavani himanshu.bhavani@siliconsignals.io>

The following features are supported:
- Manual exposure an gain control support.
- vblank/hblank control support.
- Supported resolution: 2880 x 2156 30fps (SRGGB10)

The driver has been tested on the mainline v7.0-rc6 kernel on the Fairphone 4 running postmarketOS.

fairphone-fp4:~$ v4l2-compliance -d /dev/v4l-subdev21
v4l2-compliance 1.32.0, 64 bits, 64-bit time_t

Compliance test for device /dev/v4l-subdev21:

Driver Info:
        Driver version   : 7.0.0
        Capabilities     : 0x00000000
        Client Capabilities: 0x0000000000000002
interval-uses-which
Required ioctls:
        test VIDIOC_SUDBEV_QUERYCAP: OK
        test invalid ioctls: OK

Allow for multiple opens:
        test second /dev/v4l-subdev21 open: OK
        test VIDIOC_SUBDEV_QUERYCAP: OK
        test for unlimited opens: OK

Debug ioctls:
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls:
        test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
        test VIDIOC_QUERYCTRL: OK
        test VIDIOC_G/S_CTRL: OK
        test VIDIOC_G/S/TRY_EXT_CTRLS: OK
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
        test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
        Standard Controls: 12 Private Controls: 0

Format ioctls:
        test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
        test VIDIOC_G/S_PARM: OK (Not Supported)
        test VIDIOC_G_FBUF: OK (Not Supported)
        test VIDIOC_G_FMT: OK (Not Supported)
        test VIDIOC_TRY_FMT: OK (Not Supported)
        test VIDIOC_S_FMT: OK (Not Supported)
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        test Cropping: OK (Not Supported)
        test Composing: OK (Not Supported)
        test Scaling: OK (Not Supported)

Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
        test CREATE_BUFS maximum buffers: OK
        test VIDIOC_REMOVE_BUFS: OK
        test VIDIOC_EXPBUF: OK (Not Supported)
        test Requests: OK (Not Supported)
        test blocking wait: OK (Not Supported)

Total for device /dev/v4l-subdev21: 46, Succeeded: 46, Failed: 0, Warnings: 0

Hardevsinh Palaniya (2):
  dt-bindings: media: i2c: Add imx576 sensor
  arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera
    support

Himanshu Bhavani (1):
  media: i2c: add imx576 image sensor driver

 .../bindings/media/i2c/sony,imx576.yaml       |  111 ++
 MAINTAINERS                                   |    8 +
 .../boot/dts/qcom/sm7225-fairphone-fp4.dts    |   54 +
 drivers/media/i2c/Kconfig                     |   10 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/imx576.c                    | 1029 +++++++++++++++++
 6 files changed, 1213 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
 create mode 100644 drivers/media/i2c/imx576.c

--
2.34.1


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

* [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor
  2026-05-20 11:56 [PATCH 0/3] media: i2c: Add imx576 camera sensor driver Himanshu Bhavani
@ 2026-05-20 11:56 ` Himanshu Bhavani
  2026-05-20 12:08   ` sashiko-bot
  2026-05-20 12:09   ` Luca Weiss
  2026-05-20 11:56 ` [PATCH 2/3] media: i2c: add imx576 image sensor driver Himanshu Bhavani
  2026-05-20 11:56 ` [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support Himanshu Bhavani
  2 siblings, 2 replies; 10+ messages in thread
From: Himanshu Bhavani @ 2026-05-20 11:56 UTC (permalink / raw)
  To: sakari.ailus
  Cc: Hardevsinh Palaniya, Himanshu Bhavani, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Hans Verkuil, Hans de Goede, Vladimir Zapolskiy,
	Elgin Perumbilly, Laurent Pinchart, Xiaolei Wang,
	Walter Werner Schneider, Kate Hsuan, Svyatoslav Ryhel,
	linux-media, devicetree, linux-kernel, linux-arm-msm

From: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

Add bindings for Sony IMX576 sensor

Signed-off-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
---
 .../bindings/media/i2c/sony,imx576.yaml       | 111 ++++++++++++++++++
 MAINTAINERS                                   |   7 ++
 2 files changed, 118 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
new file mode 100644
index 000000000000..b74253004fae
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx576.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX576 Image Sensor
+
+maintainers:
+  - Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
+
+description:
+  IMX576 sensor is a Sony CMOS active pixel digital image sensor with an active
+  array size of 5760(H) × 4312(V). It is programmable through an I2C interface.
+  Image data is transmitted through MIPI CSI-2. It supports RAW10/RAW8, COMP8
+  output formats.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+  compatible:
+    const: sony,imx576
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: INCLK clock
+
+  avdd-supply:
+    description: Analog Domain Power Supply (2.8v)
+
+  dovdd-supply:
+    description: I/O Domain Power Supply (1.05v)
+
+  dvdd-supply:
+    description: Digital Domain Power Supply (1.8v)
+
+  reset-gpios:
+    maxItems: 1
+    description: Reset Pin GPIO Control (active low)
+
+  port:
+    description: MIPI CSI-2 transmitter port
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          data-lanes:
+            oneOf:
+              - items:
+                  - const: 1
+                  - const: 2
+                  - const: 3
+                  - const: 4
+              - items:
+                  - const: 1
+                  - const: 2
+        required:
+          - data-lanes
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - avdd-supply
+  - dovdd-supply
+  - dvdd-supply
+  - port
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/media/video-interfaces.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        camera-sensor@10 {
+            compatible = "sony,imx576";
+            reg = <0x10>;
+            clocks = <&imx576_clk>;
+            reset-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
+
+            avdd-supply = <&vreg_l3p>;
+            dovdd-supply = <&vreg_l6p>;
+            dvdd-supply = <&vreg_32m_cam_dvdd_1p05>;
+
+            orientation = <0>;
+            rotation = <90>;
+
+            port {
+                cam_out: endpoint {
+                   remote-endpoint = <&csiphy3_ep>;
+                   data-lanes = <1 2 3 4>;
+                   link-frequencies = /bits/ 64 <600000000>;
+                };
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index ff935e197c21..1b15fa355e8b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24856,6 +24856,13 @@ T:	git git://linuxtv.org/media.git
 F:	Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
 F:	drivers/media/i2c/imx415.c

+SONY IMX576 SENSOR DRIVER
+M:	Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
+M:	Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
+
 SONY MEMORYSTICK SUBSYSTEM
 M:	Maxim Levitsky <maximlevitsky@gmail.com>
 M:	Alex Dubov <oakad@yahoo.com>
--
2.34.1


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

* [PATCH 2/3] media: i2c: add imx576 image sensor driver
  2026-05-20 11:56 [PATCH 0/3] media: i2c: Add imx576 camera sensor driver Himanshu Bhavani
  2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
@ 2026-05-20 11:56 ` Himanshu Bhavani
  2026-05-20 12:33   ` Laurent Pinchart
  2026-05-20 12:43   ` sashiko-bot
  2026-05-20 11:56 ` [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support Himanshu Bhavani
  2 siblings, 2 replies; 10+ messages in thread
From: Himanshu Bhavani @ 2026-05-20 11:56 UTC (permalink / raw)
  To: sakari.ailus
  Cc: Himanshu Bhavani, Hardevsinh Palaniya, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Hans Verkuil, Hans de Goede, Vladimir Zapolskiy,
	Mehdi Djait, Elgin Perumbilly, Xiaolei Wang, Laurent Pinchart,
	Walter Werner Schneider, Kate Hsuan, Svyatoslav Ryhel,
	linux-media, devicetree, linux-kernel, linux-arm-msm

Add a v4l2 subdevice driver for the Sony imx576 sensor.

The Sony IMX576 image sensor with an active
array size of 5760 x 4312

The following features are supported:
- Manual exposure an gain control support
- vblank/hblank control support
- Supported resolution: 2880 x 2156 30fps (SRGGB10)

Signed-off-by: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
---
 MAINTAINERS                |    1 +
 drivers/media/i2c/Kconfig  |   10 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/imx576.c | 1029 ++++++++++++++++++++++++++++++++++++
 4 files changed, 1041 insertions(+)
 create mode 100644 drivers/media/i2c/imx576.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1b15fa355e8b..768a1eb3627a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24862,6 +24862,7 @@ M:	Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
 L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
+F:	drivers/media/i2c/imx576.c

 SONY MEMORYSTICK SUBSYSTEM
 M:	Maxim Levitsky <maximlevitsky@gmail.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index fc2954098eaf..05d1e69c2f33 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -288,6 +288,16 @@ config VIDEO_IMX415
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx415.

+config VIDEO_IMX576
+        tristate "Sony IMX576 sensor support"
+        select V4L2_CCI_I2C
+        help
+          This is a Video4Linux2 sensor driver for the Sony
+          IMX576 camera.
+
+          To compile this driver as a module, choose M here: the
+          module will be called imx576.
+
 config VIDEO_MAX9271_LIB
 	tristate

diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 90b276a7417a..e96c083e03d9 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
 obj-$(CONFIG_VIDEO_IMX355) += imx355.o
 obj-$(CONFIG_VIDEO_IMX412) += imx412.o
 obj-$(CONFIG_VIDEO_IMX415) += imx415.o
+obj-$(CONFIG_VIDEO_IMX576) += imx576.o
 obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
 obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
 obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
diff --git a/drivers/media/i2c/imx576.c b/drivers/media/i2c/imx576.c
new file mode 100644
index 000000000000..910cbcfb6031
--- /dev/null
+++ b/drivers/media/i2c/imx576.c
@@ -0,0 +1,1029 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * V4L2 Support for the IMX576
+ *
+ * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
+ *
+ * Copyright (C) 2024 Luca Weiss <luca.weiss@fairphone.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/time.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>
+#include <media/v4l2-mediabus.h>
+
+#define IMX576_INCLK_RATE		(24 * HZ_PER_MHZ)
+
+#define IMX576_REG_CHIP_ID		CCI_REG16(0x0016)
+#define IMX576_CHIP_ID			0x0576
+
+#define IMX576_REG_MODE_SELECT		CCI_REG8(0x0100)
+#define IMX576_MODE_STANDBY		0x00
+#define IMX576_MODE_STREAMING		0x01
+
+#define IMX576_REG_HOLD			CCI_REG8(0x0104)
+
+#define IMX576_REG_EXPOSURE		CCI_REG16(0x0202)
+#define IMX576_EXPOSURE_MIN		8
+#define IMX576_EXPOSURE_OFFSET		22
+#define IMX576_EXPOSURE_STEP		1
+#define IMX576_EXPOSURE_DEFAULT		0x0648
+
+#define IMX576_REG_ANALOG_GAIN		CCI_REG16(0x0204)
+#define IMX576_ANA_GAIN_MIN		0
+#define IMX576_ANA_GAIN_MAX		978
+#define IMX576_ANA_GAIN_STEP		1
+#define IMX576_ANA_GAIN_DEFAULT		0
+
+#define IMX576_REG_VTS			CCI_REG16(0x0340)
+#define IMX576_REG_HTS			CCI_REG16(0x0342)
+
+/* FIXME: Exact VBLANK limit unknown (no datasheet). */
+#define IMX576_VBLANK_MAX		32420
+
+#define IMX576_LINK_FREQ_600MHZ		(600 * HZ_PER_MHZ)
+#define IMX576_NUM_DATA_LANES		4
+
+/* IMX576 native and active pixel array size */
+static const struct v4l2_rect imx576_native_area = {
+	.top = 0,
+	.left = 0,
+	.width = 5792,
+	.height = 4464,
+};
+
+static const struct v4l2_rect imx576_active_area = {
+	.top = 136,
+	.left = 16,
+	.width = 5760,
+	.height = 4312,
+};
+
+static const char * const imx576_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+static const struct cci_reg_sequence imx576_common_regs[] = {
+	{ CCI_REG8(0x0136), 0x18 },
+	{ CCI_REG8(0x0137), 0x00 },
+	{ CCI_REG8(0x3c7e), 0x05 },
+	{ CCI_REG8(0x3c7f), 0x07 },
+	{ CCI_REG8(0x380d), 0x80 },
+	{ CCI_REG8(0x3c00), 0x1a },
+	{ CCI_REG8(0x3c01), 0x1a },
+	{ CCI_REG8(0x3c02), 0x1a },
+	{ CCI_REG8(0x3c03), 0x1a },
+	{ CCI_REG8(0x3c04), 0x1a },
+	{ CCI_REG8(0x3c05), 0x01 },
+	{ CCI_REG8(0x3c08), 0xff },
+	{ CCI_REG8(0x3c09), 0xff },
+	{ CCI_REG8(0x3c0a), 0x01 },
+	{ CCI_REG8(0x3c0d), 0xff },
+	{ CCI_REG8(0x3c0e), 0xff },
+	{ CCI_REG8(0x3c0f), 0x20 },
+	{ CCI_REG8(0x3f89), 0x01 },
+	{ CCI_REG8(0x4b8e), 0x18 },
+	{ CCI_REG8(0x4b8f), 0x10 },
+	{ CCI_REG8(0x4ba8), 0x08 },
+	{ CCI_REG8(0x4baa), 0x08 },
+	{ CCI_REG8(0x4bab), 0x08 },
+	{ CCI_REG8(0x4bc9), 0x10 },
+	{ CCI_REG8(0x5511), 0x01 },
+	{ CCI_REG8(0x560b), 0x5b },
+	{ CCI_REG8(0x56a7), 0x60 },
+	{ CCI_REG8(0x5b3b), 0x60 },
+	{ CCI_REG8(0x5ba7), 0x60 },
+	{ CCI_REG8(0x6002), 0x00 },
+	{ CCI_REG8(0x6014), 0x01 },
+	{ CCI_REG8(0x6118), 0x0a },
+	{ CCI_REG8(0x6122), 0x0a },
+	{ CCI_REG8(0x6128), 0x0a },
+	{ CCI_REG8(0x6132), 0x0a },
+	{ CCI_REG8(0x6138), 0x0a },
+	{ CCI_REG8(0x6142), 0x0a },
+	{ CCI_REG8(0x6148), 0x0a },
+	{ CCI_REG8(0x6152), 0x0a },
+	{ CCI_REG8(0x617b), 0x04 },
+	{ CCI_REG8(0x617e), 0x04 },
+	{ CCI_REG8(0x6187), 0x04 },
+	{ CCI_REG8(0x618a), 0x04 },
+	{ CCI_REG8(0x6193), 0x04 },
+	{ CCI_REG8(0x6196), 0x04 },
+	{ CCI_REG8(0x619f), 0x04 },
+	{ CCI_REG8(0x61a2), 0x04 },
+	{ CCI_REG8(0x61ab), 0x04 },
+	{ CCI_REG8(0x61ae), 0x04 },
+	{ CCI_REG8(0x61b7), 0x04 },
+	{ CCI_REG8(0x61ba), 0x04 },
+	{ CCI_REG8(0x61c3), 0x04 },
+	{ CCI_REG8(0x61c6), 0x04 },
+	{ CCI_REG8(0x61cf), 0x04 },
+	{ CCI_REG8(0x61d2), 0x04 },
+	{ CCI_REG8(0x61db), 0x04 },
+	{ CCI_REG8(0x61de), 0x04 },
+	{ CCI_REG8(0x61e7), 0x04 },
+	{ CCI_REG8(0x61ea), 0x04 },
+	{ CCI_REG8(0x61f3), 0x04 },
+	{ CCI_REG8(0x61f6), 0x04 },
+	{ CCI_REG8(0x61ff), 0x04 },
+	{ CCI_REG8(0x6202), 0x04 },
+	{ CCI_REG8(0x620b), 0x04 },
+	{ CCI_REG8(0x620e), 0x04 },
+	{ CCI_REG8(0x6217), 0x04 },
+	{ CCI_REG8(0x621a), 0x04 },
+	{ CCI_REG8(0x6223), 0x04 },
+	{ CCI_REG8(0x6226), 0x04 },
+	{ CCI_REG8(0x6b0b), 0x02 },
+	{ CCI_REG8(0x6b0c), 0x01 },
+	{ CCI_REG8(0x6b0d), 0x05 },
+	{ CCI_REG8(0x6b0f), 0x04 },
+	{ CCI_REG8(0x6b10), 0x02 },
+	{ CCI_REG8(0x6b11), 0x06 },
+	{ CCI_REG8(0x6b12), 0x03 },
+	{ CCI_REG8(0x6b13), 0x07 },
+	{ CCI_REG8(0x6b14), 0x0d },
+	{ CCI_REG8(0x6b15), 0x09 },
+	{ CCI_REG8(0x6b16), 0x0c },
+	{ CCI_REG8(0x6b17), 0x08 },
+	{ CCI_REG8(0x6b18), 0x0e },
+	{ CCI_REG8(0x6b19), 0x0a },
+	{ CCI_REG8(0x6b1a), 0x0f },
+	{ CCI_REG8(0x6b1b), 0x0b },
+	{ CCI_REG8(0x6b1c), 0x01 },
+	{ CCI_REG8(0x6b1d), 0x05 },
+	{ CCI_REG8(0x6b1f), 0x04 },
+	{ CCI_REG8(0x6b20), 0x02 },
+	{ CCI_REG8(0x6b21), 0x06 },
+	{ CCI_REG8(0x6b22), 0x03 },
+	{ CCI_REG8(0x6b23), 0x07 },
+	{ CCI_REG8(0x6b24), 0x0d },
+	{ CCI_REG8(0x6b25), 0x09 },
+	{ CCI_REG8(0x6b26), 0x0c },
+	{ CCI_REG8(0x6b27), 0x08 },
+	{ CCI_REG8(0x6b28), 0x0e },
+	{ CCI_REG8(0x6b29), 0x0a },
+	{ CCI_REG8(0x6b2a), 0x0f },
+	{ CCI_REG8(0x6b2b), 0x0b },
+	{ CCI_REG8(0x7948), 0x01 },
+	{ CCI_REG8(0x7949), 0x06 },
+	{ CCI_REG8(0x794b), 0x04 },
+	{ CCI_REG8(0x794c), 0x04 },
+	{ CCI_REG8(0x794d), 0x3a },
+	{ CCI_REG8(0x7951), 0x00 },
+	{ CCI_REG8(0x7952), 0x01 },
+	{ CCI_REG8(0x7955), 0x00 },
+	{ CCI_REG8(0x9004), 0x10 },
+	{ CCI_REG8(0x9200), 0xa0 },
+	{ CCI_REG8(0x9201), 0xa7 },
+	{ CCI_REG8(0x9202), 0xa0 },
+	{ CCI_REG8(0x9203), 0xaa },
+	{ CCI_REG8(0x9204), 0xa0 },
+	{ CCI_REG8(0x9205), 0xad },
+	{ CCI_REG8(0x9206), 0xa0 },
+	{ CCI_REG8(0x9207), 0xb0 },
+	{ CCI_REG8(0x9208), 0xa0 },
+	{ CCI_REG8(0x9209), 0xb3 },
+	{ CCI_REG8(0x920a), 0xb7 },
+	{ CCI_REG8(0x920b), 0x34 },
+	{ CCI_REG8(0x920c), 0xb7 },
+	{ CCI_REG8(0x920d), 0x36 },
+	{ CCI_REG8(0x920e), 0xb7 },
+	{ CCI_REG8(0x920f), 0x37 },
+	{ CCI_REG8(0x9210), 0xb7 },
+	{ CCI_REG8(0x9211), 0x38 },
+	{ CCI_REG8(0x9212), 0xb7 },
+	{ CCI_REG8(0x9213), 0x39 },
+	{ CCI_REG8(0x9214), 0xb7 },
+	{ CCI_REG8(0x9215), 0x3a },
+	{ CCI_REG8(0x9216), 0xb7 },
+	{ CCI_REG8(0x9217), 0x3c },
+	{ CCI_REG8(0x9218), 0xb7 },
+	{ CCI_REG8(0x9219), 0x3d },
+	{ CCI_REG8(0x921a), 0xb7 },
+	{ CCI_REG8(0x921b), 0x3e },
+	{ CCI_REG8(0x921c), 0xb7 },
+	{ CCI_REG8(0x921d), 0x3f },
+	{ CCI_REG8(0x921e), 0x7f },
+	{ CCI_REG8(0x921f), 0x77 },
+	{ CCI_REG8(0x99af), 0x0f },
+	{ CCI_REG8(0x99b0), 0x0f },
+	{ CCI_REG8(0x99b1), 0x0f },
+	{ CCI_REG8(0x99b2), 0x0f },
+	{ CCI_REG8(0x99b3), 0x0f },
+	{ CCI_REG8(0x99e1), 0x0f },
+	{ CCI_REG8(0x99e2), 0x0f },
+	{ CCI_REG8(0x99e3), 0x0f },
+	{ CCI_REG8(0x99e4), 0x0f },
+	{ CCI_REG8(0x99e5), 0x0f },
+	{ CCI_REG8(0x99e6), 0x0f },
+	{ CCI_REG8(0x99e7), 0x0f },
+	{ CCI_REG8(0x99e8), 0x0f },
+	{ CCI_REG8(0x99e9), 0x0f },
+	{ CCI_REG8(0x99ea), 0x0f },
+	{ CCI_REG8(0xe286), 0x31 },
+	{ CCI_REG8(0xe2a6), 0x32 },
+	{ CCI_REG8(0xe2c6), 0x33 },
+	{ CCI_REG8(0x4038), 0x00 },
+	{ CCI_REG8(0x9856), 0xa0 },
+	{ CCI_REG8(0x9857), 0x78 },
+	{ CCI_REG8(0x9858), 0x64 },
+	{ CCI_REG8(0x986e), 0x64 },
+	{ CCI_REG8(0x9870), 0x3c },
+	{ CCI_REG8(0x993a), 0x0e },
+	{ CCI_REG8(0x993b), 0x0e },
+	{ CCI_REG8(0x9953), 0x08 },
+	{ CCI_REG8(0x9954), 0x08 },
+	{ CCI_REG8(0x996b), 0x0f },
+	{ CCI_REG8(0x996d), 0x0f },
+	{ CCI_REG8(0x996f), 0x0f },
+	{ CCI_REG8(0x998e), 0x0f },
+	{ CCI_REG8(0xa101), 0x01 },
+	{ CCI_REG8(0xa103), 0x01 },
+	{ CCI_REG8(0xa105), 0x01 },
+	{ CCI_REG8(0xa107), 0x01 },
+	{ CCI_REG8(0xa109), 0x01 },
+	{ CCI_REG8(0xa10b), 0x01 },
+	{ CCI_REG8(0xa10d), 0x01 },
+	{ CCI_REG8(0xa10f), 0x01 },
+	{ CCI_REG8(0xa111), 0x01 },
+	{ CCI_REG8(0xa113), 0x01 },
+	{ CCI_REG8(0xa115), 0x01 },
+	{ CCI_REG8(0xa117), 0x01 },
+	{ CCI_REG8(0xa119), 0x01 },
+	{ CCI_REG8(0xa11b), 0x01 },
+	{ CCI_REG8(0xa11d), 0x01 },
+	{ CCI_REG8(0xaa58), 0x00 },
+	{ CCI_REG8(0xaa59), 0x01 },
+	{ CCI_REG8(0xab03), 0x10 },
+	{ CCI_REG8(0xab04), 0x10 },
+	{ CCI_REG8(0xab05), 0x10 },
+	{ CCI_REG8(0xad6a), 0x03 },
+	{ CCI_REG8(0xad6b), 0xff },
+	{ CCI_REG8(0xad77), 0x00 },
+	{ CCI_REG8(0xad82), 0x03 },
+	{ CCI_REG8(0xad83), 0xff },
+	{ CCI_REG8(0xae06), 0x04 },
+	{ CCI_REG8(0xae07), 0x16 },
+	{ CCI_REG8(0xae08), 0xff },
+	{ CCI_REG8(0xae09), 0x04 },
+	{ CCI_REG8(0xae0a), 0x16 },
+	{ CCI_REG8(0xae0b), 0xff },
+	{ CCI_REG8(0xaf01), 0x04 },
+	{ CCI_REG8(0xaf03), 0x0a },
+	{ CCI_REG8(0xaf05), 0x18 },
+	{ CCI_REG8(0xb048), 0x0a },
+};
+
+static const struct cci_reg_sequence mode_2880x2156_regs[] = {
+	{ CCI_REG8(0x0112), 0x0a },
+	{ CCI_REG8(0x0113), 0x0a },
+	{ CCI_REG8(0x0114), 0x03 },
+	{ CCI_REG8(0x0342), 0x0c },
+	{ CCI_REG8(0x0343), 0x5d },
+	{ CCI_REG8(0x0344), 0x00 },
+	{ CCI_REG8(0x0345), 0x00 },
+	{ CCI_REG8(0x0346), 0x00 },
+	{ CCI_REG8(0x0347), 0x00 },
+	{ CCI_REG8(0x0348), 0x16 },
+	{ CCI_REG8(0x0349), 0x7f },
+	{ CCI_REG8(0x034a), 0x10 },
+	{ CCI_REG8(0x034b), 0xd7 },
+	{ CCI_REG8(0x0220), 0x62 },
+	{ CCI_REG8(0x0900), 0x01 },
+	{ CCI_REG8(0x0901), 0x22 },
+	{ CCI_REG8(0x0902), 0x08 },
+	{ CCI_REG8(0x3140), 0x00 },
+	{ CCI_REG8(0x3246), 0x81 },
+	{ CCI_REG8(0x3247), 0x81 },
+	{ CCI_REG8(0x0401), 0x00 },
+	{ CCI_REG8(0x0404), 0x00 },
+	{ CCI_REG8(0x0405), 0x10 },
+	{ CCI_REG8(0x0408), 0x00 },
+	{ CCI_REG8(0x0409), 0x00 },
+	{ CCI_REG8(0x040a), 0x00 },
+	{ CCI_REG8(0x040b), 0x00 },
+	{ CCI_REG8(0x040c), 0x0b },
+	{ CCI_REG8(0x040d), 0x40 },
+	{ CCI_REG8(0x040e), 0x08 },
+	{ CCI_REG8(0x040f), 0x6c },
+	{ CCI_REG8(0x034c), 0x0b },
+	{ CCI_REG8(0x034d), 0x40 },
+	{ CCI_REG8(0x034e), 0x08 },
+	{ CCI_REG8(0x034f), 0x6c },
+	{ CCI_REG8(0x0301), 0x05 },
+	{ CCI_REG8(0x0303), 0x04 },
+	{ CCI_REG8(0x0305), 0x04 },
+	{ CCI_REG8(0x0306), 0x00 },
+	{ CCI_REG8(0x0307), 0xaf },
+	{ CCI_REG8(0x030b), 0x02 },
+	{ CCI_REG8(0x030d), 0x04 },
+	{ CCI_REG8(0x030e), 0x00 },
+	{ CCI_REG8(0x030f), 0xd1 },
+	{ CCI_REG8(0x0310), 0x01 },
+	{ CCI_REG8(0x0b06), 0x01 },
+	{ CCI_REG8(0x3620), 0x00 },
+	{ CCI_REG8(0x3f0c), 0x00 },
+	{ CCI_REG8(0x3f14), 0x01 },
+	{ CCI_REG8(0x3f80), 0x03 },
+	{ CCI_REG8(0x3f81), 0xe8 },
+	{ CCI_REG8(0x3ffc), 0x00 },
+	{ CCI_REG8(0x3ffd), 0x26 },
+	{ CCI_REG8(0x0202), 0x07 },
+	{ CCI_REG8(0x0203), 0xd0 },
+	{ CCI_REG8(0x0224), 0x01 },
+	{ CCI_REG8(0x0225), 0xf4 },
+	{ CCI_REG8(0x3fe0), 0x03 },
+	{ CCI_REG8(0x3fe1), 0xe8 },
+	{ CCI_REG8(0x0204), 0x00 },
+	{ CCI_REG8(0x0205), 0x00 },
+	{ CCI_REG8(0x0216), 0x00 },
+	{ CCI_REG8(0x0217), 0x00 },
+	{ CCI_REG8(0x0218), 0x01 },
+	{ CCI_REG8(0x0219), 0x00 },
+	{ CCI_REG8(0x020e), 0x01 },
+	{ CCI_REG8(0x020f), 0x00 },
+	{ CCI_REG8(0x3fe2), 0x00 },
+	{ CCI_REG8(0x3fe3), 0x00 },
+	{ CCI_REG8(0x3fe4), 0x01 },
+	{ CCI_REG8(0x3fe5), 0x00 },
+};
+
+struct imx576 {
+	struct device *dev;
+	struct regmap *regmap;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct gpio_desc *reset_gpio;
+	struct clk *inclk;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(imx576_supply_names)];
+
+	/* V4L2 Controls */
+	struct v4l2_ctrl_handler handler;
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *gain;
+
+	u32 link_freq_index;
+};
+
+struct imx576_reg_list {
+	u32 num_of_regs;
+	const struct cci_reg_sequence *regs;
+};
+
+struct imx576_mode {
+	u32 width;
+	u32 height;
+	u32 hts;
+	u32 vts;
+	struct imx576_reg_list reg_list;
+};
+
+static const struct imx576_mode supported_modes_10bit[] = {
+	{
+		.width = 2880,
+		.height = 2156,
+		.hts = 3165,
+		.vts = 2172,
+		.reg_list = {
+			.num_of_regs = ARRAY_SIZE(mode_2880x2156_regs),
+			.regs = mode_2880x2156_regs,
+		},
+	},
+};
+
+static const s64 link_freq[] = {
+	IMX576_LINK_FREQ_600MHZ,
+};
+
+static const u32 imx576_mbus_codes[] = {
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+};
+
+static inline struct imx576 *to_imx576(struct v4l2_subdev *sd)
+{
+	return container_of_const(sd, struct imx576, sd);
+}
+
+static int imx576_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imx576 *imx576 = container_of_const(ctrl->handler,
+						   struct imx576, handler);
+	struct v4l2_subdev_state *state;
+	struct v4l2_mbus_framefmt *fmt;
+	int ret = 0;
+
+	state = v4l2_subdev_get_locked_active_state(&imx576->sd);
+	fmt = v4l2_subdev_state_get_format(state, 0);
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		/* Honour the VBLANK limits when setting exposure */
+		ret = __v4l2_ctrl_modify_range(imx576->exposure,
+					       IMX576_EXPOSURE_MIN,
+					       fmt->height + ctrl->val -
+					       IMX576_EXPOSURE_OFFSET,
+					       1, IMX576_EXPOSURE_DEFAULT);
+		if (ret)
+			return ret;
+	}
+
+	if (pm_runtime_get_if_active(imx576->dev) == 0)
+		return 0;
+
+	cci_write(imx576->regmap, IMX576_REG_HOLD, 1, &ret);
+
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK: {
+		u64 vmax = fmt->height + ctrl->val;
+
+		ret = cci_write(imx576->regmap, IMX576_REG_VTS, vmax, &ret);
+		break;
+	}
+	case V4L2_CID_EXPOSURE:
+		ret = cci_write(imx576->regmap, IMX576_REG_EXPOSURE, ctrl->val, &ret);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ret = cci_write(imx576->regmap, IMX576_REG_ANALOG_GAIN,
+				ctrl->val, &ret);
+		break;
+	default:
+		dev_err(imx576->dev, "Invalid control %d\n", ctrl->id);
+		ret = -EINVAL;
+		break;
+	}
+
+	cci_write(imx576->regmap, IMX576_REG_HOLD, 0, &ret);
+
+	pm_runtime_put(imx576->dev);
+
+	return ret;
+}
+
+static int imx576_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(imx576_mbus_codes))
+		return -EINVAL;
+
+	code->code = imx576_mbus_codes[code->index];
+
+	return 0;
+}
+
+static int imx576_set_pad_format(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_format *fmt)
+{
+	const struct imx576_mode *mode = &supported_modes_10bit[0];
+	struct imx576 *imx576 = to_imx576(sd);
+	struct v4l2_mbus_framefmt *format;
+	int ret;
+
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.code = imx576_mbus_codes[0];
+	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+	fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
+	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+	format = v4l2_subdev_state_get_format(sd_state, 0);
+
+	*format = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		u32 vblank_def = mode->vts - mode->height;
+
+		ret = __v4l2_ctrl_modify_range(imx576->vblank, vblank_def,
+					       IMX576_VBLANK_MAX, 1, vblank_def);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int imx576_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *sd_state,
+				struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r = imx576_native_area;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r = imx576_active_area;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int imx576_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_frame_size_enum *fsize)
+{
+	if (fsize->index >= ARRAY_SIZE(supported_modes_10bit))
+		return -EINVAL;
+
+	if (fsize->code != imx576_mbus_codes[0])
+		return -EINVAL;
+
+	fsize->min_width = supported_modes_10bit[fsize->index].width;
+	fsize->max_width = fsize->min_width;
+	fsize->min_height = supported_modes_10bit[fsize->index].height;
+	fsize->max_height = fsize->min_height;
+
+	return 0;
+}
+
+static int imx576_enable_streams(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state,
+				 u32 pad, u64 streams_mask)
+{
+	const struct imx576_reg_list *reg_list;
+	struct imx576 *imx576 = to_imx576(sd);
+	const struct v4l2_mbus_framefmt *fmt;
+	const struct imx576_mode *mode;
+	int ret;
+
+	fmt = v4l2_subdev_state_get_format(state, 0);
+	mode = v4l2_find_nearest_size(supported_modes_10bit,
+				      ARRAY_SIZE(supported_modes_10bit), width,
+				      height, fmt->width, fmt->height);
+
+	ret = pm_runtime_resume_and_get(imx576->dev);
+	if (ret < 0)
+		return ret;
+
+	/* Write common registers */
+	ret = cci_multi_reg_write(imx576->regmap, imx576_common_regs,
+				  ARRAY_SIZE(imx576_common_regs), NULL);
+	if (ret) {
+		dev_err(imx576->dev, "failed to write common registers\n");
+		goto err_rpm_put;
+	}
+
+	/* Write sensor mode registers */
+	reg_list = &mode->reg_list;
+	ret = cci_multi_reg_write(imx576->regmap, reg_list->regs,
+				  reg_list->num_of_regs, NULL);
+	if (ret) {
+		dev_err(imx576->dev, "fail to write initial registers\n");
+		goto err_rpm_put;
+	}
+
+	/* Apply customized user controls */
+	ret =  __v4l2_ctrl_handler_setup(imx576->sd.ctrl_handler);
+	if (ret) {
+		dev_err(imx576->dev, "fail to setup handler\n");
+		goto err_rpm_put;
+	}
+
+	/* T7: delay before sending stream command */
+	usleep_range(8000, 9000);
+
+	/* Start streaming */
+	cci_write(imx576->regmap, IMX576_REG_MODE_SELECT, IMX576_MODE_STREAMING, &ret);
+	if (ret) {
+		dev_err(imx576->dev, "fail to start streaming\n");
+		goto err_rpm_put;
+	}
+
+	return 0;
+
+err_rpm_put:
+	pm_runtime_put(imx576->dev);
+
+	return ret;
+}
+
+static int imx576_disable_streams(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state,
+				  u32 pad, u64 streams_mask)
+{
+	struct imx576 *imx576 = to_imx576(sd);
+	int ret;
+
+	ret = cci_write(imx576->regmap, IMX576_REG_MODE_SELECT,
+			IMX576_MODE_STANDBY, NULL);
+	if (ret)
+		dev_err(imx576->dev, "failed to set stream off\n");
+
+	pm_runtime_put(imx576->dev);
+
+	return 0;
+}
+
+static int imx576_init_state(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *sd_state)
+{
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.pad = 0,
+		.format = {
+			.code = imx576_mbus_codes[0],
+			.width = supported_modes_10bit[0].width,
+			.height = supported_modes_10bit[0].height,
+		},
+	};
+
+	imx576_set_pad_format(sd, sd_state, &fmt);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx576_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops imx576_pad_ops = {
+	.enum_mbus_code = imx576_enum_mbus_code,
+	.enum_frame_size = imx576_enum_frame_size,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = imx576_set_pad_format,
+	.get_selection = imx576_get_selection,
+	.enable_streams = imx576_enable_streams,
+	.disable_streams = imx576_disable_streams,
+};
+
+static const struct v4l2_subdev_internal_ops imx576_internal_ops = {
+	.init_state = imx576_init_state,
+};
+
+static const struct v4l2_subdev_ops imx576_subdev_ops = {
+	.video = &imx576_video_ops,
+	.pad = &imx576_pad_ops,
+};
+
+static const struct v4l2_ctrl_ops imx576_ctrl_ops = {
+	.s_ctrl = imx576_set_ctrl,
+};
+
+static int imx576_detect(struct imx576 *imx576)
+{
+	int ret;
+	u64 val;
+
+	ret = cci_read(imx576->regmap, IMX576_REG_CHIP_ID, &val, NULL);
+	if (ret)
+		return dev_err_probe(imx576->dev, ret,
+				     "failed to read chip id %x\n",
+				     IMX576_CHIP_ID);
+
+	if (val != IMX576_CHIP_ID)
+		return dev_err_probe(imx576->dev, -EIO,
+				     "chip id mismatch: %x!=%llx\n",
+				     IMX576_CHIP_ID, val);
+
+	return 0;
+}
+
+static int imx576_power_on(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct imx576 *imx576 = to_imx576(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(imx576_supply_names),
+				    imx576->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable regulators\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(imx576->inclk);
+	if (ret) {
+		dev_err(imx576->dev, "fail to enable inclk\n");
+		goto err_regulator_off;
+	}
+
+	gpiod_set_value_cansleep(imx576->reset_gpio, 0);
+
+	/* T6: Wait for internal init before CCI access */
+	usleep_range(1000, 1200);
+
+	return 0;
+
+err_regulator_off:
+	regulator_bulk_disable(ARRAY_SIZE(imx576_supply_names),
+			       imx576->supplies);
+
+	return ret;
+}
+
+static int imx576_power_off(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct imx576 *imx576 = to_imx576(sd);
+
+	clk_disable_unprepare(imx576->inclk);
+
+	gpiod_set_value_cansleep(imx576->reset_gpio, 1);
+
+	regulator_bulk_disable(ARRAY_SIZE(imx576_supply_names),
+			       imx576->supplies);
+
+	return 0;
+}
+
+static int imx576_parse_endpoint(struct imx576 *imx576)
+{
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY,
+	};
+	unsigned long link_freq_bitmap;
+	struct fwnode_handle *ep;
+	int ret;
+
+	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(imx576->dev), 0, 0, 0);
+	if (!ep)
+		return dev_err_probe(imx576->dev, -ENXIO,
+				     "Failed to get next endpoint\n");
+
+	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 != IMX576_NUM_DATA_LANES) {
+		ret = dev_err_probe(imx576->dev, -EINVAL,
+				    "only 4 data lanes are supported\n");
+		goto error_out;
+	}
+
+	ret = v4l2_link_freq_to_bitmap(imx576->dev, bus_cfg.link_frequencies,
+				       bus_cfg.nr_of_link_frequencies,
+				       link_freq, ARRAY_SIZE(link_freq),
+				       &link_freq_bitmap);
+	if (ret) {
+		ret = dev_err_probe(imx576->dev, -EINVAL,
+				    "only 600MHz frequency is available\n");
+		goto error_out;
+	}
+
+error_out:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+
+	return ret;
+}
+
+static int imx576_init_controls(struct imx576 *imx576)
+{
+	const struct imx576_mode *mode = &supported_modes_10bit[0];
+	struct v4l2_fwnode_device_properties props;
+	u64 vblank_def, hblank_def, pixel_rate;
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	u32 lpfr;
+	int ret;
+
+	ctrl_hdlr = &imx576->handler;
+	v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+
+	/* Initialize exposure and gain */
+	vblank_def = mode->vts - mode->height;
+	lpfr = vblank_def + mode->height;
+	imx576->exposure = v4l2_ctrl_new_std(ctrl_hdlr,
+					     &imx576_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     IMX576_EXPOSURE_MIN,
+					     lpfr - IMX576_EXPOSURE_OFFSET,
+					     IMX576_EXPOSURE_STEP,
+					     IMX576_EXPOSURE_DEFAULT);
+
+	imx576->gain = v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops,
+					 V4L2_CID_ANALOGUE_GAIN,
+					 IMX576_ANA_GAIN_MIN,
+					 IMX576_ANA_GAIN_MAX,
+					 IMX576_ANA_GAIN_STEP,
+					 IMX576_ANA_GAIN_DEFAULT);
+
+	imx576->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops,
+					   V4L2_CID_VBLANK, vblank_def,
+					   IMX576_VBLANK_MAX, 1,
+					   vblank_def);
+
+	/* pixel_rate = link_frequency * 2 * nr_of_lanes / bits_per_sample */
+	pixel_rate = div_u64(IMX576_LINK_FREQ_600MHZ * 2 * IMX576_NUM_DATA_LANES, 10);
+	v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops, V4L2_CID_PIXEL_RATE,
+			  pixel_rate, pixel_rate, 1, pixel_rate);
+
+	imx576->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx576_ctrl_ops,
+						   V4L2_CID_LINK_FREQ,
+						   ARRAY_SIZE(link_freq) - 1,
+						   imx576->link_freq_index,
+						   link_freq);
+	if (imx576->link_freq)
+		imx576->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	hblank_def = mode->hts - mode->width;
+	imx576->hblank = v4l2_ctrl_new_std(ctrl_hdlr,
+					   &imx576_ctrl_ops,
+					   V4L2_CID_HBLANK,
+					   hblank_def,
+					   hblank_def,
+					   1, hblank_def);
+	if (imx576->hblank)
+		imx576->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	ret = v4l2_fwnode_device_parse(imx576->dev, &props);
+	if (ret)
+		goto err_handler_free;
+
+	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx576_ctrl_ops, &props);
+	if (ret)
+		goto err_handler_free;
+
+	imx576->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+
+err_handler_free:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+
+	return ret;
+}
+
+static int imx576_probe(struct i2c_client *client)
+{
+	struct imx576 *imx576;
+	unsigned int inclk_freq;
+	int ret;
+
+	imx576 = devm_kzalloc(&client->dev, sizeof(*imx576), GFP_KERNEL);
+	if (!imx576)
+		return -ENOMEM;
+
+	imx576->dev = &client->dev;
+
+	/* Initialize subdev */
+	v4l2_i2c_subdev_init(&imx576->sd, client, &imx576_subdev_ops);
+	imx576->sd.internal_ops = &imx576_internal_ops;
+
+	imx576->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(imx576->regmap))
+		return dev_err_probe(imx576->dev, PTR_ERR(imx576->regmap),
+				     "failed to initialize CCI\n");
+
+	ret = imx576_parse_endpoint(imx576);
+	if (ret)
+		return dev_err_probe(imx576->dev, ret,
+				     "failed to parse endpoint configuration\n");
+
+	/* Get sensor input clock */
+	imx576->inclk = devm_v4l2_sensor_clk_get(imx576->dev, NULL);
+	if (IS_ERR(imx576->inclk))
+		return dev_err_probe(imx576->dev, PTR_ERR(imx576->inclk),
+				     "failed to get inclk\n");
+
+	inclk_freq = clk_get_rate(imx576->inclk);
+	if (inclk_freq != IMX576_INCLK_RATE)
+		return dev_err_probe(imx576->dev, -EINVAL,
+				     "inclk frequency not supported: %u Hz\n",
+				     inclk_freq);
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(imx576_supply_names); i++)
+		imx576->supplies[i].supply = imx576_supply_names[i];
+
+	ret = devm_regulator_bulk_get(imx576->dev,
+				      ARRAY_SIZE(imx576_supply_names),
+				      imx576->supplies);
+	if (ret)
+		return dev_err_probe(imx576->dev, ret,
+				     "failed to get regulators\n");
+
+	imx576->reset_gpio = devm_gpiod_get_optional(imx576->dev, "reset",
+						     GPIOD_OUT_HIGH);
+	if (IS_ERR(imx576->reset_gpio))
+		return dev_err_probe(imx576->dev, PTR_ERR(imx576->reset_gpio),
+				     "failed to get reset GPIO\n");
+
+	ret = imx576_power_on(imx576->dev);
+	if (ret)
+		return ret;
+
+	ret = imx576_detect(imx576);
+	if (ret)
+		goto error_power_off;
+
+	ret = imx576_init_controls(imx576);
+	if (ret) {
+		dev_err(imx576->dev, "failed to init controls: %d\n", ret);
+		goto error_power_off;
+	}
+
+	/* Initialize subdev */
+	imx576->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	imx576->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	imx576->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&imx576->sd.entity, 1, &imx576->pad);
+	if (ret) {
+		dev_err(imx576->dev, "failed to init entity pads: %d\n", ret);
+		goto error_handler_free;
+	}
+
+	imx576->sd.state_lock = imx576->handler.lock;
+	ret = v4l2_subdev_init_finalize(&imx576->sd);
+	if (ret < 0) {
+		dev_err_probe(imx576->dev, ret, "subdev init error\n");
+		goto error_media_entity;
+	}
+
+	pm_runtime_set_active(imx576->dev);
+	pm_runtime_enable(imx576->dev);
+
+	ret = v4l2_async_register_subdev_sensor(&imx576->sd);
+	if (ret < 0) {
+		dev_err_probe(imx576->dev, ret,
+			      "failed to register imx576 sub-device\n");
+		goto error_subdev_cleanup;
+	}
+
+	pm_runtime_idle(imx576->dev);
+
+	return 0;
+
+error_subdev_cleanup:
+	v4l2_subdev_cleanup(&imx576->sd);
+	pm_runtime_disable(imx576->dev);
+	pm_runtime_set_suspended(imx576->dev);
+
+error_media_entity:
+	media_entity_cleanup(&imx576->sd.entity);
+
+error_handler_free:
+	v4l2_ctrl_handler_free(imx576->sd.ctrl_handler);
+
+error_power_off:
+	imx576_power_off(imx576->dev);
+
+	return ret;
+}
+
+static void imx576_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx576 *imx576 = to_imx576(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	v4l2_subdev_cleanup(&imx576->sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(imx576->sd.ctrl_handler);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev)) {
+		imx576_power_off(&client->dev);
+		pm_runtime_set_suspended(&client->dev);
+	}
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx576_pm_ops,
+				 imx576_power_off, imx576_power_on, NULL);
+
+static const struct of_device_id imx576_of_match[] = {
+	{ .compatible = "sony,imx576" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx576_of_match);
+
+static struct i2c_driver imx576_driver = {
+	.driver = {
+		.name = "imx576",
+		.pm = &imx576_pm_ops,
+		.of_match_table = imx576_of_match,
+	},
+	.probe = imx576_probe,
+	.remove = imx576_remove,
+};
+module_i2c_driver(imx576_driver);
+
+MODULE_DESCRIPTION("IMX576 Camera Sensor Driver");
+MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>");
+MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>");
+MODULE_LICENSE("GPL");
--
2.34.1


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

* [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support
  2026-05-20 11:56 [PATCH 0/3] media: i2c: Add imx576 camera sensor driver Himanshu Bhavani
  2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
  2026-05-20 11:56 ` [PATCH 2/3] media: i2c: add imx576 image sensor driver Himanshu Bhavani
@ 2026-05-20 11:56 ` Himanshu Bhavani
  2026-05-20 12:14   ` Luca Weiss
  2026-05-20 13:38   ` sashiko-bot
  2 siblings, 2 replies; 10+ messages in thread
From: Himanshu Bhavani @ 2026-05-20 11:56 UTC (permalink / raw)
  To: sakari.ailus
  Cc: Hardevsinh Palaniya, Himanshu Bhavani, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Hans Verkuil, Hans de Goede, Vladimir Zapolskiy,
	Elgin Perumbilly, Laurent Pinchart, Walter Werner Schneider,
	Kate Hsuan, Svyatoslav Ryhel, linux-media, devicetree,
	linux-kernel, linux-arm-msm

From: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

Add device tree support for the Sony IMX576 front camera
sensor and connect it to CAMSS via CSIPHY3.

Signed-off-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
---
 .../boot/dts/qcom/sm7225-fairphone-fp4.dts    | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
index 3964aae47fd4..5fb0f3eddf66 100644
--- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
+++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
@@ -17,6 +17,7 @@
 #include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
 #include <dt-bindings/input/input.h>
 #include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interfaces.h>
 #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include <dt-bindings/sound/qcom,q6asm.h>
@@ -547,6 +548,24 @@ vreg_bob: bob {
 	};
 };

+&camss {
+
+	vdd-csiphy3-0p9-supply = <&vreg_l18a>;
+	vdd-csiphy3-1p25-supply = <&vreg_l22a>;
+
+	status = "okay";
+
+	ports {
+		port@3 {
+			csiphy3_ep: endpoint {
+				data-lanes = <0 1 2 3>;
+				bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+				remote-endpoint = <&camera_imx576_ep>;
+			};
+		};
+	};
+};
+
 &cci0 {
 	status = "okay";
 };
@@ -582,6 +601,34 @@ &cci1 {

 &cci1_i2c0 {
 	/* Front cam (Sony IMX576) @ 0x10 */
+	camera@10 {
+		compatible = "sony,imx576";
+		reg = <0x10>;
+
+		avdd-supply = <&vreg_l3p>;
+		dovdd-supply = <&vreg_32m_cam_dvdd_1p05>;
+		dvdd-supply = <&vreg_l6p>;
+
+		clocks = <&camcc CAMCC_MCLK1_CLK>;
+		assigned-clocks = <&camcc CAMCC_MCLK1_CLK>;
+		assigned-clock-rates = <24000000>;
+
+		reset-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
+		pinctrl-0 = <&cam_mclk1_default>;
+		pinctrl-names = "default";
+
+		orientation = <0>;
+		rotation = <90>;
+
+		port {
+			camera_imx576_ep: endpoint {
+				data-lanes = <1 2 3 4>;
+				bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+				link-frequencies = /bits/ 64 <600000000>;
+				remote-endpoint = <&csiphy3_ep>;
+			};
+		};
+	};

 	eeprom@50 {
 		compatible = "giantec,gt24p64a", "atmel,24c64";
@@ -1115,6 +1162,13 @@ platform {
 &tlmm {
 	gpio-reserved-ranges = <13 4>, <56 2>;

+	cam_mclk1_default: cam-mclk1-default-state {
+		pins = "gpio30";
+		function = "cam_mclk1";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
 	qup_uart1_sleep_cts: qup-uart1-sleep-cts-state {
 		pins = "gpio61";
 		function = "gpio";
--
2.34.1


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

* Re: [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor
  2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
@ 2026-05-20 12:08   ` sashiko-bot
  2026-05-20 12:09   ` Luca Weiss
  1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-05-20 12:08 UTC (permalink / raw)
  To: Himanshu Bhavani; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The voltage specifications for DOVDD (I/O) and DVDD (Digital Core) are swapped in the property descriptions.
--

commit 9b15ba2292de0fba24f7e296cd6c14ec7bd1a56b
Author: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

dt-bindings: media: i2c: Add imx576 sensor

This commit adds device tree bindings for the Sony IMX576 image sensor.
It defines the hardware interface properties including MIPI CSI-2 endpoints,
regulators, and clock requirements.

> diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
[ ... ]
> +  avdd-supply:
> +    description: Analog Domain Power Supply (2.8v)
> +
> +  dovdd-supply:
> +    description: I/O Domain Power Supply (1.05v)
> +
> +  dvdd-supply:
> +    description: Digital Domain Power Supply (1.8v)

[Severity: Critical]
Are the voltage specifications for the DOVDD and DVDD supplies swapped in
these descriptions?

For Sony IMX sensors, DOVDD typically operates at 1.8V and DVDD operates at
~1.05V.

Following this documentation caused a subsequent commit ("arm64: dts: qcom:
sm7225-fairphone-fp4: Add Sony IMX576 front camera support") to map a 1.8V
regulator to dvdd-supply and a 1.05V regulator to dovdd-supply.

Can applying 1.8V to the 1.05V digital core domain cause permanent hardware
damage to the sensor?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520115641.11729-1-himanshu.bhavani@siliconsignals.io?part=1

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

* Re: [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor
  2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
  2026-05-20 12:08   ` sashiko-bot
@ 2026-05-20 12:09   ` Luca Weiss
  1 sibling, 0 replies; 10+ messages in thread
From: Luca Weiss @ 2026-05-20 12:09 UTC (permalink / raw)
  To: Himanshu Bhavani, sakari.ailus
  Cc: Hardevsinh Palaniya, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Hans Verkuil, Hans de Goede, Vladimir Zapolskiy, Elgin Perumbilly,
	Laurent Pinchart, Xiaolei Wang, Walter Werner Schneider,
	Kate Hsuan, Svyatoslav Ryhel, linux-media, devicetree,
	linux-kernel, linux-arm-msm

Hi Himanshu,

On Wed May 20, 2026 at 1:56 PM CEST, Himanshu Bhavani wrote:
> From: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
>
> Add bindings for Sony IMX576 sensor
>
> Signed-off-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

This patch is missing your Signed-off-by I believe since you sent the
patch.

> ---
>  .../bindings/media/i2c/sony,imx576.yaml       | 111 ++++++++++++++++++
>  MAINTAINERS                                   |   7 ++
>  2 files changed, 118 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
> new file mode 100644
> index 000000000000..b74253004fae
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
> @@ -0,0 +1,111 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/sony,imx576.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sony IMX576 Image Sensor
> +
> +maintainers:
> +  - Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
> +
> +description:
> +  IMX576 sensor is a Sony CMOS active pixel digital image sensor with an active
> +  array size of 5760(H) × 4312(V). It is programmable through an I2C interface.
> +  Image data is transmitted through MIPI CSI-2. It supports RAW10/RAW8, COMP8
> +  output formats.
> +
> +allOf:
> +  - $ref: /schemas/media/video-interface-devices.yaml#
> +
> +properties:
> +  compatible:
> +    const: sony,imx576
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: INCLK clock
> +
> +  avdd-supply:
> +    description: Analog Domain Power Supply (2.8v)
> +
> +  dovdd-supply:
> +    description: I/O Domain Power Supply (1.05v)
> +
> +  dvdd-supply:
> +    description: Digital Domain Power Supply (1.8v)

Sony calls them vana, vdig and vif.

Regards
Luca

> +
> +  reset-gpios:
> +    maxItems: 1
> +    description: Reset Pin GPIO Control (active low)
> +
> +  port:
> +    description: MIPI CSI-2 transmitter port
> +    $ref: /schemas/graph.yaml#/$defs/port-base
> +    additionalProperties: false
> +
> +    properties:
> +      endpoint:
> +        $ref: /schemas/media/video-interfaces.yaml#
> +        unevaluatedProperties: false
> +
> +        properties:
> +          data-lanes:
> +            oneOf:
> +              - items:
> +                  - const: 1
> +                  - const: 2
> +                  - const: 3
> +                  - const: 4
> +              - items:
> +                  - const: 1
> +                  - const: 2
> +        required:
> +          - data-lanes
> +          - link-frequencies
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - avdd-supply
> +  - dovdd-supply
> +  - dvdd-supply
> +  - port
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/media/video-interfaces.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        camera-sensor@10 {
> +            compatible = "sony,imx576";
> +            reg = <0x10>;
> +            clocks = <&imx576_clk>;
> +            reset-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
> +
> +            avdd-supply = <&vreg_l3p>;
> +            dovdd-supply = <&vreg_l6p>;
> +            dvdd-supply = <&vreg_32m_cam_dvdd_1p05>;
> +
> +            orientation = <0>;
> +            rotation = <90>;
> +
> +            port {
> +                cam_out: endpoint {
> +                   remote-endpoint = <&csiphy3_ep>;
> +                   data-lanes = <1 2 3 4>;
> +                   link-frequencies = /bits/ 64 <600000000>;
> +                };
> +            };
> +        };
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ff935e197c21..1b15fa355e8b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24856,6 +24856,13 @@ T:	git git://linuxtv.org/media.git
>  F:	Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
>  F:	drivers/media/i2c/imx415.c
>
> +SONY IMX576 SENSOR DRIVER
> +M:	Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
> +M:	Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
> +L:	linux-media@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
> +
>  SONY MEMORYSTICK SUBSYSTEM
>  M:	Maxim Levitsky <maximlevitsky@gmail.com>
>  M:	Alex Dubov <oakad@yahoo.com>
> --
> 2.34.1


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

* Re: [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support
  2026-05-20 11:56 ` [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support Himanshu Bhavani
@ 2026-05-20 12:14   ` Luca Weiss
  2026-05-20 13:38   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: Luca Weiss @ 2026-05-20 12:14 UTC (permalink / raw)
  To: Himanshu Bhavani, sakari.ailus
  Cc: Hardevsinh Palaniya, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Hans Verkuil, Hans de Goede, Vladimir Zapolskiy, Elgin Perumbilly,
	Laurent Pinchart, Walter Werner Schneider, Kate Hsuan,
	Svyatoslav Ryhel, linux-media, devicetree, linux-kernel,
	linux-arm-msm

Hi Himanshu,

On Wed May 20, 2026 at 1:56 PM CEST, Himanshu Bhavani wrote:
> From: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
>
> Add device tree support for the Sony IMX576 front camera
> sensor and connect it to CAMSS via CSIPHY3.
>
> Signed-off-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

Your Signed-off-by is missing here as well

> ---
>  .../boot/dts/qcom/sm7225-fairphone-fp4.dts    | 54 +++++++++++++++++++
>  1 file changed, 54 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
> index 3964aae47fd4..5fb0f3eddf66 100644
> --- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
> +++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
> @@ -17,6 +17,7 @@
>  #include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
>  #include <dt-bindings/input/input.h>
>  #include <dt-bindings/leds/common.h>
> +#include <dt-bindings/media/video-interfaces.h>
>  #include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
>  #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
>  #include <dt-bindings/sound/qcom,q6asm.h>
> @@ -547,6 +548,24 @@ vreg_bob: bob {
>  	};
>  };
>
> +&camss {
> +

random extra newline

> +	vdd-csiphy3-0p9-supply = <&vreg_l18a>;
> +	vdd-csiphy3-1p25-supply = <&vreg_l22a>;

If you're already enabling that, please also add the supplies for the
other CSIPHYs (0-3).

But also, since camss for sm6350 hasn't been merged upstream yet, this
patchset depends on
https://lore.kernel.org/linux-arm-msm/20260216-sm6350-camss-v4-0-b9df35f87edb@fairphone.com/
You need to declare that in the cover letter!

> +
> +	status = "okay";
> +
> +	ports {
> +		port@3 {
> +			csiphy3_ep: endpoint {
> +				data-lanes = <0 1 2 3>;
> +				bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
> +				remote-endpoint = <&camera_imx576_ep>;
> +			};
> +		};
> +	};
> +};
> +
>  &cci0 {
>  	status = "okay";
>  };
> @@ -582,6 +601,34 @@ &cci1 {
>
>  &cci1_i2c0 {
>  	/* Front cam (Sony IMX576) @ 0x10 */

Remove the comment now since you're adding the node.

> +	camera@10 {
> +		compatible = "sony,imx576";
> +		reg = <0x10>;
> +
> +		avdd-supply = <&vreg_l3p>;
> +		dovdd-supply = <&vreg_32m_cam_dvdd_1p05>;
> +		dvdd-supply = <&vreg_l6p>;
> +
> +		clocks = <&camcc CAMCC_MCLK1_CLK>;
> +		assigned-clocks = <&camcc CAMCC_MCLK1_CLK>;
> +		assigned-clock-rates = <24000000>;
> +
> +		reset-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
> +		pinctrl-0 = <&cam_mclk1_default>;
> +		pinctrl-names = "default";
> +
> +		orientation = <0>;

I personally like adding a comment to make clear what '0' is:

		orientation = <0>; /* Front facing */

> +		rotation = <90>;
> +
> +		port {
> +			camera_imx576_ep: endpoint {
> +				data-lanes = <1 2 3 4>;
> +				bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
> +				link-frequencies = /bits/ 64 <600000000>;
> +				remote-endpoint = <&csiphy3_ep>;
> +			};
> +		};
> +	};
>
>  	eeprom@50 {
>  		compatible = "giantec,gt24p64a", "atmel,24c64";
> @@ -1115,6 +1162,13 @@ platform {
>  &tlmm {
>  	gpio-reserved-ranges = <13 4>, <56 2>;
>
> +	cam_mclk1_default: cam-mclk1-default-state {
> +		pins = "gpio30";
> +		function = "cam_mclk1";
> +		drive-strength = <2>;
> +		bias-disable;
> +	};

I think I was told before that this can go into the SoC .dtsi

Regards
Luca

> +
>  	qup_uart1_sleep_cts: qup-uart1-sleep-cts-state {
>  		pins = "gpio61";
>  		function = "gpio";
> --
> 2.34.1


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

* Re: [PATCH 2/3] media: i2c: add imx576 image sensor driver
  2026-05-20 11:56 ` [PATCH 2/3] media: i2c: add imx576 image sensor driver Himanshu Bhavani
@ 2026-05-20 12:33   ` Laurent Pinchart
  2026-05-20 12:43   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: Laurent Pinchart @ 2026-05-20 12:33 UTC (permalink / raw)
  To: Himanshu Bhavani
  Cc: sakari.ailus, Hardevsinh Palaniya, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Hans Verkuil, Hans de Goede, Vladimir Zapolskiy,
	Mehdi Djait, Elgin Perumbilly, Xiaolei Wang,
	Walter Werner Schneider, Kate Hsuan, Svyatoslav Ryhel,
	linux-media, devicetree, linux-kernel, linux-arm-msm

On Wed, May 20, 2026 at 05:26:34PM +0530, Himanshu Bhavani wrote:
> Add a v4l2 subdevice driver for the Sony imx576 sensor.
> 
> The Sony IMX576 image sensor with an active
> array size of 5760 x 4312
> 
> The following features are supported:
> - Manual exposure an gain control support
> - vblank/hblank control support
> - Supported resolution: 2880 x 2156 30fps (SRGGB10)
> 
> Signed-off-by: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
> ---
>  MAINTAINERS                |    1 +
>  drivers/media/i2c/Kconfig  |   10 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/imx576.c | 1029 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 1041 insertions(+)
>  create mode 100644 drivers/media/i2c/imx576.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1b15fa355e8b..768a1eb3627a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24862,6 +24862,7 @@ M:	Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
>  L:	linux-media@vger.kernel.org
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/media/i2c/sony,imx576.yaml
> +F:	drivers/media/i2c/imx576.c
> 
>  SONY MEMORYSTICK SUBSYSTEM
>  M:	Maxim Levitsky <maximlevitsky@gmail.com>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index fc2954098eaf..05d1e69c2f33 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -288,6 +288,16 @@ config VIDEO_IMX415
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called imx415.
> 
> +config VIDEO_IMX576
> +        tristate "Sony IMX576 sensor support"
> +        select V4L2_CCI_I2C
> +        help
> +          This is a Video4Linux2 sensor driver for the Sony
> +          IMX576 camera.
> +
> +          To compile this driver as a module, choose M here: the
> +          module will be called imx576.
> +
>  config VIDEO_MAX9271_LIB
>  	tristate
> 
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 90b276a7417a..e96c083e03d9 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
>  obj-$(CONFIG_VIDEO_IMX355) += imx355.o
>  obj-$(CONFIG_VIDEO_IMX412) += imx412.o
>  obj-$(CONFIG_VIDEO_IMX415) += imx415.o
> +obj-$(CONFIG_VIDEO_IMX576) += imx576.o
>  obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
>  obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
>  obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
> diff --git a/drivers/media/i2c/imx576.c b/drivers/media/i2c/imx576.c
> new file mode 100644
> index 000000000000..910cbcfb6031
> --- /dev/null
> +++ b/drivers/media/i2c/imx576.c
> @@ -0,0 +1,1029 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * V4L2 Support for the IMX576
> + *
> + * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
> + *
> + * Copyright (C) 2024 Luca Weiss <luca.weiss@fairphone.com>
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/container_of.h>
> +#include <linux/delay.h>
> +#include <linux/device/devres.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +#include <linux/time.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>
> +#include <media/v4l2-mediabus.h>
> +
> +#define IMX576_INCLK_RATE		(24 * HZ_PER_MHZ)
> +
> +#define IMX576_REG_CHIP_ID		CCI_REG16(0x0016)
> +#define IMX576_CHIP_ID			0x0576
> +
> +#define IMX576_REG_MODE_SELECT		CCI_REG8(0x0100)
> +#define IMX576_MODE_STANDBY		0x00
> +#define IMX576_MODE_STREAMING		0x01
> +
> +#define IMX576_REG_HOLD			CCI_REG8(0x0104)
> +
> +#define IMX576_REG_EXPOSURE		CCI_REG16(0x0202)
> +#define IMX576_EXPOSURE_MIN		8
> +#define IMX576_EXPOSURE_OFFSET		22
> +#define IMX576_EXPOSURE_STEP		1
> +#define IMX576_EXPOSURE_DEFAULT		0x0648
> +
> +#define IMX576_REG_ANALOG_GAIN		CCI_REG16(0x0204)
> +#define IMX576_ANA_GAIN_MIN		0
> +#define IMX576_ANA_GAIN_MAX		978
> +#define IMX576_ANA_GAIN_STEP		1
> +#define IMX576_ANA_GAIN_DEFAULT		0
> +
> +#define IMX576_REG_VTS			CCI_REG16(0x0340)
> +#define IMX576_REG_HTS			CCI_REG16(0x0342)
> +
> +/* FIXME: Exact VBLANK limit unknown (no datasheet). */
> +#define IMX576_VBLANK_MAX		32420
> +
> +#define IMX576_LINK_FREQ_600MHZ		(600 * HZ_PER_MHZ)
> +#define IMX576_NUM_DATA_LANES		4
> +
> +/* IMX576 native and active pixel array size */
> +static const struct v4l2_rect imx576_native_area = {
> +	.top = 0,
> +	.left = 0,
> +	.width = 5792,
> +	.height = 4464,
> +};
> +
> +static const struct v4l2_rect imx576_active_area = {
> +	.top = 136,
> +	.left = 16,
> +	.width = 5760,
> +	.height = 4312,
> +};
> +
> +static const char * const imx576_supply_names[] = {
> +	"avdd",		/* Analog power */
> +	"dovdd",	/* Digital I/O power */
> +	"dvdd",		/* Digital core power */
> +};
> +
> +static const struct cci_reg_sequence imx576_common_regs[] = {
> +	{ CCI_REG8(0x0136), 0x18 },
> +	{ CCI_REG8(0x0137), 0x00 },
> +	{ CCI_REG8(0x3c7e), 0x05 },
> +	{ CCI_REG8(0x3c7f), 0x07 },
> +	{ CCI_REG8(0x380d), 0x80 },
> +	{ CCI_REG8(0x3c00), 0x1a },
> +	{ CCI_REG8(0x3c01), 0x1a },
> +	{ CCI_REG8(0x3c02), 0x1a },
> +	{ CCI_REG8(0x3c03), 0x1a },
> +	{ CCI_REG8(0x3c04), 0x1a },
> +	{ CCI_REG8(0x3c05), 0x01 },
> +	{ CCI_REG8(0x3c08), 0xff },
> +	{ CCI_REG8(0x3c09), 0xff },
> +	{ CCI_REG8(0x3c0a), 0x01 },
> +	{ CCI_REG8(0x3c0d), 0xff },
> +	{ CCI_REG8(0x3c0e), 0xff },
> +	{ CCI_REG8(0x3c0f), 0x20 },
> +	{ CCI_REG8(0x3f89), 0x01 },
> +	{ CCI_REG8(0x4b8e), 0x18 },
> +	{ CCI_REG8(0x4b8f), 0x10 },
> +	{ CCI_REG8(0x4ba8), 0x08 },
> +	{ CCI_REG8(0x4baa), 0x08 },
> +	{ CCI_REG8(0x4bab), 0x08 },
> +	{ CCI_REG8(0x4bc9), 0x10 },
> +	{ CCI_REG8(0x5511), 0x01 },
> +	{ CCI_REG8(0x560b), 0x5b },
> +	{ CCI_REG8(0x56a7), 0x60 },
> +	{ CCI_REG8(0x5b3b), 0x60 },
> +	{ CCI_REG8(0x5ba7), 0x60 },
> +	{ CCI_REG8(0x6002), 0x00 },
> +	{ CCI_REG8(0x6014), 0x01 },
> +	{ CCI_REG8(0x6118), 0x0a },
> +	{ CCI_REG8(0x6122), 0x0a },
> +	{ CCI_REG8(0x6128), 0x0a },
> +	{ CCI_REG8(0x6132), 0x0a },
> +	{ CCI_REG8(0x6138), 0x0a },
> +	{ CCI_REG8(0x6142), 0x0a },
> +	{ CCI_REG8(0x6148), 0x0a },
> +	{ CCI_REG8(0x6152), 0x0a },
> +	{ CCI_REG8(0x617b), 0x04 },
> +	{ CCI_REG8(0x617e), 0x04 },
> +	{ CCI_REG8(0x6187), 0x04 },
> +	{ CCI_REG8(0x618a), 0x04 },
> +	{ CCI_REG8(0x6193), 0x04 },
> +	{ CCI_REG8(0x6196), 0x04 },
> +	{ CCI_REG8(0x619f), 0x04 },
> +	{ CCI_REG8(0x61a2), 0x04 },
> +	{ CCI_REG8(0x61ab), 0x04 },
> +	{ CCI_REG8(0x61ae), 0x04 },
> +	{ CCI_REG8(0x61b7), 0x04 },
> +	{ CCI_REG8(0x61ba), 0x04 },
> +	{ CCI_REG8(0x61c3), 0x04 },
> +	{ CCI_REG8(0x61c6), 0x04 },
> +	{ CCI_REG8(0x61cf), 0x04 },
> +	{ CCI_REG8(0x61d2), 0x04 },
> +	{ CCI_REG8(0x61db), 0x04 },
> +	{ CCI_REG8(0x61de), 0x04 },
> +	{ CCI_REG8(0x61e7), 0x04 },
> +	{ CCI_REG8(0x61ea), 0x04 },
> +	{ CCI_REG8(0x61f3), 0x04 },
> +	{ CCI_REG8(0x61f6), 0x04 },
> +	{ CCI_REG8(0x61ff), 0x04 },
> +	{ CCI_REG8(0x6202), 0x04 },
> +	{ CCI_REG8(0x620b), 0x04 },
> +	{ CCI_REG8(0x620e), 0x04 },
> +	{ CCI_REG8(0x6217), 0x04 },
> +	{ CCI_REG8(0x621a), 0x04 },
> +	{ CCI_REG8(0x6223), 0x04 },
> +	{ CCI_REG8(0x6226), 0x04 },
> +	{ CCI_REG8(0x6b0b), 0x02 },
> +	{ CCI_REG8(0x6b0c), 0x01 },
> +	{ CCI_REG8(0x6b0d), 0x05 },
> +	{ CCI_REG8(0x6b0f), 0x04 },
> +	{ CCI_REG8(0x6b10), 0x02 },
> +	{ CCI_REG8(0x6b11), 0x06 },
> +	{ CCI_REG8(0x6b12), 0x03 },
> +	{ CCI_REG8(0x6b13), 0x07 },
> +	{ CCI_REG8(0x6b14), 0x0d },
> +	{ CCI_REG8(0x6b15), 0x09 },
> +	{ CCI_REG8(0x6b16), 0x0c },
> +	{ CCI_REG8(0x6b17), 0x08 },
> +	{ CCI_REG8(0x6b18), 0x0e },
> +	{ CCI_REG8(0x6b19), 0x0a },
> +	{ CCI_REG8(0x6b1a), 0x0f },
> +	{ CCI_REG8(0x6b1b), 0x0b },
> +	{ CCI_REG8(0x6b1c), 0x01 },
> +	{ CCI_REG8(0x6b1d), 0x05 },
> +	{ CCI_REG8(0x6b1f), 0x04 },
> +	{ CCI_REG8(0x6b20), 0x02 },
> +	{ CCI_REG8(0x6b21), 0x06 },
> +	{ CCI_REG8(0x6b22), 0x03 },
> +	{ CCI_REG8(0x6b23), 0x07 },
> +	{ CCI_REG8(0x6b24), 0x0d },
> +	{ CCI_REG8(0x6b25), 0x09 },
> +	{ CCI_REG8(0x6b26), 0x0c },
> +	{ CCI_REG8(0x6b27), 0x08 },
> +	{ CCI_REG8(0x6b28), 0x0e },
> +	{ CCI_REG8(0x6b29), 0x0a },
> +	{ CCI_REG8(0x6b2a), 0x0f },
> +	{ CCI_REG8(0x6b2b), 0x0b },
> +	{ CCI_REG8(0x7948), 0x01 },
> +	{ CCI_REG8(0x7949), 0x06 },
> +	{ CCI_REG8(0x794b), 0x04 },
> +	{ CCI_REG8(0x794c), 0x04 },
> +	{ CCI_REG8(0x794d), 0x3a },
> +	{ CCI_REG8(0x7951), 0x00 },
> +	{ CCI_REG8(0x7952), 0x01 },
> +	{ CCI_REG8(0x7955), 0x00 },
> +	{ CCI_REG8(0x9004), 0x10 },
> +	{ CCI_REG8(0x9200), 0xa0 },
> +	{ CCI_REG8(0x9201), 0xa7 },
> +	{ CCI_REG8(0x9202), 0xa0 },
> +	{ CCI_REG8(0x9203), 0xaa },
> +	{ CCI_REG8(0x9204), 0xa0 },
> +	{ CCI_REG8(0x9205), 0xad },
> +	{ CCI_REG8(0x9206), 0xa0 },
> +	{ CCI_REG8(0x9207), 0xb0 },
> +	{ CCI_REG8(0x9208), 0xa0 },
> +	{ CCI_REG8(0x9209), 0xb3 },
> +	{ CCI_REG8(0x920a), 0xb7 },
> +	{ CCI_REG8(0x920b), 0x34 },
> +	{ CCI_REG8(0x920c), 0xb7 },
> +	{ CCI_REG8(0x920d), 0x36 },
> +	{ CCI_REG8(0x920e), 0xb7 },
> +	{ CCI_REG8(0x920f), 0x37 },
> +	{ CCI_REG8(0x9210), 0xb7 },
> +	{ CCI_REG8(0x9211), 0x38 },
> +	{ CCI_REG8(0x9212), 0xb7 },
> +	{ CCI_REG8(0x9213), 0x39 },
> +	{ CCI_REG8(0x9214), 0xb7 },
> +	{ CCI_REG8(0x9215), 0x3a },
> +	{ CCI_REG8(0x9216), 0xb7 },
> +	{ CCI_REG8(0x9217), 0x3c },
> +	{ CCI_REG8(0x9218), 0xb7 },
> +	{ CCI_REG8(0x9219), 0x3d },
> +	{ CCI_REG8(0x921a), 0xb7 },
> +	{ CCI_REG8(0x921b), 0x3e },
> +	{ CCI_REG8(0x921c), 0xb7 },
> +	{ CCI_REG8(0x921d), 0x3f },
> +	{ CCI_REG8(0x921e), 0x7f },
> +	{ CCI_REG8(0x921f), 0x77 },
> +	{ CCI_REG8(0x99af), 0x0f },
> +	{ CCI_REG8(0x99b0), 0x0f },
> +	{ CCI_REG8(0x99b1), 0x0f },
> +	{ CCI_REG8(0x99b2), 0x0f },
> +	{ CCI_REG8(0x99b3), 0x0f },
> +	{ CCI_REG8(0x99e1), 0x0f },
> +	{ CCI_REG8(0x99e2), 0x0f },
> +	{ CCI_REG8(0x99e3), 0x0f },
> +	{ CCI_REG8(0x99e4), 0x0f },
> +	{ CCI_REG8(0x99e5), 0x0f },
> +	{ CCI_REG8(0x99e6), 0x0f },
> +	{ CCI_REG8(0x99e7), 0x0f },
> +	{ CCI_REG8(0x99e8), 0x0f },
> +	{ CCI_REG8(0x99e9), 0x0f },
> +	{ CCI_REG8(0x99ea), 0x0f },
> +	{ CCI_REG8(0xe286), 0x31 },
> +	{ CCI_REG8(0xe2a6), 0x32 },
> +	{ CCI_REG8(0xe2c6), 0x33 },
> +	{ CCI_REG8(0x4038), 0x00 },
> +	{ CCI_REG8(0x9856), 0xa0 },
> +	{ CCI_REG8(0x9857), 0x78 },
> +	{ CCI_REG8(0x9858), 0x64 },
> +	{ CCI_REG8(0x986e), 0x64 },
> +	{ CCI_REG8(0x9870), 0x3c },
> +	{ CCI_REG8(0x993a), 0x0e },
> +	{ CCI_REG8(0x993b), 0x0e },
> +	{ CCI_REG8(0x9953), 0x08 },
> +	{ CCI_REG8(0x9954), 0x08 },
> +	{ CCI_REG8(0x996b), 0x0f },
> +	{ CCI_REG8(0x996d), 0x0f },
> +	{ CCI_REG8(0x996f), 0x0f },
> +	{ CCI_REG8(0x998e), 0x0f },
> +	{ CCI_REG8(0xa101), 0x01 },
> +	{ CCI_REG8(0xa103), 0x01 },
> +	{ CCI_REG8(0xa105), 0x01 },
> +	{ CCI_REG8(0xa107), 0x01 },
> +	{ CCI_REG8(0xa109), 0x01 },
> +	{ CCI_REG8(0xa10b), 0x01 },
> +	{ CCI_REG8(0xa10d), 0x01 },
> +	{ CCI_REG8(0xa10f), 0x01 },
> +	{ CCI_REG8(0xa111), 0x01 },
> +	{ CCI_REG8(0xa113), 0x01 },
> +	{ CCI_REG8(0xa115), 0x01 },
> +	{ CCI_REG8(0xa117), 0x01 },
> +	{ CCI_REG8(0xa119), 0x01 },
> +	{ CCI_REG8(0xa11b), 0x01 },
> +	{ CCI_REG8(0xa11d), 0x01 },
> +	{ CCI_REG8(0xaa58), 0x00 },
> +	{ CCI_REG8(0xaa59), 0x01 },
> +	{ CCI_REG8(0xab03), 0x10 },
> +	{ CCI_REG8(0xab04), 0x10 },
> +	{ CCI_REG8(0xab05), 0x10 },
> +	{ CCI_REG8(0xad6a), 0x03 },
> +	{ CCI_REG8(0xad6b), 0xff },
> +	{ CCI_REG8(0xad77), 0x00 },
> +	{ CCI_REG8(0xad82), 0x03 },
> +	{ CCI_REG8(0xad83), 0xff },
> +	{ CCI_REG8(0xae06), 0x04 },
> +	{ CCI_REG8(0xae07), 0x16 },
> +	{ CCI_REG8(0xae08), 0xff },
> +	{ CCI_REG8(0xae09), 0x04 },
> +	{ CCI_REG8(0xae0a), 0x16 },
> +	{ CCI_REG8(0xae0b), 0xff },
> +	{ CCI_REG8(0xaf01), 0x04 },
> +	{ CCI_REG8(0xaf03), 0x0a },
> +	{ CCI_REG8(0xaf05), 0x18 },
> +	{ CCI_REG8(0xb048), 0x0a },
> +};
> +
> +static const struct cci_reg_sequence mode_2880x2156_regs[] = {
> +	{ CCI_REG8(0x0112), 0x0a },
> +	{ CCI_REG8(0x0113), 0x0a },
> +	{ CCI_REG8(0x0114), 0x03 },
> +	{ CCI_REG8(0x0342), 0x0c },
> +	{ CCI_REG8(0x0343), 0x5d },
> +	{ CCI_REG8(0x0344), 0x00 },
> +	{ CCI_REG8(0x0345), 0x00 },
> +	{ CCI_REG8(0x0346), 0x00 },
> +	{ CCI_REG8(0x0347), 0x00 },
> +	{ CCI_REG8(0x0348), 0x16 },
> +	{ CCI_REG8(0x0349), 0x7f },
> +	{ CCI_REG8(0x034a), 0x10 },
> +	{ CCI_REG8(0x034b), 0xd7 },
> +	{ CCI_REG8(0x0220), 0x62 },
> +	{ CCI_REG8(0x0900), 0x01 },
> +	{ CCI_REG8(0x0901), 0x22 },
> +	{ CCI_REG8(0x0902), 0x08 },
> +	{ CCI_REG8(0x3140), 0x00 },
> +	{ CCI_REG8(0x3246), 0x81 },
> +	{ CCI_REG8(0x3247), 0x81 },
> +	{ CCI_REG8(0x0401), 0x00 },
> +	{ CCI_REG8(0x0404), 0x00 },
> +	{ CCI_REG8(0x0405), 0x10 },
> +	{ CCI_REG8(0x0408), 0x00 },
> +	{ CCI_REG8(0x0409), 0x00 },
> +	{ CCI_REG8(0x040a), 0x00 },
> +	{ CCI_REG8(0x040b), 0x00 },
> +	{ CCI_REG8(0x040c), 0x0b },
> +	{ CCI_REG8(0x040d), 0x40 },
> +	{ CCI_REG8(0x040e), 0x08 },
> +	{ CCI_REG8(0x040f), 0x6c },
> +	{ CCI_REG8(0x034c), 0x0b },
> +	{ CCI_REG8(0x034d), 0x40 },
> +	{ CCI_REG8(0x034e), 0x08 },
> +	{ CCI_REG8(0x034f), 0x6c },
> +	{ CCI_REG8(0x0301), 0x05 },
> +	{ CCI_REG8(0x0303), 0x04 },
> +	{ CCI_REG8(0x0305), 0x04 },
> +	{ CCI_REG8(0x0306), 0x00 },
> +	{ CCI_REG8(0x0307), 0xaf },
> +	{ CCI_REG8(0x030b), 0x02 },
> +	{ CCI_REG8(0x030d), 0x04 },
> +	{ CCI_REG8(0x030e), 0x00 },
> +	{ CCI_REG8(0x030f), 0xd1 },
> +	{ CCI_REG8(0x0310), 0x01 },
> +	{ CCI_REG8(0x0b06), 0x01 },
> +	{ CCI_REG8(0x3620), 0x00 },
> +	{ CCI_REG8(0x3f0c), 0x00 },
> +	{ CCI_REG8(0x3f14), 0x01 },
> +	{ CCI_REG8(0x3f80), 0x03 },
> +	{ CCI_REG8(0x3f81), 0xe8 },
> +	{ CCI_REG8(0x3ffc), 0x00 },
> +	{ CCI_REG8(0x3ffd), 0x26 },
> +	{ CCI_REG8(0x0202), 0x07 },
> +	{ CCI_REG8(0x0203), 0xd0 },
> +	{ CCI_REG8(0x0224), 0x01 },
> +	{ CCI_REG8(0x0225), 0xf4 },
> +	{ CCI_REG8(0x3fe0), 0x03 },
> +	{ CCI_REG8(0x3fe1), 0xe8 },
> +	{ CCI_REG8(0x0204), 0x00 },
> +	{ CCI_REG8(0x0205), 0x00 },
> +	{ CCI_REG8(0x0216), 0x00 },
> +	{ CCI_REG8(0x0217), 0x00 },
> +	{ CCI_REG8(0x0218), 0x01 },
> +	{ CCI_REG8(0x0219), 0x00 },
> +	{ CCI_REG8(0x020e), 0x01 },
> +	{ CCI_REG8(0x020f), 0x00 },
> +	{ CCI_REG8(0x3fe2), 0x00 },
> +	{ CCI_REG8(0x3fe3), 0x00 },
> +	{ CCI_REG8(0x3fe4), 0x01 },
> +	{ CCI_REG8(0x3fe5), 0x00 },
> +};
> +
> +struct imx576 {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct v4l2_subdev sd;
> +	struct media_pad pad;
> +	struct gpio_desc *reset_gpio;
> +	struct clk *inclk;
> +	struct regulator_bulk_data supplies[ARRAY_SIZE(imx576_supply_names)];
> +
> +	/* V4L2 Controls */
> +	struct v4l2_ctrl_handler handler;
> +	struct v4l2_ctrl *link_freq;
> +	struct v4l2_ctrl *hblank;
> +	struct v4l2_ctrl *vblank;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *gain;
> +
> +	u32 link_freq_index;
> +};
> +
> +struct imx576_reg_list {
> +	u32 num_of_regs;
> +	const struct cci_reg_sequence *regs;
> +};
> +
> +struct imx576_mode {
> +	u32 width;
> +	u32 height;
> +	u32 hts;
> +	u32 vts;
> +	struct imx576_reg_list reg_list;
> +};
> +
> +static const struct imx576_mode supported_modes_10bit[] = {
> +	{
> +		.width = 2880,
> +		.height = 2156,
> +		.hts = 3165,
> +		.vts = 2172,
> +		.reg_list = {
> +			.num_of_regs = ARRAY_SIZE(mode_2880x2156_regs),
> +			.regs = mode_2880x2156_regs,
> +		},
> +	},
> +};

No mode tables please. Control the analog crop and binning through the
selection and format APIs.

> +
> +static const s64 link_freq[] = {
> +	IMX576_LINK_FREQ_600MHZ,

The link frequency should be selectable from DT, with PLL parameters (if
any) computed by the driver.

> +};
> +
> +static const u32 imx576_mbus_codes[] = {
> +	MEDIA_BUS_FMT_SRGGB10_1X10,
> +};
> +
> +static inline struct imx576 *to_imx576(struct v4l2_subdev *sd)
> +{
> +	return container_of_const(sd, struct imx576, sd);
> +}
> +
> +static int imx576_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct imx576 *imx576 = container_of_const(ctrl->handler,
> +						   struct imx576, handler);
> +	struct v4l2_subdev_state *state;
> +	struct v4l2_mbus_framefmt *fmt;
> +	int ret = 0;
> +
> +	state = v4l2_subdev_get_locked_active_state(&imx576->sd);
> +	fmt = v4l2_subdev_state_get_format(state, 0);
> +
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		/* Honour the VBLANK limits when setting exposure */
> +		ret = __v4l2_ctrl_modify_range(imx576->exposure,
> +					       IMX576_EXPOSURE_MIN,
> +					       fmt->height + ctrl->val -
> +					       IMX576_EXPOSURE_OFFSET,
> +					       1, IMX576_EXPOSURE_DEFAULT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (pm_runtime_get_if_active(imx576->dev) == 0)
> +		return 0;
> +
> +	cci_write(imx576->regmap, IMX576_REG_HOLD, 1, &ret);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK: {
> +		u64 vmax = fmt->height + ctrl->val;
> +
> +		ret = cci_write(imx576->regmap, IMX576_REG_VTS, vmax, &ret);
> +		break;
> +	}
> +	case V4L2_CID_EXPOSURE:
> +		ret = cci_write(imx576->regmap, IMX576_REG_EXPOSURE, ctrl->val, &ret);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ret = cci_write(imx576->regmap, IMX576_REG_ANALOG_GAIN,
> +				ctrl->val, &ret);
> +		break;
> +	default:
> +		dev_err(imx576->dev, "Invalid control %d\n", ctrl->id);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	cci_write(imx576->regmap, IMX576_REG_HOLD, 0, &ret);
> +
> +	pm_runtime_put(imx576->dev);
> +
> +	return ret;
> +}
> +
> +static int imx576_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index >= ARRAY_SIZE(imx576_mbus_codes))
> +		return -EINVAL;
> +
> +	code->code = imx576_mbus_codes[code->index];
> +
> +	return 0;
> +}
> +
> +static int imx576_set_pad_format(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_format *fmt)
> +{
> +	const struct imx576_mode *mode = &supported_modes_10bit[0];
> +	struct imx576 *imx576 = to_imx576(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	int ret;
> +
> +	fmt->format.width = mode->width;
> +	fmt->format.height = mode->height;
> +	fmt->format.code = imx576_mbus_codes[0];
> +	fmt->format.field = V4L2_FIELD_NONE;
> +	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
> +	fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +	fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT;
> +	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
> +
> +	format = v4l2_subdev_state_get_format(sd_state, 0);
> +
> +	*format = fmt->format;
> +
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +		u32 vblank_def = mode->vts - mode->height;
> +
> +		ret = __v4l2_ctrl_modify_range(imx576->vblank, vblank_def,
> +					       IMX576_VBLANK_MAX, 1, vblank_def);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int imx576_get_selection(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_state *sd_state,
> +				struct v4l2_subdev_selection *sel)
> +{
> +	switch (sel->target) {
> +	case V4L2_SEL_TGT_NATIVE_SIZE:
> +	case V4L2_SEL_TGT_CROP_BOUNDS:
> +		sel->r = imx576_native_area;
> +		return 0;
> +	case V4L2_SEL_TGT_CROP:
> +	case V4L2_SEL_TGT_CROP_DEFAULT:
> +		sel->r = imx576_active_area;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int imx576_enum_frame_size(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *sd_state,
> +				  struct v4l2_subdev_frame_size_enum *fsize)
> +{
> +	if (fsize->index >= ARRAY_SIZE(supported_modes_10bit))
> +		return -EINVAL;
> +
> +	if (fsize->code != imx576_mbus_codes[0])
> +		return -EINVAL;
> +
> +	fsize->min_width = supported_modes_10bit[fsize->index].width;
> +	fsize->max_width = fsize->min_width;
> +	fsize->min_height = supported_modes_10bit[fsize->index].height;
> +	fsize->max_height = fsize->min_height;
> +
> +	return 0;
> +}
> +
> +static int imx576_enable_streams(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *state,
> +				 u32 pad, u64 streams_mask)
> +{
> +	const struct imx576_reg_list *reg_list;
> +	struct imx576 *imx576 = to_imx576(sd);
> +	const struct v4l2_mbus_framefmt *fmt;
> +	const struct imx576_mode *mode;
> +	int ret;
> +
> +	fmt = v4l2_subdev_state_get_format(state, 0);
> +	mode = v4l2_find_nearest_size(supported_modes_10bit,
> +				      ARRAY_SIZE(supported_modes_10bit), width,
> +				      height, fmt->width, fmt->height);
> +
> +	ret = pm_runtime_resume_and_get(imx576->dev);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Write common registers */
> +	ret = cci_multi_reg_write(imx576->regmap, imx576_common_regs,
> +				  ARRAY_SIZE(imx576_common_regs), NULL);
> +	if (ret) {
> +		dev_err(imx576->dev, "failed to write common registers\n");
> +		goto err_rpm_put;
> +	}
> +
> +	/* Write sensor mode registers */
> +	reg_list = &mode->reg_list;
> +	ret = cci_multi_reg_write(imx576->regmap, reg_list->regs,
> +				  reg_list->num_of_regs, NULL);
> +	if (ret) {
> +		dev_err(imx576->dev, "fail to write initial registers\n");
> +		goto err_rpm_put;
> +	}
> +
> +	/* Apply customized user controls */
> +	ret =  __v4l2_ctrl_handler_setup(imx576->sd.ctrl_handler);
> +	if (ret) {
> +		dev_err(imx576->dev, "fail to setup handler\n");
> +		goto err_rpm_put;
> +	}
> +
> +	/* T7: delay before sending stream command */
> +	usleep_range(8000, 9000);
> +
> +	/* Start streaming */
> +	cci_write(imx576->regmap, IMX576_REG_MODE_SELECT, IMX576_MODE_STREAMING, &ret);
> +	if (ret) {
> +		dev_err(imx576->dev, "fail to start streaming\n");
> +		goto err_rpm_put;
> +	}
> +
> +	return 0;
> +
> +err_rpm_put:
> +	pm_runtime_put(imx576->dev);
> +
> +	return ret;
> +}
> +
> +static int imx576_disable_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state,
> +				  u32 pad, u64 streams_mask)
> +{
> +	struct imx576 *imx576 = to_imx576(sd);
> +	int ret;
> +
> +	ret = cci_write(imx576->regmap, IMX576_REG_MODE_SELECT,
> +			IMX576_MODE_STANDBY, NULL);
> +	if (ret)
> +		dev_err(imx576->dev, "failed to set stream off\n");
> +
> +	pm_runtime_put(imx576->dev);
> +
> +	return 0;
> +}
> +
> +static int imx576_init_state(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_state *sd_state)
> +{
> +	struct v4l2_subdev_format fmt = {
> +		.which = V4L2_SUBDEV_FORMAT_TRY,
> +		.pad = 0,
> +		.format = {
> +			.code = imx576_mbus_codes[0],
> +			.width = supported_modes_10bit[0].width,
> +			.height = supported_modes_10bit[0].height,
> +		},
> +	};
> +
> +	imx576_set_pad_format(sd, sd_state, &fmt);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops imx576_video_ops = {
> +	.s_stream = v4l2_subdev_s_stream_helper,
> +};
> +
> +static const struct v4l2_subdev_pad_ops imx576_pad_ops = {
> +	.enum_mbus_code = imx576_enum_mbus_code,
> +	.enum_frame_size = imx576_enum_frame_size,
> +	.get_fmt = v4l2_subdev_get_fmt,
> +	.set_fmt = imx576_set_pad_format,
> +	.get_selection = imx576_get_selection,
> +	.enable_streams = imx576_enable_streams,
> +	.disable_streams = imx576_disable_streams,
> +};
> +
> +static const struct v4l2_subdev_internal_ops imx576_internal_ops = {
> +	.init_state = imx576_init_state,
> +};
> +
> +static const struct v4l2_subdev_ops imx576_subdev_ops = {
> +	.video = &imx576_video_ops,
> +	.pad = &imx576_pad_ops,
> +};
> +
> +static const struct v4l2_ctrl_ops imx576_ctrl_ops = {
> +	.s_ctrl = imx576_set_ctrl,
> +};
> +
> +static int imx576_detect(struct imx576 *imx576)
> +{
> +	int ret;
> +	u64 val;
> +
> +	ret = cci_read(imx576->regmap, IMX576_REG_CHIP_ID, &val, NULL);
> +	if (ret)
> +		return dev_err_probe(imx576->dev, ret,
> +				     "failed to read chip id %x\n",
> +				     IMX576_CHIP_ID);
> +
> +	if (val != IMX576_CHIP_ID)
> +		return dev_err_probe(imx576->dev, -EIO,
> +				     "chip id mismatch: %x!=%llx\n",
> +				     IMX576_CHIP_ID, val);
> +
> +	return 0;
> +}
> +
> +static int imx576_power_on(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct imx576 *imx576 = to_imx576(sd);
> +	int ret;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(imx576_supply_names),
> +				    imx576->supplies);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to enable regulators\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(imx576->inclk);
> +	if (ret) {
> +		dev_err(imx576->dev, "fail to enable inclk\n");
> +		goto err_regulator_off;
> +	}
> +
> +	gpiod_set_value_cansleep(imx576->reset_gpio, 0);
> +
> +	/* T6: Wait for internal init before CCI access */
> +	usleep_range(1000, 1200);
> +
> +	return 0;
> +
> +err_regulator_off:
> +	regulator_bulk_disable(ARRAY_SIZE(imx576_supply_names),
> +			       imx576->supplies);
> +
> +	return ret;
> +}
> +
> +static int imx576_power_off(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct imx576 *imx576 = to_imx576(sd);
> +
> +	clk_disable_unprepare(imx576->inclk);
> +
> +	gpiod_set_value_cansleep(imx576->reset_gpio, 1);
> +
> +	regulator_bulk_disable(ARRAY_SIZE(imx576_supply_names),
> +			       imx576->supplies);
> +
> +	return 0;
> +}
> +
> +static int imx576_parse_endpoint(struct imx576 *imx576)
> +{
> +	struct v4l2_fwnode_endpoint bus_cfg = {
> +		.bus_type = V4L2_MBUS_CSI2_DPHY,
> +	};
> +	unsigned long link_freq_bitmap;
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(imx576->dev), 0, 0, 0);
> +	if (!ep)
> +		return dev_err_probe(imx576->dev, -ENXIO,
> +				     "Failed to get next endpoint\n");
> +
> +	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 != IMX576_NUM_DATA_LANES) {
> +		ret = dev_err_probe(imx576->dev, -EINVAL,
> +				    "only 4 data lanes are supported\n");
> +		goto error_out;
> +	}
> +
> +	ret = v4l2_link_freq_to_bitmap(imx576->dev, bus_cfg.link_frequencies,
> +				       bus_cfg.nr_of_link_frequencies,
> +				       link_freq, ARRAY_SIZE(link_freq),
> +				       &link_freq_bitmap);
> +	if (ret) {
> +		ret = dev_err_probe(imx576->dev, -EINVAL,
> +				    "only 600MHz frequency is available\n");
> +		goto error_out;
> +	}
> +
> +error_out:
> +	v4l2_fwnode_endpoint_free(&bus_cfg);
> +
> +	return ret;
> +}
> +
> +static int imx576_init_controls(struct imx576 *imx576)
> +{
> +	const struct imx576_mode *mode = &supported_modes_10bit[0];
> +	struct v4l2_fwnode_device_properties props;
> +	u64 vblank_def, hblank_def, pixel_rate;
> +	struct v4l2_ctrl_handler *ctrl_hdlr;
> +	u32 lpfr;
> +	int ret;
> +
> +	ctrl_hdlr = &imx576->handler;
> +	v4l2_ctrl_handler_init(ctrl_hdlr, 8);
> +
> +	/* Initialize exposure and gain */
> +	vblank_def = mode->vts - mode->height;
> +	lpfr = vblank_def + mode->height;
> +	imx576->exposure = v4l2_ctrl_new_std(ctrl_hdlr,
> +					     &imx576_ctrl_ops,
> +					     V4L2_CID_EXPOSURE,
> +					     IMX576_EXPOSURE_MIN,
> +					     lpfr - IMX576_EXPOSURE_OFFSET,
> +					     IMX576_EXPOSURE_STEP,
> +					     IMX576_EXPOSURE_DEFAULT);
> +
> +	imx576->gain = v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops,
> +					 V4L2_CID_ANALOGUE_GAIN,
> +					 IMX576_ANA_GAIN_MIN,
> +					 IMX576_ANA_GAIN_MAX,
> +					 IMX576_ANA_GAIN_STEP,
> +					 IMX576_ANA_GAIN_DEFAULT);
> +
> +	imx576->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops,
> +					   V4L2_CID_VBLANK, vblank_def,
> +					   IMX576_VBLANK_MAX, 1,
> +					   vblank_def);
> +
> +	/* pixel_rate = link_frequency * 2 * nr_of_lanes / bits_per_sample */
> +	pixel_rate = div_u64(IMX576_LINK_FREQ_600MHZ * 2 * IMX576_NUM_DATA_LANES, 10);
> +	v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops, V4L2_CID_PIXEL_RATE,
> +			  pixel_rate, pixel_rate, 1, pixel_rate);
> +
> +	imx576->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx576_ctrl_ops,
> +						   V4L2_CID_LINK_FREQ,
> +						   ARRAY_SIZE(link_freq) - 1,
> +						   imx576->link_freq_index,
> +						   link_freq);
> +	if (imx576->link_freq)
> +		imx576->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	hblank_def = mode->hts - mode->width;
> +	imx576->hblank = v4l2_ctrl_new_std(ctrl_hdlr,
> +					   &imx576_ctrl_ops,
> +					   V4L2_CID_HBLANK,
> +					   hblank_def,
> +					   hblank_def,
> +					   1, hblank_def);
> +	if (imx576->hblank)
> +		imx576->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	ret = v4l2_fwnode_device_parse(imx576->dev, &props);
> +	if (ret)
> +		goto err_handler_free;
> +
> +	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx576_ctrl_ops, &props);
> +	if (ret)
> +		goto err_handler_free;
> +
> +	imx576->sd.ctrl_handler = ctrl_hdlr;
> +
> +	return 0;
> +
> +err_handler_free:
> +	v4l2_ctrl_handler_free(ctrl_hdlr);
> +
> +	return ret;
> +}
> +
> +static int imx576_probe(struct i2c_client *client)
> +{
> +	struct imx576 *imx576;
> +	unsigned int inclk_freq;
> +	int ret;
> +
> +	imx576 = devm_kzalloc(&client->dev, sizeof(*imx576), GFP_KERNEL);
> +	if (!imx576)
> +		return -ENOMEM;
> +
> +	imx576->dev = &client->dev;
> +
> +	/* Initialize subdev */
> +	v4l2_i2c_subdev_init(&imx576->sd, client, &imx576_subdev_ops);
> +	imx576->sd.internal_ops = &imx576_internal_ops;
> +
> +	imx576->regmap = devm_cci_regmap_init_i2c(client, 16);
> +	if (IS_ERR(imx576->regmap))
> +		return dev_err_probe(imx576->dev, PTR_ERR(imx576->regmap),
> +				     "failed to initialize CCI\n");
> +
> +	ret = imx576_parse_endpoint(imx576);
> +	if (ret)
> +		return dev_err_probe(imx576->dev, ret,
> +				     "failed to parse endpoint configuration\n");
> +
> +	/* Get sensor input clock */
> +	imx576->inclk = devm_v4l2_sensor_clk_get(imx576->dev, NULL);
> +	if (IS_ERR(imx576->inclk))
> +		return dev_err_probe(imx576->dev, PTR_ERR(imx576->inclk),
> +				     "failed to get inclk\n");
> +
> +	inclk_freq = clk_get_rate(imx576->inclk);
> +	if (inclk_freq != IMX576_INCLK_RATE)
> +		return dev_err_probe(imx576->dev, -EINVAL,
> +				     "inclk frequency not supported: %u Hz\n",
> +				     inclk_freq);
> +
> +	for (unsigned int i = 0; i < ARRAY_SIZE(imx576_supply_names); i++)
> +		imx576->supplies[i].supply = imx576_supply_names[i];
> +
> +	ret = devm_regulator_bulk_get(imx576->dev,
> +				      ARRAY_SIZE(imx576_supply_names),
> +				      imx576->supplies);
> +	if (ret)
> +		return dev_err_probe(imx576->dev, ret,
> +				     "failed to get regulators\n");
> +
> +	imx576->reset_gpio = devm_gpiod_get_optional(imx576->dev, "reset",
> +						     GPIOD_OUT_HIGH);
> +	if (IS_ERR(imx576->reset_gpio))
> +		return dev_err_probe(imx576->dev, PTR_ERR(imx576->reset_gpio),
> +				     "failed to get reset GPIO\n");
> +
> +	ret = imx576_power_on(imx576->dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = imx576_detect(imx576);
> +	if (ret)
> +		goto error_power_off;
> +
> +	ret = imx576_init_controls(imx576);
> +	if (ret) {
> +		dev_err(imx576->dev, "failed to init controls: %d\n", ret);
> +		goto error_power_off;
> +	}
> +
> +	/* Initialize subdev */
> +	imx576->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	imx576->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	imx576->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> +	ret = media_entity_pads_init(&imx576->sd.entity, 1, &imx576->pad);
> +	if (ret) {
> +		dev_err(imx576->dev, "failed to init entity pads: %d\n", ret);
> +		goto error_handler_free;
> +	}
> +
> +	imx576->sd.state_lock = imx576->handler.lock;
> +	ret = v4l2_subdev_init_finalize(&imx576->sd);
> +	if (ret < 0) {
> +		dev_err_probe(imx576->dev, ret, "subdev init error\n");
> +		goto error_media_entity;
> +	}
> +
> +	pm_runtime_set_active(imx576->dev);
> +	pm_runtime_enable(imx576->dev);
> +
> +	ret = v4l2_async_register_subdev_sensor(&imx576->sd);
> +	if (ret < 0) {
> +		dev_err_probe(imx576->dev, ret,
> +			      "failed to register imx576 sub-device\n");
> +		goto error_subdev_cleanup;
> +	}
> +
> +	pm_runtime_idle(imx576->dev);
> +
> +	return 0;
> +
> +error_subdev_cleanup:
> +	v4l2_subdev_cleanup(&imx576->sd);
> +	pm_runtime_disable(imx576->dev);
> +	pm_runtime_set_suspended(imx576->dev);
> +
> +error_media_entity:
> +	media_entity_cleanup(&imx576->sd.entity);
> +
> +error_handler_free:
> +	v4l2_ctrl_handler_free(imx576->sd.ctrl_handler);
> +
> +error_power_off:
> +	imx576_power_off(imx576->dev);
> +
> +	return ret;
> +}
> +
> +static void imx576_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx576 *imx576 = to_imx576(sd);
> +
> +	v4l2_async_unregister_subdev(sd);
> +	v4l2_subdev_cleanup(&imx576->sd);
> +	media_entity_cleanup(&sd->entity);
> +	v4l2_ctrl_handler_free(imx576->sd.ctrl_handler);
> +
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev)) {
> +		imx576_power_off(&client->dev);
> +		pm_runtime_set_suspended(&client->dev);
> +	}
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(imx576_pm_ops,
> +				 imx576_power_off, imx576_power_on, NULL);
> +
> +static const struct of_device_id imx576_of_match[] = {
> +	{ .compatible = "sony,imx576" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx576_of_match);
> +
> +static struct i2c_driver imx576_driver = {
> +	.driver = {
> +		.name = "imx576",
> +		.pm = &imx576_pm_ops,
> +		.of_match_table = imx576_of_match,
> +	},
> +	.probe = imx576_probe,
> +	.remove = imx576_remove,
> +};
> +module_i2c_driver(imx576_driver);
> +
> +MODULE_DESCRIPTION("IMX576 Camera Sensor Driver");
> +MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>");
> +MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>");
> +MODULE_LICENSE("GPL");

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 2/3] media: i2c: add imx576 image sensor driver
  2026-05-20 11:56 ` [PATCH 2/3] media: i2c: add imx576 image sensor driver Himanshu Bhavani
  2026-05-20 12:33   ` Laurent Pinchart
@ 2026-05-20 12:43   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-05-20 12:43 UTC (permalink / raw)
  To: Himanshu Bhavani; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Incorrect check of pm_runtime_get_if_active() return value allows usage count underflow and unprotected hardware access.
- [High] The sensor's HOLD register is not released if a control write fails, permanently freezing parameter updates.
- [High] The calculation of `pixel_rate` suffers from a 32-bit unsigned integer overflow, resulting in severely incorrect pixel rate reporting on 32-bit architectures.
- [Low] The dev_pm_ops structure is assigned unconditionally to the driver, producing dead code when CONFIG_PM is disabled.
--

commit 59203a6d78786f12f9149e145b22e86d70893eef
Author: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
Date:   Wed May 20 11:56:34 2026 +0000

media: i2c: add imx576 image sensor driver

Add a v4l2 subdevice driver for the Sony imx576 sensor. The driver includes
support for manual exposure, analog gain, and vblank/hblank controls.

> diff --git a/drivers/media/i2c/imx576.c b/drivers/media/i2c/imx576.c
> new file mode 100644
> index 0000000000000..910cbcfb6031f
> --- /dev/null
> +++ b/drivers/media/i2c/imx576.c
[ ... ]
> +static int imx576_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct imx576 *imx576 = container_of_const(ctrl->handler,
> +						   struct imx576, handler);
> +	struct v4l2_subdev_state *state;
> +	struct v4l2_mbus_framefmt *fmt;
> +	int ret = 0;
> +
> +	state = v4l2_subdev_get_locked_active_state(&imx576->sd);
> +	fmt = v4l2_subdev_state_get_format(state, 0);
> +
> +	if (ctrl->id == V4L2_CID_VBLANK) {
> +		/* Honour the VBLANK limits when setting exposure */
> +		ret = __v4l2_ctrl_modify_range(imx576->exposure,
> +					       IMX576_EXPOSURE_MIN,
> +					       fmt->height + ctrl->val -
> +					       IMX576_EXPOSURE_OFFSET,
> +					       1, IMX576_EXPOSURE_DEFAULT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (pm_runtime_get_if_active(imx576->dev) == 0)
> +		return 0;

[Severity: High]
Could this lead to an unbalanced usage counter and invalid hardware access if
Runtime PM is disabled? pm_runtime_get_if_active() can return a negative error
code (like -EINVAL). If it returns less than zero, this check evaluates to
false, which would allow the function to continue accessing hardware and
eventually call pm_runtime_put() without a prior successful get. Should this
check be <= 0 instead?

> +
> +	cci_write(imx576->regmap, IMX576_REG_HOLD, 1, &ret);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK: {
> +		u64 vmax = fmt->height + ctrl->val;
> +
> +		ret = cci_write(imx576->regmap, IMX576_REG_VTS, vmax, &ret);
> +		break;
> +	}
> +	case V4L2_CID_EXPOSURE:
> +		ret = cci_write(imx576->regmap, IMX576_REG_EXPOSURE, ctrl->val, &ret);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ret = cci_write(imx576->regmap, IMX576_REG_ANALOG_GAIN,
> +				ctrl->val, &ret);
> +		break;
> +	default:
> +		dev_err(imx576->dev, "Invalid control %d\n", ctrl->id);
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	cci_write(imx576->regmap, IMX576_REG_HOLD, 0, &ret);

[Severity: High]
Does this leave the sensor permanently stuck in HOLD mode if any of the
preceding control writes fail?

Since cci_write() acts as a no-op when the passed error pointer holds a
non-zero value, a failure in updating exposure or gain would cause this final
write to be skipped.

Should this final write use a separate error variable or pass NULL to
guarantee the HOLD register is released?

> +
> +	pm_runtime_put(imx576->dev);
> +
> +	return ret;
> +}
[ ... ]
> +static int imx576_init_controls(struct imx576 *imx576)
> +{
> +	const struct imx576_mode *mode = &supported_modes_10bit[0];
> +	struct v4l2_fwnode_device_properties props;
> +	u64 vblank_def, hblank_def, pixel_rate;
> +	struct v4l2_ctrl_handler *ctrl_hdlr;
> +	u32 lpfr;
> +	int ret;
[ ... ]
> +	imx576->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops,
> +					   V4L2_CID_VBLANK, vblank_def,
> +					   IMX576_VBLANK_MAX, 1,
> +					   vblank_def);
> +
> +	/* pixel_rate = link_frequency * 2 * nr_of_lanes / bits_per_sample */
> +	pixel_rate = div_u64(IMX576_LINK_FREQ_600MHZ * 2 * IMX576_NUM_DATA_LANES, 10);

[Severity: High]
Can this multiplication overflow on 32-bit architectures?

IMX576_LINK_FREQ_600MHZ expands to (600 * HZ_PER_MHZ), which evaluates as a
32-bit unsigned long. Multiplying this by 2 and then by 4 (the number of
data lanes) yields 4,800,000,000, which exceeds the maximum 32-bit unsigned
value and will wrap around before it is implicitly promoted to u64 for
div_u64().

Would it be safer to explicitly cast IMX576_LINK_FREQ_600MHZ to u64 before
the multiplication?

> +	v4l2_ctrl_new_std(ctrl_hdlr, &imx576_ctrl_ops, V4L2_CID_PIXEL_RATE,
> +			  pixel_rate, pixel_rate, 1, pixel_rate);
[ ... ]
> +static DEFINE_RUNTIME_DEV_PM_OPS(imx576_pm_ops,
> +				 imx576_power_off, imx576_power_on, NULL);
> +
> +static const struct of_device_id imx576_of_match[] = {
> +	{ .compatible = "sony,imx576" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx576_of_match);
> +
> +static struct i2c_driver imx576_driver = {
> +	.driver = {
> +		.name = "imx576",
> +		.pm = &imx576_pm_ops,

[Severity: Low]
Will this cause dead code to be compiled into the kernel when CONFIG_PM
is disabled? According to the Power Management subsystem guidelines,
the dev_pm_ops structure pointer should be assigned using the pm_ptr()
wrapper to allow the compiler to eliminate the unused structure and its
callbacks.

> +		.of_match_table = imx576_of_match,
> +	},
> +	.probe = imx576_probe,
> +	.remove = imx576_remove,
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520115641.11729-1-himanshu.bhavani@siliconsignals.io?part=2

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

* Re: [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support
  2026-05-20 11:56 ` [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support Himanshu Bhavani
  2026-05-20 12:14   ` Luca Weiss
@ 2026-05-20 13:38   ` sashiko-bot
  1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-05-20 13:38 UTC (permalink / raw)
  To: Himanshu Bhavani; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The DOVDD (Digital I/O) and DVDD (Digital Core) power supplies are swapped, applying 1.8V to the 1.05V digital core domain and risking irreversible hardware damage.
- [Medium] The custom `vdd-csiphy3-0p9-supply` and `vdd-csiphy3-1p25-supply` properties are not supported by the upstream CAMSS driver.
- [Low] The patch references an undefined `&camss` label, which will cause a Device Tree Compiler (DTC) build failure.
- [Low] The `port@3` node is missing the required `reg = <3>;` property.
--

commit e96fe4e80a1f906989411986c814165ecd67c277
Author: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>

arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support

This commit adds device tree support for the Sony IMX576 front camera
sensor. It connects the sensor to the CAMSS subsystem via CSIPHY3.

> diff --git a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
> --- a/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts
> +++ b/arch/arm64/boot/dts/qcom/sm7225-fairphone-fp4.dts

[ ... ]

> @@ -547,6 +548,24 @@ vreg_bob: bob {
>  	};
>  };
>  
> +&camss {

[Severity: Low]
Is the camss label defined anywhere in the tree? It doesn't appear to be
present in sm7225-fairphone-fp4.dts or its included dtsi files, which
would cause a build error.

> +
> +	vdd-csiphy3-0p9-supply = <&vreg_l18a>;
> +	vdd-csiphy3-1p25-supply = <&vreg_l22a>;

[Severity: Medium]
Does the upstream CAMSS driver support these custom supply names? The
upstream driver maps regulator supplies using hardcoded string arrays instead
of custom supply properties like vdd-csiphy3-0p9-supply.

> +
> +	status = "okay";
> +
> +	ports {
> +		port@3 {

[Severity: Low]
Should this node include a reg = <3>; property to match the unit address?

> +			csiphy3_ep: endpoint {
> +				data-lanes = <0 1 2 3>;
> +				bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
> +				remote-endpoint = <&camera_imx576_ep>;
> +			};
> +		};
> +	};
> +};

[ ... ]

> @@ -582,6 +601,34 @@ &cci1 {
>  
>  &cci1_i2c0 {
>  	/* Front cam (Sony IMX576) @ 0x10 */
> +	camera@10 {
> +		compatible = "sony,imx576";
> +		reg = <0x10>;
> +
> +		avdd-supply = <&vreg_l3p>;
> +		dovdd-supply = <&vreg_32m_cam_dvdd_1p05>;
> +		dvdd-supply = <&vreg_l6p>;

[Severity: High]
Can this damage the hardware? The 1.05V regulator (vreg_32m_cam_dvdd_1p05)
is assigned to dovdd-supply, while the 1.8V regulator (vreg_l6p) is assigned
to dvdd-supply.

Should these be swapped to avoid applying 1.8V to the 1.05V digital core
domain?

> +
> +		clocks = <&camcc CAMCC_MCLK1_CLK>;

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520115641.11729-1-himanshu.bhavani@siliconsignals.io?part=3

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

end of thread, other threads:[~2026-05-20 13:38 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 11:56 [PATCH 0/3] media: i2c: Add imx576 camera sensor driver Himanshu Bhavani
2026-05-20 11:56 ` [PATCH 1/3] dt-bindings: media: i2c: Add imx576 sensor Himanshu Bhavani
2026-05-20 12:08   ` sashiko-bot
2026-05-20 12:09   ` Luca Weiss
2026-05-20 11:56 ` [PATCH 2/3] media: i2c: add imx576 image sensor driver Himanshu Bhavani
2026-05-20 12:33   ` Laurent Pinchart
2026-05-20 12:43   ` sashiko-bot
2026-05-20 11:56 ` [PATCH 3/3] arm64: dts: qcom: sm7225-fairphone-fp4: Add Sony IMX576 front camera support Himanshu Bhavani
2026-05-20 12:14   ` Luca Weiss
2026-05-20 13:38   ` sashiko-bot

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