public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
@ 2025-10-28  9:21 Svyatoslav Ryhel
  2025-10-28  9:21 ` [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor Svyatoslav Ryhel
  2025-10-28  9:22 ` [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
  0 siblings, 2 replies; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-28  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sakari Ailus, Svyatoslav Ryhel, Hans Verkuil,
	Hans de Goede, Arnd Bergmann, Dongcheng Yan, André Apitzsch,
	Sylvain Petinot, Benjamin Mugnier, Heimir Thor Sverrisson
  Cc: linux-media, devicetree, linux-kernel

Add driver for Sony IMX111 CMOS sensor found in LG Optimus 4X and Vu
smartphones.

---
Changes in v2:
- added nvmem-consumer and dropped eeprom
- switched to unevaluatedProperties
- dropped flash-leds, lens-focus, orientation and rotation since they are
  inherited from video-interface-devices by unevaluatedProperties
- dropped assigned-clock* props
- improved code style
- adjusted colorspace definitions
---

Svyatoslav Ryhel (2):
  dt-bindings: media: i2c: document Sony IMX111 CMOS sensor
  media: i2c: add Sony IMX111 CMOS camera sensor driver

 .../bindings/media/i2c/sony,imx111.yaml       |  112 ++
 drivers/media/i2c/Kconfig                     |   10 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/imx111.c                    | 1614 +++++++++++++++++
 4 files changed, 1737 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
 create mode 100644 drivers/media/i2c/imx111.c

-- 
2.48.1


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

* [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor
  2025-10-28  9:21 [PATCH v2 0/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
@ 2025-10-28  9:21 ` Svyatoslav Ryhel
  2025-10-28 16:26   ` Sakari Ailus
  2025-10-28  9:22 ` [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
  1 sibling, 1 reply; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-28  9:21 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sakari Ailus, Svyatoslav Ryhel, Hans Verkuil,
	Hans de Goede, Arnd Bergmann, Dongcheng Yan, André Apitzsch,
	Sylvain Petinot, Benjamin Mugnier, Heimir Thor Sverrisson
  Cc: linux-media, devicetree, linux-kernel

Add bindings for Sony IMX111 CMOS Digital Image Sensor found in LG
Optimus 4X (P880) and Optimus Vu (P895) smartphones.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../bindings/media/i2c/sony,imx111.yaml       | 112 ++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
new file mode 100644
index 000000000000..a70017588891
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx111.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX111 8MP CMOS Digital Image Sensor
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description:
+  IMX111 sensor is a Sony CMOS active pixel digital image sensor with an active
+  array size of 2464H x 3280V. It is programmable through I2C interface. Image
+  data is sent through MIPI CSI-2, through 1 or 2 lanes.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+  - $ref: /schemas/nvmem/nvmem-consumer.yaml#
+
+properties:
+  compatible:
+    const: sony,imx111
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    description: EXTCLK with possible frequency from 6 to 54 MHz
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  iovdd-supply:
+    description: Digital IO power supply (1.8V)
+
+  dvdd-supply:
+    description: Digital power supply (1.2V)
+
+  avdd-supply:
+    description: Analog power supply (2.7V)
+
+  port:
+    additionalProperties: false
+    $ref: /schemas/graph.yaml#/$defs/port-base
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          data-lanes: true
+          bus-type: true
+          link-frequencies: true
+
+        required:
+          - data-lanes
+          - bus-type
+          - link-frequencies
+
+    required:
+      - endpoint
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - 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@10 {
+            compatible = "sony,imx111";
+            reg = <0x10>;
+
+            clocks = <&imx111_clk>;
+
+            iovdd-supply = <&camera_vddio_1v8>;
+            dvdd-supply = <&camera_vddd_1v2>;
+            avdd-supply = <&camera_vdda_2v7>;
+
+            orientation = <1>;
+            rotation = <90>;
+
+            nvmem = <&eeprom>;
+            flash-leds = <&led>;
+            lens-focus = <&vcm>;
+
+            reset-gpios = <&gpio 84 GPIO_ACTIVE_LOW>;
+
+            port {
+                imx111_output: endpoint {
+                    data-lanes = <1 2>;
+                    bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+                    link-frequencies = /bits/ 64 <271200000>;
+                    remote-endpoint = <&csi_input>;
+                };
+            };
+        };
+    };
+...
-- 
2.48.1


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

* [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-28  9:21 [PATCH v2 0/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
  2025-10-28  9:21 ` [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor Svyatoslav Ryhel
@ 2025-10-28  9:22 ` Svyatoslav Ryhel
  2025-10-28 17:58   ` Sakari Ailus
  1 sibling, 1 reply; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-28  9:22 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Sakari Ailus, Svyatoslav Ryhel, Hans Verkuil,
	Hans de Goede, Arnd Bergmann, Dongcheng Yan, André Apitzsch,
	Sylvain Petinot, Benjamin Mugnier, Heimir Thor Sverrisson
  Cc: linux-media, devicetree, linux-kernel

Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
camera sensor using the i2c bus for control and the csi-2 bus for data.

The following features are supported:
- manual exposure, digital and analog gain control support
- pixel rate/link freq control support
- supported resolution up to 3280x2464 for single shot capture
- supported resolution up to 1920x1080 @ 30fps for video
- supported bayer order output SGBRG10 and SGBRG8

Camera module seems to be partially compatible with Nokia SMIA but it
lacks a few registers required for clock calculations and has different
vendor-specific per-mode configurations which makes it incompatible with
existing CCS driver.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/media/i2c/Kconfig  |   10 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/imx111.c | 1614 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1625 insertions(+)
 create mode 100644 drivers/media/i2c/imx111.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e68202954a8f..8ec1f369f043 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -127,6 +127,16 @@ config VIDEO_HI847
           To compile this driver as a module, choose M here: the
           module will be called hi847.
 
+config VIDEO_IMX111
+	tristate "Sony IMX111 sensor support"
+	select V4L2_CCI_I2C
+	help
+	  This is a V4L2 sensor driver for the Sony IMX111 camera
+	  sensors.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx111.
+
 config VIDEO_IMX208
 	tristate "Sony IMX208 sensor support"
 	help
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 5873d29433ee..67b810c91870 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o
 obj-$(CONFIG_VIDEO_HI846) += hi846.o
 obj-$(CONFIG_VIDEO_HI847) += hi847.o
 obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
+obj-$(CONFIG_VIDEO_IMX111) += imx111.o
 obj-$(CONFIG_VIDEO_IMX208) += imx208.o
 obj-$(CONFIG_VIDEO_IMX214) += imx214.o
 obj-$(CONFIG_VIDEO_IMX219) += imx219.o
diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c
new file mode 100644
index 000000000000..814c557d9e96
--- /dev/null
+++ b/drivers/media/i2c/imx111.c
@@ -0,0 +1,1614 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/units.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+
+/* product information registers */
+#define IMX111_PRODUCT_ID			CCI_REG16(0x0000)
+#define   IMX111_CHIP_ID			0x111
+#define IMX111_REVISION				CCI_REG8(0x0002)
+#define IMX111_MANUFACTURER_ID			CCI_REG8(0x0003)
+#define IMX111_SMIA_VER				CCI_REG8(0x0004)
+#define IMX111_FRAME_COUNTER			CCI_REG8(0x0005)
+#define IMX111_PIXEL_ORDER			CCI_REG8(0x0006)
+
+/* general configuration registers */
+#define IMX111_STREAMING_MODE			CCI_REG8(0x0100)
+#define   IMX111_MODE_STANDBY			0
+#define   IMX111_MODE_STREAMING			1
+#define IMX111_IMAGE_ORIENTATION		CCI_REG8(0x0101)
+#define   IMX111_IMAGE_HFLIP			BIT(0)
+#define   IMX111_IMAGE_VFLIP			BIT(1)
+#define IMX111_SOFTWARE_RESET			CCI_REG8(0x0103)
+#define   IMX111_RESET_ON			1
+#define IMX111_GROUP_WRITE			CCI_REG8(0x0104)
+#define   IMX111_GROUP_WRITE_ON			1
+#define IMX111_FRAME_DROP			CCI_REG8(0x0105)
+#define   IMX111_FRAME_DROP_ON			1
+#define IMX111_CHANNEL_ID			CCI_REG8(0x0110)
+#define IMX111_SIGNALLING_MODE			CCI_REG8(0x0111)
+#define IMX111_DATA_DEPTH			CCI_REG16(0x0112)
+#define   IMX111_DATA_DEPTH_RAW8		0x08
+#define   IMX111_DATA_DEPTH_RAW10		0x0a
+
+/* integration time registers */
+#define IMX111_INTEGRATION_TIME			CCI_REG16(0x0202)
+#define IMX111_INTEGRATION_TIME_MIN		0x1
+#define IMX111_INTEGRATION_TIME_MAX		0xffff
+#define IMX111_INTEGRATION_TIME_STEP		1
+
+/* analog gain control */
+#define IMX111_REG_ANALOG_GAIN			CCI_REG8(0x0205)
+#define IMX111_ANA_GAIN_MIN			0
+#define IMX111_ANA_GAIN_MAX			240
+#define IMX111_ANA_GAIN_STEP			1
+#define IMX111_ANA_GAIN_DEFAULT			0
+
+/* digital gain control */
+#define IMX111_REG_DIG_GAIN_GREENR		CCI_REG16(0x020e)
+#define IMX111_REG_DIG_GAIN_RED			CCI_REG16(0x0210)
+#define IMX111_REG_DIG_GAIN_BLUE		CCI_REG16(0x0212)
+#define IMX111_REG_DIG_GAIN_GREENB		CCI_REG16(0x0214)
+#define IMX111_DGTL_GAIN_MIN			0x0100
+#define IMX111_DGTL_GAIN_MAX			0x0fff
+#define IMX111_DGTL_GAIN_DEFAULT		0x0100
+#define IMX111_DGTL_GAIN_STEP			1
+
+/* clock configuration registers */
+#define IMX111_PIXEL_CLK_DIVIDER_PLL1		CCI_REG8(0x0301) /* fixed to 10 */
+#define IMX111_SYSTEM_CLK_DIVIDER_PLL1		CCI_REG8(0x0303) /* fixed to 1 */
+#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1		CCI_REG8(0x0305)
+#define IMX111_PLL_MULTIPLIER_PLL1		CCI_REG8(0x0307)
+#define IMX111_PLL_SETTLING_TIME		CCI_REG8(0x303c)
+#define   IMX111_PLL_SETTLING_TIME_DEFAULT	200
+#define IMX111_POST_DIVIDER			CCI_REG8(0x30a4)
+#define   IMX111_POST_DIVIDER_DIV1		2
+#define   IMX111_POST_DIVIDER_DIV2		0
+#define   IMX111_POST_DIVIDER_DIV4		1
+
+/* frame timing registers */
+#define IMX111_VERTICAL_TOTAL_LENGTH		CCI_REG16(0x0340)
+#define IMX111_HORIZONTAL_TOTAL_LENGTH		CCI_REG16(0x0342)
+
+/* image size registers */
+#define IMX111_HORIZONTAL_START			CCI_REG16(0x0344)
+#define IMX111_VERTICAL_START			CCI_REG16(0x0346)
+#define IMX111_HORIZONTAL_END			CCI_REG16(0x0348)
+#define IMX111_VERTICAL_END			CCI_REG16(0x034a)
+#define IMX111_IMAGE_WIDTH			CCI_REG16(0x034c)
+#define IMX111_IMAGE_HEIGHT			CCI_REG16(0x034e)
+
+/* test pattern registers */
+#define IMX111_TEST_PATTERN			CCI_REG8(0x0601)
+#define   IMX111_TEST_PATTERN_NONE		0
+#define   IMX111_TEST_PATTERN_SOLID		1
+#define   IMX111_TEST_PATTERN_BARS		2
+#define   IMX111_TEST_PATTERN_FADE		3
+#define   IMX111_TEST_PATTERN_PN9		4
+#define IMX111_SOLID_COLOR_RED			CCI_REG16(0x0602)
+#define IMX111_SOLID_COLOR_GR			CCI_REG16(0x0604)
+#define IMX111_SOLID_COLOR_BLUE			CCI_REG16(0x0606)
+#define IMX111_SOLID_COLOR_GB			CCI_REG16(0x0608)
+#define IMX111_TESTP_COLOUR_MIN			0
+#define IMX111_TESTP_COLOUR_MAX			0x03ff
+#define IMX111_TESTP_COLOUR_STEP		1
+
+#define IMX111_FRAME_RATE_STEP			5
+
+#define IMX111_PIXEL_ARRAY_WIDTH		3280U
+#define IMX111_PIXEL_ARRAY_HEIGHT		2464U
+
+enum {
+	IMX111_MODE_3280x2464,
+	IMX111_MODE_3280x1848,
+	IMX111_MODE_3280x1098,
+	IMX111_MODE_2100x1200,
+	IMX111_MODE_1952x1098,
+	IMX111_MODE_1920x1080,
+	IMX111_MODE_1640x1232,
+	IMX111_MODE_1440x1080,
+	IMX111_MODE_1640x924,
+	IMX111_MODE_1308x736,
+	IMX111_MODE_1280x720,
+	IMX111_MODE_820x614,
+	IMX111_MODE_640x480,
+};
+
+struct imx111_mode {
+	u32 width;
+	u32 height;
+	struct {
+		const struct cci_reg_sequence *regs;
+		u32 num_of_regs;
+	} reg_list;
+};
+
+struct imx111_pll {
+	u64 extclk_rate;
+	u8 pre_div;
+	u8 mult;
+};
+
+struct imx111 {
+	struct regmap *regmap;
+
+	struct clk *extclk;
+	struct gpio_desc *reset;
+	struct regulator_bulk_data supplies[3];
+
+	struct v4l2_fwnode_endpoint bus_cfg;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+
+	/* V4L2 Controls */
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+
+	/* Current mode */
+	const struct imx111_mode *cur_mode;
+	const struct imx111_pll *pll;
+	u32 data_depth;
+
+	u64 pixel_clk_raw;
+	s64 default_link_freq;
+};
+
+static const struct imx111_pll imx111_pll[] = {
+	{
+		.extclk_rate =  6000000, .pre_div = 1, .mult = 113,
+	}, {
+		.extclk_rate = 12000000, .pre_div = 2, .mult = 113,
+	}, {
+		.extclk_rate = 13500000, .pre_div = 1, .mult = 50,
+	}, {
+		.extclk_rate = 18000000, .pre_div = 2, .mult = 75,
+	}, {
+		.extclk_rate = 24000000, .pre_div = 4, .mult = 113,
+	}, {
+		.extclk_rate = 27000000, .pre_div = 2, .mult = 50,
+	}, {
+		.extclk_rate = 36000000, .pre_div = 4, .mult = 75,
+	}, {
+		.extclk_rate = 54000000, .pre_div = 4, .mult = 50,
+	},
+};
+
+/*
+ * This table MUST contain 4 entries per format, to cover the various flip
+ * combinations in the order
+ * - no flip
+ * - h flip
+ * - v flip
+ * - h&v flips
+ */
+static const u32 imx111_mbus_formats[] = {
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+};
+
+static const struct cci_reg_sequence imx111_global_init[] = {
+	{ CCI_REG8(0x3080), 0x50 },
+	{ CCI_REG8(0x3087), 0x53 },
+	{ CCI_REG8(0x309d), 0x94 },
+	{ CCI_REG8(0x30b1), 0x03 },
+	{ CCI_REG8(0x30c6), 0x00 },
+	{ CCI_REG8(0x30c7), 0x00 },
+	{ CCI_REG8(0x3115), 0x0b },
+	{ CCI_REG8(0x3118), 0x30 },
+	{ CCI_REG8(0x311d), 0x25 },
+	{ CCI_REG8(0x3121), 0x0a },
+	{ CCI_REG8(0x3212), 0xf2 },
+	{ CCI_REG8(0x3213), 0x0f },
+	{ CCI_REG8(0x3215), 0x0f },
+	{ CCI_REG8(0x3217), 0x0b },
+	{ CCI_REG8(0x3219), 0x0b },
+	{ CCI_REG8(0x321b), 0x0d },
+	{ CCI_REG8(0x321d), 0x0d },
+	{ CCI_REG8(0x32aa), 0x11 },
+	{ CCI_REG8(0x3032), 0x40 },
+};
+
+static const struct cci_reg_sequence mode_820x614[] = {
+	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xec },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x34 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcb },
+	{ CCI_REG8(0x034c), 0x03 },	{ CCI_REG8(0x034d), 0x34 },
+	{ CCI_REG8(0x034e), 0x02 },	{ CCI_REG8(0x034f), 0x66 },
+	{ CCI_REG8(0x0381), 0x05 },	{ CCI_REG8(0x0383), 0x03 },
+	{ CCI_REG8(0x0385), 0x05 },	{ CCI_REG8(0x0387), 0x03 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x03 },	{ CCI_REG8(0x30d5), 0x09 },
+	{ CCI_REG8(0x30d6), 0x00 },	{ CCI_REG8(0x30d7), 0x00 },
+	{ CCI_REG8(0x30d8), 0x00 },	{ CCI_REG8(0x30d9), 0x00 },
+	{ CCI_REG8(0x30de), 0x04 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
+	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
+	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
+	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
+	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
+	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x7a },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1308x736[] = {
+	{ CCI_REG8(0x0340), 0x09 },	{ CCI_REG8(0x0341), 0x41 },
+	{ CCI_REG8(0x0342), 0x07 },	{ CCI_REG8(0x0343), 0x68 },
+	{ CCI_REG8(0x0344), 0x01 },	{ CCI_REG8(0x0345), 0x54 },
+	{ CCI_REG8(0x0346), 0x02 },	{ CCI_REG8(0x0347), 0x20 },
+	{ CCI_REG8(0x0348), 0x0b },	{ CCI_REG8(0x0349), 0x8b },
+	{ CCI_REG8(0x034a), 0x07 },	{ CCI_REG8(0x034b), 0xdf },
+	{ CCI_REG8(0x034c), 0x05 },	{ CCI_REG8(0x034d), 0x1c },
+	{ CCI_REG8(0x034e), 0x02 },	{ CCI_REG8(0x034f), 0xe0 },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
+	{ CCI_REG8(0x3033), 0x84 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
+	{ CCI_REG8(0x304c), 0xd7 },	{ CCI_REG8(0x304d), 0x01 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x48 },	{ CCI_REG8(0x309c), 0x12 },
+	{ CCI_REG8(0x309e), 0x04 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x0a },	{ CCI_REG8(0x30aa), 0x01 },
+	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x04 },
+	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
+	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
+	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
+	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x42 },
+	{ CCI_REG8(0x315d), 0x41 },	{ CCI_REG8(0x316e), 0x43 },
+	{ CCI_REG8(0x316f), 0x42 },	{ CCI_REG8(0x3318), 0x62 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1640x924[] = {
+	{ CCI_REG8(0x0340), 0x03 },	{ CCI_REG8(0x0341), 0xb2 },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x64 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x9b },
+	{ CCI_REG8(0x034c), 0x06 },	{ CCI_REG8(0x034d), 0x68 },
+	{ CCI_REG8(0x034e), 0x03 },	{ CCI_REG8(0x034f), 0x9c },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x03 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x09 },
+	{ CCI_REG8(0x30d6), 0x01 },	{ CCI_REG8(0x30d7), 0x01 },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x02 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
+	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
+	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
+	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
+	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
+	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x72 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1640x1232[] = {
+	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xe6 },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x30 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcf },
+	{ CCI_REG8(0x034c), 0x06 },	{ CCI_REG8(0x034d), 0x68 },
+	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0xd0 },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x03 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x09 },
+	{ CCI_REG8(0x30d6), 0x01 },	{ CCI_REG8(0x30d7), 0x01 },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x02 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
+	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
+	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
+	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
+	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
+	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x72 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1952x1098[] = {
+	{ CCI_REG8(0x0340), 0x07 },	{ CCI_REG8(0x0341), 0x5c },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xac },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x16 },
+	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x6e },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xcb },
+	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x93 },
+	{ CCI_REG8(0x034c), 0x07 },	{ CCI_REG8(0x034d), 0xa0 },
+	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0x4a },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x00 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x91 },	{ CCI_REG8(0x3048), 0x00 },
+	{ CCI_REG8(0x304c), 0x67 },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x10 },	{ CCI_REG8(0x3073), 0xa0 },
+	{ CCI_REG8(0x3074), 0x12 },	{ CCI_REG8(0x3075), 0x12 },
+	{ CCI_REG8(0x3076), 0x12 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x0a },	{ CCI_REG8(0x307a), 0x0a },
+	{ CCI_REG8(0x309b), 0x60 },	{ CCI_REG8(0x309e), 0x04 },
+	{ CCI_REG8(0x30a0), 0x15 },	{ CCI_REG8(0x30a1), 0x08 },
+	{ CCI_REG8(0x30aa), 0x03 },	{ CCI_REG8(0x30b2), 0x05 },
+	{ CCI_REG8(0x30d5), 0x20 },	{ CCI_REG8(0x30d6), 0x85 },
+	{ CCI_REG8(0x30d7), 0x2a },	{ CCI_REG8(0x30d8), 0x64 },
+	{ CCI_REG8(0x30d9), 0x89 },	{ CCI_REG8(0x30de), 0x00 },
+	{ CCI_REG8(0x30df), 0x21 },	{ CCI_REG8(0x3102), 0x08 },
+	{ CCI_REG8(0x3103), 0x1d },	{ CCI_REG8(0x3104), 0x1e },
+	{ CCI_REG8(0x3105), 0x00 },	{ CCI_REG8(0x3106), 0x74 },
+	{ CCI_REG8(0x3107), 0x00 },	{ CCI_REG8(0x3108), 0x03 },
+	{ CCI_REG8(0x3109), 0x02 },	{ CCI_REG8(0x310a), 0x03 },
+	{ CCI_REG8(0x315c), 0x37 },	{ CCI_REG8(0x315d), 0x36 },
+	{ CCI_REG8(0x316e), 0x38 },	{ CCI_REG8(0x316f), 0x37 },
+	{ CCI_REG8(0x3318), 0x63 },	{ CCI_REG8(0x3348), 0xA0 },
+};
+
+static const struct cci_reg_sequence mode_2100x1200[] = {
+	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xec },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x02 },	{ CCI_REG8(0x0345), 0x56 },
+	{ CCI_REG8(0x0346), 0x02 },	{ CCI_REG8(0x0347), 0xa8 },
+	{ CCI_REG8(0x0348), 0x0a },	{ CCI_REG8(0x0349), 0x89 },
+	{ CCI_REG8(0x034a), 0x07 },	{ CCI_REG8(0x034b), 0x57 },
+	{ CCI_REG8(0x034c), 0x08 },	{ CCI_REG8(0x034d), 0x34 },
+	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0xb0 },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
+	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
+	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
+	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
+	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
+	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
+	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x62 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x1098[] = {
+	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0x6a },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xac },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0xf6 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x0b },
+	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
+	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0x4a },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
+	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x93 },	{ CCI_REG8(0x3048), 0x00 },
+	{ CCI_REG8(0x304c), 0x67 },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0xe0 },
+	{ CCI_REG8(0x3074), 0x12 },	{ CCI_REG8(0x3075), 0x12 },
+	{ CCI_REG8(0x3076), 0x12 },	{ CCI_REG8(0x3077), 0x12 },
+	{ CCI_REG8(0x3079), 0x2a },	{ CCI_REG8(0x307a), 0x0a },
+	{ CCI_REG8(0x309b), 0x60 },	{ CCI_REG8(0x309e), 0x04 },
+	{ CCI_REG8(0x30a0), 0x15 },	{ CCI_REG8(0x30a1), 0x08 },
+	{ CCI_REG8(0x30aa), 0x03 },	{ CCI_REG8(0x30b2), 0x05 },
+	{ CCI_REG8(0x30d5), 0x00 },	{ CCI_REG8(0x30d6), 0x85 },
+	{ CCI_REG8(0x30d7), 0x2a },	{ CCI_REG8(0x30d8), 0x64 },
+	{ CCI_REG8(0x30d9), 0x89 },	{ CCI_REG8(0x30de), 0x00 },
+	{ CCI_REG8(0x30df), 0x20 },	{ CCI_REG8(0x3102), 0x08 },
+	{ CCI_REG8(0x3103), 0x1d },	{ CCI_REG8(0x3104), 0x1e },
+	{ CCI_REG8(0x3105), 0x00 },	{ CCI_REG8(0x3106), 0x74 },
+	{ CCI_REG8(0x3107), 0x00 },	{ CCI_REG8(0x3108), 0x03 },
+	{ CCI_REG8(0x3109), 0x02 },	{ CCI_REG8(0x310a), 0x03 },
+	{ CCI_REG8(0x315c), 0x37 },	{ CCI_REG8(0x315d), 0x36 },
+	{ CCI_REG8(0x316e), 0x38 },	{ CCI_REG8(0x316f), 0x37 },
+	{ CCI_REG8(0x3318), 0x63 },	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x1848[] = {
+	{ CCI_REG8(0x0340), 0x07 },	{ CCI_REG8(0x0341), 0x52 },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x64 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x9b },
+	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
+	{ CCI_REG8(0x034e), 0x07 },	{ CCI_REG8(0x034f), 0x38 },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x00 },
+	{ CCI_REG8(0x303e), 0x41 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
+	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x10 },	{ CCI_REG8(0x3103), 0x44 },
+	{ CCI_REG8(0x3104), 0x40 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x0d },	{ CCI_REG8(0x3107), 0x01 },
+	{ CCI_REG8(0x3108), 0x09 },	{ CCI_REG8(0x3109), 0x08 },
+	{ CCI_REG8(0x310a), 0x0f },	{ CCI_REG8(0x315c), 0x5d },
+	{ CCI_REG8(0x315d), 0x5c },	{ CCI_REG8(0x316e), 0x5e },
+	{ CCI_REG8(0x316f), 0x5d },	{ CCI_REG8(0x3318), 0x60 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x2464[] = {
+	{ CCI_REG8(0x0340), 0x09 },	{ CCI_REG8(0x0341), 0xba },
+	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
+	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
+	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x30 },
+	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
+	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcf },
+	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
+	{ CCI_REG8(0x034e), 0x09 },	{ CCI_REG8(0x034f), 0xa0 },
+	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
+	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
+	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x00 },
+	{ CCI_REG8(0x303e), 0x41 },	{ CCI_REG8(0x3040), 0x08 },
+	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
+	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
+	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
+	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
+	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
+	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
+	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
+	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
+	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
+	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
+	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
+	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
+	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
+	{ CCI_REG8(0x3102), 0x10 },	{ CCI_REG8(0x3103), 0x44 },
+	{ CCI_REG8(0x3104), 0x40 },	{ CCI_REG8(0x3105), 0x00 },
+	{ CCI_REG8(0x3106), 0x0d },	{ CCI_REG8(0x3107), 0x01 },
+	{ CCI_REG8(0x3108), 0x09 },	{ CCI_REG8(0x3109), 0x08 },
+	{ CCI_REG8(0x310a), 0x0f },	{ CCI_REG8(0x315c), 0x5d },
+	{ CCI_REG8(0x315d), 0x5c },	{ CCI_REG8(0x316e), 0x5e },
+	{ CCI_REG8(0x316f), 0x5d },	{ CCI_REG8(0x3318), 0x60 },
+	{ CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct imx111_mode imx111_modes[] = {
+	[IMX111_MODE_3280x2464] = {
+		.width = 3280,
+		.height = 2464,
+		.reg_list = {
+			.regs = mode_3280x2464,
+			.num_of_regs = ARRAY_SIZE(mode_3280x2464),
+		},
+	},
+	[IMX111_MODE_3280x1848] = {
+		.width = 3280,
+		.height = 1848,
+		.reg_list = {
+			.regs = mode_3280x1848,
+			.num_of_regs = ARRAY_SIZE(mode_3280x1848),
+		},
+	},
+	[IMX111_MODE_3280x1098] = {
+		.width = 3280,
+		.height = 1098,
+		.reg_list = {
+			.regs = mode_3280x1098,
+			.num_of_regs = ARRAY_SIZE(mode_3280x1098),
+		},
+	},
+	[IMX111_MODE_2100x1200] = {
+		.width = 2100,
+		.height = 1200,
+		.reg_list = {
+			.regs = mode_2100x1200,
+			.num_of_regs = ARRAY_SIZE(mode_2100x1200),
+		},
+	},
+	[IMX111_MODE_1952x1098] = {
+		.width = 1952,
+		.height = 1098,
+		.reg_list = {
+			.regs = mode_1952x1098,
+			.num_of_regs = ARRAY_SIZE(mode_1952x1098),
+		},
+	},
+	[IMX111_MODE_1920x1080] = {
+		.width = 1920,
+		.height = 1080,
+		.reg_list = {
+			.regs = mode_1952x1098,
+			.num_of_regs = ARRAY_SIZE(mode_1952x1098),
+		},
+	},
+	[IMX111_MODE_1640x1232] = {
+		.width = 1640,
+		.height = 1232,
+		.reg_list = {
+			.regs = mode_1640x1232,
+			.num_of_regs = ARRAY_SIZE(mode_1640x1232),
+		},
+	},
+	[IMX111_MODE_1440x1080] = {
+		.width = 1440,
+		.height = 1080,
+		.reg_list = {
+			.regs = mode_1640x1232,
+			.num_of_regs = ARRAY_SIZE(mode_1640x1232),
+		},
+	},
+	[IMX111_MODE_1640x924] = {
+		.width = 1640,
+		.height = 924,
+		.reg_list = {
+			.regs = mode_1640x924,
+			.num_of_regs = ARRAY_SIZE(mode_1640x924),
+		},
+	},
+	[IMX111_MODE_1308x736] = {
+		.width = 1308,
+		.height = 736,
+		.reg_list = {
+			.regs = mode_1308x736,
+			.num_of_regs = ARRAY_SIZE(mode_1308x736),
+		},
+	},
+	[IMX111_MODE_1280x720] = {
+		.width = 1280,
+		.height = 720,
+		.reg_list = {
+			.regs = mode_1308x736,
+			.num_of_regs = ARRAY_SIZE(mode_1308x736),
+		},
+	},
+	[IMX111_MODE_820x614] = {
+		.width = 820,
+		.height = 614,
+		.reg_list = {
+			.regs = mode_820x614,
+			.num_of_regs = ARRAY_SIZE(mode_820x614),
+		},
+	},
+	[IMX111_MODE_640x480] = {
+		.width = 640,
+		.height = 480,
+		.reg_list = {
+			.regs = mode_820x614,
+			.num_of_regs = ARRAY_SIZE(mode_820x614),
+		},
+	},
+};
+
+static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct imx111, sd);
+}
+
+static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct imx111, hdl);
+}
+
+static u8 to_settle_delay(u64 extclk_rate)
+{
+	u64 extclk_mhz = div_u64(extclk_rate, MEGA);
+
+	return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63, 64);
+}
+
+static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool test)
+{
+	u32 i;
+
+	for (i = 0; i < ARRAY_SIZE(imx111_mbus_formats); i++)
+		if (imx111_mbus_formats[i] == code)
+			break;
+
+	if (i >= ARRAY_SIZE(imx111_mbus_formats))
+		i = 0;
+
+	if (test)
+		return imx111_mbus_formats[i];
+
+	i = (i & ~3) | (sensor->vflip->val ? 2 : 0) |
+	    (sensor->hflip->val ? 1 : 0);
+
+	return imx111_mbus_formats[i];
+}
+
+static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format)
+{
+	switch (format->code) {
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+		return 8;
+
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	default:
+		return 10;
+	}
+}
+
+static int imx111_update_digital_gain(struct imx111 *sensor, u32 val)
+{
+	int ret = 0;
+
+	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+			IMX111_GROUP_WRITE_ON, NULL);
+
+	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret);
+	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret);
+	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret);
+	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret);
+
+	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+			0, NULL);
+
+	return ret;
+}
+
+static int imx111_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imx111 *sensor = ctrl_to_imx111(ctrl);
+	struct device *dev = regmap_get_device(sensor->regmap);
+	int ret = 0;
+
+	/*
+	 * Applying V4L2 control value only happens
+	 * when power is up for streaming
+	 */
+	if (!pm_runtime_get_if_in_use(dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, &ret);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ret = imx111_update_digital_gain(sensor, ctrl->val);
+		break;
+	case V4L2_CID_EXPOSURE:
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				IMX111_GROUP_WRITE_ON, NULL);
+		cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, NULL);
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				0, NULL);
+		break;
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION,
+			  sensor->hflip->val | sensor->vflip->val << 1, &ret);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, &ret);
+		break;
+	case V4L2_CID_TEST_PATTERN_RED:
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				IMX111_GROUP_WRITE_ON, NULL);
+		cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, &ret);
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				0, NULL);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENR:
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				IMX111_GROUP_WRITE_ON, NULL);
+		cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, &ret);
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				0, NULL);
+		break;
+	case V4L2_CID_TEST_PATTERN_BLUE:
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				IMX111_GROUP_WRITE_ON, NULL);
+		cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, &ret);
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				0, NULL);
+		break;
+	case V4L2_CID_TEST_PATTERN_GREENB:
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				IMX111_GROUP_WRITE_ON, NULL);
+		cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, &ret);
+		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+				0, NULL);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops imx111_ctrl_ops = {
+	.s_ctrl = imx111_set_ctrl,
+};
+
+static const char * const test_pattern_menu[] = {
+	"Disabled",
+	"Solid Color Fill",
+	"Standard Color Bars",
+	"Fade To Grey Color Bars",
+	"Pseudorandom data",
+};
+
+static int imx111_init_controls(struct imx111 *sensor)
+{
+	const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
+	struct device *dev = regmap_get_device(sensor->regmap);
+	struct v4l2_fwnode_device_properties props;
+	struct v4l2_subdev *sd = &sensor->sd;
+	struct v4l2_ctrl_handler *hdl = &sensor->hdl;
+	s64 pixel_rate_min, pixel_rate_max;
+	int i, ret;
+
+	ret = v4l2_fwnode_device_parse(dev, &props);
+	if (ret < 0)
+		return ret;
+
+	ret = v4l2_ctrl_handler_init(hdl, 13);
+	if (ret)
+		return ret;
+
+	pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
+	pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
+	sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
+					       pixel_rate_min, pixel_rate_max,
+					       1, div_u64(sensor->pixel_clk_raw,
+					       2 * sensor->data_depth));
+
+	sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
+						   0, 0, &sensor->default_link_freq);
+	if (sensor->link_freq)
+		sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+			  IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
+			  IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+			  IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
+			  IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
+
+	sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (sensor->hflip)
+		sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (sensor->vflip)
+		sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	/*
+	 * The maximum coarse integration time is the frame length in lines
+	 * minus five.
+	 */
+	sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+					     IMX111_INTEGRATION_TIME_MIN,
+					     IMX111_PIXEL_ARRAY_HEIGHT - 5,
+					     IMX111_INTEGRATION_TIME_STEP,
+					     IMX111_PIXEL_ARRAY_HEIGHT - 5);
+
+	v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+
+	v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
+				     test_pattern_menu);
+	for (i = 0; i < 4; i++) {
+		/*
+		 * The assumption is that
+		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
+		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
+		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
+		 */
+		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i,
+				  IMX111_TESTP_COLOUR_MIN, IMX111_TESTP_COLOUR_MAX,
+				  IMX111_TESTP_COLOUR_STEP, IMX111_TESTP_COLOUR_MAX);
+		/* The "Solid color" pattern is white by default */
+	}
+
+	if (hdl->error)
+		return hdl->error;
+
+	sd->ctrl_handler = hdl;
+
+	return 0;
+};
+
+static int imx111_start_streaming(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+	const struct imx111_mode *mode = sensor->cur_mode;
+	int ret;
+
+	/* Apply default values of current mode */
+	ret = cci_multi_reg_write(sensor->regmap, mode->reg_list.regs,
+				  mode->reg_list.num_of_regs, NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to initialize the sensor\n");
+		return ret;
+	}
+
+	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+			IMX111_GROUP_WRITE_ON, NULL);
+	cci_write(sensor->regmap, IMX111_DATA_DEPTH,
+		  sensor->data_depth | sensor->data_depth << 8, NULL);
+	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
+			0, NULL);
+
+	ret = __v4l2_ctrl_handler_setup(&sensor->hdl);
+	if (ret)
+		return ret;
+
+	ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STREAMING, NULL);
+	if (ret)
+		dev_err(dev, "failed to start stream");
+
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(sensor->vflip, true);
+	__v4l2_ctrl_grab(sensor->hflip, true);
+
+	msleep(30);
+
+	return ret;
+}
+
+static void imx111_stop_streaming(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+
+	if (cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STANDBY, NULL))
+		dev_err(dev, "failed to stop stream");
+
+	__v4l2_ctrl_grab(sensor->vflip, false);
+	__v4l2_ctrl_grab(sensor->hflip, false);
+}
+
+static int imx111_initialize(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+	int ret;
+
+	imx111_stop_streaming(sensor);
+	msleep(30);
+
+	/* Configure the PLL. */
+	cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1,
+		  sensor->pll->pre_div, &ret);
+	cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, sensor->pll->mult, &ret);
+	cci_write(sensor->regmap, IMX111_POST_DIVIDER, IMX111_POST_DIVIDER_DIV1, &ret);
+	cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME,
+		  to_settle_delay(sensor->pll->extclk_rate), &ret);
+
+	ret = cci_multi_reg_write(sensor->regmap, imx111_global_init,
+				  ARRAY_SIZE(imx111_global_init), NULL);
+	if (ret < 0) {
+		dev_err(dev, "Failed to initialize the sensor\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int imx111_set_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	struct device *dev = regmap_get_device(sensor->regmap);
+	struct v4l2_subdev_state *state;
+	int ret = 0;
+
+	state = v4l2_subdev_lock_and_get_active_state(sd);
+
+	if (enable) {
+		ret = pm_runtime_resume_and_get(dev);
+		if (ret)
+			goto finish_unlock;
+
+		ret = imx111_start_streaming(sensor);
+		if (!ret)
+			goto finish_unlock;
+
+		dev_err(dev, "Failed to start stream: %d\n", ret);
+		enable = 0;
+	}
+
+	imx111_stop_streaming(sensor);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+finish_unlock:
+	v4l2_subdev_unlock_state(state);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * IMX111 Pad Subdev Init and Operations
+ */
+static int imx111_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+
+	if (code->index >= (ARRAY_SIZE(imx111_mbus_formats) / 4))
+		return -EINVAL;
+
+	code->code = imx111_get_format_code(sensor, imx111_mbus_formats[code->index * 4], false);
+
+	return 0;
+}
+
+static int imx111_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	u32 code;
+
+	if (fse->index >= ARRAY_SIZE(imx111_modes))
+		return -EINVAL;
+
+	code = imx111_get_format_code(sensor, fse->code, true);
+	if (fse->code != code)
+		return -EINVAL;
+
+	fse->min_width = imx111_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = imx111_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int imx111_enum_frame_interval(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_state *state,
+				      struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	const struct imx111_mode *mode;
+	u32 framerate, code;
+
+	if (fie->index > 0)
+		return -EINVAL;
+
+	code = imx111_get_format_code(sensor, fie->code, true);
+	if (fie->code != code)
+		return -EINVAL;
+
+	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
+				      width, height,
+				      fie->width, fie->height);
+	if (fie->width > mode->width || fie->height > mode->height)
+		return -EINVAL;
+
+	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
+			    2 * sensor->data_depth);
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
+
+	return 0;
+}
+
+static int imx111_set_format(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *state,
+			     struct v4l2_subdev_format *format)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
+	struct v4l2_mbus_framefmt *fmt;
+	struct v4l2_fract *interval;
+	const struct imx111_mode *mode;
+	u32 framerate;
+
+	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
+				      width, height,
+				      mbus_fmt->width, mbus_fmt->height);
+	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
+			    2 * sensor->data_depth);
+
+	fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+	fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false);
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->colorspace = V4L2_COLORSPACE_RAW;
+
+	*mbus_fmt = *fmt;
+
+	interval = v4l2_subdev_state_get_interval(state, format->pad);
+	interval->numerator = 1;
+	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
+
+	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)	{
+		sensor->cur_mode = mode;
+		sensor->data_depth = imx111_get_format_bpp(fmt);
+		__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
+					 div_u64(sensor->pixel_clk_raw, 2 * sensor->data_depth));
+	}
+
+	return 0;
+}
+
+static int imx111_set_frame_interval(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_state *state,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	const struct imx111_mode *mode;
+	const struct v4l2_mbus_framefmt *mbus_fmt;
+	struct v4l2_fract *interval;
+	u32 framerate;
+
+	mbus_fmt = v4l2_subdev_state_get_format(state, fi->pad);
+
+	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
+				      width, height,
+				      mbus_fmt->width, mbus_fmt->height);
+	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
+			    2 * sensor->data_depth);
+
+	interval = v4l2_subdev_state_get_interval(state, fi->pad);
+
+	interval->numerator = 1;
+	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
+
+	fi->interval = *interval;
+
+	return 0;
+}
+
+static int imx111_init_state(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *sd_state)
+{
+	struct imx111 *sensor = sd_to_imx111(sd);
+	const struct imx111_mode *mode = sensor->cur_mode;
+	struct v4l2_mbus_framefmt *fmt;
+	struct v4l2_fract *interval;
+	u32 framerate;
+
+	fmt = v4l2_subdev_state_get_format(sd_state, 0);
+	interval = v4l2_subdev_state_get_interval(sd_state, 0);
+
+	fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->colorspace = V4L2_COLORSPACE_RAW;
+	fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+
+	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
+			    2 * sensor->data_depth);
+
+	interval->numerator = 1;
+	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx111_video_ops = {
+	.s_stream = imx111_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx111_pad_ops = {
+	.enum_mbus_code = imx111_enum_mbus_code,
+	.enum_frame_size = imx111_enum_frame_size,
+	.enum_frame_interval = imx111_enum_frame_interval,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = imx111_set_format,
+	.get_frame_interval = v4l2_subdev_get_frame_interval,
+	.set_frame_interval = imx111_set_frame_interval,
+};
+
+static const struct v4l2_subdev_ops imx111_subdev_ops = {
+	.video = &imx111_video_ops,
+	.pad = &imx111_pad_ops,
+};
+
+static const struct media_entity_operations imx111_subdev_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops imx111_internal_ops = {
+	.init_state = imx111_init_state,
+};
+
+static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct v4l2_subdev *sd = &sensor->sd;
+	struct media_pad *pad = &sensor->pad;
+	struct v4l2_ctrl_handler *hdl = &sensor->hdl;
+	int ret;
+
+	/* Initialize the subdev. */
+	v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops);
+
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	sd->internal_ops = &imx111_internal_ops;
+
+	/* Initialize the media entity. */
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	sd->entity.ops = &imx111_subdev_entity_ops;
+	pad->flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&sd->entity, 1, pad);
+	if (ret < 0) {
+		dev_err(dev, "failed to init entity pads: %d", ret);
+		return ret;
+	}
+
+	/* Initialize the control handler. */
+	ret = imx111_init_controls(sensor);
+	if (ret)
+		goto error;
+
+	return 0;
+error:
+	v4l2_ctrl_handler_free(hdl);
+	media_entity_cleanup(&sd->entity);
+	return ret;
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int imx111_power_on(struct imx111 *sensor)
+{
+	int ret;
+
+	if (sensor->reset)
+		gpiod_set_value(sensor->reset, 1);
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
+				    sensor->supplies);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(500, 600);
+
+	if (sensor->reset)
+		gpiod_set_value(sensor->reset, 0);
+
+	usleep_range(200, 250);
+
+	ret = clk_prepare_enable(sensor->extclk);
+	if (ret < 0)
+		goto error_regulator;
+
+	usleep_range(200, 250);
+
+	return 0;
+
+error_regulator:
+	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+	return ret;
+}
+
+static void imx111_power_off(struct imx111 *sensor)
+{
+	if (sensor->reset)
+		gpiod_set_value(sensor->reset, 1);
+	usleep_range(1000, 2000);
+
+	clk_disable_unprepare(sensor->extclk);
+	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
+}
+
+static int __maybe_unused imx111_pm_runtime_resume(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct imx111 *sensor = sd_to_imx111(sd);
+	int ret;
+
+	ret = imx111_power_on(sensor);
+	if (ret)
+		return ret;
+
+	ret = imx111_initialize(sensor);
+	if (ret) {
+		imx111_power_off(sensor);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct imx111 *sensor = sd_to_imx111(sd);
+
+	imx111_power_off(sensor);
+
+	return 0;
+}
+
+static const struct dev_pm_ops imx111_pm_ops = {
+	SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend,
+			   imx111_pm_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int imx111_identify_module(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+	u64 value, revision, manufacturer;
+	int ret;
+
+	ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL);
+	if (ret)
+		return ret;
+
+	if (value != IMX111_CHIP_ID) {
+		dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID, value);
+		return -ENXIO;
+	}
+
+	cci_read(sensor->regmap, IMX111_REVISION, &revision, NULL);
+	cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, NULL);
+
+	dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n",
+		value, revision, manufacturer);
+
+	return 0;
+}
+
+static int imx111_clk_init(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+	u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	u64 extclk_rate, system_clk;
+	int i;
+
+	extclk_rate = clk_get_rate(sensor->extclk);
+	if (!extclk_rate)
+		return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n");
+
+	for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) {
+		if (clk_get_rate(sensor->extclk) == imx111_pll[i].extclk_rate) {
+			sensor->pll = &imx111_pll[i];
+			break;
+		}
+	}
+	if (!sensor->pll)
+		return dev_err_probe(dev, -EINVAL, "Unsupported EXTCLK rate %llu\n", extclk_rate);
+
+	system_clk = div_u64(extclk_rate, sensor->pll->pre_div) * sensor->pll->mult;
+
+	/*
+	 * Pixel clock or Logic clock is used for internal image processing is
+	 * generated by dividing into 1/10 or 1/8 frequency according to the
+	 * word length of the CSI2 interface. This clock is designating the pixel
+	 * rate and used as the base of integration time, frame rate etc.
+	 */
+	sensor->pixel_clk_raw = system_clk * ndata_lanes;
+
+	/*
+	 * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR over n lanes
+	 * for RAW10 default format.
+	 */
+	sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8,
+					    ndata_lanes *
+					    2 * IMX111_DATA_DEPTH_RAW10);
+
+	if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
+	    sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq)
+		return dev_err_probe(dev, -EINVAL,
+				     "Unsupported DT link-frequencies, expected %llu\n",
+				     sensor->default_link_freq);
+
+	return 0;
+}
+
+static int imx111_parse_dt(struct imx111 *sensor)
+{
+	struct device *dev = regmap_get_device(sensor->regmap);
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+	struct fwnode_handle *ep;
+	int ret;
+
+	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+	if (!ep) {
+		dev_err(dev, "No endpoint found\n");
+		return -EINVAL;
+	}
+
+	sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN;
+	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
+	fwnode_handle_put(ep);
+	if (ret < 0) {
+		dev_err(dev, "Failed to parse endpoint\n");
+		goto error;
+	}
+
+	switch (sensor->bus_cfg.bus_type) {
+	case V4L2_MBUS_CSI2_DPHY:
+		break;
+
+	default:
+		dev_err(dev, "unsupported bus type %u\n", sensor->bus_cfg.bus_type);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/* Check the number of MIPI CSI2 data lanes */
+	if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) {
+		dev_err(dev, "number of lanes is more than 2\n");
+		ret = -EINVAL;
+		goto error;
+	}
+
+	return 0;
+
+error:
+	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+	return ret;
+}
+
+static int imx111_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct imx111 *sensor;
+	int ret;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(sensor->regmap))
+		return dev_err_probe(dev, PTR_ERR(sensor->regmap),
+				     "Failed to allocate register map\n");
+
+	sensor->extclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(sensor->extclk))
+		return dev_err_probe(dev, PTR_ERR(sensor->extclk), "Failed to get clock\n");
+
+	sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(sensor->reset))
+		return dev_err_probe(dev, PTR_ERR(sensor->reset), "Failed to get reset GPIO\n");
+
+	sensor->supplies[0].supply = "iovdd";
+	sensor->supplies[1].supply = "dvdd";
+	sensor->supplies[2].supply = "avdd";
+
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sensor->supplies), sensor->supplies);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+	ret = imx111_parse_dt(sensor);
+	if (ret < 0)
+		return ret;
+
+	ret = imx111_clk_init(sensor);
+	if (ret < 0)
+		goto error_ep_free;
+
+	ret = imx111_power_on(sensor);
+	if (ret < 0) {
+		dev_err_probe(dev, ret, "Could not power on the device\n");
+		goto error_ep_free;
+	}
+
+	ret = imx111_identify_module(sensor);
+	if (ret < 0) {
+		dev_err_probe(dev, ret, "Could not identify module\n");
+		goto error_power_off;
+	}
+
+	sensor->cur_mode = &imx111_modes[IMX111_MODE_3280x2464];
+	sensor->data_depth = IMX111_DATA_DEPTH_RAW10;
+
+	ret = imx111_initialize(sensor);
+	if (ret < 0)
+		goto error_power_off;
+
+	ret = imx111_init_subdev(sensor, client);
+	if (ret < 0) {
+		dev_err(dev, "failed to init controls: %d", ret);
+		goto error_v4l2_ctrl_handler_free;
+	}
+
+	ret = v4l2_subdev_init_finalize(&sensor->sd);
+	if (ret)
+		goto error_v4l2_ctrl_handler_free;
+
+	/*
+	 * Enable runtime PM with autosuspend. As the device has been powered
+	 * manually, mark it as active, and increase the usage count without
+	 * resuming the device.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_get_noresume(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+
+	ret = v4l2_async_register_subdev_sensor(&sensor->sd);
+	if (ret < 0) {
+		dev_err(dev, "failed to register V4L2 subdev: %d", ret);
+		goto error_pm;
+	}
+
+	/*
+	 * Decrease the PM usage count. The device will get suspended after the
+	 * autosuspend delay, turning the power off.
+	 */
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return 0;
+
+error_pm:
+	pm_runtime_disable(dev);
+	pm_runtime_put_noidle(dev);
+	v4l2_subdev_cleanup(&sensor->sd);
+
+error_v4l2_ctrl_handler_free:
+	v4l2_ctrl_handler_free(&sensor->hdl);
+	media_entity_cleanup(&sensor->sd.entity);
+
+error_power_off:
+	imx111_power_off(sensor);
+
+error_ep_free:
+	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+
+	return ret;
+}
+
+static void imx111_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct imx111 *sensor = sd_to_imx111(sd);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_subdev_cleanup(sd);
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_ctrl_handler_free(&sensor->hdl);
+	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+
+	/*
+	 * Disable runtime PM. In case runtime PM is disabled in the kernel,
+	 * make sure to turn power off manually.
+	 */
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev)) {
+		imx111_power_off(sensor);
+		pm_runtime_set_suspended(&client->dev);
+	}
+}
+
+static const struct i2c_device_id imx111_id[] = {
+	{ "imx111" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, imx111_id);
+
+static const struct of_device_id imx111_of_match[] = {
+	{ .compatible = "sony,imx111" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx111_of_match);
+
+static struct i2c_driver imx111_i2c_driver = {
+	.driver = {
+		.name = "imx111",
+		.of_match_table = imx111_of_match,
+		.pm = &imx111_pm_ops,
+	},
+	.id_table = imx111_id,
+	.probe = imx111_probe,
+	.remove = imx111_remove,
+};
+module_i2c_driver(imx111_i2c_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.48.1


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

* Re: [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor
  2025-10-28  9:21 ` [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor Svyatoslav Ryhel
@ 2025-10-28 16:26   ` Sakari Ailus
  2025-10-28 16:29     ` Svyatoslav Ryhel
  0 siblings, 1 reply; 10+ messages in thread
From: Sakari Ailus @ 2025-10-28 16:26 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Arnd Bergmann,
	Dongcheng Yan, André Apitzsch, Sylvain Petinot,
	Benjamin Mugnier, Heimir Thor Sverrisson, linux-media, devicetree,
	linux-kernel

Hi Svyatoslav,

On Tue, Oct 28, 2025 at 11:21:59AM +0200, Svyatoslav Ryhel wrote:
> Add bindings for Sony IMX111 CMOS Digital Image Sensor found in LG
> Optimus 4X (P880) and Optimus Vu (P895) smartphones.
> 
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
>  .../bindings/media/i2c/sony,imx111.yaml       | 112 ++++++++++++++++++
>  1 file changed, 112 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> new file mode 100644
> index 000000000000..a70017588891
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> @@ -0,0 +1,112 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/sony,imx111.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sony IMX111 8MP CMOS Digital Image Sensor
> +
> +maintainers:
> +  - Svyatoslav Ryhel <clamor95@gmail.com>
> +
> +description:
> +  IMX111 sensor is a Sony CMOS active pixel digital image sensor with an active
> +  array size of 2464H x 3280V. It is programmable through I2C interface. Image
> +  data is sent through MIPI CSI-2, through 1 or 2 lanes.
> +
> +allOf:
> +  - $ref: /schemas/media/video-interface-devices.yaml#
> +  - $ref: /schemas/nvmem/nvmem-consumer.yaml#
> +
> +properties:
> +  compatible:
> +    const: sony,imx111
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    description: EXTCLK with possible frequency from 6 to 54 MHz
> +    maxItems: 1
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  iovdd-supply:
> +    description: Digital IO power supply (1.8V)
> +
> +  dvdd-supply:
> +    description: Digital power supply (1.2V)
> +
> +  avdd-supply:
> +    description: Analog power supply (2.7V)
> +
> +  port:
> +    additionalProperties: false
> +    $ref: /schemas/graph.yaml#/$defs/port-base
> +
> +    properties:
> +      endpoint:
> +        $ref: /schemas/media/video-interfaces.yaml#
> +        unevaluatedProperties: false
> +
> +        properties:
> +          data-lanes: true
> +          bus-type: true
> +          link-frequencies: true
> +
> +        required:
> +          - data-lanes
> +          - bus-type

Can you drop bus-type? I.e. is the sensor D-PHY-only?

> +          - link-frequencies
> +
> +    required:
> +      - endpoint
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - 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@10 {
> +            compatible = "sony,imx111";
> +            reg = <0x10>;
> +
> +            clocks = <&imx111_clk>;
> +
> +            iovdd-supply = <&camera_vddio_1v8>;
> +            dvdd-supply = <&camera_vddd_1v2>;
> +            avdd-supply = <&camera_vdda_2v7>;
> +
> +            orientation = <1>;
> +            rotation = <90>;
> +
> +            nvmem = <&eeprom>;
> +            flash-leds = <&led>;
> +            lens-focus = <&vcm>;
> +
> +            reset-gpios = <&gpio 84 GPIO_ACTIVE_LOW>;
> +
> +            port {
> +                imx111_output: endpoint {
> +                    data-lanes = <1 2>;
> +                    bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
> +                    link-frequencies = /bits/ 64 <271200000>;
> +                    remote-endpoint = <&csi_input>;
> +                };
> +            };
> +        };
> +    };
> +...

-- 
Regards,

Sakari Ailus

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

* Re: [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor
  2025-10-28 16:26   ` Sakari Ailus
@ 2025-10-28 16:29     ` Svyatoslav Ryhel
  0 siblings, 0 replies; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-28 16:29 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Arnd Bergmann,
	Dongcheng Yan, André Apitzsch, Sylvain Petinot,
	Benjamin Mugnier, Heimir Thor Sverrisson, linux-media, devicetree,
	linux-kernel

вт, 28 жовт. 2025 р. о 18:26 Sakari Ailus <sakari.ailus@linux.intel.com> пише:
>
> Hi Svyatoslav,
>
> On Tue, Oct 28, 2025 at 11:21:59AM +0200, Svyatoslav Ryhel wrote:
> > Add bindings for Sony IMX111 CMOS Digital Image Sensor found in LG
> > Optimus 4X (P880) and Optimus Vu (P895) smartphones.
> >
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> >  .../bindings/media/i2c/sony,imx111.yaml       | 112 ++++++++++++++++++
> >  1 file changed, 112 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> > new file mode 100644
> > index 000000000000..a70017588891
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
> > @@ -0,0 +1,112 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/i2c/sony,imx111.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Sony IMX111 8MP CMOS Digital Image Sensor
> > +
> > +maintainers:
> > +  - Svyatoslav Ryhel <clamor95@gmail.com>
> > +
> > +description:
> > +  IMX111 sensor is a Sony CMOS active pixel digital image sensor with an active
> > +  array size of 2464H x 3280V. It is programmable through I2C interface. Image
> > +  data is sent through MIPI CSI-2, through 1 or 2 lanes.
> > +
> > +allOf:
> > +  - $ref: /schemas/media/video-interface-devices.yaml#
> > +  - $ref: /schemas/nvmem/nvmem-consumer.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    const: sony,imx111
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    description: EXTCLK with possible frequency from 6 to 54 MHz
> > +    maxItems: 1
> > +
> > +  reset-gpios:
> > +    maxItems: 1
> > +
> > +  iovdd-supply:
> > +    description: Digital IO power supply (1.8V)
> > +
> > +  dvdd-supply:
> > +    description: Digital power supply (1.2V)
> > +
> > +  avdd-supply:
> > +    description: Analog power supply (2.7V)
> > +
> > +  port:
> > +    additionalProperties: false
> > +    $ref: /schemas/graph.yaml#/$defs/port-base
> > +
> > +    properties:
> > +      endpoint:
> > +        $ref: /schemas/media/video-interfaces.yaml#
> > +        unevaluatedProperties: false
> > +
> > +        properties:
> > +          data-lanes: true
> > +          bus-type: true
> > +          link-frequencies: true
> > +
> > +        required:
> > +          - data-lanes
> > +          - bus-type
>
> Can you drop bus-type? I.e. is the sensor D-PHY-only?
>

Yes, there were suggestions from schema maintainers and I have
included them locally, but forgot about them since no review from
media maintainers. Please skip this patch and review the driver
itself. I will include all schema maintainers suggestions along side
your suggestions regarding the driver in v3.

> > +          - link-frequencies
> > +
> > +    required:
> > +      - endpoint
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - clocks
> > +  - 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@10 {
> > +            compatible = "sony,imx111";
> > +            reg = <0x10>;
> > +
> > +            clocks = <&imx111_clk>;
> > +
> > +            iovdd-supply = <&camera_vddio_1v8>;
> > +            dvdd-supply = <&camera_vddd_1v2>;
> > +            avdd-supply = <&camera_vdda_2v7>;
> > +
> > +            orientation = <1>;
> > +            rotation = <90>;
> > +
> > +            nvmem = <&eeprom>;
> > +            flash-leds = <&led>;
> > +            lens-focus = <&vcm>;
> > +
> > +            reset-gpios = <&gpio 84 GPIO_ACTIVE_LOW>;
> > +
> > +            port {
> > +                imx111_output: endpoint {
> > +                    data-lanes = <1 2>;
> > +                    bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
> > +                    link-frequencies = /bits/ 64 <271200000>;
> > +                    remote-endpoint = <&csi_input>;
> > +                };
> > +            };
> > +        };
> > +    };
> > +...
>
> --
> Regards,
>
> Sakari Ailus

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

* Re: [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-28  9:22 ` [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
@ 2025-10-28 17:58   ` Sakari Ailus
  2025-10-28 18:57     ` Svyatoslav Ryhel
  0 siblings, 1 reply; 10+ messages in thread
From: Sakari Ailus @ 2025-10-28 17:58 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Arnd Bergmann,
	Dongcheng Yan, André Apitzsch, Sylvain Petinot,
	Benjamin Mugnier, Heimir Thor Sverrisson, linux-media, devicetree,
	linux-kernel

Hi Svyatoslav,

Btw. there's no need to resend media patches; just ping for reviews
instead.

On Tue, Oct 28, 2025 at 11:22:00AM +0200, Svyatoslav Ryhel wrote:
> Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> camera sensor using the i2c bus for control and the csi-2 bus for data.
> 
> The following features are supported:
> - manual exposure, digital and analog gain control support
> - pixel rate/link freq control support
> - supported resolution up to 3280x2464 for single shot capture
> - supported resolution up to 1920x1080 @ 30fps for video
> - supported bayer order output SGBRG10 and SGBRG8
> 
> Camera module seems to be partially compatible with Nokia SMIA but it
> lacks a few registers required for clock calculations and has different
> vendor-specific per-mode configurations which makes it incompatible with
> existing CCS driver.
> 
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
>  drivers/media/i2c/Kconfig  |   10 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/imx111.c | 1614 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 1625 insertions(+)
>  create mode 100644 drivers/media/i2c/imx111.c
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index e68202954a8f..8ec1f369f043 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -127,6 +127,16 @@ config VIDEO_HI847
>            To compile this driver as a module, choose M here: the
>            module will be called hi847.
>  
> +config VIDEO_IMX111
> +	tristate "Sony IMX111 sensor support"
> +	select V4L2_CCI_I2C
> +	help
> +	  This is a V4L2 sensor driver for the Sony IMX111 camera
> +	  sensors.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called imx111.
> +
>  config VIDEO_IMX208
>  	tristate "Sony IMX208 sensor support"
>  	help
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 5873d29433ee..67b810c91870 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o
>  obj-$(CONFIG_VIDEO_HI846) += hi846.o
>  obj-$(CONFIG_VIDEO_HI847) += hi847.o
>  obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
> +obj-$(CONFIG_VIDEO_IMX111) += imx111.o
>  obj-$(CONFIG_VIDEO_IMX208) += imx208.o
>  obj-$(CONFIG_VIDEO_IMX214) += imx214.o
>  obj-$(CONFIG_VIDEO_IMX219) += imx219.o
> diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c
> new file mode 100644
> index 000000000000..814c557d9e96
> --- /dev/null
> +++ b/drivers/media/i2c/imx111.c
> @@ -0,0 +1,1614 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/media.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/ratelimit.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/units.h>
> +
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-cci.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-image-sizes.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-mediabus.h>
> +
> +/* product information registers */
> +#define IMX111_PRODUCT_ID			CCI_REG16(0x0000)
> +#define   IMX111_CHIP_ID			0x111
> +#define IMX111_REVISION				CCI_REG8(0x0002)
> +#define IMX111_MANUFACTURER_ID			CCI_REG8(0x0003)
> +#define IMX111_SMIA_VER				CCI_REG8(0x0004)
> +#define IMX111_FRAME_COUNTER			CCI_REG8(0x0005)
> +#define IMX111_PIXEL_ORDER			CCI_REG8(0x0006)
> +
> +/* general configuration registers */
> +#define IMX111_STREAMING_MODE			CCI_REG8(0x0100)
> +#define   IMX111_MODE_STANDBY			0
> +#define   IMX111_MODE_STREAMING			1
> +#define IMX111_IMAGE_ORIENTATION		CCI_REG8(0x0101)
> +#define   IMX111_IMAGE_HFLIP			BIT(0)
> +#define   IMX111_IMAGE_VFLIP			BIT(1)
> +#define IMX111_SOFTWARE_RESET			CCI_REG8(0x0103)
> +#define   IMX111_RESET_ON			1
> +#define IMX111_GROUP_WRITE			CCI_REG8(0x0104)
> +#define   IMX111_GROUP_WRITE_ON			1
> +#define IMX111_FRAME_DROP			CCI_REG8(0x0105)
> +#define   IMX111_FRAME_DROP_ON			1
> +#define IMX111_CHANNEL_ID			CCI_REG8(0x0110)
> +#define IMX111_SIGNALLING_MODE			CCI_REG8(0x0111)
> +#define IMX111_DATA_DEPTH			CCI_REG16(0x0112)
> +#define   IMX111_DATA_DEPTH_RAW8		0x08
> +#define   IMX111_DATA_DEPTH_RAW10		0x0a
> +
> +/* integration time registers */
> +#define IMX111_INTEGRATION_TIME			CCI_REG16(0x0202)
> +#define IMX111_INTEGRATION_TIME_MIN		0x1
> +#define IMX111_INTEGRATION_TIME_MAX		0xffff
> +#define IMX111_INTEGRATION_TIME_STEP		1
> +
> +/* analog gain control */
> +#define IMX111_REG_ANALOG_GAIN			CCI_REG8(0x0205)
> +#define IMX111_ANA_GAIN_MIN			0
> +#define IMX111_ANA_GAIN_MAX			240
> +#define IMX111_ANA_GAIN_STEP			1
> +#define IMX111_ANA_GAIN_DEFAULT			0
> +
> +/* digital gain control */
> +#define IMX111_REG_DIG_GAIN_GREENR		CCI_REG16(0x020e)
> +#define IMX111_REG_DIG_GAIN_RED			CCI_REG16(0x0210)
> +#define IMX111_REG_DIG_GAIN_BLUE		CCI_REG16(0x0212)
> +#define IMX111_REG_DIG_GAIN_GREENB		CCI_REG16(0x0214)
> +#define IMX111_DGTL_GAIN_MIN			0x0100
> +#define IMX111_DGTL_GAIN_MAX			0x0fff
> +#define IMX111_DGTL_GAIN_DEFAULT		0x0100
> +#define IMX111_DGTL_GAIN_STEP			1
> +
> +/* clock configuration registers */
> +#define IMX111_PIXEL_CLK_DIVIDER_PLL1		CCI_REG8(0x0301) /* fixed to 10 */
> +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1		CCI_REG8(0x0303) /* fixed to 1 */
> +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1		CCI_REG8(0x0305)
> +#define IMX111_PLL_MULTIPLIER_PLL1		CCI_REG8(0x0307)
> +#define IMX111_PLL_SETTLING_TIME		CCI_REG8(0x303c)
> +#define   IMX111_PLL_SETTLING_TIME_DEFAULT	200
> +#define IMX111_POST_DIVIDER			CCI_REG8(0x30a4)
> +#define   IMX111_POST_DIVIDER_DIV1		2
> +#define   IMX111_POST_DIVIDER_DIV2		0
> +#define   IMX111_POST_DIVIDER_DIV4		1
> +
> +/* frame timing registers */
> +#define IMX111_VERTICAL_TOTAL_LENGTH		CCI_REG16(0x0340)
> +#define IMX111_HORIZONTAL_TOTAL_LENGTH		CCI_REG16(0x0342)
> +
> +/* image size registers */
> +#define IMX111_HORIZONTAL_START			CCI_REG16(0x0344)
> +#define IMX111_VERTICAL_START			CCI_REG16(0x0346)
> +#define IMX111_HORIZONTAL_END			CCI_REG16(0x0348)
> +#define IMX111_VERTICAL_END			CCI_REG16(0x034a)
> +#define IMX111_IMAGE_WIDTH			CCI_REG16(0x034c)
> +#define IMX111_IMAGE_HEIGHT			CCI_REG16(0x034e)
> +
> +/* test pattern registers */
> +#define IMX111_TEST_PATTERN			CCI_REG8(0x0601)
> +#define   IMX111_TEST_PATTERN_NONE		0
> +#define   IMX111_TEST_PATTERN_SOLID		1
> +#define   IMX111_TEST_PATTERN_BARS		2
> +#define   IMX111_TEST_PATTERN_FADE		3
> +#define   IMX111_TEST_PATTERN_PN9		4
> +#define IMX111_SOLID_COLOR_RED			CCI_REG16(0x0602)
> +#define IMX111_SOLID_COLOR_GR			CCI_REG16(0x0604)
> +#define IMX111_SOLID_COLOR_BLUE			CCI_REG16(0x0606)
> +#define IMX111_SOLID_COLOR_GB			CCI_REG16(0x0608)
> +#define IMX111_TESTP_COLOUR_MIN			0
> +#define IMX111_TESTP_COLOUR_MAX			0x03ff
> +#define IMX111_TESTP_COLOUR_STEP		1
> +
> +#define IMX111_FRAME_RATE_STEP			5
> +
> +#define IMX111_PIXEL_ARRAY_WIDTH		3280U
> +#define IMX111_PIXEL_ARRAY_HEIGHT		2464U
> +
> +enum {
> +	IMX111_MODE_3280x2464,
> +	IMX111_MODE_3280x1848,
> +	IMX111_MODE_3280x1098,
> +	IMX111_MODE_2100x1200,
> +	IMX111_MODE_1952x1098,
> +	IMX111_MODE_1920x1080,
> +	IMX111_MODE_1640x1232,
> +	IMX111_MODE_1440x1080,
> +	IMX111_MODE_1640x924,
> +	IMX111_MODE_1308x736,
> +	IMX111_MODE_1280x720,
> +	IMX111_MODE_820x614,
> +	IMX111_MODE_640x480,
> +};
> +
> +struct imx111_mode {
> +	u32 width;
> +	u32 height;
> +	struct {
> +		const struct cci_reg_sequence *regs;
> +		u32 num_of_regs;
> +	} reg_list;
> +};
> +
> +struct imx111_pll {
> +	u64 extclk_rate;
> +	u8 pre_div;
> +	u8 mult;
> +};
> +
> +struct imx111 {
> +	struct regmap *regmap;
> +
> +	struct clk *extclk;
> +	struct gpio_desc *reset;
> +	struct regulator_bulk_data supplies[3];
> +
> +	struct v4l2_fwnode_endpoint bus_cfg;
> +	struct v4l2_subdev sd;
> +	struct media_pad pad;
> +
> +	/* V4L2 Controls */
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_ctrl *pixel_rate;
> +	struct v4l2_ctrl *link_freq;
> +	struct v4l2_ctrl *exposure;
> +	struct v4l2_ctrl *hflip;
> +	struct v4l2_ctrl *vflip;
> +
> +	/* Current mode */
> +	const struct imx111_mode *cur_mode;
> +	const struct imx111_pll *pll;
> +	u32 data_depth;
> +
> +	u64 pixel_clk_raw;
> +	s64 default_link_freq;
> +};
> +
> +static const struct imx111_pll imx111_pll[] = {
> +	{
> +		.extclk_rate =  6000000, .pre_div = 1, .mult = 113,

Could you write this as:

	{ .extclk_rate = 6000000, .pre_div = 1, .mult = 113, },

etc.

> +	}, {
> +		.extclk_rate = 12000000, .pre_div = 2, .mult = 113,
> +	}, {
> +		.extclk_rate = 13500000, .pre_div = 1, .mult = 50,
> +	}, {
> +		.extclk_rate = 18000000, .pre_div = 2, .mult = 75,
> +	}, {
> +		.extclk_rate = 24000000, .pre_div = 4, .mult = 113,
> +	}, {
> +		.extclk_rate = 27000000, .pre_div = 2, .mult = 50,
> +	}, {
> +		.extclk_rate = 36000000, .pre_div = 4, .mult = 75,
> +	}, {
> +		.extclk_rate = 54000000, .pre_div = 4, .mult = 50,
> +	},
> +};
> +
> +/*
> + * This table MUST contain 4 entries per format, to cover the various flip
> + * combinations in the order
> + * - no flip
> + * - h flip
> + * - v flip
> + * - h&v flips
> + */
> +static const u32 imx111_mbus_formats[] = {
> +	MEDIA_BUS_FMT_SGBRG10_1X10,
> +	MEDIA_BUS_FMT_SBGGR10_1X10,
> +	MEDIA_BUS_FMT_SRGGB10_1X10,
> +	MEDIA_BUS_FMT_SGRBG10_1X10,
> +
> +	MEDIA_BUS_FMT_SGBRG8_1X8,
> +	MEDIA_BUS_FMT_SBGGR8_1X8,
> +	MEDIA_BUS_FMT_SRGGB8_1X8,
> +	MEDIA_BUS_FMT_SGRBG8_1X8,
> +};
> +
> +static const struct cci_reg_sequence imx111_global_init[] = {
> +	{ CCI_REG8(0x3080), 0x50 },
> +	{ CCI_REG8(0x3087), 0x53 },
> +	{ CCI_REG8(0x309d), 0x94 },
> +	{ CCI_REG8(0x30b1), 0x03 },
> +	{ CCI_REG8(0x30c6), 0x00 },
> +	{ CCI_REG8(0x30c7), 0x00 },
> +	{ CCI_REG8(0x3115), 0x0b },
> +	{ CCI_REG8(0x3118), 0x30 },
> +	{ CCI_REG8(0x311d), 0x25 },
> +	{ CCI_REG8(0x3121), 0x0a },
> +	{ CCI_REG8(0x3212), 0xf2 },
> +	{ CCI_REG8(0x3213), 0x0f },
> +	{ CCI_REG8(0x3215), 0x0f },
> +	{ CCI_REG8(0x3217), 0x0b },
> +	{ CCI_REG8(0x3219), 0x0b },
> +	{ CCI_REG8(0x321b), 0x0d },
> +	{ CCI_REG8(0x321d), 0x0d },
> +	{ CCI_REG8(0x32aa), 0x11 },
> +	{ CCI_REG8(0x3032), 0x40 },
> +};
> +
> +static const struct cci_reg_sequence mode_820x614[] = {
> +	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xec },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x34 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcb },
> +	{ CCI_REG8(0x034c), 0x03 },	{ CCI_REG8(0x034d), 0x34 },
> +	{ CCI_REG8(0x034e), 0x02 },	{ CCI_REG8(0x034f), 0x66 },
> +	{ CCI_REG8(0x0381), 0x05 },	{ CCI_REG8(0x0383), 0x03 },
> +	{ CCI_REG8(0x0385), 0x05 },	{ CCI_REG8(0x0387), 0x03 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x03 },	{ CCI_REG8(0x30d5), 0x09 },
> +	{ CCI_REG8(0x30d6), 0x00 },	{ CCI_REG8(0x30d7), 0x00 },
> +	{ CCI_REG8(0x30d8), 0x00 },	{ CCI_REG8(0x30d9), 0x00 },
> +	{ CCI_REG8(0x30de), 0x04 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
> +	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
> +	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
> +	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
> +	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
> +	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x7a },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_1308x736[] = {
> +	{ CCI_REG8(0x0340), 0x09 },	{ CCI_REG8(0x0341), 0x41 },
> +	{ CCI_REG8(0x0342), 0x07 },	{ CCI_REG8(0x0343), 0x68 },
> +	{ CCI_REG8(0x0344), 0x01 },	{ CCI_REG8(0x0345), 0x54 },
> +	{ CCI_REG8(0x0346), 0x02 },	{ CCI_REG8(0x0347), 0x20 },
> +	{ CCI_REG8(0x0348), 0x0b },	{ CCI_REG8(0x0349), 0x8b },
> +	{ CCI_REG8(0x034a), 0x07 },	{ CCI_REG8(0x034b), 0xdf },
> +	{ CCI_REG8(0x034c), 0x05 },	{ CCI_REG8(0x034d), 0x1c },
> +	{ CCI_REG8(0x034e), 0x02 },	{ CCI_REG8(0x034f), 0xe0 },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
> +	{ CCI_REG8(0x3033), 0x84 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
> +	{ CCI_REG8(0x304c), 0xd7 },	{ CCI_REG8(0x304d), 0x01 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x48 },	{ CCI_REG8(0x309c), 0x12 },
> +	{ CCI_REG8(0x309e), 0x04 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x0a },	{ CCI_REG8(0x30aa), 0x01 },
> +	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x04 },
> +	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
> +	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
> +	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
> +	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x42 },
> +	{ CCI_REG8(0x315d), 0x41 },	{ CCI_REG8(0x316e), 0x43 },
> +	{ CCI_REG8(0x316f), 0x42 },	{ CCI_REG8(0x3318), 0x62 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_1640x924[] = {
> +	{ CCI_REG8(0x0340), 0x03 },	{ CCI_REG8(0x0341), 0xb2 },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x64 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x9b },
> +	{ CCI_REG8(0x034c), 0x06 },	{ CCI_REG8(0x034d), 0x68 },
> +	{ CCI_REG8(0x034e), 0x03 },	{ CCI_REG8(0x034f), 0x9c },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x03 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x09 },
> +	{ CCI_REG8(0x30d6), 0x01 },	{ CCI_REG8(0x30d7), 0x01 },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x02 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
> +	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
> +	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
> +	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
> +	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
> +	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x72 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_1640x1232[] = {
> +	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xe6 },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x30 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcf },
> +	{ CCI_REG8(0x034c), 0x06 },	{ CCI_REG8(0x034d), 0x68 },
> +	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0xd0 },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x03 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x03 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x01 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x28 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x09 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x05 },	{ CCI_REG8(0x30d5), 0x09 },
> +	{ CCI_REG8(0x30d6), 0x01 },	{ CCI_REG8(0x30d7), 0x01 },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x02 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
> +	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
> +	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
> +	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
> +	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
> +	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x72 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_1952x1098[] = {
> +	{ CCI_REG8(0x0340), 0x07 },	{ CCI_REG8(0x0341), 0x5c },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xac },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x16 },
> +	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x6e },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xcb },
> +	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x93 },
> +	{ CCI_REG8(0x034c), 0x07 },	{ CCI_REG8(0x034d), 0xa0 },
> +	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0x4a },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x00 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x91 },	{ CCI_REG8(0x3048), 0x00 },
> +	{ CCI_REG8(0x304c), 0x67 },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x10 },	{ CCI_REG8(0x3073), 0xa0 },
> +	{ CCI_REG8(0x3074), 0x12 },	{ CCI_REG8(0x3075), 0x12 },
> +	{ CCI_REG8(0x3076), 0x12 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x0a },	{ CCI_REG8(0x307a), 0x0a },
> +	{ CCI_REG8(0x309b), 0x60 },	{ CCI_REG8(0x309e), 0x04 },
> +	{ CCI_REG8(0x30a0), 0x15 },	{ CCI_REG8(0x30a1), 0x08 },
> +	{ CCI_REG8(0x30aa), 0x03 },	{ CCI_REG8(0x30b2), 0x05 },
> +	{ CCI_REG8(0x30d5), 0x20 },	{ CCI_REG8(0x30d6), 0x85 },
> +	{ CCI_REG8(0x30d7), 0x2a },	{ CCI_REG8(0x30d8), 0x64 },
> +	{ CCI_REG8(0x30d9), 0x89 },	{ CCI_REG8(0x30de), 0x00 },
> +	{ CCI_REG8(0x30df), 0x21 },	{ CCI_REG8(0x3102), 0x08 },
> +	{ CCI_REG8(0x3103), 0x1d },	{ CCI_REG8(0x3104), 0x1e },
> +	{ CCI_REG8(0x3105), 0x00 },	{ CCI_REG8(0x3106), 0x74 },
> +	{ CCI_REG8(0x3107), 0x00 },	{ CCI_REG8(0x3108), 0x03 },
> +	{ CCI_REG8(0x3109), 0x02 },	{ CCI_REG8(0x310a), 0x03 },
> +	{ CCI_REG8(0x315c), 0x37 },	{ CCI_REG8(0x315d), 0x36 },
> +	{ CCI_REG8(0x316e), 0x38 },	{ CCI_REG8(0x316f), 0x37 },
> +	{ CCI_REG8(0x3318), 0x63 },	{ CCI_REG8(0x3348), 0xA0 },
> +};
> +
> +static const struct cci_reg_sequence mode_2100x1200[] = {
> +	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0xec },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x02 },	{ CCI_REG8(0x0345), 0x56 },
> +	{ CCI_REG8(0x0346), 0x02 },	{ CCI_REG8(0x0347), 0xa8 },
> +	{ CCI_REG8(0x0348), 0x0a },	{ CCI_REG8(0x0349), 0x89 },
> +	{ CCI_REG8(0x034a), 0x07 },	{ CCI_REG8(0x034b), 0x57 },
> +	{ CCI_REG8(0x034c), 0x08 },	{ CCI_REG8(0x034d), 0x34 },
> +	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0xb0 },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
> +	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x08 },	{ CCI_REG8(0x3103), 0x22 },
> +	{ CCI_REG8(0x3104), 0x20 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x87 },	{ CCI_REG8(0x3107), 0x00 },
> +	{ CCI_REG8(0x3108), 0x03 },	{ CCI_REG8(0x3109), 0x02 },
> +	{ CCI_REG8(0x310a), 0x03 },	{ CCI_REG8(0x315c), 0x9c },
> +	{ CCI_REG8(0x315d), 0x9b },	{ CCI_REG8(0x316e), 0x9d },
> +	{ CCI_REG8(0x316f), 0x9c },	{ CCI_REG8(0x3318), 0x62 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_3280x1098[] = {
> +	{ CCI_REG8(0x0340), 0x04 },	{ CCI_REG8(0x0341), 0x6a },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xac },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0xf6 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x0b },
> +	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
> +	{ CCI_REG8(0x034e), 0x04 },	{ CCI_REG8(0x034f), 0x4a },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x10 },
> +	{ CCI_REG8(0x303e), 0x40 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x93 },	{ CCI_REG8(0x3048), 0x00 },
> +	{ CCI_REG8(0x304c), 0x67 },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0xe0 },
> +	{ CCI_REG8(0x3074), 0x12 },	{ CCI_REG8(0x3075), 0x12 },
> +	{ CCI_REG8(0x3076), 0x12 },	{ CCI_REG8(0x3077), 0x12 },
> +	{ CCI_REG8(0x3079), 0x2a },	{ CCI_REG8(0x307a), 0x0a },
> +	{ CCI_REG8(0x309b), 0x60 },	{ CCI_REG8(0x309e), 0x04 },
> +	{ CCI_REG8(0x30a0), 0x15 },	{ CCI_REG8(0x30a1), 0x08 },
> +	{ CCI_REG8(0x30aa), 0x03 },	{ CCI_REG8(0x30b2), 0x05 },
> +	{ CCI_REG8(0x30d5), 0x00 },	{ CCI_REG8(0x30d6), 0x85 },
> +	{ CCI_REG8(0x30d7), 0x2a },	{ CCI_REG8(0x30d8), 0x64 },
> +	{ CCI_REG8(0x30d9), 0x89 },	{ CCI_REG8(0x30de), 0x00 },
> +	{ CCI_REG8(0x30df), 0x20 },	{ CCI_REG8(0x3102), 0x08 },
> +	{ CCI_REG8(0x3103), 0x1d },	{ CCI_REG8(0x3104), 0x1e },
> +	{ CCI_REG8(0x3105), 0x00 },	{ CCI_REG8(0x3106), 0x74 },
> +	{ CCI_REG8(0x3107), 0x00 },	{ CCI_REG8(0x3108), 0x03 },
> +	{ CCI_REG8(0x3109), 0x02 },	{ CCI_REG8(0x310a), 0x03 },
> +	{ CCI_REG8(0x315c), 0x37 },	{ CCI_REG8(0x315d), 0x36 },
> +	{ CCI_REG8(0x316e), 0x38 },	{ CCI_REG8(0x316f), 0x37 },
> +	{ CCI_REG8(0x3318), 0x63 },	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_3280x1848[] = {
> +	{ CCI_REG8(0x0340), 0x07 },	{ CCI_REG8(0x0341), 0x52 },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x01 },	{ CCI_REG8(0x0347), 0x64 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x08 },	{ CCI_REG8(0x034b), 0x9b },
> +	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
> +	{ CCI_REG8(0x034e), 0x07 },	{ CCI_REG8(0x034f), 0x38 },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x00 },
> +	{ CCI_REG8(0x303e), 0x41 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
> +	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x10 },	{ CCI_REG8(0x3103), 0x44 },
> +	{ CCI_REG8(0x3104), 0x40 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x0d },	{ CCI_REG8(0x3107), 0x01 },
> +	{ CCI_REG8(0x3108), 0x09 },	{ CCI_REG8(0x3109), 0x08 },
> +	{ CCI_REG8(0x310a), 0x0f },	{ CCI_REG8(0x315c), 0x5d },
> +	{ CCI_REG8(0x315d), 0x5c },	{ CCI_REG8(0x316e), 0x5e },
> +	{ CCI_REG8(0x316f), 0x5d },	{ CCI_REG8(0x3318), 0x60 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct cci_reg_sequence mode_3280x2464[] = {
> +	{ CCI_REG8(0x0340), 0x09 },	{ CCI_REG8(0x0341), 0xba },
> +	{ CCI_REG8(0x0342), 0x0d },	{ CCI_REG8(0x0343), 0xd0 },
> +	{ CCI_REG8(0x0344), 0x00 },	{ CCI_REG8(0x0345), 0x08 },
> +	{ CCI_REG8(0x0346), 0x00 },	{ CCI_REG8(0x0347), 0x30 },
> +	{ CCI_REG8(0x0348), 0x0c },	{ CCI_REG8(0x0349), 0xd7 },
> +	{ CCI_REG8(0x034a), 0x09 },	{ CCI_REG8(0x034b), 0xcf },
> +	{ CCI_REG8(0x034c), 0x0c },	{ CCI_REG8(0x034d), 0xd0 },
> +	{ CCI_REG8(0x034e), 0x09 },	{ CCI_REG8(0x034f), 0xa0 },
> +	{ CCI_REG8(0x0381), 0x01 },	{ CCI_REG8(0x0383), 0x01 },
> +	{ CCI_REG8(0x0385), 0x01 },	{ CCI_REG8(0x0387), 0x01 },
> +	{ CCI_REG8(0x3033), 0x00 },	{ CCI_REG8(0x303d), 0x00 },
> +	{ CCI_REG8(0x303e), 0x41 },	{ CCI_REG8(0x3040), 0x08 },
> +	{ CCI_REG8(0x3041), 0x97 },	{ CCI_REG8(0x3048), 0x00 },
> +	{ CCI_REG8(0x304c), 0x6f },	{ CCI_REG8(0x304d), 0x03 },
> +	{ CCI_REG8(0x3064), 0x12 },	{ CCI_REG8(0x3073), 0x00 },
> +	{ CCI_REG8(0x3074), 0x11 },	{ CCI_REG8(0x3075), 0x11 },
> +	{ CCI_REG8(0x3076), 0x11 },	{ CCI_REG8(0x3077), 0x11 },
> +	{ CCI_REG8(0x3079), 0x00 },	{ CCI_REG8(0x307a), 0x00 },
> +	{ CCI_REG8(0x309b), 0x20 },	{ CCI_REG8(0x309c), 0x13 },
> +	{ CCI_REG8(0x309e), 0x00 },	{ CCI_REG8(0x30a0), 0x14 },
> +	{ CCI_REG8(0x30a1), 0x08 },	{ CCI_REG8(0x30aa), 0x03 },
> +	{ CCI_REG8(0x30b2), 0x07 },	{ CCI_REG8(0x30d5), 0x00 },
> +	{ CCI_REG8(0x30d6), 0x85 },	{ CCI_REG8(0x30d7), 0x2a },
> +	{ CCI_REG8(0x30d8), 0x64 },	{ CCI_REG8(0x30d9), 0x89 },
> +	{ CCI_REG8(0x30de), 0x00 },	{ CCI_REG8(0x30df), 0x20 },
> +	{ CCI_REG8(0x3102), 0x10 },	{ CCI_REG8(0x3103), 0x44 },
> +	{ CCI_REG8(0x3104), 0x40 },	{ CCI_REG8(0x3105), 0x00 },
> +	{ CCI_REG8(0x3106), 0x0d },	{ CCI_REG8(0x3107), 0x01 },
> +	{ CCI_REG8(0x3108), 0x09 },	{ CCI_REG8(0x3109), 0x08 },
> +	{ CCI_REG8(0x310a), 0x0f },	{ CCI_REG8(0x315c), 0x5d },
> +	{ CCI_REG8(0x315d), 0x5c },	{ CCI_REG8(0x316e), 0x5e },
> +	{ CCI_REG8(0x316f), 0x5d },	{ CCI_REG8(0x3318), 0x60 },
> +	{ CCI_REG8(0x3348), 0xe0 },
> +};
> +
> +static const struct imx111_mode imx111_modes[] = {
> +	[IMX111_MODE_3280x2464] = {
> +		.width = 3280,
> +		.height = 2464,
> +		.reg_list = {
> +			.regs = mode_3280x2464,
> +			.num_of_regs = ARRAY_SIZE(mode_3280x2464),
> +		},
> +	},
> +	[IMX111_MODE_3280x1848] = {
> +		.width = 3280,
> +		.height = 1848,
> +		.reg_list = {
> +			.regs = mode_3280x1848,
> +			.num_of_regs = ARRAY_SIZE(mode_3280x1848),
> +		},
> +	},
> +	[IMX111_MODE_3280x1098] = {
> +		.width = 3280,
> +		.height = 1098,
> +		.reg_list = {
> +			.regs = mode_3280x1098,
> +			.num_of_regs = ARRAY_SIZE(mode_3280x1098),
> +		},
> +	},
> +	[IMX111_MODE_2100x1200] = {
> +		.width = 2100,
> +		.height = 1200,
> +		.reg_list = {
> +			.regs = mode_2100x1200,
> +			.num_of_regs = ARRAY_SIZE(mode_2100x1200),
> +		},
> +	},
> +	[IMX111_MODE_1952x1098] = {
> +		.width = 1952,
> +		.height = 1098,
> +		.reg_list = {
> +			.regs = mode_1952x1098,
> +			.num_of_regs = ARRAY_SIZE(mode_1952x1098),
> +		},
> +	},
> +	[IMX111_MODE_1920x1080] = {
> +		.width = 1920,
> +		.height = 1080,
> +		.reg_list = {
> +			.regs = mode_1952x1098,
> +			.num_of_regs = ARRAY_SIZE(mode_1952x1098),
> +		},
> +	},
> +	[IMX111_MODE_1640x1232] = {
> +		.width = 1640,
> +		.height = 1232,
> +		.reg_list = {
> +			.regs = mode_1640x1232,
> +			.num_of_regs = ARRAY_SIZE(mode_1640x1232),
> +		},
> +	},
> +	[IMX111_MODE_1440x1080] = {
> +		.width = 1440,
> +		.height = 1080,
> +		.reg_list = {
> +			.regs = mode_1640x1232,
> +			.num_of_regs = ARRAY_SIZE(mode_1640x1232),
> +		},
> +	},
> +	[IMX111_MODE_1640x924] = {
> +		.width = 1640,
> +		.height = 924,
> +		.reg_list = {
> +			.regs = mode_1640x924,
> +			.num_of_regs = ARRAY_SIZE(mode_1640x924),
> +		},
> +	},
> +	[IMX111_MODE_1308x736] = {
> +		.width = 1308,
> +		.height = 736,
> +		.reg_list = {
> +			.regs = mode_1308x736,
> +			.num_of_regs = ARRAY_SIZE(mode_1308x736),
> +		},
> +	},
> +	[IMX111_MODE_1280x720] = {
> +		.width = 1280,
> +		.height = 720,
> +		.reg_list = {
> +			.regs = mode_1308x736,
> +			.num_of_regs = ARRAY_SIZE(mode_1308x736),
> +		},
> +	},
> +	[IMX111_MODE_820x614] = {
> +		.width = 820,
> +		.height = 614,
> +		.reg_list = {
> +			.regs = mode_820x614,
> +			.num_of_regs = ARRAY_SIZE(mode_820x614),
> +		},
> +	},
> +	[IMX111_MODE_640x480] = {
> +		.width = 640,
> +		.height = 480,
> +		.reg_list = {
> +			.regs = mode_820x614,
> +			.num_of_regs = ARRAY_SIZE(mode_820x614),
> +		},
> +	},
> +};
> +
> +static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct imx111, sd);

container_of_const(), please.

> +}
> +
> +static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl)
> +{
> +	return container_of(ctrl->handler, struct imx111, hdl);
> +}
> +
> +static u8 to_settle_delay(u64 extclk_rate)
> +{
> +	u64 extclk_mhz = div_u64(extclk_rate, MEGA);
> +
> +	return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63, 64);
> +}
> +
> +static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool test)
> +{
> +	u32 i;
> +
> +	for (i = 0; i < ARRAY_SIZE(imx111_mbus_formats); i++)
> +		if (imx111_mbus_formats[i] == code)
> +			break;
> +
> +	if (i >= ARRAY_SIZE(imx111_mbus_formats))
> +		i = 0;
> +
> +	if (test)
> +		return imx111_mbus_formats[i];
> +
> +	i = (i & ~3) | (sensor->vflip->val ? 2 : 0) |
> +	    (sensor->hflip->val ? 1 : 0);
> +
> +	return imx111_mbus_formats[i];
> +}
> +
> +static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format)
> +{
> +	switch (format->code) {
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +		return 8;
> +
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	default:
> +		return 10;
> +	}
> +}
> +
> +static int imx111_update_digital_gain(struct imx111 *sensor, u32 val)
> +{
> +	int ret = 0;
> +
> +	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +			IMX111_GROUP_WRITE_ON, NULL);

Missing error handling.

> +
> +	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret);
> +	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret);
> +	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret);
> +	cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret);
> +
> +	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +			0, NULL);

Ditto.

> +
> +	return ret;
> +}
> +
> +static int imx111_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct imx111 *sensor = ctrl_to_imx111(ctrl);
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	int ret = 0;
> +
> +	/*
> +	 * Applying V4L2 control value only happens
> +	 * when power is up for streaming
> +	 */
> +	if (!pm_runtime_get_if_in_use(dev))
> +		return 0;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, &ret);
> +		break;
> +	case V4L2_CID_DIGITAL_GAIN:
> +		ret = imx111_update_digital_gain(sensor, ctrl->val);
> +		break;
> +	case V4L2_CID_EXPOSURE:
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				IMX111_GROUP_WRITE_ON, NULL);
> +		cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, NULL);
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				0, NULL);
> +		break;
> +	case V4L2_CID_HFLIP:
> +	case V4L2_CID_VFLIP:
> +		cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION,
> +			  sensor->hflip->val | sensor->vflip->val << 1, &ret);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, &ret);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_RED:
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				IMX111_GROUP_WRITE_ON, NULL);
> +		cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, &ret);
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				0, NULL);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_GREENR:
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				IMX111_GROUP_WRITE_ON, NULL);
> +		cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, &ret);
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				0, NULL);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_BLUE:
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				IMX111_GROUP_WRITE_ON, NULL);
> +		cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, &ret);
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				0, NULL);
> +		break;
> +	case V4L2_CID_TEST_PATTERN_GREENB:
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				IMX111_GROUP_WRITE_ON, NULL);
> +		cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, &ret);
> +		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +				0, NULL);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	pm_runtime_mark_last_busy(dev);

You can drop this now (and elsewhere in driver code, too).

> +	pm_runtime_put_autosuspend(dev);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops imx111_ctrl_ops = {
> +	.s_ctrl = imx111_set_ctrl,
> +};
> +
> +static const char * const test_pattern_menu[] = {
> +	"Disabled",
> +	"Solid Color Fill",
> +	"Standard Color Bars",
> +	"Fade To Grey Color Bars",
> +	"Pseudorandom data",
> +};
> +
> +static int imx111_init_controls(struct imx111 *sensor)
> +{
> +	const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	struct v4l2_fwnode_device_properties props;
> +	struct v4l2_subdev *sd = &sensor->sd;
> +	struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> +	s64 pixel_rate_min, pixel_rate_max;
> +	int i, ret;
> +
> +	ret = v4l2_fwnode_device_parse(dev, &props);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = v4l2_ctrl_handler_init(hdl, 13);
> +	if (ret)
> +		return ret;
> +
> +	pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
> +	pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
> +	sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
> +					       pixel_rate_min, pixel_rate_max,
> +					       1, div_u64(sensor->pixel_clk_raw,
> +					       2 * sensor->data_depth));
> +
> +	sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
> +						   0, 0, &sensor->default_link_freq);
> +	if (sensor->link_freq)
> +		sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> +			  IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
> +			  IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
> +
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
> +			  IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
> +			  IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
> +
> +	sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	if (sensor->hflip)
> +		sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> +
> +	sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	if (sensor->vflip)
> +		sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;

Could you also add VBLANK and HBLANK controls, please?

> +
> +	/*
> +	 * The maximum coarse integration time is the frame length in lines
> +	 * minus five.
> +	 */
> +	sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
> +					     IMX111_INTEGRATION_TIME_MIN,
> +					     IMX111_PIXEL_ARRAY_HEIGHT - 5,
> +					     IMX111_INTEGRATION_TIME_STEP,
> +					     IMX111_PIXEL_ARRAY_HEIGHT - 5);
> +
> +	v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
> +
> +	v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
> +				     ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
> +				     test_pattern_menu);
> +	for (i = 0; i < 4; i++) {
> +		/*
> +		 * The assumption is that
> +		 * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
> +		 * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
> +		 * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
> +		 */
> +		v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i,
> +				  IMX111_TESTP_COLOUR_MIN, IMX111_TESTP_COLOUR_MAX,
> +				  IMX111_TESTP_COLOUR_STEP, IMX111_TESTP_COLOUR_MAX);
> +		/* The "Solid color" pattern is white by default */
> +	}
> +
> +	if (hdl->error)
> +		return hdl->error;
> +
> +	sd->ctrl_handler = hdl;
> +
> +	return 0;
> +};
> +
> +static int imx111_start_streaming(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	const struct imx111_mode *mode = sensor->cur_mode;
> +	int ret;
> +
> +	/* Apply default values of current mode */
> +	ret = cci_multi_reg_write(sensor->regmap, mode->reg_list.regs,
> +				  mode->reg_list.num_of_regs, NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to initialize the sensor\n");
> +		return ret;
> +	}
> +
> +	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +			IMX111_GROUP_WRITE_ON, NULL);

Please add error handling for group write register access, too. Errors
aren't supposed to happen here anyway unless something is definitely wrong.

> +	cci_write(sensor->regmap, IMX111_DATA_DEPTH,
> +		  sensor->data_depth | sensor->data_depth << 8, NULL);
> +	cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> +			0, NULL);
> +
> +	ret = __v4l2_ctrl_handler_setup(&sensor->hdl);
> +	if (ret)
> +		return ret;
> +
> +	ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STREAMING, NULL);
> +	if (ret)
> +		dev_err(dev, "failed to start stream");
> +
> +	/* vflip and hflip cannot change during streaming */
> +	__v4l2_ctrl_grab(sensor->vflip, true);
> +	__v4l2_ctrl_grab(sensor->hflip, true);
> +
> +	msleep(30);

Why the msleep()?

> +
> +	return ret;
> +}
> +
> +static void imx111_stop_streaming(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +
> +	if (cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STANDBY, NULL))
> +		dev_err(dev, "failed to stop stream");
> +
> +	__v4l2_ctrl_grab(sensor->vflip, false);
> +	__v4l2_ctrl_grab(sensor->hflip, false);
> +}
> +
> +static int imx111_initialize(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	int ret;
> +
> +	imx111_stop_streaming(sensor);
> +	msleep(30);
> +
> +	/* Configure the PLL. */
> +	cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1,
> +		  sensor->pll->pre_div, &ret);
> +	cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, sensor->pll->mult, &ret);
> +	cci_write(sensor->regmap, IMX111_POST_DIVIDER, IMX111_POST_DIVIDER_DIV1, &ret);
> +	cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME,
> +		  to_settle_delay(sensor->pll->extclk_rate), &ret);
> +
> +	ret = cci_multi_reg_write(sensor->regmap, imx111_global_init,
> +				  ARRAY_SIZE(imx111_global_init), NULL);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to initialize the sensor\n");
> +		return ret;
> +	}
> +
> +	return ret;

	return 0;

> +}
> +
> +static int imx111_set_stream(struct v4l2_subdev *sd, int enable)

Could you use {en,dis}able_streams() pad ops instead?

> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	struct v4l2_subdev_state *state;
> +	int ret = 0;
> +
> +	state = v4l2_subdev_lock_and_get_active_state(sd);
> +
> +	if (enable) {
> +		ret = pm_runtime_resume_and_get(dev);
> +		if (ret)
> +			goto finish_unlock;
> +
> +		ret = imx111_start_streaming(sensor);
> +		if (!ret)
> +			goto finish_unlock;
> +
> +		dev_err(dev, "Failed to start stream: %d\n", ret);
> +		enable = 0;
> +	}
> +
> +	imx111_stop_streaming(sensor);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_put_autosuspend(dev);
> +
> +finish_unlock:
> +	v4l2_subdev_unlock_state(state);
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * IMX111 Pad Subdev Init and Operations
> + */
> +static int imx111_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *sd_state,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +
> +	if (code->index >= (ARRAY_SIZE(imx111_mbus_formats) / 4))

Redundant parentheses.

> +		return -EINVAL;
> +
> +	code->code = imx111_get_format_code(sensor, imx111_mbus_formats[code->index * 4], false);
> +
> +	return 0;
> +}
> +
> +static int imx111_enum_frame_size(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *sd_state,
> +				  struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	u32 code;
> +
> +	if (fse->index >= ARRAY_SIZE(imx111_modes))
> +		return -EINVAL;
> +
> +	code = imx111_get_format_code(sensor, fse->code, true);
> +	if (fse->code != code)
> +		return -EINVAL;
> +
> +	fse->min_width = imx111_modes[fse->index].width;
> +	fse->max_width = fse->min_width;
> +	fse->min_height = imx111_modes[fse->index].height;
> +	fse->max_height = fse->min_height;
> +
> +	return 0;
> +}
> +
> +static int imx111_enum_frame_interval(struct v4l2_subdev *sd,
> +				      struct v4l2_subdev_state *state,
> +				      struct v4l2_subdev_frame_interval_enum *fie)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	const struct imx111_mode *mode;
> +	u32 framerate, code;
> +
> +	if (fie->index > 0)
> +		return -EINVAL;
> +
> +	code = imx111_get_format_code(sensor, fie->code, true);
> +	if (fie->code != code)
> +		return -EINVAL;
> +
> +	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> +				      width, height,
> +				      fie->width, fie->height);
> +	if (fie->width > mode->width || fie->height > mode->height)
> +		return -EINVAL;
> +
> +	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> +			    2 * sensor->data_depth);
> +
> +	fie->interval.numerator = 1;
> +	fie->interval.denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> +
> +	return 0;
> +}
> +
> +static int imx111_set_format(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_state *state,
> +			     struct v4l2_subdev_format *format)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *interval;
> +	const struct imx111_mode *mode;
> +	u32 framerate;
> +
> +	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> +				      width, height,
> +				      mbus_fmt->width, mbus_fmt->height);
> +	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> +			    2 * sensor->data_depth);
> +
> +	fmt = v4l2_subdev_state_get_format(state, format->pad);
> +
> +	fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false);
> +	fmt->width = mode->width;
> +	fmt->height = mode->height;
> +	fmt->colorspace = V4L2_COLORSPACE_RAW;
> +
> +	*mbus_fmt = *fmt;
> +
> +	interval = v4l2_subdev_state_get_interval(state, format->pad);
> +	interval->numerator = 1;
> +	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);

Frame rate configuration should be done using VBLANK/HBLANK controls, not
the SUBDEV_S_FRAME_INTERVAL IOCTL. Please see
<URL:https://www.linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/drivers/camera-sensor.html#raw-camera-sensors>.

> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)	{
> +		sensor->cur_mode = mode;
> +		sensor->data_depth = imx111_get_format_bpp(fmt);
> +		__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
> +					 div_u64(sensor->pixel_clk_raw, 2 * sensor->data_depth));
> +	}
> +
> +	return 0;
> +}
> +
> +static int imx111_set_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_state *state,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	const struct imx111_mode *mode;
> +	const struct v4l2_mbus_framefmt *mbus_fmt;
> +	struct v4l2_fract *interval;
> +	u32 framerate;
> +
> +	mbus_fmt = v4l2_subdev_state_get_format(state, fi->pad);
> +
> +	mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> +				      width, height,
> +				      mbus_fmt->width, mbus_fmt->height);
> +	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> +			    2 * sensor->data_depth);
> +
> +	interval = v4l2_subdev_state_get_interval(state, fi->pad);
> +
> +	interval->numerator = 1;
> +	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> +
> +	fi->interval = *interval;
> +
> +	return 0;
> +}
> +
> +static int imx111_init_state(struct v4l2_subdev *sd,
> +			     struct v4l2_subdev_state *sd_state)
> +{
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	const struct imx111_mode *mode = sensor->cur_mode;
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *interval;
> +	u32 framerate;
> +
> +	fmt = v4l2_subdev_state_get_format(sd_state, 0);
> +	interval = v4l2_subdev_state_get_interval(sd_state, 0);
> +
> +	fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10;
> +	fmt->width = mode->width;
> +	fmt->height = mode->height;
> +	fmt->field = V4L2_FIELD_NONE;
> +	fmt->colorspace = V4L2_COLORSPACE_RAW;
> +	fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
> +	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> +	fmt->xfer_func = V4L2_XFER_FUNC_NONE;
> +
> +	framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> +			    2 * sensor->data_depth);
> +
> +	interval->numerator = 1;
> +	interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops imx111_video_ops = {
> +	.s_stream = imx111_set_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops imx111_pad_ops = {
> +	.enum_mbus_code = imx111_enum_mbus_code,
> +	.enum_frame_size = imx111_enum_frame_size,
> +	.enum_frame_interval = imx111_enum_frame_interval,
> +	.get_fmt = v4l2_subdev_get_fmt,
> +	.set_fmt = imx111_set_format,
> +	.get_frame_interval = v4l2_subdev_get_frame_interval,
> +	.set_frame_interval = imx111_set_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_ops imx111_subdev_ops = {
> +	.video = &imx111_video_ops,
> +	.pad = &imx111_pad_ops,
> +};
> +
> +static const struct media_entity_operations imx111_subdev_entity_ops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static const struct v4l2_subdev_internal_ops imx111_internal_ops = {
> +	.init_state = imx111_init_state,
> +};
> +
> +static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct v4l2_subdev *sd = &sensor->sd;
> +	struct media_pad *pad = &sensor->pad;
> +	struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> +	int ret;
> +
> +	/* Initialize the subdev. */
> +	v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops);
> +
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	sd->internal_ops = &imx111_internal_ops;
> +
> +	/* Initialize the media entity. */
> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	sd->entity.ops = &imx111_subdev_entity_ops;
> +	pad->flags = MEDIA_PAD_FL_SOURCE;
> +
> +	ret = media_entity_pads_init(&sd->entity, 1, pad);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init entity pads: %d", ret);
> +		return ret;
> +	}
> +
> +	/* Initialize the control handler. */
> +	ret = imx111_init_controls(sensor);
> +	if (ret)
> +		goto error;
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(hdl);
> +	media_entity_cleanup(&sd->entity);
> +	return ret;
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Power Management
> + */
> +
> +static int imx111_power_on(struct imx111 *sensor)
> +{
> +	int ret;
> +
> +	if (sensor->reset)
> +		gpiod_set_value(sensor->reset, 1);
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
> +				    sensor->supplies);
> +	if (ret < 0)
> +		return ret;
> +
> +	usleep_range(500, 600);
> +
> +	if (sensor->reset)
> +		gpiod_set_value(sensor->reset, 0);
> +
> +	usleep_range(200, 250);
> +
> +	ret = clk_prepare_enable(sensor->extclk);
> +	if (ret < 0)
> +		goto error_regulator;
> +
> +	usleep_range(200, 250);
> +
> +	return 0;
> +
> +error_regulator:
> +	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
> +	return ret;
> +}
> +
> +static void imx111_power_off(struct imx111 *sensor)
> +{
> +	if (sensor->reset)
> +		gpiod_set_value(sensor->reset, 1);
> +	usleep_range(1000, 2000);
> +
> +	clk_disable_unprepare(sensor->extclk);
> +	regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
> +}
> +
> +static int __maybe_unused imx111_pm_runtime_resume(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +	int ret;
> +
> +	ret = imx111_power_on(sensor);
> +	if (ret)
> +		return ret;
> +
> +	ret = imx111_initialize(sensor);
> +	if (ret) {
> +		imx111_power_off(sensor);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev)
> +{
> +	struct v4l2_subdev *sd = dev_get_drvdata(dev);
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +
> +	imx111_power_off(sensor);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops imx111_pm_ops = {
> +	SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend,
> +			   imx111_pm_runtime_resume, NULL)
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Probe & Remove
> + */
> +
> +static int imx111_identify_module(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	u64 value, revision, manufacturer;
> +	int ret;
> +
> +	ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (value != IMX111_CHIP_ID) {
> +		dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID, value);
> +		return -ENXIO;
> +	}
> +
> +	cci_read(sensor->regmap, IMX111_REVISION, &revision, NULL);
> +	cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, NULL);
> +
> +	dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n",
> +		value, revision, manufacturer);
> +
> +	return 0;
> +}
> +
> +static int imx111_clk_init(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes;
> +	u64 extclk_rate, system_clk;
> +	int i;

unsigned int, please (and you could declare it for the loop only).

> +
> +	extclk_rate = clk_get_rate(sensor->extclk);
> +	if (!extclk_rate)
> +		return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n");
> +
> +	for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) {
> +		if (clk_get_rate(sensor->extclk) == imx111_pll[i].extclk_rate) {
> +			sensor->pll = &imx111_pll[i];
> +			break;
> +		}
> +	}
> +	if (!sensor->pll)
> +		return dev_err_probe(dev, -EINVAL, "Unsupported EXTCLK rate %llu\n", extclk_rate);
> +
> +	system_clk = div_u64(extclk_rate, sensor->pll->pre_div) * sensor->pll->mult;
> +
> +	/*
> +	 * Pixel clock or Logic clock is used for internal image processing is
> +	 * generated by dividing into 1/10 or 1/8 frequency according to the
> +	 * word length of the CSI2 interface. This clock is designating the pixel
> +	 * rate and used as the base of integration time, frame rate etc.
> +	 */

Can you run

	/scripts/checkpatch.pl --strict --max-line-length=80

on the patch, please?

> +	sensor->pixel_clk_raw = system_clk * ndata_lanes;
> +
> +	/*
> +	 * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR over n lanes
> +	 * for RAW10 default format.
> +	 */
> +	sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8,
> +					    ndata_lanes *
> +					    2 * IMX111_DATA_DEPTH_RAW10);

This looks a bit odd; I'm not sure the bit depth should make a difference
here. Why is pixel_clk_raw multiplied by 8?

> +
> +	if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
> +	    sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq)
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Unsupported DT link-frequencies, expected %llu\n",
> +				     sensor->default_link_freq);
> +
> +	return 0;
> +}
> +
> +static int imx111_parse_dt(struct imx111 *sensor)
> +{
> +	struct device *dev = regmap_get_device(sensor->regmap);
> +	struct fwnode_handle *fwnode = dev_fwnode(dev);
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
> +	if (!ep) {
> +		dev_err(dev, "No endpoint found\n");
> +		return -EINVAL;
> +	}
> +
> +	sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN;

This should be V4L2_MBUS_CSI2_DPHY.

> +	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
> +	fwnode_handle_put(ep);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to parse endpoint\n");
> +		goto error;
> +	}
> +
> +	switch (sensor->bus_cfg.bus_type) {

And then you can omit this check.

> +	case V4L2_MBUS_CSI2_DPHY:
> +		break;
> +
> +	default:
> +		dev_err(dev, "unsupported bus type %u\n", sensor->bus_cfg.bus_type);
> +		ret = -EINVAL;
> +		goto error;
> +	}
> +
> +	/* Check the number of MIPI CSI2 data lanes */
> +	if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) {
> +		dev_err(dev, "number of lanes is more than 2\n");
> +		ret = -EINVAL;
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> +	return ret;
> +}
> +
> +static int imx111_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct imx111 *sensor;
> +	int ret;
> +
> +	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
> +	if (!sensor)
> +		return -ENOMEM;
> +
> +	sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
> +	if (IS_ERR(sensor->regmap))
> +		return dev_err_probe(dev, PTR_ERR(sensor->regmap),
> +				     "Failed to allocate register map\n");
> +
> +	sensor->extclk = devm_clk_get(dev, NULL);

Could you use devm_v4l2_sensor_clk_get() instead, please?

> +	if (IS_ERR(sensor->extclk))
> +		return dev_err_probe(dev, PTR_ERR(sensor->extclk), "Failed to get clock\n");
> +
> +	sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(sensor->reset))
> +		return dev_err_probe(dev, PTR_ERR(sensor->reset), "Failed to get reset GPIO\n");
> +
> +	sensor->supplies[0].supply = "iovdd";
> +	sensor->supplies[1].supply = "dvdd";
> +	sensor->supplies[2].supply = "avdd";
> +
> +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sensor->supplies), sensor->supplies);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "Failed to get regulators\n");
> +
> +	ret = imx111_parse_dt(sensor);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = imx111_clk_init(sensor);
> +	if (ret < 0)
> +		goto error_ep_free;
> +
> +	ret = imx111_power_on(sensor);
> +	if (ret < 0) {
> +		dev_err_probe(dev, ret, "Could not power on the device\n");
> +		goto error_ep_free;
> +	}
> +
> +	ret = imx111_identify_module(sensor);
> +	if (ret < 0) {
> +		dev_err_probe(dev, ret, "Could not identify module\n");
> +		goto error_power_off;
> +	}
> +
> +	sensor->cur_mode = &imx111_modes[IMX111_MODE_3280x2464];
> +	sensor->data_depth = IMX111_DATA_DEPTH_RAW10;
> +
> +	ret = imx111_initialize(sensor);
> +	if (ret < 0)
> +		goto error_power_off;
> +
> +	ret = imx111_init_subdev(sensor, client);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to init controls: %d", ret);
> +		goto error_v4l2_ctrl_handler_free;
> +	}
> +
> +	ret = v4l2_subdev_init_finalize(&sensor->sd);
> +	if (ret)
> +		goto error_v4l2_ctrl_handler_free;
> +
> +	/*
> +	 * Enable runtime PM with autosuspend. As the device has been powered
> +	 * manually, mark it as active, and increase the usage count without
> +	 * resuming the device.
> +	 */
> +	pm_runtime_set_active(dev);
> +	pm_runtime_get_noresume(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_set_autosuspend_delay(dev, 1000);
> +	pm_runtime_use_autosuspend(dev);

Also disable autosuspend at driver unbind (or probe failure).

> +
> +	ret = v4l2_async_register_subdev_sensor(&sensor->sd);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to register V4L2 subdev: %d", ret);
> +		goto error_pm;
> +	}
> +
> +	/*
> +	 * Decrease the PM usage count. The device will get suspended after the
> +	 * autosuspend delay, turning the power off.
> +	 */
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_put_autosuspend(dev);

	pm_runtime_idle(dev);

And you can drop pm_runtime_get_noresume() above (also error handling will
be affected).

> +
> +	return 0;
> +
> +error_pm:
> +	pm_runtime_disable(dev);
> +	pm_runtime_put_noidle(dev);
> +	v4l2_subdev_cleanup(&sensor->sd);
> +
> +error_v4l2_ctrl_handler_free:
> +	v4l2_ctrl_handler_free(&sensor->hdl);
> +	media_entity_cleanup(&sensor->sd.entity);
> +
> +error_power_off:
> +	imx111_power_off(sensor);
> +
> +error_ep_free:
> +	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> +
> +	return ret;
> +}
> +
> +static void imx111_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct imx111 *sensor = sd_to_imx111(sd);
> +
> +	v4l2_async_unregister_subdev(&sensor->sd);
> +	v4l2_subdev_cleanup(sd);
> +	media_entity_cleanup(&sensor->sd.entity);
> +	v4l2_ctrl_handler_free(&sensor->hdl);
> +	v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> +
> +	/*
> +	 * Disable runtime PM. In case runtime PM is disabled in the kernel,
> +	 * make sure to turn power off manually.
> +	 */
> +	pm_runtime_disable(&client->dev);
> +	if (!pm_runtime_status_suspended(&client->dev)) {
> +		imx111_power_off(sensor);
> +		pm_runtime_set_suspended(&client->dev);
> +	}
> +}
> +
> +static const struct i2c_device_id imx111_id[] = {
> +	{ "imx111" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(i2c, imx111_id);

Do you need the I²C ID table still?

> +
> +static const struct of_device_id imx111_of_match[] = {
> +	{ .compatible = "sony,imx111" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx111_of_match);
> +
> +static struct i2c_driver imx111_i2c_driver = {
> +	.driver = {
> +		.name = "imx111",
> +		.of_match_table = imx111_of_match,
> +		.pm = &imx111_pm_ops,
> +	},
> +	.id_table = imx111_id,
> +	.probe = imx111_probe,
> +	.remove = imx111_remove,
> +};
> +module_i2c_driver(imx111_i2c_driver);
> +
> +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
> +MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver");
> +MODULE_LICENSE("GPL");

-- 
Kind regards,

Sakari Ailus

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

* Re: [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-28 17:58   ` Sakari Ailus
@ 2025-10-28 18:57     ` Svyatoslav Ryhel
  2025-10-29 15:19       ` Tarang Raval
  0 siblings, 1 reply; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-28 18:57 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Arnd Bergmann,
	Dongcheng Yan, André Apitzsch, Sylvain Petinot,
	Benjamin Mugnier, Heimir Thor Sverrisson, linux-media, devicetree,
	linux-kernel

вт, 28 жовт. 2025 р. о 19:58 Sakari Ailus <sakari.ailus@linux.intel.com> пише:
>
> Hi Svyatoslav,
>
> Btw. there's no need to resend media patches; just ping for reviews
> instead.

With other subsystems resends are preferred so I resend in general.

>
> On Tue, Oct 28, 2025 at 11:22:00AM +0200, Svyatoslav Ryhel wrote:
> > Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> > camera sensor using the i2c bus for control and the csi-2 bus for data.
> >
> > The following features are supported:
> > - manual exposure, digital and analog gain control support
> > - pixel rate/link freq control support
> > - supported resolution up to 3280x2464 for single shot capture
> > - supported resolution up to 1920x1080 @ 30fps for video
> > - supported bayer order output SGBRG10 and SGBRG8
> >
> > Camera module seems to be partially compatible with Nokia SMIA but it
> > lacks a few registers required for clock calculations and has different
> > vendor-specific per-mode configurations which makes it incompatible with
> > existing CCS driver.
> >
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> >  drivers/media/i2c/Kconfig  |   10 +
> >  drivers/media/i2c/Makefile |    1 +
> >  drivers/media/i2c/imx111.c | 1614 ++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1625 insertions(+)
> >  create mode 100644 drivers/media/i2c/imx111.c
> >
> > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> > index e68202954a8f..8ec1f369f043 100644
> > --- a/drivers/media/i2c/Kconfig
> > +++ b/drivers/media/i2c/Kconfig
> > @@ -127,6 +127,16 @@ config VIDEO_HI847
> >            To compile this driver as a module, choose M here: the
> >            module will be called hi847.
> >
> > +config VIDEO_IMX111
> > +     tristate "Sony IMX111 sensor support"
> > +     select V4L2_CCI_I2C
> > +     help
> > +       This is a V4L2 sensor driver for the Sony IMX111 camera
> > +       sensors.
> > +
> > +       To compile this driver as a module, choose M here: the
> > +       module will be called imx111.
> > +
> >  config VIDEO_IMX208
> >       tristate "Sony IMX208 sensor support"
> >       help
> > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> > index 5873d29433ee..67b810c91870 100644
> > --- a/drivers/media/i2c/Makefile
> > +++ b/drivers/media/i2c/Makefile
> > @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o
> >  obj-$(CONFIG_VIDEO_HI846) += hi846.o
> >  obj-$(CONFIG_VIDEO_HI847) += hi847.o
> >  obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
> > +obj-$(CONFIG_VIDEO_IMX111) += imx111.o
> >  obj-$(CONFIG_VIDEO_IMX208) += imx208.o
> >  obj-$(CONFIG_VIDEO_IMX214) += imx214.o
> >  obj-$(CONFIG_VIDEO_IMX219) += imx219.o
> > diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c
> > new file mode 100644
> > index 000000000000..814c557d9e96
> > --- /dev/null
> > +++ b/drivers/media/i2c/imx111.c
> > @@ -0,0 +1,1614 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/i2c.h>
> > +#include <linux/kernel.h>
> > +#include <linux/media.h>
> > +#include <linux/module.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/ratelimit.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +#include <linux/slab.h>
> > +#include <linux/string.h>
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +#include <linux/units.h>
> > +
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-cci.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-image-sizes.h>
> > +#include <media/v4l2-subdev.h>
> > +#include <media/v4l2-mediabus.h>
> > +
> > +/* product information registers */
> > +#define IMX111_PRODUCT_ID                    CCI_REG16(0x0000)
> > +#define   IMX111_CHIP_ID                     0x111
> > +#define IMX111_REVISION                              CCI_REG8(0x0002)
> > +#define IMX111_MANUFACTURER_ID                       CCI_REG8(0x0003)
> > +#define IMX111_SMIA_VER                              CCI_REG8(0x0004)
> > +#define IMX111_FRAME_COUNTER                 CCI_REG8(0x0005)
> > +#define IMX111_PIXEL_ORDER                   CCI_REG8(0x0006)
> > +
> > +/* general configuration registers */
> > +#define IMX111_STREAMING_MODE                        CCI_REG8(0x0100)
> > +#define   IMX111_MODE_STANDBY                        0
> > +#define   IMX111_MODE_STREAMING                      1
> > +#define IMX111_IMAGE_ORIENTATION             CCI_REG8(0x0101)
> > +#define   IMX111_IMAGE_HFLIP                 BIT(0)
> > +#define   IMX111_IMAGE_VFLIP                 BIT(1)
> > +#define IMX111_SOFTWARE_RESET                        CCI_REG8(0x0103)
> > +#define   IMX111_RESET_ON                    1
> > +#define IMX111_GROUP_WRITE                   CCI_REG8(0x0104)
> > +#define   IMX111_GROUP_WRITE_ON                      1
> > +#define IMX111_FRAME_DROP                    CCI_REG8(0x0105)
> > +#define   IMX111_FRAME_DROP_ON                       1
> > +#define IMX111_CHANNEL_ID                    CCI_REG8(0x0110)
> > +#define IMX111_SIGNALLING_MODE                       CCI_REG8(0x0111)
> > +#define IMX111_DATA_DEPTH                    CCI_REG16(0x0112)
> > +#define   IMX111_DATA_DEPTH_RAW8             0x08
> > +#define   IMX111_DATA_DEPTH_RAW10            0x0a
> > +
> > +/* integration time registers */
> > +#define IMX111_INTEGRATION_TIME                      CCI_REG16(0x0202)
> > +#define IMX111_INTEGRATION_TIME_MIN          0x1
> > +#define IMX111_INTEGRATION_TIME_MAX          0xffff
> > +#define IMX111_INTEGRATION_TIME_STEP         1
> > +
> > +/* analog gain control */
> > +#define IMX111_REG_ANALOG_GAIN                       CCI_REG8(0x0205)
> > +#define IMX111_ANA_GAIN_MIN                  0
> > +#define IMX111_ANA_GAIN_MAX                  240
> > +#define IMX111_ANA_GAIN_STEP                 1
> > +#define IMX111_ANA_GAIN_DEFAULT                      0
> > +
> > +/* digital gain control */
> > +#define IMX111_REG_DIG_GAIN_GREENR           CCI_REG16(0x020e)
> > +#define IMX111_REG_DIG_GAIN_RED                      CCI_REG16(0x0210)
> > +#define IMX111_REG_DIG_GAIN_BLUE             CCI_REG16(0x0212)
> > +#define IMX111_REG_DIG_GAIN_GREENB           CCI_REG16(0x0214)
> > +#define IMX111_DGTL_GAIN_MIN                 0x0100
> > +#define IMX111_DGTL_GAIN_MAX                 0x0fff
> > +#define IMX111_DGTL_GAIN_DEFAULT             0x0100
> > +#define IMX111_DGTL_GAIN_STEP                        1
> > +
> > +/* clock configuration registers */
> > +#define IMX111_PIXEL_CLK_DIVIDER_PLL1                CCI_REG8(0x0301) /* fixed to 10 */
> > +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1               CCI_REG8(0x0303) /* fixed to 1 */
> > +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1              CCI_REG8(0x0305)
> > +#define IMX111_PLL_MULTIPLIER_PLL1           CCI_REG8(0x0307)
> > +#define IMX111_PLL_SETTLING_TIME             CCI_REG8(0x303c)
> > +#define   IMX111_PLL_SETTLING_TIME_DEFAULT   200
> > +#define IMX111_POST_DIVIDER                  CCI_REG8(0x30a4)
> > +#define   IMX111_POST_DIVIDER_DIV1           2
> > +#define   IMX111_POST_DIVIDER_DIV2           0
> > +#define   IMX111_POST_DIVIDER_DIV4           1
> > +
> > +/* frame timing registers */
> > +#define IMX111_VERTICAL_TOTAL_LENGTH         CCI_REG16(0x0340)
> > +#define IMX111_HORIZONTAL_TOTAL_LENGTH               CCI_REG16(0x0342)
> > +
> > +/* image size registers */
> > +#define IMX111_HORIZONTAL_START                      CCI_REG16(0x0344)
> > +#define IMX111_VERTICAL_START                        CCI_REG16(0x0346)
> > +#define IMX111_HORIZONTAL_END                        CCI_REG16(0x0348)
> > +#define IMX111_VERTICAL_END                  CCI_REG16(0x034a)
> > +#define IMX111_IMAGE_WIDTH                   CCI_REG16(0x034c)
> > +#define IMX111_IMAGE_HEIGHT                  CCI_REG16(0x034e)
> > +
> > +/* test pattern registers */
> > +#define IMX111_TEST_PATTERN                  CCI_REG8(0x0601)
> > +#define   IMX111_TEST_PATTERN_NONE           0
> > +#define   IMX111_TEST_PATTERN_SOLID          1
> > +#define   IMX111_TEST_PATTERN_BARS           2
> > +#define   IMX111_TEST_PATTERN_FADE           3
> > +#define   IMX111_TEST_PATTERN_PN9            4
> > +#define IMX111_SOLID_COLOR_RED                       CCI_REG16(0x0602)
> > +#define IMX111_SOLID_COLOR_GR                        CCI_REG16(0x0604)
> > +#define IMX111_SOLID_COLOR_BLUE                      CCI_REG16(0x0606)
> > +#define IMX111_SOLID_COLOR_GB                        CCI_REG16(0x0608)
> > +#define IMX111_TESTP_COLOUR_MIN                      0
> > +#define IMX111_TESTP_COLOUR_MAX                      0x03ff
> > +#define IMX111_TESTP_COLOUR_STEP             1
> > +
> > +#define IMX111_FRAME_RATE_STEP                       5
> > +
> > +#define IMX111_PIXEL_ARRAY_WIDTH             3280U
> > +#define IMX111_PIXEL_ARRAY_HEIGHT            2464U
> > +
> > +enum {
> > +     IMX111_MODE_3280x2464,
> > +     IMX111_MODE_3280x1848,
> > +     IMX111_MODE_3280x1098,
> > +     IMX111_MODE_2100x1200,
> > +     IMX111_MODE_1952x1098,
> > +     IMX111_MODE_1920x1080,
> > +     IMX111_MODE_1640x1232,
> > +     IMX111_MODE_1440x1080,
> > +     IMX111_MODE_1640x924,
> > +     IMX111_MODE_1308x736,
> > +     IMX111_MODE_1280x720,
> > +     IMX111_MODE_820x614,
> > +     IMX111_MODE_640x480,
> > +};
> > +
> > +struct imx111_mode {
> > +     u32 width;
> > +     u32 height;
> > +     struct {
> > +             const struct cci_reg_sequence *regs;
> > +             u32 num_of_regs;
> > +     } reg_list;
> > +};
> > +
> > +struct imx111_pll {
> > +     u64 extclk_rate;
> > +     u8 pre_div;
> > +     u8 mult;
> > +};
> > +
> > +struct imx111 {
> > +     struct regmap *regmap;
> > +
> > +     struct clk *extclk;
> > +     struct gpio_desc *reset;
> > +     struct regulator_bulk_data supplies[3];
> > +
> > +     struct v4l2_fwnode_endpoint bus_cfg;
> > +     struct v4l2_subdev sd;
> > +     struct media_pad pad;
> > +
> > +     /* V4L2 Controls */
> > +     struct v4l2_ctrl_handler hdl;
> > +     struct v4l2_ctrl *pixel_rate;
> > +     struct v4l2_ctrl *link_freq;
> > +     struct v4l2_ctrl *exposure;
> > +     struct v4l2_ctrl *hflip;
> > +     struct v4l2_ctrl *vflip;
> > +
> > +     /* Current mode */
> > +     const struct imx111_mode *cur_mode;
> > +     const struct imx111_pll *pll;
> > +     u32 data_depth;
> > +
> > +     u64 pixel_clk_raw;
> > +     s64 default_link_freq;
> > +};
> > +
> > +static const struct imx111_pll imx111_pll[] = {
> > +     {
> > +             .extclk_rate =  6000000, .pre_div = 1, .mult = 113,
>
> Could you write this as:
>
>         { .extclk_rate = 6000000, .pre_div = 1, .mult = 113, },
>
> etc.
>
> > +     }, {
> > +             .extclk_rate = 12000000, .pre_div = 2, .mult = 113,
> > +     }, {
> > +             .extclk_rate = 13500000, .pre_div = 1, .mult = 50,
> > +     }, {
> > +             .extclk_rate = 18000000, .pre_div = 2, .mult = 75,
> > +     }, {
> > +             .extclk_rate = 24000000, .pre_div = 4, .mult = 113,
> > +     }, {
> > +             .extclk_rate = 27000000, .pre_div = 2, .mult = 50,
> > +     }, {
> > +             .extclk_rate = 36000000, .pre_div = 4, .mult = 75,
> > +     }, {
> > +             .extclk_rate = 54000000, .pre_div = 4, .mult = 50,
> > +     },
> > +};
> > +
> > +/*
> > + * This table MUST contain 4 entries per format, to cover the various flip
> > + * combinations in the order
> > + * - no flip
> > + * - h flip
> > + * - v flip
> > + * - h&v flips
> > + */
> > +static const u32 imx111_mbus_formats[] = {
> > +     MEDIA_BUS_FMT_SGBRG10_1X10,
> > +     MEDIA_BUS_FMT_SBGGR10_1X10,
> > +     MEDIA_BUS_FMT_SRGGB10_1X10,
> > +     MEDIA_BUS_FMT_SGRBG10_1X10,
> > +
> > +     MEDIA_BUS_FMT_SGBRG8_1X8,
> > +     MEDIA_BUS_FMT_SBGGR8_1X8,
> > +     MEDIA_BUS_FMT_SRGGB8_1X8,
> > +     MEDIA_BUS_FMT_SGRBG8_1X8,
> > +};
> > +
> > +static const struct cci_reg_sequence imx111_global_init[] = {
> > +     { CCI_REG8(0x3080), 0x50 },
> > +     { CCI_REG8(0x3087), 0x53 },
> > +     { CCI_REG8(0x309d), 0x94 },
> > +     { CCI_REG8(0x30b1), 0x03 },
> > +     { CCI_REG8(0x30c6), 0x00 },
> > +     { CCI_REG8(0x30c7), 0x00 },
> > +     { CCI_REG8(0x3115), 0x0b },
> > +     { CCI_REG8(0x3118), 0x30 },
> > +     { CCI_REG8(0x311d), 0x25 },
> > +     { CCI_REG8(0x3121), 0x0a },
> > +     { CCI_REG8(0x3212), 0xf2 },
> > +     { CCI_REG8(0x3213), 0x0f },
> > +     { CCI_REG8(0x3215), 0x0f },
> > +     { CCI_REG8(0x3217), 0x0b },
> > +     { CCI_REG8(0x3219), 0x0b },
> > +     { CCI_REG8(0x321b), 0x0d },
> > +     { CCI_REG8(0x321d), 0x0d },
> > +     { CCI_REG8(0x32aa), 0x11 },
> > +     { CCI_REG8(0x3032), 0x40 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_820x614[] = {
> > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xec },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x34 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcb },
> > +     { CCI_REG8(0x034c), 0x03 },     { CCI_REG8(0x034d), 0x34 },
> > +     { CCI_REG8(0x034e), 0x02 },     { CCI_REG8(0x034f), 0x66 },
> > +     { CCI_REG8(0x0381), 0x05 },     { CCI_REG8(0x0383), 0x03 },
> > +     { CCI_REG8(0x0385), 0x05 },     { CCI_REG8(0x0387), 0x03 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x28 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x09 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x03 },     { CCI_REG8(0x30d5), 0x09 },
> > +     { CCI_REG8(0x30d6), 0x00 },     { CCI_REG8(0x30d7), 0x00 },
> > +     { CCI_REG8(0x30d8), 0x00 },     { CCI_REG8(0x30d9), 0x00 },
> > +     { CCI_REG8(0x30de), 0x04 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x7a },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_1308x736[] = {
> > +     { CCI_REG8(0x0340), 0x09 },     { CCI_REG8(0x0341), 0x41 },
> > +     { CCI_REG8(0x0342), 0x07 },     { CCI_REG8(0x0343), 0x68 },
> > +     { CCI_REG8(0x0344), 0x01 },     { CCI_REG8(0x0345), 0x54 },
> > +     { CCI_REG8(0x0346), 0x02 },     { CCI_REG8(0x0347), 0x20 },
> > +     { CCI_REG8(0x0348), 0x0b },     { CCI_REG8(0x0349), 0x8b },
> > +     { CCI_REG8(0x034a), 0x07 },     { CCI_REG8(0x034b), 0xdf },
> > +     { CCI_REG8(0x034c), 0x05 },     { CCI_REG8(0x034d), 0x1c },
> > +     { CCI_REG8(0x034e), 0x02 },     { CCI_REG8(0x034f), 0xe0 },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x03 },
> > +     { CCI_REG8(0x3033), 0x84 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > +     { CCI_REG8(0x304c), 0xd7 },     { CCI_REG8(0x304d), 0x01 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x48 },     { CCI_REG8(0x309c), 0x12 },
> > +     { CCI_REG8(0x309e), 0x04 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x0a },     { CCI_REG8(0x30aa), 0x01 },
> > +     { CCI_REG8(0x30b2), 0x05 },     { CCI_REG8(0x30d5), 0x04 },
> > +     { CCI_REG8(0x30d6), 0x85 },     { CCI_REG8(0x30d7), 0x2a },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x00 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x42 },
> > +     { CCI_REG8(0x315d), 0x41 },     { CCI_REG8(0x316e), 0x43 },
> > +     { CCI_REG8(0x316f), 0x42 },     { CCI_REG8(0x3318), 0x62 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_1640x924[] = {
> > +     { CCI_REG8(0x0340), 0x03 },     { CCI_REG8(0x0341), 0xb2 },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x01 },     { CCI_REG8(0x0347), 0x64 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x08 },     { CCI_REG8(0x034b), 0x9b },
> > +     { CCI_REG8(0x034c), 0x06 },     { CCI_REG8(0x034d), 0x68 },
> > +     { CCI_REG8(0x034e), 0x03 },     { CCI_REG8(0x034f), 0x9c },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x03 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x03 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x28 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x09 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x05 },     { CCI_REG8(0x30d5), 0x09 },
> > +     { CCI_REG8(0x30d6), 0x01 },     { CCI_REG8(0x30d7), 0x01 },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x02 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x72 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_1640x1232[] = {
> > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xe6 },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x30 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcf },
> > +     { CCI_REG8(0x034c), 0x06 },     { CCI_REG8(0x034d), 0x68 },
> > +     { CCI_REG8(0x034e), 0x04 },     { CCI_REG8(0x034f), 0xd0 },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x03 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x03 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x28 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x09 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x05 },     { CCI_REG8(0x30d5), 0x09 },
> > +     { CCI_REG8(0x30d6), 0x01 },     { CCI_REG8(0x30d7), 0x01 },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x02 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x72 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_1952x1098[] = {
> > +     { CCI_REG8(0x0340), 0x07 },     { CCI_REG8(0x0341), 0x5c },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xac },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x16 },
> > +     { CCI_REG8(0x0346), 0x01 },     { CCI_REG8(0x0347), 0x6e },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xcb },
> > +     { CCI_REG8(0x034a), 0x08 },     { CCI_REG8(0x034b), 0x93 },
> > +     { CCI_REG8(0x034c), 0x07 },     { CCI_REG8(0x034d), 0xa0 },
> > +     { CCI_REG8(0x034e), 0x04 },     { CCI_REG8(0x034f), 0x4a },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x01 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x00 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x91 },     { CCI_REG8(0x3048), 0x00 },
> > +     { CCI_REG8(0x304c), 0x67 },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x10 },     { CCI_REG8(0x3073), 0xa0 },
> > +     { CCI_REG8(0x3074), 0x12 },     { CCI_REG8(0x3075), 0x12 },
> > +     { CCI_REG8(0x3076), 0x12 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x0a },     { CCI_REG8(0x307a), 0x0a },
> > +     { CCI_REG8(0x309b), 0x60 },     { CCI_REG8(0x309e), 0x04 },
> > +     { CCI_REG8(0x30a0), 0x15 },     { CCI_REG8(0x30a1), 0x08 },
> > +     { CCI_REG8(0x30aa), 0x03 },     { CCI_REG8(0x30b2), 0x05 },
> > +     { CCI_REG8(0x30d5), 0x20 },     { CCI_REG8(0x30d6), 0x85 },
> > +     { CCI_REG8(0x30d7), 0x2a },     { CCI_REG8(0x30d8), 0x64 },
> > +     { CCI_REG8(0x30d9), 0x89 },     { CCI_REG8(0x30de), 0x00 },
> > +     { CCI_REG8(0x30df), 0x21 },     { CCI_REG8(0x3102), 0x08 },
> > +     { CCI_REG8(0x3103), 0x1d },     { CCI_REG8(0x3104), 0x1e },
> > +     { CCI_REG8(0x3105), 0x00 },     { CCI_REG8(0x3106), 0x74 },
> > +     { CCI_REG8(0x3107), 0x00 },     { CCI_REG8(0x3108), 0x03 },
> > +     { CCI_REG8(0x3109), 0x02 },     { CCI_REG8(0x310a), 0x03 },
> > +     { CCI_REG8(0x315c), 0x37 },     { CCI_REG8(0x315d), 0x36 },
> > +     { CCI_REG8(0x316e), 0x38 },     { CCI_REG8(0x316f), 0x37 },
> > +     { CCI_REG8(0x3318), 0x63 },     { CCI_REG8(0x3348), 0xA0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_2100x1200[] = {
> > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xec },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x02 },     { CCI_REG8(0x0345), 0x56 },
> > +     { CCI_REG8(0x0346), 0x02 },     { CCI_REG8(0x0347), 0xa8 },
> > +     { CCI_REG8(0x0348), 0x0a },     { CCI_REG8(0x0349), 0x89 },
> > +     { CCI_REG8(0x034a), 0x07 },     { CCI_REG8(0x034b), 0x57 },
> > +     { CCI_REG8(0x034c), 0x08 },     { CCI_REG8(0x034d), 0x34 },
> > +     { CCI_REG8(0x034e), 0x04 },     { CCI_REG8(0x034f), 0xb0 },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x01 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x00 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x20 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x08 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x07 },     { CCI_REG8(0x30d5), 0x00 },
> > +     { CCI_REG8(0x30d6), 0x85 },     { CCI_REG8(0x30d7), 0x2a },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x00 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x62 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_3280x1098[] = {
> > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0x6a },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xac },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x01 },     { CCI_REG8(0x0347), 0xf6 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x08 },     { CCI_REG8(0x034b), 0x0b },
> > +     { CCI_REG8(0x034c), 0x0c },     { CCI_REG8(0x034d), 0xd0 },
> > +     { CCI_REG8(0x034e), 0x04 },     { CCI_REG8(0x034f), 0x4a },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x01 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x93 },     { CCI_REG8(0x3048), 0x00 },
> > +     { CCI_REG8(0x304c), 0x67 },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0xe0 },
> > +     { CCI_REG8(0x3074), 0x12 },     { CCI_REG8(0x3075), 0x12 },
> > +     { CCI_REG8(0x3076), 0x12 },     { CCI_REG8(0x3077), 0x12 },
> > +     { CCI_REG8(0x3079), 0x2a },     { CCI_REG8(0x307a), 0x0a },
> > +     { CCI_REG8(0x309b), 0x60 },     { CCI_REG8(0x309e), 0x04 },
> > +     { CCI_REG8(0x30a0), 0x15 },     { CCI_REG8(0x30a1), 0x08 },
> > +     { CCI_REG8(0x30aa), 0x03 },     { CCI_REG8(0x30b2), 0x05 },
> > +     { CCI_REG8(0x30d5), 0x00 },     { CCI_REG8(0x30d6), 0x85 },
> > +     { CCI_REG8(0x30d7), 0x2a },     { CCI_REG8(0x30d8), 0x64 },
> > +     { CCI_REG8(0x30d9), 0x89 },     { CCI_REG8(0x30de), 0x00 },
> > +     { CCI_REG8(0x30df), 0x20 },     { CCI_REG8(0x3102), 0x08 },
> > +     { CCI_REG8(0x3103), 0x1d },     { CCI_REG8(0x3104), 0x1e },
> > +     { CCI_REG8(0x3105), 0x00 },     { CCI_REG8(0x3106), 0x74 },
> > +     { CCI_REG8(0x3107), 0x00 },     { CCI_REG8(0x3108), 0x03 },
> > +     { CCI_REG8(0x3109), 0x02 },     { CCI_REG8(0x310a), 0x03 },
> > +     { CCI_REG8(0x315c), 0x37 },     { CCI_REG8(0x315d), 0x36 },
> > +     { CCI_REG8(0x316e), 0x38 },     { CCI_REG8(0x316f), 0x37 },
> > +     { CCI_REG8(0x3318), 0x63 },     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_3280x1848[] = {
> > +     { CCI_REG8(0x0340), 0x07 },     { CCI_REG8(0x0341), 0x52 },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x01 },     { CCI_REG8(0x0347), 0x64 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x08 },     { CCI_REG8(0x034b), 0x9b },
> > +     { CCI_REG8(0x034c), 0x0c },     { CCI_REG8(0x034d), 0xd0 },
> > +     { CCI_REG8(0x034e), 0x07 },     { CCI_REG8(0x034f), 0x38 },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x01 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x00 },
> > +     { CCI_REG8(0x303e), 0x41 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x00 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x20 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x08 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x07 },     { CCI_REG8(0x30d5), 0x00 },
> > +     { CCI_REG8(0x30d6), 0x85 },     { CCI_REG8(0x30d7), 0x2a },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x00 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x10 },     { CCI_REG8(0x3103), 0x44 },
> > +     { CCI_REG8(0x3104), 0x40 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x0d },     { CCI_REG8(0x3107), 0x01 },
> > +     { CCI_REG8(0x3108), 0x09 },     { CCI_REG8(0x3109), 0x08 },
> > +     { CCI_REG8(0x310a), 0x0f },     { CCI_REG8(0x315c), 0x5d },
> > +     { CCI_REG8(0x315d), 0x5c },     { CCI_REG8(0x316e), 0x5e },
> > +     { CCI_REG8(0x316f), 0x5d },     { CCI_REG8(0x3318), 0x60 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct cci_reg_sequence mode_3280x2464[] = {
> > +     { CCI_REG8(0x0340), 0x09 },     { CCI_REG8(0x0341), 0xba },
> > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x30 },
> > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcf },
> > +     { CCI_REG8(0x034c), 0x0c },     { CCI_REG8(0x034d), 0xd0 },
> > +     { CCI_REG8(0x034e), 0x09 },     { CCI_REG8(0x034f), 0xa0 },
> > +     { CCI_REG8(0x0381), 0x01 },     { CCI_REG8(0x0383), 0x01 },
> > +     { CCI_REG8(0x0385), 0x01 },     { CCI_REG8(0x0387), 0x01 },
> > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x00 },
> > +     { CCI_REG8(0x303e), 0x41 },     { CCI_REG8(0x3040), 0x08 },
> > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x00 },
> > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > +     { CCI_REG8(0x309b), 0x20 },     { CCI_REG8(0x309c), 0x13 },
> > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > +     { CCI_REG8(0x30a1), 0x08 },     { CCI_REG8(0x30aa), 0x03 },
> > +     { CCI_REG8(0x30b2), 0x07 },     { CCI_REG8(0x30d5), 0x00 },
> > +     { CCI_REG8(0x30d6), 0x85 },     { CCI_REG8(0x30d7), 0x2a },
> > +     { CCI_REG8(0x30d8), 0x64 },     { CCI_REG8(0x30d9), 0x89 },
> > +     { CCI_REG8(0x30de), 0x00 },     { CCI_REG8(0x30df), 0x20 },
> > +     { CCI_REG8(0x3102), 0x10 },     { CCI_REG8(0x3103), 0x44 },
> > +     { CCI_REG8(0x3104), 0x40 },     { CCI_REG8(0x3105), 0x00 },
> > +     { CCI_REG8(0x3106), 0x0d },     { CCI_REG8(0x3107), 0x01 },
> > +     { CCI_REG8(0x3108), 0x09 },     { CCI_REG8(0x3109), 0x08 },
> > +     { CCI_REG8(0x310a), 0x0f },     { CCI_REG8(0x315c), 0x5d },
> > +     { CCI_REG8(0x315d), 0x5c },     { CCI_REG8(0x316e), 0x5e },
> > +     { CCI_REG8(0x316f), 0x5d },     { CCI_REG8(0x3318), 0x60 },
> > +     { CCI_REG8(0x3348), 0xe0 },
> > +};
> > +
> > +static const struct imx111_mode imx111_modes[] = {
> > +     [IMX111_MODE_3280x2464] = {
> > +             .width = 3280,
> > +             .height = 2464,
> > +             .reg_list = {
> > +                     .regs = mode_3280x2464,
> > +                     .num_of_regs = ARRAY_SIZE(mode_3280x2464),
> > +             },
> > +     },
> > +     [IMX111_MODE_3280x1848] = {
> > +             .width = 3280,
> > +             .height = 1848,
> > +             .reg_list = {
> > +                     .regs = mode_3280x1848,
> > +                     .num_of_regs = ARRAY_SIZE(mode_3280x1848),
> > +             },
> > +     },
> > +     [IMX111_MODE_3280x1098] = {
> > +             .width = 3280,
> > +             .height = 1098,
> > +             .reg_list = {
> > +                     .regs = mode_3280x1098,
> > +                     .num_of_regs = ARRAY_SIZE(mode_3280x1098),
> > +             },
> > +     },
> > +     [IMX111_MODE_2100x1200] = {
> > +             .width = 2100,
> > +             .height = 1200,
> > +             .reg_list = {
> > +                     .regs = mode_2100x1200,
> > +                     .num_of_regs = ARRAY_SIZE(mode_2100x1200),
> > +             },
> > +     },
> > +     [IMX111_MODE_1952x1098] = {
> > +             .width = 1952,
> > +             .height = 1098,
> > +             .reg_list = {
> > +                     .regs = mode_1952x1098,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1952x1098),
> > +             },
> > +     },
> > +     [IMX111_MODE_1920x1080] = {
> > +             .width = 1920,
> > +             .height = 1080,
> > +             .reg_list = {
> > +                     .regs = mode_1952x1098,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1952x1098),
> > +             },
> > +     },
> > +     [IMX111_MODE_1640x1232] = {
> > +             .width = 1640,
> > +             .height = 1232,
> > +             .reg_list = {
> > +                     .regs = mode_1640x1232,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1640x1232),
> > +             },
> > +     },
> > +     [IMX111_MODE_1440x1080] = {
> > +             .width = 1440,
> > +             .height = 1080,
> > +             .reg_list = {
> > +                     .regs = mode_1640x1232,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1640x1232),
> > +             },
> > +     },
> > +     [IMX111_MODE_1640x924] = {
> > +             .width = 1640,
> > +             .height = 924,
> > +             .reg_list = {
> > +                     .regs = mode_1640x924,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1640x924),
> > +             },
> > +     },
> > +     [IMX111_MODE_1308x736] = {
> > +             .width = 1308,
> > +             .height = 736,
> > +             .reg_list = {
> > +                     .regs = mode_1308x736,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1308x736),
> > +             },
> > +     },
> > +     [IMX111_MODE_1280x720] = {
> > +             .width = 1280,
> > +             .height = 720,
> > +             .reg_list = {
> > +                     .regs = mode_1308x736,
> > +                     .num_of_regs = ARRAY_SIZE(mode_1308x736),
> > +             },
> > +     },
> > +     [IMX111_MODE_820x614] = {
> > +             .width = 820,
> > +             .height = 614,
> > +             .reg_list = {
> > +                     .regs = mode_820x614,
> > +                     .num_of_regs = ARRAY_SIZE(mode_820x614),
> > +             },
> > +     },
> > +     [IMX111_MODE_640x480] = {
> > +             .width = 640,
> > +             .height = 480,
> > +             .reg_list = {
> > +                     .regs = mode_820x614,
> > +                     .num_of_regs = ARRAY_SIZE(mode_820x614),
> > +             },
> > +     },
> > +};
> > +
> > +static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd)
> > +{
> > +     return container_of(sd, struct imx111, sd);
>
> container_of_const(), please.
>
> > +}
> > +
> > +static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl)
> > +{
> > +     return container_of(ctrl->handler, struct imx111, hdl);
> > +}
> > +
> > +static u8 to_settle_delay(u64 extclk_rate)
> > +{
> > +     u64 extclk_mhz = div_u64(extclk_rate, MEGA);
> > +
> > +     return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63, 64);
> > +}
> > +
> > +static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool test)
> > +{
> > +     u32 i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(imx111_mbus_formats); i++)
> > +             if (imx111_mbus_formats[i] == code)
> > +                     break;
> > +
> > +     if (i >= ARRAY_SIZE(imx111_mbus_formats))
> > +             i = 0;
> > +
> > +     if (test)
> > +             return imx111_mbus_formats[i];
> > +
> > +     i = (i & ~3) | (sensor->vflip->val ? 2 : 0) |
> > +         (sensor->hflip->val ? 1 : 0);
> > +
> > +     return imx111_mbus_formats[i];
> > +}
> > +
> > +static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format)
> > +{
> > +     switch (format->code) {
> > +     case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +     case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +     case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +     case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +             return 8;
> > +
> > +     case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +     case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +     case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +     case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +     default:
> > +             return 10;
> > +     }
> > +}
> > +
> > +static int imx111_update_digital_gain(struct imx111 *sensor, u32 val)
> > +{
> > +     int ret = 0;
> > +
> > +     cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                     IMX111_GROUP_WRITE_ON, NULL);
>
> Missing error handling.
>
> > +
> > +     cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret);
> > +     cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret);
> > +     cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret);
> > +     cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret);
> > +
> > +     cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                     0, NULL);
>
> Ditto.
>
> > +
> > +     return ret;
> > +}
> > +
> > +static int imx111_set_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +     struct imx111 *sensor = ctrl_to_imx111(ctrl);
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     int ret = 0;
> > +
> > +     /*
> > +      * Applying V4L2 control value only happens
> > +      * when power is up for streaming
> > +      */
> > +     if (!pm_runtime_get_if_in_use(dev))
> > +             return 0;
> > +
> > +     switch (ctrl->id) {
> > +     case V4L2_CID_ANALOGUE_GAIN:
> > +             cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val, &ret);
> > +             break;
> > +     case V4L2_CID_DIGITAL_GAIN:
> > +             ret = imx111_update_digital_gain(sensor, ctrl->val);
> > +             break;
> > +     case V4L2_CID_EXPOSURE:
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             IMX111_GROUP_WRITE_ON, NULL);
> > +             cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val, NULL);
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             0, NULL);
> > +             break;
> > +     case V4L2_CID_HFLIP:
> > +     case V4L2_CID_VFLIP:
> > +             cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION,
> > +                       sensor->hflip->val | sensor->vflip->val << 1, &ret);
> > +             break;
> > +     case V4L2_CID_TEST_PATTERN:
> > +             cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val, &ret);
> > +             break;
> > +     case V4L2_CID_TEST_PATTERN_RED:
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             IMX111_GROUP_WRITE_ON, NULL);
> > +             cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val, &ret);
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             0, NULL);
> > +             break;
> > +     case V4L2_CID_TEST_PATTERN_GREENR:
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             IMX111_GROUP_WRITE_ON, NULL);
> > +             cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val, &ret);
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             0, NULL);
> > +             break;
> > +     case V4L2_CID_TEST_PATTERN_BLUE:
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             IMX111_GROUP_WRITE_ON, NULL);
> > +             cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val, &ret);
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             0, NULL);
> > +             break;
> > +     case V4L2_CID_TEST_PATTERN_GREENB:
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             IMX111_GROUP_WRITE_ON, NULL);
> > +             cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val, &ret);
> > +             cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                             0, NULL);
> > +             break;
> > +     default:
> > +             ret = -EINVAL;
> > +     }
> > +
> > +     pm_runtime_mark_last_busy(dev);
>
> You can drop this now (and elsewhere in driver code, too).
>
> > +     pm_runtime_put_autosuspend(dev);
> > +
> > +     return ret;
> > +}
> > +
> > +static const struct v4l2_ctrl_ops imx111_ctrl_ops = {
> > +     .s_ctrl = imx111_set_ctrl,
> > +};
> > +
> > +static const char * const test_pattern_menu[] = {
> > +     "Disabled",
> > +     "Solid Color Fill",
> > +     "Standard Color Bars",
> > +     "Fade To Grey Color Bars",
> > +     "Pseudorandom data",
> > +};
> > +
> > +static int imx111_init_controls(struct imx111 *sensor)
> > +{
> > +     const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     struct v4l2_fwnode_device_properties props;
> > +     struct v4l2_subdev *sd = &sensor->sd;
> > +     struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> > +     s64 pixel_rate_min, pixel_rate_max;
> > +     int i, ret;
> > +
> > +     ret = v4l2_fwnode_device_parse(dev, &props);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ret = v4l2_ctrl_handler_init(hdl, 13);
> > +     if (ret)
> > +             return ret;
> > +
> > +     pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
> > +     pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
> > +     sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
> > +                                            pixel_rate_min, pixel_rate_max,
> > +                                            1, div_u64(sensor->pixel_clk_raw,
> > +                                            2 * sensor->data_depth));
> > +
> > +     sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
> > +                                                0, 0, &sensor->default_link_freq);
> > +     if (sensor->link_freq)
> > +             sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > +
> > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> > +                       IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
> > +                       IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
> > +
> > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
> > +                       IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
> > +                       IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
> > +
> > +     sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> > +     if (sensor->hflip)
> > +             sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> > +
> > +     sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> > +     if (sensor->vflip)
> > +             sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
>
> Could you also add VBLANK and HBLANK controls, please?
>

I may try to, but since no datasheet is available, my suggestion may
be quite arbitrary. In this iteration v/hblank are considered to be 0.

> > +
> > +     /*
> > +      * The maximum coarse integration time is the frame length in lines
> > +      * minus five.
> > +      */
> > +     sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
> > +                                          IMX111_INTEGRATION_TIME_MIN,
> > +                                          IMX111_PIXEL_ARRAY_HEIGHT - 5,
> > +                                          IMX111_INTEGRATION_TIME_STEP,
> > +                                          IMX111_PIXEL_ARRAY_HEIGHT - 5);
> > +
> > +     v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
> > +
> > +     v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
> > +                                  ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
> > +                                  test_pattern_menu);
> > +     for (i = 0; i < 4; i++) {
> > +             /*
> > +              * The assumption is that
> > +              * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
> > +              * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
> > +              * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
> > +              */
> > +             v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i,
> > +                               IMX111_TESTP_COLOUR_MIN, IMX111_TESTP_COLOUR_MAX,
> > +                               IMX111_TESTP_COLOUR_STEP, IMX111_TESTP_COLOUR_MAX);
> > +             /* The "Solid color" pattern is white by default */
> > +     }
> > +
> > +     if (hdl->error)
> > +             return hdl->error;
> > +
> > +     sd->ctrl_handler = hdl;
> > +
> > +     return 0;
> > +};
> > +
> > +static int imx111_start_streaming(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     const struct imx111_mode *mode = sensor->cur_mode;
> > +     int ret;
> > +
> > +     /* Apply default values of current mode */
> > +     ret = cci_multi_reg_write(sensor->regmap, mode->reg_list.regs,
> > +                               mode->reg_list.num_of_regs, NULL);
> > +     if (ret < 0) {
> > +             dev_err(dev, "Failed to initialize the sensor\n");
> > +             return ret;
> > +     }
> > +
> > +     cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                     IMX111_GROUP_WRITE_ON, NULL);
>
> Please add error handling for group write register access, too. Errors
> aren't supposed to happen here anyway unless something is definitely wrong.
>
> > +     cci_write(sensor->regmap, IMX111_DATA_DEPTH,
> > +               sensor->data_depth | sensor->data_depth << 8, NULL);
> > +     cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, IMX111_GROUP_WRITE_ON,
> > +                     0, NULL);
> > +
> > +     ret = __v4l2_ctrl_handler_setup(&sensor->hdl);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STREAMING, NULL);
> > +     if (ret)
> > +             dev_err(dev, "failed to start stream");
> > +
> > +     /* vflip and hflip cannot change during streaming */
> > +     __v4l2_ctrl_grab(sensor->vflip, true);
> > +     __v4l2_ctrl_grab(sensor->hflip, true);
> > +
> > +     msleep(30);
>
> Why the msleep()?
>

Should I use mdelay? This delay originates from downstream camera
sequence without clear explanation. I assume it is needed for the
sensor to stabilize before streaming starts but that is only an
assumption.

> > +
> > +     return ret;
> > +}
> > +
> > +static void imx111_stop_streaming(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +
> > +     if (cci_write(sensor->regmap, IMX111_STREAMING_MODE, IMX111_MODE_STANDBY, NULL))
> > +             dev_err(dev, "failed to stop stream");
> > +
> > +     __v4l2_ctrl_grab(sensor->vflip, false);
> > +     __v4l2_ctrl_grab(sensor->hflip, false);
> > +}
> > +
> > +static int imx111_initialize(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     int ret;
> > +
> > +     imx111_stop_streaming(sensor);
> > +     msleep(30);
> > +
> > +     /* Configure the PLL. */
> > +     cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1,
> > +               sensor->pll->pre_div, &ret);
> > +     cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1, sensor->pll->mult, &ret);
> > +     cci_write(sensor->regmap, IMX111_POST_DIVIDER, IMX111_POST_DIVIDER_DIV1, &ret);
> > +     cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME,
> > +               to_settle_delay(sensor->pll->extclk_rate), &ret);
> > +
> > +     ret = cci_multi_reg_write(sensor->regmap, imx111_global_init,
> > +                               ARRAY_SIZE(imx111_global_init), NULL);
> > +     if (ret < 0) {
> > +             dev_err(dev, "Failed to initialize the sensor\n");
> > +             return ret;
> > +     }
> > +
> > +     return ret;
>
>         return 0;
>
> > +}
> > +
> > +static int imx111_set_stream(struct v4l2_subdev *sd, int enable)
>
> Could you use {en,dis}able_streams() pad ops instead?
>
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     struct v4l2_subdev_state *state;
> > +     int ret = 0;
> > +
> > +     state = v4l2_subdev_lock_and_get_active_state(sd);
> > +
> > +     if (enable) {
> > +             ret = pm_runtime_resume_and_get(dev);
> > +             if (ret)
> > +                     goto finish_unlock;
> > +
> > +             ret = imx111_start_streaming(sensor);
> > +             if (!ret)
> > +                     goto finish_unlock;
> > +
> > +             dev_err(dev, "Failed to start stream: %d\n", ret);
> > +             enable = 0;
> > +     }
> > +
> > +     imx111_stop_streaming(sensor);
> > +     pm_runtime_mark_last_busy(dev);
> > +     pm_runtime_put_autosuspend(dev);
> > +
> > +finish_unlock:
> > +     v4l2_subdev_unlock_state(state);
> > +
> > +     return ret;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * IMX111 Pad Subdev Init and Operations
> > + */
> > +static int imx111_enum_mbus_code(struct v4l2_subdev *sd,
> > +                              struct v4l2_subdev_state *sd_state,
> > +                              struct v4l2_subdev_mbus_code_enum *code)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +
> > +     if (code->index >= (ARRAY_SIZE(imx111_mbus_formats) / 4))
>
> Redundant parentheses.
>
> > +             return -EINVAL;
> > +
> > +     code->code = imx111_get_format_code(sensor, imx111_mbus_formats[code->index * 4], false);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_enum_frame_size(struct v4l2_subdev *sd,
> > +                               struct v4l2_subdev_state *sd_state,
> > +                               struct v4l2_subdev_frame_size_enum *fse)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     u32 code;
> > +
> > +     if (fse->index >= ARRAY_SIZE(imx111_modes))
> > +             return -EINVAL;
> > +
> > +     code = imx111_get_format_code(sensor, fse->code, true);
> > +     if (fse->code != code)
> > +             return -EINVAL;
> > +
> > +     fse->min_width = imx111_modes[fse->index].width;
> > +     fse->max_width = fse->min_width;
> > +     fse->min_height = imx111_modes[fse->index].height;
> > +     fse->max_height = fse->min_height;
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_enum_frame_interval(struct v4l2_subdev *sd,
> > +                                   struct v4l2_subdev_state *state,
> > +                                   struct v4l2_subdev_frame_interval_enum *fie)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     const struct imx111_mode *mode;
> > +     u32 framerate, code;
> > +
> > +     if (fie->index > 0)
> > +             return -EINVAL;
> > +
> > +     code = imx111_get_format_code(sensor, fie->code, true);
> > +     if (fie->code != code)
> > +             return -EINVAL;
> > +
> > +     mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> > +                                   width, height,
> > +                                   fie->width, fie->height);
> > +     if (fie->width > mode->width || fie->height > mode->height)
> > +             return -EINVAL;
> > +
> > +     framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> > +                         2 * sensor->data_depth);
> > +
> > +     fie->interval.numerator = 1;
> > +     fie->interval.denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_set_format(struct v4l2_subdev *sd,
> > +                          struct v4l2_subdev_state *state,
> > +                          struct v4l2_subdev_format *format)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
> > +     struct v4l2_mbus_framefmt *fmt;
> > +     struct v4l2_fract *interval;
> > +     const struct imx111_mode *mode;
> > +     u32 framerate;
> > +
> > +     mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> > +                                   width, height,
> > +                                   mbus_fmt->width, mbus_fmt->height);
> > +     framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> > +                         2 * sensor->data_depth);
> > +
> > +     fmt = v4l2_subdev_state_get_format(state, format->pad);
> > +
> > +     fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false);
> > +     fmt->width = mode->width;
> > +     fmt->height = mode->height;
> > +     fmt->colorspace = V4L2_COLORSPACE_RAW;
> > +
> > +     *mbus_fmt = *fmt;
> > +
> > +     interval = v4l2_subdev_state_get_interval(state, format->pad);
> > +     interval->numerator = 1;
> > +     interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
>
> Frame rate configuration should be done using VBLANK/HBLANK controls, not
> the SUBDEV_S_FRAME_INTERVAL IOCTL. Please see
> <URL:https://www.linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/drivers/camera-sensor.html#raw-camera-sensors>.
>
> > +
> > +     if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> > +             sensor->cur_mode = mode;
> > +             sensor->data_depth = imx111_get_format_bpp(fmt);
> > +             __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
> > +                                      div_u64(sensor->pixel_clk_raw, 2 * sensor->data_depth));
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_set_frame_interval(struct v4l2_subdev *sd,
> > +                                  struct v4l2_subdev_state *state,
> > +                                  struct v4l2_subdev_frame_interval *fi)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     const struct imx111_mode *mode;
> > +     const struct v4l2_mbus_framefmt *mbus_fmt;
> > +     struct v4l2_fract *interval;
> > +     u32 framerate;
> > +
> > +     mbus_fmt = v4l2_subdev_state_get_format(state, fi->pad);
> > +
> > +     mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
> > +                                   width, height,
> > +                                   mbus_fmt->width, mbus_fmt->height);
> > +     framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> > +                         2 * sensor->data_depth);
> > +
> > +     interval = v4l2_subdev_state_get_interval(state, fi->pad);
> > +
> > +     interval->numerator = 1;
> > +     interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> > +
> > +     fi->interval = *interval;
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_init_state(struct v4l2_subdev *sd,
> > +                          struct v4l2_subdev_state *sd_state)
> > +{
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     const struct imx111_mode *mode = sensor->cur_mode;
> > +     struct v4l2_mbus_framefmt *fmt;
> > +     struct v4l2_fract *interval;
> > +     u32 framerate;
> > +
> > +     fmt = v4l2_subdev_state_get_format(sd_state, 0);
> > +     interval = v4l2_subdev_state_get_interval(sd_state, 0);
> > +
> > +     fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10;
> > +     fmt->width = mode->width;
> > +     fmt->height = mode->height;
> > +     fmt->field = V4L2_FIELD_NONE;
> > +     fmt->colorspace = V4L2_COLORSPACE_RAW;
> > +     fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
> > +     fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> > +     fmt->xfer_func = V4L2_XFER_FUNC_NONE;
> > +
> > +     framerate = div_u64(sensor->pixel_clk_raw, mode->width * mode->height *
> > +                         2 * sensor->data_depth);
> > +
> > +     interval->numerator = 1;
> > +     interval->denominator = rounddown(framerate, IMX111_FRAME_RATE_STEP);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct v4l2_subdev_video_ops imx111_video_ops = {
> > +     .s_stream = imx111_set_stream,
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops imx111_pad_ops = {
> > +     .enum_mbus_code = imx111_enum_mbus_code,
> > +     .enum_frame_size = imx111_enum_frame_size,
> > +     .enum_frame_interval = imx111_enum_frame_interval,
> > +     .get_fmt = v4l2_subdev_get_fmt,
> > +     .set_fmt = imx111_set_format,
> > +     .get_frame_interval = v4l2_subdev_get_frame_interval,
> > +     .set_frame_interval = imx111_set_frame_interval,
> > +};
> > +
> > +static const struct v4l2_subdev_ops imx111_subdev_ops = {
> > +     .video = &imx111_video_ops,
> > +     .pad = &imx111_pad_ops,
> > +};
> > +
> > +static const struct media_entity_operations imx111_subdev_entity_ops = {
> > +     .link_validate = v4l2_subdev_link_validate,
> > +};
> > +
> > +static const struct v4l2_subdev_internal_ops imx111_internal_ops = {
> > +     .init_state = imx111_init_state,
> > +};
> > +
> > +static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *client)
> > +{
> > +     struct device *dev = &client->dev;
> > +     struct v4l2_subdev *sd = &sensor->sd;
> > +     struct media_pad *pad = &sensor->pad;
> > +     struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> > +     int ret;
> > +
> > +     /* Initialize the subdev. */
> > +     v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops);
> > +
> > +     sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +     sd->internal_ops = &imx111_internal_ops;
> > +
> > +     /* Initialize the media entity. */
> > +     sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > +     sd->entity.ops = &imx111_subdev_entity_ops;
> > +     pad->flags = MEDIA_PAD_FL_SOURCE;
> > +
> > +     ret = media_entity_pads_init(&sd->entity, 1, pad);
> > +     if (ret < 0) {
> > +             dev_err(dev, "failed to init entity pads: %d", ret);
> > +             return ret;
> > +     }
> > +
> > +     /* Initialize the control handler. */
> > +     ret = imx111_init_controls(sensor);
> > +     if (ret)
> > +             goto error;
> > +
> > +     return 0;
> > +error:
> > +     v4l2_ctrl_handler_free(hdl);
> > +     media_entity_cleanup(&sd->entity);
> > +     return ret;
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Power Management
> > + */
> > +
> > +static int imx111_power_on(struct imx111 *sensor)
> > +{
> > +     int ret;
> > +
> > +     if (sensor->reset)
> > +             gpiod_set_value(sensor->reset, 1);
> > +
> > +     ret = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
> > +                                 sensor->supplies);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     usleep_range(500, 600);
> > +
> > +     if (sensor->reset)
> > +             gpiod_set_value(sensor->reset, 0);
> > +
> > +     usleep_range(200, 250);
> > +
> > +     ret = clk_prepare_enable(sensor->extclk);
> > +     if (ret < 0)
> > +             goto error_regulator;
> > +
> > +     usleep_range(200, 250);
> > +
> > +     return 0;
> > +
> > +error_regulator:
> > +     regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
> > +     return ret;
> > +}
> > +
> > +static void imx111_power_off(struct imx111 *sensor)
> > +{
> > +     if (sensor->reset)
> > +             gpiod_set_value(sensor->reset, 1);
> > +     usleep_range(1000, 2000);
> > +
> > +     clk_disable_unprepare(sensor->extclk);
> > +     regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
> > +}
> > +
> > +static int __maybe_unused imx111_pm_runtime_resume(struct device *dev)
> > +{
> > +     struct v4l2_subdev *sd = dev_get_drvdata(dev);
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +     int ret;
> > +
> > +     ret = imx111_power_on(sensor);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = imx111_initialize(sensor);
> > +     if (ret) {
> > +             imx111_power_off(sensor);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev)
> > +{
> > +     struct v4l2_subdev *sd = dev_get_drvdata(dev);
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +
> > +     imx111_power_off(sensor);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct dev_pm_ops imx111_pm_ops = {
> > +     SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend,
> > +                        imx111_pm_runtime_resume, NULL)
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Probe & Remove
> > + */
> > +
> > +static int imx111_identify_module(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     u64 value, revision, manufacturer;
> > +     int ret;
> > +
> > +     ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (value != IMX111_CHIP_ID) {
> > +             dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID, value);
> > +             return -ENXIO;
> > +     }
> > +
> > +     cci_read(sensor->regmap, IMX111_REVISION, &revision, NULL);
> > +     cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, NULL);
> > +
> > +     dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n",
> > +             value, revision, manufacturer);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_clk_init(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes;
> > +     u64 extclk_rate, system_clk;
> > +     int i;
>
> unsigned int, please (and you could declare it for the loop only).
>
> > +
> > +     extclk_rate = clk_get_rate(sensor->extclk);
> > +     if (!extclk_rate)
> > +             return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n");
> > +
> > +     for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) {
> > +             if (clk_get_rate(sensor->extclk) == imx111_pll[i].extclk_rate) {
> > +                     sensor->pll = &imx111_pll[i];
> > +                     break;
> > +             }
> > +     }
> > +     if (!sensor->pll)
> > +             return dev_err_probe(dev, -EINVAL, "Unsupported EXTCLK rate %llu\n", extclk_rate);
> > +
> > +     system_clk = div_u64(extclk_rate, sensor->pll->pre_div) * sensor->pll->mult;
> > +
> > +     /*
> > +      * Pixel clock or Logic clock is used for internal image processing is
> > +      * generated by dividing into 1/10 or 1/8 frequency according to the
> > +      * word length of the CSI2 interface. This clock is designating the pixel
> > +      * rate and used as the base of integration time, frame rate etc.
> > +      */
>
> Can you run
>
>         /scripts/checkpatch.pl --strict --max-line-length=80
>
> on the patch, please?
>

Why? 80 char length is not enforced, up to 100 char per line is allowed.

> > +     sensor->pixel_clk_raw = system_clk * ndata_lanes;
> > +
> > +     /*
> > +      * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR over n lanes
> > +      * for RAW10 default format.
> > +      */
> > +     sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8,
> > +                                         ndata_lanes *
> > +                                         2 * IMX111_DATA_DEPTH_RAW10);
>
> This looks a bit odd; I'm not sure the bit depth should make a difference
> here. Why is pixel_clk_raw multiplied by 8?
>

8 pops as 16/2 16 bit in ddr mode and this results in modules PLL

from what I have gathered default_link_freq of this module is
calculated with dividing PLL (pixel_clk_raw * 8) by clock divisor.
This clock divisor is data depth * 2 and since default format is RAW10
it will be 10 * 2. ndata_lanes should not be in this calculation, it
is here mistakenly.

> > +
> > +     if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
> > +         sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq)
> > +             return dev_err_probe(dev, -EINVAL,
> > +                                  "Unsupported DT link-frequencies, expected %llu\n",
> > +                                  sensor->default_link_freq);
> > +
> > +     return 0;
> > +}
> > +
> > +static int imx111_parse_dt(struct imx111 *sensor)
> > +{
> > +     struct device *dev = regmap_get_device(sensor->regmap);
> > +     struct fwnode_handle *fwnode = dev_fwnode(dev);
> > +     struct fwnode_handle *ep;
> > +     int ret;
> > +
> > +     ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
> > +     if (!ep) {
> > +             dev_err(dev, "No endpoint found\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN;
>
> This should be V4L2_MBUS_CSI2_DPHY.
>
> > +     ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
> > +     fwnode_handle_put(ep);
> > +     if (ret < 0) {
> > +             dev_err(dev, "Failed to parse endpoint\n");
> > +             goto error;
> > +     }
> > +
> > +     switch (sensor->bus_cfg.bus_type) {
>
> And then you can omit this check.
>
> > +     case V4L2_MBUS_CSI2_DPHY:
> > +             break;
> > +
> > +     default:
> > +             dev_err(dev, "unsupported bus type %u\n", sensor->bus_cfg.bus_type);
> > +             ret = -EINVAL;
> > +             goto error;
> > +     }
> > +
> > +     /* Check the number of MIPI CSI2 data lanes */
> > +     if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) {
> > +             dev_err(dev, "number of lanes is more than 2\n");
> > +             ret = -EINVAL;
> > +             goto error;
> > +     }
> > +
> > +     return 0;
> > +
> > +error:
> > +     v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> > +     return ret;
> > +}
> > +
> > +static int imx111_probe(struct i2c_client *client)
> > +{
> > +     struct device *dev = &client->dev;
> > +     struct imx111 *sensor;
> > +     int ret;
> > +
> > +     sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
> > +     if (!sensor)
> > +             return -ENOMEM;
> > +
> > +     sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
> > +     if (IS_ERR(sensor->regmap))
> > +             return dev_err_probe(dev, PTR_ERR(sensor->regmap),
> > +                                  "Failed to allocate register map\n");
> > +
> > +     sensor->extclk = devm_clk_get(dev, NULL);
>
> Could you use devm_v4l2_sensor_clk_get() instead, please?
>
> > +     if (IS_ERR(sensor->extclk))
> > +             return dev_err_probe(dev, PTR_ERR(sensor->extclk), "Failed to get clock\n");
> > +
> > +     sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
> > +     if (IS_ERR(sensor->reset))
> > +             return dev_err_probe(dev, PTR_ERR(sensor->reset), "Failed to get reset GPIO\n");
> > +
> > +     sensor->supplies[0].supply = "iovdd";
> > +     sensor->supplies[1].supply = "dvdd";
> > +     sensor->supplies[2].supply = "avdd";
> > +
> > +     ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sensor->supplies), sensor->supplies);
> > +     if (ret < 0)
> > +             return dev_err_probe(dev, ret, "Failed to get regulators\n");
> > +
> > +     ret = imx111_parse_dt(sensor);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ret = imx111_clk_init(sensor);
> > +     if (ret < 0)
> > +             goto error_ep_free;
> > +
> > +     ret = imx111_power_on(sensor);
> > +     if (ret < 0) {
> > +             dev_err_probe(dev, ret, "Could not power on the device\n");
> > +             goto error_ep_free;
> > +     }
> > +
> > +     ret = imx111_identify_module(sensor);
> > +     if (ret < 0) {
> > +             dev_err_probe(dev, ret, "Could not identify module\n");
> > +             goto error_power_off;
> > +     }
> > +
> > +     sensor->cur_mode = &imx111_modes[IMX111_MODE_3280x2464];
> > +     sensor->data_depth = IMX111_DATA_DEPTH_RAW10;
> > +
> > +     ret = imx111_initialize(sensor);
> > +     if (ret < 0)
> > +             goto error_power_off;
> > +
> > +     ret = imx111_init_subdev(sensor, client);
> > +     if (ret < 0) {
> > +             dev_err(dev, "failed to init controls: %d", ret);
> > +             goto error_v4l2_ctrl_handler_free;
> > +     }
> > +
> > +     ret = v4l2_subdev_init_finalize(&sensor->sd);
> > +     if (ret)
> > +             goto error_v4l2_ctrl_handler_free;
> > +
> > +     /*
> > +      * Enable runtime PM with autosuspend. As the device has been powered
> > +      * manually, mark it as active, and increase the usage count without
> > +      * resuming the device.
> > +      */
> > +     pm_runtime_set_active(dev);
> > +     pm_runtime_get_noresume(dev);
> > +     pm_runtime_enable(dev);
> > +     pm_runtime_set_autosuspend_delay(dev, 1000);
> > +     pm_runtime_use_autosuspend(dev);
>
> Also disable autosuspend at driver unbind (or probe failure).
>

Is imx219 a proper example for pm configuration?

> > +
> > +     ret = v4l2_async_register_subdev_sensor(&sensor->sd);
> > +     if (ret < 0) {
> > +             dev_err(dev, "failed to register V4L2 subdev: %d", ret);
> > +             goto error_pm;
> > +     }
> > +
> > +     /*
> > +      * Decrease the PM usage count. The device will get suspended after the
> > +      * autosuspend delay, turning the power off.
> > +      */
> > +     pm_runtime_mark_last_busy(dev);
> > +     pm_runtime_put_autosuspend(dev);
>
>         pm_runtime_idle(dev);
>
> And you can drop pm_runtime_get_noresume() above (also error handling will
> be affected).
>
> > +
> > +     return 0;
> > +
> > +error_pm:
> > +     pm_runtime_disable(dev);
> > +     pm_runtime_put_noidle(dev);
> > +     v4l2_subdev_cleanup(&sensor->sd);
> > +
> > +error_v4l2_ctrl_handler_free:
> > +     v4l2_ctrl_handler_free(&sensor->hdl);
> > +     media_entity_cleanup(&sensor->sd.entity);
> > +
> > +error_power_off:
> > +     imx111_power_off(sensor);
> > +
> > +error_ep_free:
> > +     v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> > +
> > +     return ret;
> > +}
> > +
> > +static void imx111_remove(struct i2c_client *client)
> > +{
> > +     struct v4l2_subdev *sd = i2c_get_clientdata(client);
> > +     struct imx111 *sensor = sd_to_imx111(sd);
> > +
> > +     v4l2_async_unregister_subdev(&sensor->sd);
> > +     v4l2_subdev_cleanup(sd);
> > +     media_entity_cleanup(&sensor->sd.entity);
> > +     v4l2_ctrl_handler_free(&sensor->hdl);
> > +     v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
> > +
> > +     /*
> > +      * Disable runtime PM. In case runtime PM is disabled in the kernel,
> > +      * make sure to turn power off manually.
> > +      */
> > +     pm_runtime_disable(&client->dev);
> > +     if (!pm_runtime_status_suspended(&client->dev)) {
> > +             imx111_power_off(sensor);
> > +             pm_runtime_set_suspended(&client->dev);
> > +     }
> > +}
> > +
> > +static const struct i2c_device_id imx111_id[] = {
> > +     { "imx111" },
> > +     { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, imx111_id);
>
> Do you need the I涎 ID table still?
>

Other maintainers usually request these, if it is not needed, I will remove it.

> > +
> > +static const struct of_device_id imx111_of_match[] = {
> > +     { .compatible = "sony,imx111" },
> > +     { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, imx111_of_match);
> > +
> > +static struct i2c_driver imx111_i2c_driver = {
> > +     .driver = {
> > +             .name = "imx111",
> > +             .of_match_table = imx111_of_match,
> > +             .pm = &imx111_pm_ops,
> > +     },
> > +     .id_table = imx111_id,
> > +     .probe = imx111_probe,
> > +     .remove = imx111_remove,
> > +};
> > +module_i2c_driver(imx111_i2c_driver);
> > +
> > +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
> > +MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver");
> > +MODULE_LICENSE("GPL");
>
> --
> Kind regards,
>
> Sakari Ailus

Every other comment left without answer is acknowledged and adjustment
will be applied. Thank you!

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

* Re: [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-28 18:57     ` Svyatoslav Ryhel
@ 2025-10-29 15:19       ` Tarang Raval
  2025-10-29 15:28         ` Svyatoslav Ryhel
  0 siblings, 1 reply; 10+ messages in thread
From: Tarang Raval @ 2025-10-29 15:19 UTC (permalink / raw)
  To: Svyatoslav Ryhel, Sakari Ailus
  Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Hans Verkuil, Hans de Goede, Arnd Bergmann,
	Dongcheng Yan, André Apitzsch, Sylvain Petinot,
	Benjamin Mugnier, Heimir Thor Sverrisson,
	linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org

Hi Svyatoslav,

> > On Tue, Oct 28, 2025 at 11:22:00AM +0200, Svyatoslav Ryhel wrote:
> > > Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> > > camera sensor using the i2c bus for control and the csi-2 bus for data.
> > >
> > > The following features are supported:
> > > - manual exposure, digital and analog gain control support
> > > - pixel rate/link freq control support
> > > - supported resolution up to 3280x2464 for single shot capture
> > > - supported resolution up to 1920x1080 @ 30fps for video
> > > - supported bayer order output SGBRG10 and SGBRG8
> > >
> > > Camera module seems to be partially compatible with Nokia SMIA but it
> > > lacks a few registers required for clock calculations and has different
> > > vendor-specific per-mode configurations which makes it incompatible with
> > > existing CCS driver.
> > >
> > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>

...
 
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/i2c.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/media.h>
> > > +#include <linux/module.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/ratelimit.h>
> > > +#include <linux/regmap.h>
> > > +#include <linux/regulator/consumer.h>
> > > +#include <linux/slab.h>
> > > +#include <linux/string.h>
> > > +#include <linux/types.h>
> > > +#include <linux/videodev2.h>
> > > +#include <linux/units.h>
> > > +
> > > +#include <media/media-entity.h>
> > > +#include <media/v4l2-async.h>
> > > +#include <media/v4l2-cci.h>
> > > +#include <media/v4l2-ctrls.h>
> > > +#include <media/v4l2-device.h>
> > > +#include <media/v4l2-fwnode.h>
> > > +#include <media/v4l2-event.h>
> > > +#include <media/v4l2-image-sizes.h>
> > > +#include <media/v4l2-subdev.h>
> > > +#include <media/v4l2-mediabus.h>
 
A few of those headers seem to be unused and can be removed

Like:
 
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <media/v4l2-event.h>
#include <media/v4l2-image-sizes.h>

...

> > > +/* product information registers */
> > > +#define IMX111_PRODUCT_ID                    CCI_REG16(0x0000)
> > > +#define   IMX111_CHIP_ID                     0x111
> > > +#define IMX111_REVISION                              CCI_REG8(0x0002)
> > > +#define IMX111_MANUFACTURER_ID                       CCI_REG8(0x0003)
> > > +#define IMX111_SMIA_VER                              CCI_REG8(0x0004)
> > > +#define IMX111_FRAME_COUNTER                 CCI_REG8(0x0005)
> > > +#define IMX111_PIXEL_ORDER                   CCI_REG8(0x0006)
> > > +
> > > +/* general configuration registers */
> > > +#define IMX111_STREAMING_MODE                        CCI_REG8(0x0100)
> > > +#define   IMX111_MODE_STANDBY                        0
> > > +#define   IMX111_MODE_STREAMING                      1
> > > +#define IMX111_IMAGE_ORIENTATION             CCI_REG8(0x0101)
> > > +#define   IMX111_IMAGE_HFLIP                 BIT(0)
> > > +#define   IMX111_IMAGE_VFLIP                 BIT(1)
> > > +#define IMX111_SOFTWARE_RESET                        CCI_REG8(0x0103)
> > > +#define   IMX111_RESET_ON                    1
> > > +#define IMX111_GROUP_WRITE                   CCI_REG8(0x0104)
> > > +#define   IMX111_GROUP_WRITE_ON                      1
> > > +#define IMX111_FRAME_DROP                    CCI_REG8(0x0105)
> > > +#define   IMX111_FRAME_DROP_ON                       1
> > > +#define IMX111_CHANNEL_ID                    CCI_REG8(0x0110)
> > > +#define IMX111_SIGNALLING_MODE                       CCI_REG8(0x0111)
> > > +#define IMX111_DATA_DEPTH                    CCI_REG16(0x0112)
> > > +#define   IMX111_DATA_DEPTH_RAW8             0x08
> > > +#define   IMX111_DATA_DEPTH_RAW10            0x0a
> > > +
> > > +/* integration time registers */
> > > +#define IMX111_INTEGRATION_TIME                      CCI_REG16(0x0202)
> > > +#define IMX111_INTEGRATION_TIME_MIN          0x1
> > > +#define IMX111_INTEGRATION_TIME_MAX          0xffff
> > > +#define IMX111_INTEGRATION_TIME_STEP         1
> > > +
> > > +/* analog gain control */
> > > +#define IMX111_REG_ANALOG_GAIN                       CCI_REG8(0x0205)
> > > +#define IMX111_ANA_GAIN_MIN                  0
> > > +#define IMX111_ANA_GAIN_MAX                  240
> > > +#define IMX111_ANA_GAIN_STEP                 1
> > > +#define IMX111_ANA_GAIN_DEFAULT                      0
> > > +
> > > +/* digital gain control */
> > > +#define IMX111_REG_DIG_GAIN_GREENR           CCI_REG16(0x020e)
> > > +#define IMX111_REG_DIG_GAIN_RED                      CCI_REG16(0x0210)
> > > +#define IMX111_REG_DIG_GAIN_BLUE             CCI_REG16(0x0212)
> > > +#define IMX111_REG_DIG_GAIN_GREENB           CCI_REG16(0x0214)
> > > +#define IMX111_DGTL_GAIN_MIN                 0x0100
> > > +#define IMX111_DGTL_GAIN_MAX                 0x0fff
> > > +#define IMX111_DGTL_GAIN_DEFAULT             0x0100
> > > +#define IMX111_DGTL_GAIN_STEP                        1
> > > +
> > > +/* clock configuration registers */
> > > +#define IMX111_PIXEL_CLK_DIVIDER_PLL1                CCI_REG8(0x0301) /* fixed to 10 */
> > > +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1               CCI_REG8(0x0303) /* fixed to 1 */
> > > +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1              CCI_REG8(0x0305)
> > > +#define IMX111_PLL_MULTIPLIER_PLL1           CCI_REG8(0x0307)
> > > +#define IMX111_PLL_SETTLING_TIME             CCI_REG8(0x303c)
> > > +#define   IMX111_PLL_SETTLING_TIME_DEFAULT   200
> > > +#define IMX111_POST_DIVIDER                  CCI_REG8(0x30a4)
> > > +#define   IMX111_POST_DIVIDER_DIV1           2
> > > +#define   IMX111_POST_DIVIDER_DIV2           0
> > > +#define   IMX111_POST_DIVIDER_DIV4           1
> > > +
> > > +/* frame timing registers */
> > > +#define IMX111_VERTICAL_TOTAL_LENGTH         CCI_REG16(0x0340)
> > > +#define IMX111_HORIZONTAL_TOTAL_LENGTH               CCI_REG16(0x0342)
> > > +
> > > +/* image size registers */
> > > +#define IMX111_HORIZONTAL_START                      CCI_REG16(0x0344)
> > > +#define IMX111_VERTICAL_START                        CCI_REG16(0x0346)
> > > +#define IMX111_HORIZONTAL_END                        CCI_REG16(0x0348)
> > > +#define IMX111_VERTICAL_END                  CCI_REG16(0x034a)
> > > +#define IMX111_IMAGE_WIDTH                   CCI_REG16(0x034c)
> > > +#define IMX111_IMAGE_HEIGHT                  CCI_REG16(0x034e)

In the mode register settings, you can use the above macros.

> > > +static const struct cci_reg_sequence mode_820x614[] = {
> > > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xec },
> > > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x34 },
> > > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcb },
> > > +     { CCI_REG8(0x034c), 0x03 },     { CCI_REG8(0x034d), 0x34 },
> > > +     { CCI_REG8(0x034e), 0x02 },     { CCI_REG8(0x034f), 0x66 },
 
Here, you can use those macros.
 
Likewise, in every mode.
 
> > > +     { CCI_REG8(0x0381), 0x05 },     { CCI_REG8(0x0383), 0x03 },
> > > +     { CCI_REG8(0x0385), 0x05 },     { CCI_REG8(0x0387), 0x03 },
> > > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > > +     { CCI_REG8(0x309b), 0x28 },     { CCI_REG8(0x309c), 0x13 },
> > > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > > +     { CCI_REG8(0x30a1), 0x09 },     { CCI_REG8(0x30aa), 0x03 },
> > > +     { CCI_REG8(0x30b2), 0x03 },     { CCI_REG8(0x30d5), 0x09 },
> > > +     { CCI_REG8(0x30d6), 0x00 },     { CCI_REG8(0x30d7), 0x00 },
> > > +     { CCI_REG8(0x30d8), 0x00 },     { CCI_REG8(0x30d9), 0x00 },
> > > +     { CCI_REG8(0x30de), 0x04 },     { CCI_REG8(0x30df), 0x20 },
> > > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x7a },
> > > +     { CCI_REG8(0x3348), 0xe0 },
> > > +};

...

> > > +static int imx111_init_controls(struct imx111 *sensor)
> > > +{
> > > +     const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
> > > +     struct device *dev = regmap_get_device(sensor->regmap);
> > > +     struct v4l2_fwnode_device_properties props;
> > > +     struct v4l2_subdev *sd = &sensor->sd;
> > > +     struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> > > +     s64 pixel_rate_min, pixel_rate_max;
> > > +     int i, ret;
> > > +
> > > +     ret = v4l2_fwnode_device_parse(dev, &props);
> > > +     if (ret < 0)
> > > +             return ret;
> > > +
> > > +     ret = v4l2_ctrl_handler_init(hdl, 13);
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
> > > +     pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
> > > +     sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
> > > +                                            pixel_rate_min, pixel_rate_max,
> > > +                                            1, div_u64(sensor->pixel_clk_raw,
> > > +                                            2 * sensor->data_depth));
> > > +
> > > +     sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
> > > +                                                0, 0, &sensor->default_link_freq);
> > > +     if (sensor->link_freq)
> > > +             sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > > +
> > > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> > > +                       IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
> > > +                       IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
> > > +
> > > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
> > > +                       IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
> > > +                       IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
> > > +
> > > +     sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> > > +     if (sensor->hflip)
> > > +             sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> > > +
> > > +     sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> > > +     if (sensor->vflip)
> > > +             sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> >
> > Could you also add VBLANK and HBLANK controls, please?
> >
> 
> I may try to, but since no datasheet is available, my suggestion may
> be quite arbitrary. In this iteration v/hblank are considered to be 0.

The registers IMX111_VERTICAL_TOTAL_LENGTH and IMX111_HORIZONTAL_TOTAL_LENGTH
are used to set the vertical and horizontal total lengths in every mode. 
I believe you can use these registers to implement VBLANK and HBLANK.

You can find the appropriate values for these from the mode register settings.

Best Regards,
Tarang

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

* Re: [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-29 15:19       ` Tarang Raval
@ 2025-10-29 15:28         ` Svyatoslav Ryhel
  2025-10-30  6:52           ` Tarang Raval
  0 siblings, 1 reply; 10+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-29 15:28 UTC (permalink / raw)
  To: Tarang Raval
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Arnd Bergmann, Dongcheng Yan, André Apitzsch,
	Sylvain Petinot, Benjamin Mugnier, Heimir Thor Sverrisson,
	linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org

ср, 29 жовт. 2025 р. о 17:19 Tarang Raval <tarang.raval@siliconsignals.io> пише:
>
> Hi Svyatoslav,
>
> > > On Tue, Oct 28, 2025 at 11:22:00AM +0200, Svyatoslav Ryhel wrote:
> > > > Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> > > > camera sensor using the i2c bus for control and the csi-2 bus for data.
> > > >
> > > > The following features are supported:
> > > > - manual exposure, digital and analog gain control support
> > > > - pixel rate/link freq control support
> > > > - supported resolution up to 3280x2464 for single shot capture
> > > > - supported resolution up to 1920x1080 @ 30fps for video
> > > > - supported bayer order output SGBRG10 and SGBRG8
> > > >
> > > > Camera module seems to be partially compatible with Nokia SMIA but it
> > > > lacks a few registers required for clock calculations and has different
> > > > vendor-specific per-mode configurations which makes it incompatible with
> > > > existing CCS driver.
> > > >
> > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
>
> ...
>
> > > > +#include <linux/clk.h>
> > > > +#include <linux/delay.h>
> > > > +#include <linux/gpio/consumer.h>
> > > > +#include <linux/i2c.h>
> > > > +#include <linux/kernel.h>
> > > > +#include <linux/media.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/pm_runtime.h>
> > > > +#include <linux/ratelimit.h>
> > > > +#include <linux/regmap.h>
> > > > +#include <linux/regulator/consumer.h>
> > > > +#include <linux/slab.h>
> > > > +#include <linux/string.h>
> > > > +#include <linux/types.h>
> > > > +#include <linux/videodev2.h>
> > > > +#include <linux/units.h>
> > > > +
> > > > +#include <media/media-entity.h>
> > > > +#include <media/v4l2-async.h>
> > > > +#include <media/v4l2-cci.h>
> > > > +#include <media/v4l2-ctrls.h>
> > > > +#include <media/v4l2-device.h>
> > > > +#include <media/v4l2-fwnode.h>
> > > > +#include <media/v4l2-event.h>
> > > > +#include <media/v4l2-image-sizes.h>
> > > > +#include <media/v4l2-subdev.h>
> > > > +#include <media/v4l2-mediabus.h>
>
> A few of those headers seem to be unused and can be removed
>
> Like:
>
> #include <linux/ratelimit.h>
> #include <linux/slab.h>
> #include <linux/string.h>
> #include <media/v4l2-event.h>
> #include <media/v4l2-image-sizes.h>
>
> ...
>
> > > > +/* product information registers */
> > > > +#define IMX111_PRODUCT_ID                    CCI_REG16(0x0000)
> > > > +#define   IMX111_CHIP_ID                     0x111
> > > > +#define IMX111_REVISION                              CCI_REG8(0x0002)
> > > > +#define IMX111_MANUFACTURER_ID                       CCI_REG8(0x0003)
> > > > +#define IMX111_SMIA_VER                              CCI_REG8(0x0004)
> > > > +#define IMX111_FRAME_COUNTER                 CCI_REG8(0x0005)
> > > > +#define IMX111_PIXEL_ORDER                   CCI_REG8(0x0006)
> > > > +
> > > > +/* general configuration registers */
> > > > +#define IMX111_STREAMING_MODE                        CCI_REG8(0x0100)
> > > > +#define   IMX111_MODE_STANDBY                        0
> > > > +#define   IMX111_MODE_STREAMING                      1
> > > > +#define IMX111_IMAGE_ORIENTATION             CCI_REG8(0x0101)
> > > > +#define   IMX111_IMAGE_HFLIP                 BIT(0)
> > > > +#define   IMX111_IMAGE_VFLIP                 BIT(1)
> > > > +#define IMX111_SOFTWARE_RESET                        CCI_REG8(0x0103)
> > > > +#define   IMX111_RESET_ON                    1
> > > > +#define IMX111_GROUP_WRITE                   CCI_REG8(0x0104)
> > > > +#define   IMX111_GROUP_WRITE_ON                      1
> > > > +#define IMX111_FRAME_DROP                    CCI_REG8(0x0105)
> > > > +#define   IMX111_FRAME_DROP_ON                       1
> > > > +#define IMX111_CHANNEL_ID                    CCI_REG8(0x0110)
> > > > +#define IMX111_SIGNALLING_MODE                       CCI_REG8(0x0111)
> > > > +#define IMX111_DATA_DEPTH                    CCI_REG16(0x0112)
> > > > +#define   IMX111_DATA_DEPTH_RAW8             0x08
> > > > +#define   IMX111_DATA_DEPTH_RAW10            0x0a
> > > > +
> > > > +/* integration time registers */
> > > > +#define IMX111_INTEGRATION_TIME                      CCI_REG16(0x0202)
> > > > +#define IMX111_INTEGRATION_TIME_MIN          0x1
> > > > +#define IMX111_INTEGRATION_TIME_MAX          0xffff
> > > > +#define IMX111_INTEGRATION_TIME_STEP         1
> > > > +
> > > > +/* analog gain control */
> > > > +#define IMX111_REG_ANALOG_GAIN                       CCI_REG8(0x0205)
> > > > +#define IMX111_ANA_GAIN_MIN                  0
> > > > +#define IMX111_ANA_GAIN_MAX                  240
> > > > +#define IMX111_ANA_GAIN_STEP                 1
> > > > +#define IMX111_ANA_GAIN_DEFAULT                      0
> > > > +
> > > > +/* digital gain control */
> > > > +#define IMX111_REG_DIG_GAIN_GREENR           CCI_REG16(0x020e)
> > > > +#define IMX111_REG_DIG_GAIN_RED                      CCI_REG16(0x0210)
> > > > +#define IMX111_REG_DIG_GAIN_BLUE             CCI_REG16(0x0212)
> > > > +#define IMX111_REG_DIG_GAIN_GREENB           CCI_REG16(0x0214)
> > > > +#define IMX111_DGTL_GAIN_MIN                 0x0100
> > > > +#define IMX111_DGTL_GAIN_MAX                 0x0fff
> > > > +#define IMX111_DGTL_GAIN_DEFAULT             0x0100
> > > > +#define IMX111_DGTL_GAIN_STEP                        1
> > > > +
> > > > +/* clock configuration registers */
> > > > +#define IMX111_PIXEL_CLK_DIVIDER_PLL1                CCI_REG8(0x0301) /* fixed to 10 */
> > > > +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1               CCI_REG8(0x0303) /* fixed to 1 */
> > > > +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1              CCI_REG8(0x0305)
> > > > +#define IMX111_PLL_MULTIPLIER_PLL1           CCI_REG8(0x0307)
> > > > +#define IMX111_PLL_SETTLING_TIME             CCI_REG8(0x303c)
> > > > +#define   IMX111_PLL_SETTLING_TIME_DEFAULT   200
> > > > +#define IMX111_POST_DIVIDER                  CCI_REG8(0x30a4)
> > > > +#define   IMX111_POST_DIVIDER_DIV1           2
> > > > +#define   IMX111_POST_DIVIDER_DIV2           0
> > > > +#define   IMX111_POST_DIVIDER_DIV4           1
> > > > +
> > > > +/* frame timing registers */
> > > > +#define IMX111_VERTICAL_TOTAL_LENGTH         CCI_REG16(0x0340)
> > > > +#define IMX111_HORIZONTAL_TOTAL_LENGTH               CCI_REG16(0x0342)
> > > > +
> > > > +/* image size registers */
> > > > +#define IMX111_HORIZONTAL_START                      CCI_REG16(0x0344)
> > > > +#define IMX111_VERTICAL_START                        CCI_REG16(0x0346)
> > > > +#define IMX111_HORIZONTAL_END                        CCI_REG16(0x0348)
> > > > +#define IMX111_VERTICAL_END                  CCI_REG16(0x034a)
> > > > +#define IMX111_IMAGE_WIDTH                   CCI_REG16(0x034c)
> > > > +#define IMX111_IMAGE_HEIGHT                  CCI_REG16(0x034e)
>
> In the mode register settings, you can use the above macros.
>
> > > > +static const struct cci_reg_sequence mode_820x614[] = {
> > > > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xec },
> > > > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > > > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > > > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x34 },
> > > > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > > > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcb },
> > > > +     { CCI_REG8(0x034c), 0x03 },     { CCI_REG8(0x034d), 0x34 },
> > > > +     { CCI_REG8(0x034e), 0x02 },     { CCI_REG8(0x034f), 0x66 },
>
> Here, you can use those macros.
>
> Likewise, in every mode.
>

This is not as simple as a substitution, imx111 requires group write
in order to write 16bit values to registers. I will see what I can do
about this.

> > > > +     { CCI_REG8(0x0381), 0x05 },     { CCI_REG8(0x0383), 0x03 },
> > > > +     { CCI_REG8(0x0385), 0x05 },     { CCI_REG8(0x0387), 0x03 },
> > > > +     { CCI_REG8(0x3033), 0x00 },     { CCI_REG8(0x303d), 0x10 },
> > > > +     { CCI_REG8(0x303e), 0x40 },     { CCI_REG8(0x3040), 0x08 },
> > > > +     { CCI_REG8(0x3041), 0x97 },     { CCI_REG8(0x3048), 0x01 },
> > > > +     { CCI_REG8(0x304c), 0x6f },     { CCI_REG8(0x304d), 0x03 },
> > > > +     { CCI_REG8(0x3064), 0x12 },     { CCI_REG8(0x3073), 0x00 },
> > > > +     { CCI_REG8(0x3074), 0x11 },     { CCI_REG8(0x3075), 0x11 },
> > > > +     { CCI_REG8(0x3076), 0x11 },     { CCI_REG8(0x3077), 0x11 },
> > > > +     { CCI_REG8(0x3079), 0x00 },     { CCI_REG8(0x307a), 0x00 },
> > > > +     { CCI_REG8(0x309b), 0x28 },     { CCI_REG8(0x309c), 0x13 },
> > > > +     { CCI_REG8(0x309e), 0x00 },     { CCI_REG8(0x30a0), 0x14 },
> > > > +     { CCI_REG8(0x30a1), 0x09 },     { CCI_REG8(0x30aa), 0x03 },
> > > > +     { CCI_REG8(0x30b2), 0x03 },     { CCI_REG8(0x30d5), 0x09 },
> > > > +     { CCI_REG8(0x30d6), 0x00 },     { CCI_REG8(0x30d7), 0x00 },
> > > > +     { CCI_REG8(0x30d8), 0x00 },     { CCI_REG8(0x30d9), 0x00 },
> > > > +     { CCI_REG8(0x30de), 0x04 },     { CCI_REG8(0x30df), 0x20 },
> > > > +     { CCI_REG8(0x3102), 0x08 },     { CCI_REG8(0x3103), 0x22 },
> > > > +     { CCI_REG8(0x3104), 0x20 },     { CCI_REG8(0x3105), 0x00 },
> > > > +     { CCI_REG8(0x3106), 0x87 },     { CCI_REG8(0x3107), 0x00 },
> > > > +     { CCI_REG8(0x3108), 0x03 },     { CCI_REG8(0x3109), 0x02 },
> > > > +     { CCI_REG8(0x310a), 0x03 },     { CCI_REG8(0x315c), 0x9c },
> > > > +     { CCI_REG8(0x315d), 0x9b },     { CCI_REG8(0x316e), 0x9d },
> > > > +     { CCI_REG8(0x316f), 0x9c },     { CCI_REG8(0x3318), 0x7a },
> > > > +     { CCI_REG8(0x3348), 0xe0 },
> > > > +};
>
> ...
>
> > > > +static int imx111_init_controls(struct imx111 *sensor)
> > > > +{
> > > > +     const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
> > > > +     struct device *dev = regmap_get_device(sensor->regmap);
> > > > +     struct v4l2_fwnode_device_properties props;
> > > > +     struct v4l2_subdev *sd = &sensor->sd;
> > > > +     struct v4l2_ctrl_handler *hdl = &sensor->hdl;
> > > > +     s64 pixel_rate_min, pixel_rate_max;
> > > > +     int i, ret;
> > > > +
> > > > +     ret = v4l2_fwnode_device_parse(dev, &props);
> > > > +     if (ret < 0)
> > > > +             return ret;
> > > > +
> > > > +     ret = v4l2_ctrl_handler_init(hdl, 13);
> > > > +     if (ret)
> > > > +             return ret;
> > > > +
> > > > +     pixel_rate_min = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW10);
> > > > +     pixel_rate_max = div_u64(sensor->pixel_clk_raw, 2 * IMX111_DATA_DEPTH_RAW8);
> > > > +     sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
> > > > +                                            pixel_rate_min, pixel_rate_max,
> > > > +                                            1, div_u64(sensor->pixel_clk_raw,
> > > > +                                            2 * sensor->data_depth));
> > > > +
> > > > +     sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
> > > > +                                                0, 0, &sensor->default_link_freq);
> > > > +     if (sensor->link_freq)
> > > > +             sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> > > > +
> > > > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
> > > > +                       IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
> > > > +                       IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
> > > > +
> > > > +     v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
> > > > +                       IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
> > > > +                       IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
> > > > +
> > > > +     sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
> > > > +     if (sensor->hflip)
> > > > +             sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> > > > +
> > > > +     sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
> > > > +     if (sensor->vflip)
> > > > +             sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
> > >
> > > Could you also add VBLANK and HBLANK controls, please?
> > >
> >
> > I may try to, but since no datasheet is available, my suggestion may
> > be quite arbitrary. In this iteration v/hblank are considered to be 0.
>
> The registers IMX111_VERTICAL_TOTAL_LENGTH and IMX111_HORIZONTAL_TOTAL_LENGTH
> are used to set the vertical and horizontal total lengths in every mode.
> I believe you can use these registers to implement VBLANK and HBLANK.
>

This is what I am currently trying to implement

> You can find the appropriate values for these from the mode register settings.
>
> Best Regards,
> Tarang

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

* Re: [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver
  2025-10-29 15:28         ` Svyatoslav Ryhel
@ 2025-10-30  6:52           ` Tarang Raval
  0 siblings, 0 replies; 10+ messages in thread
From: Tarang Raval @ 2025-10-30  6:52 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Arnd Bergmann, Dongcheng Yan, André Apitzsch,
	Sylvain Petinot, Benjamin Mugnier, Heimir Thor Sverrisson,
	linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org

Hi Svyatoslav,

> > > > On Tue, Oct 28, 2025 at 11:22:00AM +0200, Svyatoslav Ryhel wrote:
> > > > > Add a v4l2 sub-device driver for the Sony IMX111 image sensor. This is a
> > > > > camera sensor using the i2c bus for control and the csi-2 bus for data.
> > > > >
> > > > > The following features are supported:
> > > > > - manual exposure, digital and analog gain control support
> > > > > - pixel rate/link freq control support
> > > > > - supported resolution up to 3280x2464 for single shot capture
> > > > > - supported resolution up to 1920x1080 @ 30fps for video
> > > > > - supported bayer order output SGBRG10 and SGBRG8
> > > > >
> > > > > Camera module seems to be partially compatible with Nokia SMIA but it
> > > > > lacks a few registers required for clock calculations and has different
> > > > > vendor-specific per-mode configurations which makes it incompatible with
> > > > > existing CCS driver.
> > > > >
> > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> >
> > ...
> >
> > > > > +#include <linux/clk.h>
> > > > > +#include <linux/delay.h>
> > > > > +#include <linux/gpio/consumer.h>
> > > > > +#include <linux/i2c.h>
> > > > > +#include <linux/kernel.h>
> > > > > +#include <linux/media.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/pm_runtime.h>
> > > > > +#include <linux/ratelimit.h>
> > > > > +#include <linux/regmap.h>
> > > > > +#include <linux/regulator/consumer.h>
> > > > > +#include <linux/slab.h>
> > > > > +#include <linux/string.h>
> > > > > +#include <linux/types.h>
> > > > > +#include <linux/videodev2.h>
> > > > > +#include <linux/units.h>
> > > > > +
> > > > > +#include <media/media-entity.h>
> > > > > +#include <media/v4l2-async.h>
> > > > > +#include <media/v4l2-cci.h>
> > > > > +#include <media/v4l2-ctrls.h>
> > > > > +#include <media/v4l2-device.h>
> > > > > +#include <media/v4l2-fwnode.h>
> > > > > +#include <media/v4l2-event.h>
> > > > > +#include <media/v4l2-image-sizes.h>
> > > > > +#include <media/v4l2-subdev.h>
> > > > > +#include <media/v4l2-mediabus.h>
> >
> > A few of those headers seem to be unused and can be removed
> >
> > Like:
> >
> > #include <linux/ratelimit.h>
> > #include <linux/slab.h>
> > #include <linux/string.h>
> > #include <media/v4l2-event.h>
> > #include <media/v4l2-image-sizes.h>
> >
> > ...
> >
> > > > > +/* product information registers */
> > > > > +#define IMX111_PRODUCT_ID                    CCI_REG16(0x0000)
> > > > > +#define   IMX111_CHIP_ID                     0x111
> > > > > +#define IMX111_REVISION                              CCI_REG8(0x0002)
> > > > > +#define IMX111_MANUFACTURER_ID                       CCI_REG8(0x0003)
> > > > > +#define IMX111_SMIA_VER                              CCI_REG8(0x0004)
> > > > > +#define IMX111_FRAME_COUNTER                 CCI_REG8(0x0005)
> > > > > +#define IMX111_PIXEL_ORDER                   CCI_REG8(0x0006)
> > > > > +
> > > > > +/* general configuration registers */
> > > > > +#define IMX111_STREAMING_MODE                        CCI_REG8(0x0100)
> > > > > +#define   IMX111_MODE_STANDBY                        0
> > > > > +#define   IMX111_MODE_STREAMING                      1
> > > > > +#define IMX111_IMAGE_ORIENTATION             CCI_REG8(0x0101)
> > > > > +#define   IMX111_IMAGE_HFLIP                 BIT(0)
> > > > > +#define   IMX111_IMAGE_VFLIP                 BIT(1)
> > > > > +#define IMX111_SOFTWARE_RESET                        CCI_REG8(0x0103)
> > > > > +#define   IMX111_RESET_ON                    1
> > > > > +#define IMX111_GROUP_WRITE                   CCI_REG8(0x0104)
> > > > > +#define   IMX111_GROUP_WRITE_ON                      1
> > > > > +#define IMX111_FRAME_DROP                    CCI_REG8(0x0105)
> > > > > +#define   IMX111_FRAME_DROP_ON                       1
> > > > > +#define IMX111_CHANNEL_ID                    CCI_REG8(0x0110)
> > > > > +#define IMX111_SIGNALLING_MODE                       CCI_REG8(0x0111)
> > > > > +#define IMX111_DATA_DEPTH                    CCI_REG16(0x0112)
> > > > > +#define   IMX111_DATA_DEPTH_RAW8             0x08
> > > > > +#define   IMX111_DATA_DEPTH_RAW10            0x0a
> > > > > +
> > > > > +/* integration time registers */
> > > > > +#define IMX111_INTEGRATION_TIME                      CCI_REG16(0x0202)
> > > > > +#define IMX111_INTEGRATION_TIME_MIN          0x1
> > > > > +#define IMX111_INTEGRATION_TIME_MAX          0xffff
> > > > > +#define IMX111_INTEGRATION_TIME_STEP         1
> > > > > +
> > > > > +/* analog gain control */
> > > > > +#define IMX111_REG_ANALOG_GAIN                       CCI_REG8(0x0205)
> > > > > +#define IMX111_ANA_GAIN_MIN                  0
> > > > > +#define IMX111_ANA_GAIN_MAX                  240
> > > > > +#define IMX111_ANA_GAIN_STEP                 1
> > > > > +#define IMX111_ANA_GAIN_DEFAULT                      0
> > > > > +
> > > > > +/* digital gain control */
> > > > > +#define IMX111_REG_DIG_GAIN_GREENR           CCI_REG16(0x020e)
> > > > > +#define IMX111_REG_DIG_GAIN_RED                      CCI_REG16(0x0210)
> > > > > +#define IMX111_REG_DIG_GAIN_BLUE             CCI_REG16(0x0212)
> > > > > +#define IMX111_REG_DIG_GAIN_GREENB           CCI_REG16(0x0214)
> > > > > +#define IMX111_DGTL_GAIN_MIN                 0x0100
> > > > > +#define IMX111_DGTL_GAIN_MAX                 0x0fff
> > > > > +#define IMX111_DGTL_GAIN_DEFAULT             0x0100
> > > > > +#define IMX111_DGTL_GAIN_STEP                        1
> > > > > +
> > > > > +/* clock configuration registers */
> > > > > +#define IMX111_PIXEL_CLK_DIVIDER_PLL1                CCI_REG8(0x0301) /* fixed to 10 */
> > > > > +#define IMX111_SYSTEM_CLK_DIVIDER_PLL1               CCI_REG8(0x0303) /* fixed to 1 */
> > > > > +#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1              CCI_REG8(0x0305)
> > > > > +#define IMX111_PLL_MULTIPLIER_PLL1           CCI_REG8(0x0307)
> > > > > +#define IMX111_PLL_SETTLING_TIME             CCI_REG8(0x303c)
> > > > > +#define   IMX111_PLL_SETTLING_TIME_DEFAULT   200
> > > > > +#define IMX111_POST_DIVIDER                  CCI_REG8(0x30a4)
> > > > > +#define   IMX111_POST_DIVIDER_DIV1           2
> > > > > +#define   IMX111_POST_DIVIDER_DIV2           0
> > > > > +#define   IMX111_POST_DIVIDER_DIV4           1
> > > > > +
> > > > > +/* frame timing registers */
> > > > > +#define IMX111_VERTICAL_TOTAL_LENGTH         CCI_REG16(0x0340)
> > > > > +#define IMX111_HORIZONTAL_TOTAL_LENGTH               CCI_REG16(0x0342)
> > > > > +
> > > > > +/* image size registers */
> > > > > +#define IMX111_HORIZONTAL_START                      CCI_REG16(0x0344)
> > > > > +#define IMX111_VERTICAL_START                        CCI_REG16(0x0346)
> > > > > +#define IMX111_HORIZONTAL_END                        CCI_REG16(0x0348)
> > > > > +#define IMX111_VERTICAL_END                  CCI_REG16(0x034a)
> > > > > +#define IMX111_IMAGE_WIDTH                   CCI_REG16(0x034c)
> > > > > +#define IMX111_IMAGE_HEIGHT                  CCI_REG16(0x034e)
> >
> > In the mode register settings, you can use the above macros.
> >
> > > > > +static const struct cci_reg_sequence mode_820x614[] = {
> > > > > +     { CCI_REG8(0x0340), 0x04 },     { CCI_REG8(0x0341), 0xec },
> > > > > +     { CCI_REG8(0x0342), 0x0d },     { CCI_REG8(0x0343), 0xd0 },
> > > > > +     { CCI_REG8(0x0344), 0x00 },     { CCI_REG8(0x0345), 0x08 },
> > > > > +     { CCI_REG8(0x0346), 0x00 },     { CCI_REG8(0x0347), 0x34 },
> > > > > +     { CCI_REG8(0x0348), 0x0c },     { CCI_REG8(0x0349), 0xd7 },
> > > > > +     { CCI_REG8(0x034a), 0x09 },     { CCI_REG8(0x034b), 0xcb },
> > > > > +     { CCI_REG8(0x034c), 0x03 },     { CCI_REG8(0x034d), 0x34 },
> > > > > +     { CCI_REG8(0x034e), 0x02 },     { CCI_REG8(0x034f), 0x66 },
> >
> > Here, you can use those macros.
> >
> > Likewise, in every mode.
> >
> 
> This is not as simple as a substitution, imx111 requires group write
> in order to write 16bit values to registers. I will see what I can do
> about this.

It is straightforward, even if group writes are present. 

static const struct cci_reg_sequence mode_820x614[] = {

	// group hold ON Register Write  

	{ IMX111_VERTICAL_TOTAL_LENGTH, 0x04ec },
	.
 	.
	.
	{ IMX111_IMAGE_HEIGHT,		0x0266 },

	// group hold OFF  Register write
};

However, in your register settings, I do not find any group hold ON or OFF
register before or after these image size–related registers. This appears 
to be a simple substitution.

If I have misunderstood something, please let me know.

One more suggestion: you can create a helper function for group writes
It is up to you, if you want, you can make this change.

static int imx111_group_write(struct imx111 *sensor, bool enable)
{
	int ret;
	
	if (enable)
		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, 
				IMX111_GROUP_WRITE_ON, IMX111_GROUP_WRITE_ON, 
				&ret); 
	else
		cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE, 
				IMX111_GROUP_WRITE_ON, 0, &ret);
	 
	return ret;
}

It removes the repeated cci_update_bits(... GROUP_WRITE ...) boilerplate.
	
Best Regards,
Tarang

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

end of thread, other threads:[~2025-10-30  6:52 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-28  9:21 [PATCH v2 0/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
2025-10-28  9:21 ` [PATCH v2 1/2 RESEND] dt-bindings: media: i2c: document Sony IMX111 CMOS sensor Svyatoslav Ryhel
2025-10-28 16:26   ` Sakari Ailus
2025-10-28 16:29     ` Svyatoslav Ryhel
2025-10-28  9:22 ` [PATCH v2 2/2 RESEND] media: i2c: add Sony IMX111 CMOS camera sensor driver Svyatoslav Ryhel
2025-10-28 17:58   ` Sakari Ailus
2025-10-28 18:57     ` Svyatoslav Ryhel
2025-10-29 15:19       ` Tarang Raval
2025-10-29 15:28         ` Svyatoslav Ryhel
2025-10-30  6:52           ` Tarang Raval

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