Linux Media Controller development
 help / color / mirror / Atom feed
* [PATCH v8 0/2] media: i2c: Add driver for Mira220
@ 2026-07-24 15:36 Jacopo Mondi
  2026-07-24 15:37 ` [PATCH v8 1/2] dt-bindings: media: i2c: Add mira220 image sensor Jacopo Mondi
  2026-07-24 15:37 ` [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220 Jacopo Mondi
  0 siblings, 2 replies; 7+ messages in thread
From: Jacopo Mondi @ 2026-07-24 15:36 UTC (permalink / raw)
  To: Philippe Baetens, Mauro Carvalho Chehab, Rob Herring,
	Sakari Ailus, Krzysztof Kozlowski, Conor Dooley
  Cc: Kieran Bingham, Jai Luthra, linux-media, devicetree, linux-kernel,
	Jacopo Mondi, Conor Dooley

Resume the work done by Philippe on Mira 220, available at
https://patchwork.linuxtv.org/project/linux-media/list/?series=18460

The detailed changelog is available on the single patches.

On the current set of supported features: mira220 doesn't support
binning/skipping but only cropping the full sensor array. It also
supports embedded data. It would be an ideal candidate for the new
RAW sensor model.

As the model is not available yet in mainline, this first version
doesn't support set_selection() and doesn't support embedded data.

These features will be implemented on top of the new RAW sensor model
once available.

Thanks
   j

Changes in v8:
- Address Jai's review comments; changelog is per-patch
- Link to v7: https://lore.kernel.org/r/20260722-mira220-v7-0-dd5f1de98cd4@ideasonboard.com

Changes in v7:
- Address Sashiko review comments; changelog is per-patch
- Link to v6: https://lore.kernel.org/r/20260722-mira220-v6-0-203e32c596de@ideasonboard.com

Changes in v6:
- Address Sashiko review comments; changelog is per-patch
- Link to v5: https://lore.kernel.org/r/20260721-mira220-v5-0-533b0ad1065e@ideasonboard.com

Just a note on hblank: prompted by sashiko review I reworked the hblank
calculation procedure to use the full line length (which will make it
easier to use the new Raw Sensor Model) and I've scaled the line lenght
according tot the number of data lanes, to match the row_length
settings, and I tested frame rate for 1 and 2 data lane operations.

I'll report the results here

1 data lane: hblank = 4480

    10fps -> vblank = 4915
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=4915
    2633.788743 (10.00 fps) cam0-stream0 seq: 000210 bytesused: 1920000
    2633.888732 (10.00 fps) cam0-stream0 seq: 000211 bytesused: 1920000
    2633.988712 (10.00 fps) cam0-stream0 seq: 000212 bytesused: 1920000
    2634.088696 (10.00 fps) cam0-stream0 seq: 000213 bytesused: 1920000

    15fps -> vblank = 2810
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=2810
    2690.081596 (15.00 fps) cam0-stream0 seq: 000833 bytesused: 1920000
    2690.148251 (15.00 fps) cam0-stream0 seq: 000834 bytesused: 1920000

    30fps -> blank = 705
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=705
    2732.142696 (29.98 fps) cam0-stream0 seq: 001736 bytesused: 1920000
    2732.176004 (30.02 fps) cam0-stream0 seq: 001737 bytesused: 1920000
    2732.209315 (30.02 fps) cam0-stream0 seq: 001738 bytesused: 1920000

    min_vblank = 14
    2823.028261 (46.07 fps) cam0-stream0 seq: 005180 bytesused: 1920000
    2823.051318 (43.37 fps) cam0-stream0 seq: 005181 bytesused: 1920000
    2823.073032 (46.05 fps) cam0-stream0 seq: 005182 bytesused: 1920000
    2823.096081 (43.39 fps) cam0-stream0 seq: 005183 bytesused: 1920000

2 data lanes: hblank = 1440

    10fps -> vblank = 11231
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=11231
    163.127449 (10.00 fps) cam0-stream0 seq: 000106 bytesused: 1920000
    163.227446 (10.00 fps) cam0-stream0 seq: 000107 bytesused: 1920000
    163.327425 (10.00 fps) cam0-stream0 seq: 000108 bytesused: 1920000

    15fps -> vblank = 7021
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=7021
    206.325595 (15.00 fps) cam0-stream0 seq: 000551 bytesused: 1920000
    206.392256 (15.00 fps) cam0-stream0 seq: 000552 bytesused: 1920000
    206.458943 (15.00 fps) cam0-stream0 seq: 000553 bytesused: 192000

    30fps -> vblank = 2810
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=2810
    244.891759 (30.00 fps) cam0-stream0 seq: 001153 bytesused: 1920000
    244.925094 (30.00 fps) cam0-stream0 seq: 001154 bytesused: 1920000
    244.958439 (29.99 fps) cam0-stream0 seq: 001155 bytesused: 1920000
    244.991746 (30.02 fps) cam0-stream0 seq: 001156 bytesused: 1920000

    60fps -> vblank = 733
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=733
    67.273857 (57.04 fps) cam0-stream0 seq: 007285 bytesused: 1920000
    367.290091 (61.60 fps) cam0-stream0 seq: 007286 bytesused: 1920000
    367.307637 (56.99 fps) cam0-stream0 seq: 007287 bytesused: 1920000
    367.323866 (61.62 fps) cam0-stream0 seq: 007288 bytesused: 1920000

    90 fps -> vblank = 22
    pi@raspberrypi:~$ v4l2-ctl -d /dev/v4l-subdev6 -c vertical_blanking=22
    326.909808 (83.83 fps) cam0-stream0 seq: 003739 bytesused: 1920000
    326.920403 (94.38 fps) cam0-stream0 seq: 003740 bytesused: 1920000
    326.931644 (88.96 fps) cam0-stream0 seq: 003741 bytesused: 1920000

-------------------------------------------------------------------------------
v4l2-compliance 1.33.0-5491, 64 bits, 64-bit time_t
v4l2-compliance SHA: b32589c51481 2026-07-16 08:51:38

Compliance test for device /dev/v4l-subdev6:

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

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

Debug ioctls:
	test VIDIOC_LOG_STATUS: OK (Not Supported)

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

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

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

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

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

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

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

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

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
Philippe Baetens (2):
      dt-bindings: media: i2c: Add mira220 image sensor
      media: i2c: Add driver for AMS-OSRAM Mira220

 .../devicetree/bindings/media/i2c/ams,mira220.yaml |  101 +
 MAINTAINERS                                        |    8 +
 drivers/media/i2c/Kconfig                          |   12 +
 drivers/media/i2c/Makefile                         |    1 +
 drivers/media/i2c/mira220.c                        | 2080 ++++++++++++++++++++
 5 files changed, 2202 insertions(+)
---
base-commit: a52e6f7923c17a672135b485ffd96fbd72f46267
change-id: 20260721-mira220-03070a173f7c

Best regards,
-- 
Jacopo Mondi <jacopo.mondi@ideasonboard.com>


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

* [PATCH v8 1/2] dt-bindings: media: i2c: Add mira220 image sensor
  2026-07-24 15:36 [PATCH v8 0/2] media: i2c: Add driver for Mira220 Jacopo Mondi
@ 2026-07-24 15:37 ` Jacopo Mondi
  2026-07-24 15:37 ` [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220 Jacopo Mondi
  1 sibling, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2026-07-24 15:37 UTC (permalink / raw)
  To: Philippe Baetens, Mauro Carvalho Chehab, Rob Herring,
	Sakari Ailus, Krzysztof Kozlowski, Conor Dooley
  Cc: Kieran Bingham, Jai Luthra, linux-media, devicetree, linux-kernel,
	Jacopo Mondi, Conor Dooley

From: Philippe Baetens <philippebaetens@gmail.com>

Add bindings for the AMSOSRAM Mira220 image sensor.

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

---
v7->v8:
- Add Conor's tag

v5->v6:
- sashiko: use 'unevaluatedProperties: false' at the top level to allow
  properties from video-interface-devices.yaml

v4->v5:
- reflow description to 80 cols and drop |-
- remove 'reg' description
- remove 'anyOf' from data-lanes
- remove 'link-frequencies' from required properties
- remove 'clock-continuous: true' as we already allow all properties
  from 'video-interfaces.yaml'
- remove tabs from the example
---
 .../devicetree/bindings/media/i2c/ams,mira220.yaml | 101 +++++++++++++++++++++
 MAINTAINERS                                        |   7 ++
 2 files changed, 108 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml b/Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml
new file mode 100644
index 000000000000..d113f7f81ffb
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ams,mira220.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AMS 2.2 MP NIR enhanced global shutter image sensor
+
+maintainers:
+  - Philippe Baetens <philippebaetens@gmail.com>
+
+description:
+  2.2 MP NIR enhanced global shutter image sensor designed for 2D and 3D
+  consumer and industrial machine vision applications. Due to its small size,
+  configurability and high sensitivity both in visual as well as NIR, the
+  Mira220 is well suited for 2D and 3D applications, which include Active Stereo
+  Vision, Structured Light Vision for Robotics and AR/VR.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+  compatible:
+    const: ams,mira220
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  vdig-supply:
+    description:
+      Digital I/O voltage supply, 1.35 volts
+
+  vana-supply:
+    description:
+      Analog1 voltage supply, 2.8 volts
+
+  vddl-supply:
+    description:
+      Digital core voltage supply, 1.8 volts
+
+  reset-gpios:
+    description: Sensor reset (XCLR) GPIO
+    maxItems: 1
+
+  port:
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+    description:
+      Video output port
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          data-lanes:
+            items:
+              - const: 1
+              - const: 2
+
+        required:
+          - data-lanes
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - vdig-supply
+  - vana-supply
+  - vddl-supply
+  - port
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        mira220: camera-sensor@54 {
+            compatible = "ams,mira220";
+            reg = <0x54>;
+            clocks = <&cam1_clk>;
+            vana-supply = <&cam1_reg>;
+            vdig-supply = <&cam_dummy_reg>;
+            vddl-supply = <&cam_dummy_reg>;
+            port {
+                mira220_ep: endpoint {
+                    clock-lanes = <0>;
+                    data-lanes = <1 2>;
+                    link-frequencies = /bits/ 64 <750000000>;
+                };
+            };
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index febe918d6316..55ac4ec433a6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1430,6 +1430,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/light/ams,as73211.yaml
 F:	drivers/iio/light/as73211.c
 
+AMS MIRA220 DRIVER
+M:	Philippe Baetens <philippebaetens@gmail.com>
+M:	Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml
+
 AMT (Automatic Multicast Tunneling)
 M:	Taehee Yoo <ap420073@gmail.com>
 L:	netdev@vger.kernel.org

-- 
2.54.0


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

* [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220
  2026-07-24 15:36 [PATCH v8 0/2] media: i2c: Add driver for Mira220 Jacopo Mondi
  2026-07-24 15:37 ` [PATCH v8 1/2] dt-bindings: media: i2c: Add mira220 image sensor Jacopo Mondi
@ 2026-07-24 15:37 ` Jacopo Mondi
  2026-07-24 18:57   ` Jai Luthra
  1 sibling, 1 reply; 7+ messages in thread
From: Jacopo Mondi @ 2026-07-24 15:37 UTC (permalink / raw)
  To: Philippe Baetens, Mauro Carvalho Chehab, Rob Herring,
	Sakari Ailus, Krzysztof Kozlowski, Conor Dooley
  Cc: Kieran Bingham, Jai Luthra, linux-media, devicetree, linux-kernel,
	Jacopo Mondi

From: Philippe Baetens <philippebaetens@gmail.com>

Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image
sensor.

Mira220 is a global shutter image sensor with a resolution of 1600x1400
pixels.

The driver implements support for mono and RGB 12, 10 and 8 bits
formats. The output data-rate per lane is 1500Mbit/s, with a maximum
frame rate up to 90 fps.

Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

---
v7->v8:
- Adjust KConfig entry to only depend on GPIOLIB and select CCI
- Register V4L2_CID_LINK_FREQ and validate the dts link-frequencies property
- Refuse the clock-continuous dts property
- Drop MIRA220_NUM_SUPPLIES and use ARRAY_SIZE()
- Rework flip index computation through a dedicated function
- Re-sort mira220_mbus_color_formats[] to avoid inverting hflip value
  when adjusting format to flips
- Address Jai's review comments
  - Re-indent macros with tabs
  - Use container_of_const
  - Use lowercase for hex values
  - Remove left-over mira220_mode *
  - Use HSTART_MASK when programming register
  - Use cci_write(..., &ret) to simplify register write sequences
  - Drop outdate comment
  - Do not * 1 sizes to initialize crop rectangle
  - Remove support for events
  - Use devm_v4l2_sensor_clk_get() to support ACPI systems

v6->v7:
- Address Sashiko review comments:
  - Return error code from mira220_power_on()
  - Use if (!pm_runtime_get_if_in_use(&client->dev)) in set_ctrl to
    support !PM
  - Power OTP off in case of errors
  - Use pm_ptr() when initializing the pm ops

v5->v6:
- Address Sashiko review comments:
  - Clamp max exposure not to overlow the 16 bits register
  - Update pad format on flip
  - Check otp_read() return value before accessing 'id'
  - Add v4l2_subdev_cleanup() to remove()
  - Do not access vblank in init_controls
  - Enable pm_runtime autosuspend last in probe
  - Scale hblank according to lane number to match row_length

v4->v5:
- Address checkpatch warnings and other minor style fixes (c++ comments
  80-cols etc)
- Restore MEDIA_BUS_FMT_Y12_1X12 format
- Run-time identify mono vs rgb variants
- Remove debug messages
- Remove delays not documented in datasheet
- Adjust min/max blankings to datasheet values
- Do not update limits of read-only controls with a single values on
  set_fmt
- Use pm_runtime autosuspend delay
- Remove fixed modes and dynamically program:
  - CSI-2 bus config (lanes number and clock mode)
  - frame windowing
  - image format and data-type
- Remove programming of secondary page for fast switiching (not used yet)
- Add support for reset_gpio in driver
- Use control's mutex for state locking
- Use format from subdev state and remove format from driver structure
- Rework OTP programming routines to shorten error handling
- Use enable/disable_streams() in place of set_stream()
- Add fwnode parsing for data lanes configuration
---
 MAINTAINERS                 |    1 +
 drivers/media/i2c/Kconfig   |   12 +
 drivers/media/i2c/Makefile  |    1 +
 drivers/media/i2c/mira220.c | 2080 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 2094 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 55ac4ec433a6..7c4f743ae026 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1436,6 +1436,7 @@ M:	Jacopo Mondi <jacopo.mondi@ideasonboard.com>
 L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/ams,mira220.yaml
+F:	drivers/media/i2c/mira220.c
 
 AMT (Automatic Multicast Tunneling)
 M:	Taehee Yoo <ap420073@gmail.com>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5c52007f9cbe..650d5c86baa4 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -313,6 +313,18 @@ config VIDEO_IMX678
 config VIDEO_MAX9271_LIB
 	tristate
 
+config VIDEO_MIRA220
+	tristate "ams MIRA220 sensor support"
+	depends on I2C && VIDEO_DEV
+	depends on GPIOLIB
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the ams
+	  MIRA220 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mira220.
+
 config VIDEO_MT9M001
 	tristate "mt9m001 support"
 	help
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d04bd5724552..6ecfe6c65af4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o
 obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
 obj-$(CONFIG_VIDEO_MAX96714) += max96714.o
 obj-$(CONFIG_VIDEO_MAX96717) += max96717.o
+obj-$(CONFIG_VIDEO_MIRA220)	+= mira220.o
 obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
 obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o
diff --git a/drivers/media/i2c/mira220.c b/drivers/media/i2c/mira220.c
new file mode 100644
index 000000000000..8b9148efc581
--- /dev/null
+++ b/drivers/media/i2c/mira220.c
@@ -0,0 +1,2080 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A V4L2 driver for ams MIRA220 cameras.
+ *
+ * Mira220 comes in monochrome, RGB and IR-RGB variants. The driver supports
+ * the mono and RGB variants.
+ *
+ * Copyright (C) 2026, ams-OSRAM
+ *
+ * Based on Sony IMX219 camera driver
+ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+/*
+ * Active pixel array is 1600 (H) x 1400 (V) pixels.
+ * Physical resolution including buffer pixels: 1642 (H) x 1464 (V) pixels.
+ */
+#define MIRA220_NATIVE_WIDTH		1642U
+#define MIRA220_NATIVE_HEIGHT		1464U
+#define MIRA220_PIXEL_ARRAY_LEFT	21U
+#define MIRA220_PIXEL_ARRAY_TOP		32U
+#define MIRA220_PIXEL_ARRAY_WIDTH	1600U
+#define MIRA220_PIXEL_ARRAY_HEIGHT	1400U
+
+/* Mira220 does not support analog gain. */
+#define MIRA220_ANALOG_GAIN_MIN		1
+#define MIRA220_ANALOG_GAIN_MAX		1
+#define MIRA220_ANALOG_GAIN_STEP	1
+#define MIRA220_ANALOG_GAIN_DEFAULT	MIRA220_ANALOG_GAIN_MIN
+
+/* Bit order */
+#define MIRA220_BIT_ORDER_REG		CCI_REG8(0x2063)
+#define MIRA220_BIT_ORDER_NORMAL	0x00
+#define MIRA220_BIT_ORDER_REVERSED	0x01
+
+/* Bit depth */
+#define MIRA220_BIT_DEPTH_REG		CCI_REG8(0x209e)
+#define MIRA220_BIT_DEPTH_12_BIT	0x02
+#define MIRA220_BIT_DEPTH_10_BIT	0x04
+#define MIRA220_BIT_DEPTH_8_BIT		0x06
+#define MIRA220_CSI_DATA_TYPE_REG	CCI_REG8(0x208d)
+#define MIRA220_CSI_DATA_TYPE_12_BIT	0x04
+#define MIRA220_CSI_DATA_TYPE_10_BIT	0x02
+#define MIRA220_CSI_DATA_TYPE_8_BIT	0x01
+
+/* Imager state master/slave registers */
+#define MIRA220_IMAGER_STATE_REG		CCI_REG8(0x1003)
+#define MIRA220_IMAGER_STATE_STOP_AT_ROW	0x02
+#define MIRA220_IMAGER_STATE_STOP_AT_FRAME	0x04
+#define MIRA220_IMAGER_STATE_MASTER_CONTROL	0x10
+#define MIRA220_IMAGER_STATE_SLAVE_CONTROL	0x08
+
+/* Start image acquisition */
+#define MIRA220_IMAGER_RUN_REG		CCI_REG8(0x10f0)
+#define MIRA220_IMAGER_RUN_START	0x01
+#define MIRA220_IMAGER_RUN_STOP		0x00
+
+/* Continuous running, not limited to nr of frames. */
+#define MIRA220_IMAGER_RUN_CONT_REG	CCI_REG8(0x1002)
+#define MIRA220_IMAGER_RUN_CONT_ENABLE	0x04
+#define MIRA220_IMAGER_RUN_CONT_DISABLE	0x00
+
+/* Exposure time is indicated in number of rows */
+#define MIRA220_EXP_TIME_REG		CCI_REG16_LE(0x100c)
+
+/* Vertical Blank */
+#define MIRA220_VBLANK_REG		CCI_REG16_LE(0x1012)
+#define MIRA220_MIN_VBLANK_MARGIN	11
+#define MIRA220_MAX_VBLANK		0xffff
+
+/* Horizontal flip */
+#define MIRA220_HFLIP_REG		CCI_REG8(0x209c)
+#define MIRA220_HFLIP_ENABLE_MIRROR	1
+#define MIRA220_HFLIP_DISABLE_MIRROR	0
+
+/* Vertical flip */
+#define MIRA220_VFLIP_REG		CCI_REG8(0x1095)
+#define MIRA220_VFLIP_ENABLE_FLIP	1
+#define MIRA220_VFLIP_DISABLE_FLIP	0
+
+#define MIRA220_VSIZE_REG		CCI_REG16_LE(0x1087)
+#define MIRA220_VSTART_REG		CCI_REG16_LE(0x107d)
+#define MIRA220_HSIZE_REG		CCI_REG16_LE(0x2008)
+#define MIRA220_HSIZE_MASK		GENMASK(9, 0)
+#define MIRA220_HSTART_REG		CCI_REG16_LE(0x200a)
+#define MIRA220_HSTART_MASK		GENMASK(9, 0)
+#define MIRA220_MIPI_HSIZE_REG		CCI_REG16_LE(0x207d)
+
+/* MIPI bus control */
+#define MIRA220_MIPI_LANES_REG		CCI_REG8(0x6012)
+
+#define MIRA220_MIPI_CLK_MODE_REG	CCI_REG8(0x6013)
+#define MIRA220_MIPI_CLK_CONTINUOUS	0x00
+#define MIRA220_MIPI_CLK_NON_CONTINUOUS	0x01
+
+/* OTP control */
+#define MIRA220_OTP_CMD_REG		CCI_REG8(0x0080)
+#define MIRA220_OTP_CMD_UP		0x4
+#define MIRA220_OTP_CMD_DOWN		0x8
+
+/* Global sampling time */
+#define MIRA220_GLOB_NUM_CLK_CYCLES	1928
+
+/* External clock frequency is 38.4 M */
+#define MIRA220_SUPPORTED_XCLK_FREQ	38400000
+
+/* Default exposure is adjusted to mode with smallest height */
+#define MIRA220_DEFAULT_EXPOSURE	1000
+#define MIRA220_MAX_EXPOSURE		0xffff
+#define MIRA220_EXPOSURE_MIN		1
+
+/*
+ * Pixel rate is an artificial value. This value is used for timing calculations
+ * in combination with vblank/hblank.
+ */
+#define MIRA220_PIXEL_RATE		384000000
+
+/* Total line length for 2 lanes calculated for ROW_LENGTH = 304. */
+#define MIRA220_LLP_1600x1400_304	3040
+
+/*
+ * ROW_LENGTH: duration of row length in clk_in cycles; set to minimum supported
+ * value for higher frame rate.
+ */
+#define MIRA220_REG_ROW_LENGTH		CCI_REG16_LE(0x102b)
+#define MIRA220_ROW_LENGTH_MIN		304
+
+/* MIPI CSI-2 link frequency for 1.5Mbps per lane */
+#define MIRA220_LINK_FREQ_750M		750000000
+
+/* Test Pattern */
+#define MIRA220_REG_TEST_PATTERN	CCI_REG8(0x2091)
+#define MIRA220_TEST_PATTERN_DISABLE	0x00
+#define MIRA220_TEST_PATTERN_VERTICAL_GRADIENT 0x01
+
+/* Module variant identifiers */
+#define MIRA220_MONO_PLAIN_PF		0x1e62a032
+#define MIRA220_MONO_PLAIN_NO_PF	0x1e62a045
+#define MIRA220_MONO_AR_PF		0x1e62a036
+#define MIRA220_RGB_PLAIN_PF		0x1e62a03b
+#define MIRA220_RGB_PLAIN_NO_PF		0x1e62a046
+#define MIRA220_RGB_AR_PF		0x1e62a03d
+
+enum mira220_variants {
+	MIRA220_VARIANT_MONO,
+	MIRA220_VARIANT_RGB,
+};
+
+static const s64 mira220_link_freqs[] = {
+	[0] = MIRA220_LINK_FREQ_750M,
+};
+
+static const struct cci_reg_sequence mira220_init_reg_list[] = {
+	/* Base configuration*/
+	{ CCI_REG8(0x6006), 0x0 },
+	{ CCI_REG8(0x6012), 0x1 },
+	{ CCI_REG8(0x6013), 0x0 },
+	{ CCI_REG8(0x6006), 0x1 },
+	{ CCI_REG8(0x205d), 0x0 },
+	{ CCI_REG8(0x2063), 0x0 },
+	{ CCI_REG8(0x24dc), 0x13 },
+	{ CCI_REG8(0x24dd), 0x3 },
+	{ CCI_REG8(0x24de), 0x3 },
+	{ CCI_REG8(0x24df), 0x0 },
+	{ CCI_REG8(0x4006), 0x8 },
+	{ CCI_REG8(0x401c), 0x6f },
+	{ CCI_REG8(0x204b), 0x3 },
+	{ CCI_REG8(0x205b), 0x64 },
+	{ CCI_REG8(0x205c), 0x0 },
+	{ CCI_REG8(0x4018), 0x3f },
+	{ CCI_REG8(0x403b), 0xb },
+	{ CCI_REG8(0x403e), 0xe },
+	{ CCI_REG8(0x402b), 0x6 },
+	{ CCI_REG8(0x401e), 0x2 },
+	{ CCI_REG8(0x4038), 0x3b },
+	{ CCI_REG8(0x1077), 0x0 },
+	{ CCI_REG8(0x1078), 0x0 },
+	{ CCI_REG8(0x1009), 0x8 },
+	{ CCI_REG8(0x100a), 0x0 },
+	{ CCI_REG8(0x110f), 0x8 },
+	{ CCI_REG8(0x1110), 0x0 },
+	{ CCI_REG8(0x1006), 0x2 },
+	{ CCI_REG8(0x402c), 0x64 },
+	{ CCI_REG8(0x3064), 0x0 },
+	{ CCI_REG8(0x3065), 0xf0 },
+	{ CCI_REG8(0x4013), 0x13 },
+	{ CCI_REG8(0x401f), 0x9 },
+	{ CCI_REG8(0x4020), 0x13 },
+	{ CCI_REG8(0x4044), 0x75 },
+	{ CCI_REG8(0x4027), 0x0 },
+	{ CCI_REG8(0x3215), 0x69 },
+	{ CCI_REG8(0x3216), 0xf },
+	{ CCI_REG8(0x322b), 0x69 },
+	{ CCI_REG8(0x322c), 0xf },
+	{ CCI_REG8(0x4051), 0x80 },
+	{ CCI_REG8(0x4052), 0x10 },
+	{ CCI_REG8(0x4057), 0x80 },
+	{ CCI_REG8(0x4058), 0x10 },
+	{ CCI_REG8(0x3212), 0x59 },
+	{ CCI_REG8(0x4047), 0x8f },
+	{ CCI_REG8(0x4026), 0x10 },
+	{ CCI_REG8(0x4032), 0x53 },
+	{ CCI_REG8(0x4036), 0x17 },
+	{ CCI_REG8(0x50b8), 0xf4 },
+	{ CCI_REG8(0x3016), 0x0 },
+	{ CCI_REG8(0x3017), 0x2c },
+	{ CCI_REG8(0x3018), 0x8c },
+	{ CCI_REG8(0x3019), 0x45 },
+	{ CCI_REG8(0x301a), 0x5 },
+	{ CCI_REG8(0x3013), 0xa },
+	{ CCI_REG8(0x301b), 0x0 },
+	{ CCI_REG8(0x301c), 0x4 },
+	{ CCI_REG8(0x301d), 0x88 },
+	{ CCI_REG8(0x301e), 0x45 },
+	{ CCI_REG8(0x301f), 0x5 },
+	{ CCI_REG8(0x3020), 0x0 },
+	{ CCI_REG8(0x3021), 0x4 },
+	{ CCI_REG8(0x3022), 0x88 },
+	{ CCI_REG8(0x3023), 0x45 },
+	{ CCI_REG8(0x3024), 0x5 },
+	{ CCI_REG8(0x3025), 0x0 },
+	{ CCI_REG8(0x3026), 0x4 },
+	{ CCI_REG8(0x3027), 0x88 },
+	{ CCI_REG8(0x3028), 0x45 },
+	{ CCI_REG8(0x3029), 0x5 },
+	{ CCI_REG8(0x302f), 0x0 },
+	{ CCI_REG8(0x3056), 0x0 },
+	{ CCI_REG8(0x3057), 0x0 },
+	{ CCI_REG8(0x3300), 0x1 },
+	{ CCI_REG8(0x3301), 0x0 },
+	{ CCI_REG8(0x3302), 0xb0 },
+	{ CCI_REG8(0x3303), 0xb0 },
+	{ CCI_REG8(0x3304), 0x16 },
+	{ CCI_REG8(0x3305), 0x15 },
+	{ CCI_REG8(0x3306), 0x1 },
+	{ CCI_REG8(0x3307), 0x0 },
+	{ CCI_REG8(0x3308), 0x30 },
+	{ CCI_REG8(0x3309), 0xa0 },
+	{ CCI_REG8(0x330a), 0x16 },
+	{ CCI_REG8(0x330b), 0x15 },
+	{ CCI_REG8(0x330c), 0x1 },
+	{ CCI_REG8(0x330d), 0x0 },
+	{ CCI_REG8(0x330e), 0x30 },
+	{ CCI_REG8(0x330f), 0xa0 },
+	{ CCI_REG8(0x3310), 0x16 },
+	{ CCI_REG8(0x3311), 0x15 },
+	{ CCI_REG8(0x3312), 0x1 },
+	{ CCI_REG8(0x3313), 0x0 },
+	{ CCI_REG8(0x3314), 0x30 },
+	{ CCI_REG8(0x3315), 0xa0 },
+	{ CCI_REG8(0x3316), 0x16 },
+	{ CCI_REG8(0x3317), 0x15 },
+	{ CCI_REG8(0x3318), 0x1 },
+	{ CCI_REG8(0x3319), 0x0 },
+	{ CCI_REG8(0x331a), 0x30 },
+	{ CCI_REG8(0x331b), 0xa0 },
+	{ CCI_REG8(0x331c), 0x16 },
+	{ CCI_REG8(0x331d), 0x15 },
+	{ CCI_REG8(0x331e), 0x1 },
+	{ CCI_REG8(0x331f), 0x0 },
+	{ CCI_REG8(0x3320), 0x30 },
+	{ CCI_REG8(0x3321), 0xa0 },
+	{ CCI_REG8(0x3322), 0x16 },
+	{ CCI_REG8(0x3323), 0x15 },
+	{ CCI_REG8(0x3324), 0x1 },
+	{ CCI_REG8(0x3325), 0x0 },
+	{ CCI_REG8(0x3326), 0x30 },
+	{ CCI_REG8(0x3327), 0xa0 },
+	{ CCI_REG8(0x3328), 0x16 },
+	{ CCI_REG8(0x3329), 0x15 },
+	{ CCI_REG8(0x332a), 0x2b },
+	{ CCI_REG8(0x332b), 0x0 },
+	{ CCI_REG8(0x332c), 0x30 },
+	{ CCI_REG8(0x332d), 0xa0 },
+	{ CCI_REG8(0x332e), 0x16 },
+	{ CCI_REG8(0x332f), 0x15 },
+	{ CCI_REG8(0x3330), 0x1 },
+	{ CCI_REG8(0x3331), 0x0 },
+	{ CCI_REG8(0x3332), 0x10 },
+	{ CCI_REG8(0x3333), 0xa0 },
+	{ CCI_REG8(0x3334), 0x16 },
+	{ CCI_REG8(0x3335), 0x15 },
+	{ CCI_REG8(0x3058), 0x8 },
+	{ CCI_REG8(0x3059), 0x0 },
+	{ CCI_REG8(0x305a), 0x9 },
+	{ CCI_REG8(0x305b), 0x0 },
+	{ CCI_REG8(0x3336), 0x1 },
+	{ CCI_REG8(0x3337), 0x0 },
+	{ CCI_REG8(0x3338), 0x90 },
+	{ CCI_REG8(0x3339), 0xb0 },
+	{ CCI_REG8(0x333a), 0x16 },
+	{ CCI_REG8(0x333b), 0x15 },
+	{ CCI_REG8(0x333c), 0x1f },
+	{ CCI_REG8(0x333d), 0x0 },
+	{ CCI_REG8(0x333e), 0x10 },
+	{ CCI_REG8(0x333f), 0xa0 },
+	{ CCI_REG8(0x3340), 0x16 },
+	{ CCI_REG8(0x3341), 0x15 },
+	{ CCI_REG8(0x3342), 0x52 },
+	{ CCI_REG8(0x3343), 0x0 },
+	{ CCI_REG8(0x3344), 0x10 },
+	{ CCI_REG8(0x3345), 0x80 },
+	{ CCI_REG8(0x3346), 0x16 },
+	{ CCI_REG8(0x3347), 0x15 },
+	{ CCI_REG8(0x3348), 0x1 },
+	{ CCI_REG8(0x3349), 0x0 },
+	{ CCI_REG8(0x334a), 0x10 },
+	{ CCI_REG8(0x334b), 0x80 },
+	{ CCI_REG8(0x334c), 0x16 },
+	{ CCI_REG8(0x334d), 0x1d },
+	{ CCI_REG8(0x334e), 0x1 },
+	{ CCI_REG8(0x334f), 0x0 },
+	{ CCI_REG8(0x3350), 0x50 },
+	{ CCI_REG8(0x3351), 0x84 },
+	{ CCI_REG8(0x3352), 0x16 },
+	{ CCI_REG8(0x3353), 0x1d },
+	{ CCI_REG8(0x3354), 0x18 },
+	{ CCI_REG8(0x3355), 0x0 },
+	{ CCI_REG8(0x3356), 0x10 },
+	{ CCI_REG8(0x3357), 0x84 },
+	{ CCI_REG8(0x3358), 0x16 },
+	{ CCI_REG8(0x3359), 0x1d },
+	{ CCI_REG8(0x335a), 0x80 },
+	{ CCI_REG8(0x335b), 0x2 },
+	{ CCI_REG8(0x335c), 0x10 },
+	{ CCI_REG8(0x335d), 0xc4 },
+	{ CCI_REG8(0x335e), 0x14 },
+	{ CCI_REG8(0x335f), 0x1d },
+	{ CCI_REG8(0x3360), 0xa5 },
+	{ CCI_REG8(0x3361), 0x0 },
+	{ CCI_REG8(0x3362), 0x10 },
+	{ CCI_REG8(0x3363), 0x84 },
+	{ CCI_REG8(0x3364), 0x16 },
+	{ CCI_REG8(0x3365), 0x1d },
+	{ CCI_REG8(0x3366), 0x1 },
+	{ CCI_REG8(0x3367), 0x0 },
+	{ CCI_REG8(0x3368), 0x90 },
+	{ CCI_REG8(0x3369), 0x84 },
+	{ CCI_REG8(0x336a), 0x16 },
+	{ CCI_REG8(0x336b), 0x1d },
+	{ CCI_REG8(0x336c), 0x12 },
+	{ CCI_REG8(0x336d), 0x0 },
+	{ CCI_REG8(0x336e), 0x10 },
+	{ CCI_REG8(0x336f), 0x84 },
+	{ CCI_REG8(0x3370), 0x16 },
+	{ CCI_REG8(0x3371), 0x15 },
+	{ CCI_REG8(0x3372), 0x32 },
+	{ CCI_REG8(0x3373), 0x0 },
+	{ CCI_REG8(0x3374), 0x30 },
+	{ CCI_REG8(0x3375), 0x84 },
+	{ CCI_REG8(0x3376), 0x16 },
+	{ CCI_REG8(0x3377), 0x15 },
+	{ CCI_REG8(0x3378), 0x26 },
+	{ CCI_REG8(0x3379), 0x0 },
+	{ CCI_REG8(0x337a), 0x10 },
+	{ CCI_REG8(0x337b), 0x84 },
+	{ CCI_REG8(0x337c), 0x16 },
+	{ CCI_REG8(0x337d), 0x15 },
+	{ CCI_REG8(0x337e), 0x80 },
+	{ CCI_REG8(0x337f), 0x2 },
+	{ CCI_REG8(0x3380), 0x10 },
+	{ CCI_REG8(0x3381), 0xc4 },
+	{ CCI_REG8(0x3382), 0x14 },
+	{ CCI_REG8(0x3383), 0x15 },
+	{ CCI_REG8(0x3384), 0xa9 },
+	{ CCI_REG8(0x3385), 0x0 },
+	{ CCI_REG8(0x3386), 0x10 },
+	{ CCI_REG8(0x3387), 0x84 },
+	{ CCI_REG8(0x3388), 0x16 },
+	{ CCI_REG8(0x3389), 0x15 },
+	{ CCI_REG8(0x338a), 0x41 },
+	{ CCI_REG8(0x338b), 0x0 },
+	{ CCI_REG8(0x338c), 0x10 },
+	{ CCI_REG8(0x338d), 0x80 },
+	{ CCI_REG8(0x338e), 0x16 },
+	{ CCI_REG8(0x338f), 0x15 },
+	{ CCI_REG8(0x3390), 0x2 },
+	{ CCI_REG8(0x3391), 0x0 },
+	{ CCI_REG8(0x3392), 0x10 },
+	{ CCI_REG8(0x3393), 0xa0 },
+	{ CCI_REG8(0x3394), 0x16 },
+	{ CCI_REG8(0x3395), 0x15 },
+	{ CCI_REG8(0x305c), 0x18 },
+	{ CCI_REG8(0x305d), 0x0 },
+	{ CCI_REG8(0x305e), 0x19 },
+	{ CCI_REG8(0x305f), 0x0 },
+	{ CCI_REG8(0x3396), 0x1 },
+	{ CCI_REG8(0x3397), 0x0 },
+	{ CCI_REG8(0x3398), 0x90 },
+	{ CCI_REG8(0x3399), 0x30 },
+	{ CCI_REG8(0x339a), 0x56 },
+	{ CCI_REG8(0x339b), 0x57 },
+	{ CCI_REG8(0x339c), 0x1 },
+	{ CCI_REG8(0x339d), 0x0 },
+	{ CCI_REG8(0x339e), 0x10 },
+	{ CCI_REG8(0x339f), 0x20 },
+	{ CCI_REG8(0x33a0), 0xd6 },
+	{ CCI_REG8(0x33a1), 0x17 },
+	{ CCI_REG8(0x33a2), 0x1 },
+	{ CCI_REG8(0x33a3), 0x0 },
+	{ CCI_REG8(0x33a4), 0x10 },
+	{ CCI_REG8(0x33a5), 0x28 },
+	{ CCI_REG8(0x33a6), 0xd6 },
+	{ CCI_REG8(0x33a7), 0x17 },
+	{ CCI_REG8(0x33a8), 0x3 },
+	{ CCI_REG8(0x33a9), 0x0 },
+	{ CCI_REG8(0x33aa), 0x10 },
+	{ CCI_REG8(0x33ab), 0x20 },
+	{ CCI_REG8(0x33ac), 0xd6 },
+	{ CCI_REG8(0x33ad), 0x17 },
+	{ CCI_REG8(0x33ae), 0x61 },
+	{ CCI_REG8(0x33af), 0x0 },
+	{ CCI_REG8(0x33b0), 0x10 },
+	{ CCI_REG8(0x33b1), 0x20 },
+	{ CCI_REG8(0x33b2), 0xd6 },
+	{ CCI_REG8(0x33b3), 0x15 },
+	{ CCI_REG8(0x33b4), 0x1 },
+	{ CCI_REG8(0x33b5), 0x0 },
+	{ CCI_REG8(0x33b6), 0x10 },
+	{ CCI_REG8(0x33b7), 0x20 },
+	{ CCI_REG8(0x33b8), 0xd6 },
+	{ CCI_REG8(0x33b9), 0x1d },
+	{ CCI_REG8(0x33ba), 0x1 },
+	{ CCI_REG8(0x33bb), 0x0 },
+	{ CCI_REG8(0x33bc), 0x50 },
+	{ CCI_REG8(0x33bd), 0x20 },
+	{ CCI_REG8(0x33be), 0xd6 },
+	{ CCI_REG8(0x33bf), 0x1d },
+	{ CCI_REG8(0x33c0), 0x2c },
+	{ CCI_REG8(0x33c1), 0x0 },
+	{ CCI_REG8(0x33c2), 0x10 },
+	{ CCI_REG8(0x33c3), 0x20 },
+	{ CCI_REG8(0x33c4), 0xd6 },
+	{ CCI_REG8(0x33c5), 0x1d },
+	{ CCI_REG8(0x33c6), 0x1 },
+	{ CCI_REG8(0x33c7), 0x0 },
+	{ CCI_REG8(0x33c8), 0x90 },
+	{ CCI_REG8(0x33c9), 0x20 },
+	{ CCI_REG8(0x33ca), 0xd6 },
+	{ CCI_REG8(0x33cb), 0x1d },
+	{ CCI_REG8(0x33cc), 0x83 },
+	{ CCI_REG8(0x33cd), 0x0 },
+	{ CCI_REG8(0x33ce), 0x10 },
+	{ CCI_REG8(0x33cf), 0x20 },
+	{ CCI_REG8(0x33d0), 0xd6 },
+	{ CCI_REG8(0x33d1), 0x15 },
+	{ CCI_REG8(0x33d2), 0x1 },
+	{ CCI_REG8(0x33d3), 0x0 },
+	{ CCI_REG8(0x33d4), 0x10 },
+	{ CCI_REG8(0x33d5), 0x30 },
+	{ CCI_REG8(0x33d6), 0xd6 },
+	{ CCI_REG8(0x33d7), 0x15 },
+	{ CCI_REG8(0x33d8), 0x1 },
+	{ CCI_REG8(0x33d9), 0x0 },
+	{ CCI_REG8(0x33da), 0x10 },
+	{ CCI_REG8(0x33db), 0x20 },
+	{ CCI_REG8(0x33dc), 0xd6 },
+	{ CCI_REG8(0x33dd), 0x15 },
+	{ CCI_REG8(0x33de), 0x1 },
+	{ CCI_REG8(0x33df), 0x0 },
+	{ CCI_REG8(0x33e0), 0x10 },
+	{ CCI_REG8(0x33e1), 0x20 },
+	{ CCI_REG8(0x33e2), 0x56 },
+	{ CCI_REG8(0x33e3), 0x15 },
+	{ CCI_REG8(0x33e4), 0x7 },
+	{ CCI_REG8(0x33e5), 0x0 },
+	{ CCI_REG8(0x33e6), 0x10 },
+	{ CCI_REG8(0x33e7), 0x20 },
+	{ CCI_REG8(0x33e8), 0x16 },
+	{ CCI_REG8(0x33e9), 0x15 },
+	{ CCI_REG8(0x3060), 0x26 },
+	{ CCI_REG8(0x3061), 0x0 },
+	{ CCI_REG8(0x302a), 0xff },
+	{ CCI_REG8(0x302b), 0xff },
+	{ CCI_REG8(0x302c), 0xff },
+	{ CCI_REG8(0x302d), 0xff },
+	{ CCI_REG8(0x302e), 0x3f },
+	{ CCI_REG8(0x3013), 0xb },
+	{ CCI_REG8(0x102b), 0x2c },
+	{ CCI_REG8(0x102c), 0x1 },
+	{ CCI_REG8(0x1035), 0x54 },
+	{ CCI_REG8(0x1036), 0x0 },
+	{ CCI_REG8(0x3090), 0x2a },
+	{ CCI_REG8(0x3091), 0x1 },
+	{ CCI_REG8(0x30c6), 0x5 },
+	{ CCI_REG8(0x30c7), 0x0 },
+	{ CCI_REG8(0x30c8), 0x0 },
+	{ CCI_REG8(0x30c9), 0x0 },
+	{ CCI_REG8(0x30ca), 0x0 },
+	{ CCI_REG8(0x30cb), 0x0 },
+	{ CCI_REG8(0x30cc), 0x0 },
+	{ CCI_REG8(0x30cd), 0x0 },
+	{ CCI_REG8(0x30ce), 0x0 },
+	{ CCI_REG8(0x30cf), 0x5 },
+	{ CCI_REG8(0x30d0), 0x0 },
+	{ CCI_REG8(0x30d1), 0x0 },
+	{ CCI_REG8(0x30d2), 0x0 },
+	{ CCI_REG8(0x30d3), 0x0 },
+	{ CCI_REG8(0x30d4), 0x0 },
+	{ CCI_REG8(0x30d5), 0x0 },
+	{ CCI_REG8(0x30d6), 0x0 },
+	{ CCI_REG8(0x30d7), 0x0 },
+	{ CCI_REG8(0x30f3), 0x5 },
+	{ CCI_REG8(0x30f4), 0x0 },
+	{ CCI_REG8(0x30f5), 0x0 },
+	{ CCI_REG8(0x30f6), 0x0 },
+	{ CCI_REG8(0x30f7), 0x0 },
+	{ CCI_REG8(0x30f8), 0x0 },
+	{ CCI_REG8(0x30f9), 0x0 },
+	{ CCI_REG8(0x30fa), 0x0 },
+	{ CCI_REG8(0x30fb), 0x0 },
+	{ CCI_REG8(0x30d8), 0x5 },
+	{ CCI_REG8(0x30d9), 0x0 },
+	{ CCI_REG8(0x30da), 0x0 },
+	{ CCI_REG8(0x30db), 0x0 },
+	{ CCI_REG8(0x30dc), 0x0 },
+	{ CCI_REG8(0x30dd), 0x0 },
+	{ CCI_REG8(0x30de), 0x0 },
+	{ CCI_REG8(0x30df), 0x0 },
+	{ CCI_REG8(0x30e0), 0x0 },
+	{ CCI_REG8(0x30e1), 0x5 },
+	{ CCI_REG8(0x30e2), 0x0 },
+	{ CCI_REG8(0x30e3), 0x0 },
+	{ CCI_REG8(0x30e4), 0x0 },
+	{ CCI_REG8(0x30e5), 0x0 },
+	{ CCI_REG8(0x30e6), 0x0 },
+	{ CCI_REG8(0x30e7), 0x0 },
+	{ CCI_REG8(0x30e8), 0x0 },
+	{ CCI_REG8(0x30e9), 0x0 },
+	{ CCI_REG8(0x30f3), 0x5 },
+	{ CCI_REG8(0x30f4), 0x2 },
+	{ CCI_REG8(0x30f5), 0x0 },
+	{ CCI_REG8(0x30f6), 0x17 },
+	{ CCI_REG8(0x30f7), 0x1 },
+	{ CCI_REG8(0x30f8), 0x0 },
+	{ CCI_REG8(0x30f9), 0x0 },
+	{ CCI_REG8(0x30fa), 0x0 },
+	{ CCI_REG8(0x30fb), 0x0 },
+	{ CCI_REG8(0x30d8), 0x3 },
+	{ CCI_REG8(0x30d9), 0x1 },
+	{ CCI_REG8(0x30da), 0x0 },
+	{ CCI_REG8(0x30db), 0x19 },
+	{ CCI_REG8(0x30dc), 0x1 },
+	{ CCI_REG8(0x30dd), 0x0 },
+	{ CCI_REG8(0x30de), 0x0 },
+	{ CCI_REG8(0x30df), 0x0 },
+	{ CCI_REG8(0x30e0), 0x0 },
+	{ CCI_REG8(0x30a2), 0x5 },
+	{ CCI_REG8(0x30a3), 0x2 },
+	{ CCI_REG8(0x30a4), 0x0 },
+	{ CCI_REG8(0x30a5), 0x22 },
+	{ CCI_REG8(0x30a6), 0x0 },
+	{ CCI_REG8(0x30a7), 0x0 },
+	{ CCI_REG8(0x30a8), 0x0 },
+	{ CCI_REG8(0x30a9), 0x0 },
+	{ CCI_REG8(0x30aa), 0x0 },
+	{ CCI_REG8(0x30ab), 0x5 },
+	{ CCI_REG8(0x30ac), 0x2 },
+	{ CCI_REG8(0x30ad), 0x0 },
+	{ CCI_REG8(0x30ae), 0x22 },
+	{ CCI_REG8(0x30af), 0x0 },
+	{ CCI_REG8(0x30b0), 0x0 },
+	{ CCI_REG8(0x30b1), 0x0 },
+	{ CCI_REG8(0x30b2), 0x0 },
+	{ CCI_REG8(0x30b3), 0x0 },
+	{ CCI_REG8(0x30bd), 0x5 },
+	{ CCI_REG8(0x30be), 0x9f },
+	{ CCI_REG8(0x30bf), 0x0 },
+	{ CCI_REG8(0x30c0), 0x7d },
+	{ CCI_REG8(0x30c1), 0x0 },
+	{ CCI_REG8(0x30c2), 0x0 },
+	{ CCI_REG8(0x30c3), 0x0 },
+	{ CCI_REG8(0x30c4), 0x0 },
+	{ CCI_REG8(0x30c5), 0x0 },
+	{ CCI_REG8(0x30b4), 0x4 },
+	{ CCI_REG8(0x30b5), 0x9c },
+	{ CCI_REG8(0x30b6), 0x0 },
+	{ CCI_REG8(0x30b7), 0x7d },
+	{ CCI_REG8(0x30b8), 0x0 },
+	{ CCI_REG8(0x30b9), 0x0 },
+	{ CCI_REG8(0x30ba), 0x0 },
+	{ CCI_REG8(0x30bb), 0x0 },
+	{ CCI_REG8(0x30bc), 0x0 },
+	{ CCI_REG8(0x30fc), 0x5 },
+	{ CCI_REG8(0x30fd), 0x0 },
+	{ CCI_REG8(0x30fe), 0x0 },
+	{ CCI_REG8(0x30ff), 0x0 },
+	{ CCI_REG8(0x3100), 0x0 },
+	{ CCI_REG8(0x3101), 0x0 },
+	{ CCI_REG8(0x3102), 0x0 },
+	{ CCI_REG8(0x3103), 0x0 },
+	{ CCI_REG8(0x3104), 0x0 },
+	{ CCI_REG8(0x3105), 0x5 },
+	{ CCI_REG8(0x3106), 0x0 },
+	{ CCI_REG8(0x3107), 0x0 },
+	{ CCI_REG8(0x3108), 0x0 },
+	{ CCI_REG8(0x3109), 0x0 },
+	{ CCI_REG8(0x310a), 0x0 },
+	{ CCI_REG8(0x310b), 0x0 },
+	{ CCI_REG8(0x310c), 0x0 },
+	{ CCI_REG8(0x310d), 0x0 },
+	{ CCI_REG8(0x3099), 0x5 },
+	{ CCI_REG8(0x309a), 0x96 },
+	{ CCI_REG8(0x309b), 0x0 },
+	{ CCI_REG8(0x309c), 0x6 },
+	{ CCI_REG8(0x309d), 0x0 },
+	{ CCI_REG8(0x309e), 0x0 },
+	{ CCI_REG8(0x309f), 0x0 },
+	{ CCI_REG8(0x30a0), 0x0 },
+	{ CCI_REG8(0x30a1), 0x0 },
+	{ CCI_REG8(0x310e), 0x5 },
+	{ CCI_REG8(0x310f), 0x2 },
+	{ CCI_REG8(0x3110), 0x0 },
+	{ CCI_REG8(0x3111), 0x2b },
+	{ CCI_REG8(0x3112), 0x0 },
+	{ CCI_REG8(0x3113), 0x0 },
+	{ CCI_REG8(0x3114), 0x0 },
+	{ CCI_REG8(0x3115), 0x0 },
+	{ CCI_REG8(0x3116), 0x0 },
+	{ CCI_REG8(0x3117), 0x5 },
+	{ CCI_REG8(0x3118), 0x2 },
+	{ CCI_REG8(0x3119), 0x0 },
+	{ CCI_REG8(0x311a), 0x2c },
+	{ CCI_REG8(0x311b), 0x0 },
+	{ CCI_REG8(0x311c), 0x0 },
+	{ CCI_REG8(0x311d), 0x0 },
+	{ CCI_REG8(0x311e), 0x0 },
+	{ CCI_REG8(0x311f), 0x0 },
+	{ CCI_REG8(0x30ea), 0x0 },
+	{ CCI_REG8(0x30eb), 0x0 },
+	{ CCI_REG8(0x30ec), 0x0 },
+	{ CCI_REG8(0x30ed), 0x0 },
+	{ CCI_REG8(0x30ee), 0x0 },
+	{ CCI_REG8(0x30ef), 0x0 },
+	{ CCI_REG8(0x30f0), 0x0 },
+	{ CCI_REG8(0x30f1), 0x0 },
+	{ CCI_REG8(0x30f2), 0x0 },
+	{ CCI_REG8(0x313b), 0x3 },
+	{ CCI_REG8(0x313c), 0x31 },
+	{ CCI_REG8(0x313d), 0x0 },
+	{ CCI_REG8(0x313e), 0x7 },
+	{ CCI_REG8(0x313f), 0x0 },
+	{ CCI_REG8(0x3140), 0x68 },
+	{ CCI_REG8(0x3141), 0x0 },
+	{ CCI_REG8(0x3142), 0x34 },
+	{ CCI_REG8(0x3143), 0x0 },
+	{ CCI_REG8(0x31a0), 0x3 },
+	{ CCI_REG8(0x31a1), 0x16 },
+	{ CCI_REG8(0x31a2), 0x0 },
+	{ CCI_REG8(0x31a3), 0x8 },
+	{ CCI_REG8(0x31a4), 0x0 },
+	{ CCI_REG8(0x31a5), 0x7e },
+	{ CCI_REG8(0x31a6), 0x0 },
+	{ CCI_REG8(0x31a7), 0x8 },
+	{ CCI_REG8(0x31a8), 0x0 },
+	{ CCI_REG8(0x31a9), 0x3 },
+	{ CCI_REG8(0x31aa), 0x16 },
+	{ CCI_REG8(0x31ab), 0x0 },
+	{ CCI_REG8(0x31ac), 0x8 },
+	{ CCI_REG8(0x31ad), 0x0 },
+	{ CCI_REG8(0x31ae), 0x7e },
+	{ CCI_REG8(0x31af), 0x0 },
+	{ CCI_REG8(0x31b0), 0x8 },
+	{ CCI_REG8(0x31b1), 0x0 },
+	{ CCI_REG8(0x31b2), 0x3 },
+	{ CCI_REG8(0x31b3), 0x16 },
+	{ CCI_REG8(0x31b4), 0x0 },
+	{ CCI_REG8(0x31b5), 0x8 },
+	{ CCI_REG8(0x31b6), 0x0 },
+	{ CCI_REG8(0x31b7), 0x7e },
+	{ CCI_REG8(0x31b8), 0x0 },
+	{ CCI_REG8(0x31b9), 0x8 },
+	{ CCI_REG8(0x31ba), 0x0 },
+	{ CCI_REG8(0x3120), 0x5 },
+	{ CCI_REG8(0x3121), 0x45 },
+	{ CCI_REG8(0x3122), 0x0 },
+	{ CCI_REG8(0x3123), 0x1d },
+	{ CCI_REG8(0x3124), 0x0 },
+	{ CCI_REG8(0x3125), 0xa9 },
+	{ CCI_REG8(0x3126), 0x0 },
+	{ CCI_REG8(0x3127), 0x6d },
+	{ CCI_REG8(0x3128), 0x0 },
+	{ CCI_REG8(0x3129), 0x5 },
+	{ CCI_REG8(0x312a), 0x15 },
+	{ CCI_REG8(0x312b), 0x0 },
+	{ CCI_REG8(0x312c), 0xa },
+	{ CCI_REG8(0x312d), 0x0 },
+	{ CCI_REG8(0x312e), 0x45 },
+	{ CCI_REG8(0x312f), 0x0 },
+	{ CCI_REG8(0x3130), 0x1d },
+	{ CCI_REG8(0x3131), 0x0 },
+	{ CCI_REG8(0x3132), 0x5 },
+	{ CCI_REG8(0x3133), 0x7d },
+	{ CCI_REG8(0x3134), 0x0 },
+	{ CCI_REG8(0x3135), 0xa },
+	{ CCI_REG8(0x3136), 0x0 },
+	{ CCI_REG8(0x3137), 0xa9 },
+	{ CCI_REG8(0x3138), 0x0 },
+	{ CCI_REG8(0x3139), 0x6d },
+	{ CCI_REG8(0x313a), 0x0 },
+	{ CCI_REG8(0x3144), 0x5 },
+	{ CCI_REG8(0x3145), 0x0 },
+	{ CCI_REG8(0x3146), 0x0 },
+	{ CCI_REG8(0x3147), 0x30 },
+	{ CCI_REG8(0x3148), 0x0 },
+	{ CCI_REG8(0x3149), 0x0 },
+	{ CCI_REG8(0x314a), 0x0 },
+	{ CCI_REG8(0x314b), 0x0 },
+	{ CCI_REG8(0x314c), 0x0 },
+	{ CCI_REG8(0x314d), 0x3 },
+	{ CCI_REG8(0x314e), 0x0 },
+	{ CCI_REG8(0x314f), 0x0 },
+	{ CCI_REG8(0x3150), 0x31 },
+	{ CCI_REG8(0x3151), 0x0 },
+	{ CCI_REG8(0x3152), 0x0 },
+	{ CCI_REG8(0x3153), 0x0 },
+	{ CCI_REG8(0x3154), 0x0 },
+	{ CCI_REG8(0x3155), 0x0 },
+	{ CCI_REG8(0x31d8), 0x5 },
+	{ CCI_REG8(0x31d9), 0x3a },
+	{ CCI_REG8(0x31da), 0x0 },
+	{ CCI_REG8(0x31db), 0x2e },
+	{ CCI_REG8(0x31dc), 0x0 },
+	{ CCI_REG8(0x31dd), 0x9e },
+	{ CCI_REG8(0x31de), 0x0 },
+	{ CCI_REG8(0x31df), 0x7e },
+	{ CCI_REG8(0x31e0), 0x0 },
+	{ CCI_REG8(0x31e1), 0x5 },
+	{ CCI_REG8(0x31e2), 0x4 },
+	{ CCI_REG8(0x31e3), 0x0 },
+	{ CCI_REG8(0x31e4), 0x4 },
+	{ CCI_REG8(0x31e5), 0x0 },
+	{ CCI_REG8(0x31e6), 0x73 },
+	{ CCI_REG8(0x31e7), 0x0 },
+	{ CCI_REG8(0x31e8), 0x4 },
+	{ CCI_REG8(0x31e9), 0x0 },
+	{ CCI_REG8(0x31ea), 0x5 },
+	{ CCI_REG8(0x31eb), 0x0 },
+	{ CCI_REG8(0x31ec), 0x0 },
+	{ CCI_REG8(0x31ed), 0x0 },
+	{ CCI_REG8(0x31ee), 0x0 },
+	{ CCI_REG8(0x31ef), 0x0 },
+	{ CCI_REG8(0x31f0), 0x0 },
+	{ CCI_REG8(0x31f1), 0x0 },
+	{ CCI_REG8(0x31f2), 0x0 },
+	{ CCI_REG8(0x31f3), 0x0 },
+	{ CCI_REG8(0x31f4), 0x0 },
+	{ CCI_REG8(0x31f5), 0x0 },
+	{ CCI_REG8(0x31f6), 0x0 },
+	{ CCI_REG8(0x31f7), 0x0 },
+	{ CCI_REG8(0x31f8), 0x0 },
+	{ CCI_REG8(0x31f9), 0x0 },
+	{ CCI_REG8(0x31fa), 0x0 },
+	{ CCI_REG8(0x31fb), 0x5 },
+	{ CCI_REG8(0x31fc), 0x0 },
+	{ CCI_REG8(0x31fd), 0x0 },
+	{ CCI_REG8(0x31fe), 0x0 },
+	{ CCI_REG8(0x31ff), 0x0 },
+	{ CCI_REG8(0x3200), 0x0 },
+	{ CCI_REG8(0x3201), 0x0 },
+	{ CCI_REG8(0x3202), 0x0 },
+	{ CCI_REG8(0x3203), 0x0 },
+	{ CCI_REG8(0x3204), 0x0 },
+	{ CCI_REG8(0x3205), 0x0 },
+	{ CCI_REG8(0x3206), 0x0 },
+	{ CCI_REG8(0x3207), 0x0 },
+	{ CCI_REG8(0x3208), 0x0 },
+	{ CCI_REG8(0x3209), 0x0 },
+	{ CCI_REG8(0x320a), 0x0 },
+	{ CCI_REG8(0x320b), 0x0 },
+	{ CCI_REG8(0x3164), 0x5 },
+	{ CCI_REG8(0x3165), 0x14 },
+	{ CCI_REG8(0x3166), 0x0 },
+	{ CCI_REG8(0x3167), 0xc },
+	{ CCI_REG8(0x3168), 0x0 },
+	{ CCI_REG8(0x3169), 0x44 },
+	{ CCI_REG8(0x316a), 0x0 },
+	{ CCI_REG8(0x316b), 0x1f },
+	{ CCI_REG8(0x316c), 0x0 },
+	{ CCI_REG8(0x316d), 0x5 },
+	{ CCI_REG8(0x316e), 0x7c },
+	{ CCI_REG8(0x316f), 0x0 },
+	{ CCI_REG8(0x3170), 0xc },
+	{ CCI_REG8(0x3171), 0x0 },
+	{ CCI_REG8(0x3172), 0xa8 },
+	{ CCI_REG8(0x3173), 0x0 },
+	{ CCI_REG8(0x3174), 0x6f },
+	{ CCI_REG8(0x3175), 0x0 },
+	{ CCI_REG8(0x31c4), 0x5 },
+	{ CCI_REG8(0x31c5), 0x24 },
+	{ CCI_REG8(0x31c6), 0x1 },
+	{ CCI_REG8(0x31c7), 0x4 },
+	{ CCI_REG8(0x31c8), 0x0 },
+	{ CCI_REG8(0x31c9), 0x5 },
+	{ CCI_REG8(0x31ca), 0x24 },
+	{ CCI_REG8(0x31cb), 0x1 },
+	{ CCI_REG8(0x31cc), 0x4 },
+	{ CCI_REG8(0x31cd), 0x0 },
+	{ CCI_REG8(0x31ce), 0x5 },
+	{ CCI_REG8(0x31cf), 0x24 },
+	{ CCI_REG8(0x31d0), 0x1 },
+	{ CCI_REG8(0x31d1), 0x4 },
+	{ CCI_REG8(0x31d2), 0x0 },
+	{ CCI_REG8(0x31d3), 0x5 },
+	{ CCI_REG8(0x31d4), 0x73 },
+	{ CCI_REG8(0x31d5), 0x0 },
+	{ CCI_REG8(0x31d6), 0xb1 },
+	{ CCI_REG8(0x31d7), 0x0 },
+	{ CCI_REG8(0x3176), 0x5 },
+	{ CCI_REG8(0x3177), 0x10 },
+	{ CCI_REG8(0x3178), 0x0 },
+	{ CCI_REG8(0x3179), 0x56 },
+	{ CCI_REG8(0x317a), 0x0 },
+	{ CCI_REG8(0x317b), 0x0 },
+	{ CCI_REG8(0x317c), 0x0 },
+	{ CCI_REG8(0x317d), 0x0 },
+	{ CCI_REG8(0x317e), 0x0 },
+	{ CCI_REG8(0x317f), 0x5 },
+	{ CCI_REG8(0x3180), 0x6a },
+	{ CCI_REG8(0x3181), 0x0 },
+	{ CCI_REG8(0x3182), 0xad },
+	{ CCI_REG8(0x3183), 0x0 },
+	{ CCI_REG8(0x3184), 0x0 },
+	{ CCI_REG8(0x3185), 0x0 },
+	{ CCI_REG8(0x3186), 0x0 },
+	{ CCI_REG8(0x3187), 0x0 },
+	{ CCI_REG8(0x100d), 0x0 },
+	{ CCI_REG8(0x1013), 0x2b },
+	/* Sensor control mode */
+	{ CCI_REG8(0x0043), 0x0 }, //  Sensor Control Mode.SLEEP_POWER_MODE(0)
+	{ CCI_REG8(0x0043), 0x0 }, //  Sensor Control Mode.IDLE_POWER_MODE(0)
+	{ CCI_REG8(0x0043), 0x4 }, //  Sensor Control Mode.SYSTEM_CLOCK_ENABLE(0)
+	{ CCI_REG8(0x0043), 0xC }, //  Sensor Control Mode.SRAM_CLOCK_ENABLE(0)
+	{ CCI_REG8(0x1001), 0x41 }, //  Sensor Control Mode.EXT_EVENT_SEL(0)
+	{ CCI_REG8(0x10f2), 0x1 }, //  Sensor Control Mode.NB_OF_FRAMES_A(0)
+	{ CCI_REG8(0x10f3), 0x0 }, //  Sensor Control Mode.NB_OF_FRAMES_A(1)
+	{ CCI_REG8(0x0012), 0x0 }, //  IO Drive Strength.DIG_DRIVE_STRENGTH(0)
+	{ CCI_REG8(0x0012), 0x0 }, //  IO Drive Strength.CCI_DRIVE_STRENGTH(0)
+	{ CCI_REG8(0x1001), 0x41 }, //  Readout && Exposure.EXT_EXP_PW_SEL(0)
+	{ CCI_REG8(0x10d0), 0x0 }, //  Readout && Exposure.EXT_EXP_PW_DELAY(0)
+	{ CCI_REG8(0x10d1), 0x0 }, //  Readout && Exposure.EXT_EXP_PW_DELAY(1)
+	/* MIPI */
+	{ CCI_REG8(0x6006), 0x0 }, //  MIPI.TX_CTRL_EN(0)
+	{ CCI_REG8(0x5004), 0x1 }, //  MIPI.datarate
+	{ CCI_REG8(0x5086), 0x2 }, //  MIPI.datarate
+	{ CCI_REG8(0x5087), 0x4e }, //  MIPI.datarate
+	{ CCI_REG8(0x5088), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x5090), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x5091), 0x8 }, //  MIPI.datarate
+	{ CCI_REG8(0x5092), 0x14 }, //  MIPI.datarate
+	{ CCI_REG8(0x5093), 0xf }, //  MIPI.datarate
+	{ CCI_REG8(0x5094), 0x6 }, //  MIPI.datarate
+	{ CCI_REG8(0x5095), 0x32 }, //  MIPI.datarate
+	{ CCI_REG8(0x5096), 0xe }, //  MIPI.datarate
+	{ CCI_REG8(0x5097), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x5098), 0x11 }, //  MIPI.datarate
+	{ CCI_REG8(0x5004), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x2066), 0x6c }, //  MIPI.datarate
+	{ CCI_REG8(0x2067), 0x7 }, //  MIPI.datarate
+	{ CCI_REG8(0x206e), 0x7e }, //  MIPI.datarate
+	{ CCI_REG8(0x206f), 0x6 }, //  MIPI.datarate
+	{ CCI_REG8(0x20ac), 0x7e }, //  MIPI.datarate
+	{ CCI_REG8(0x20ad), 0x6 }, //  MIPI.datarate
+	{ CCI_REG8(0x2076), 0xc8 }, //  MIPI.datarate
+	{ CCI_REG8(0x2077), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x20b4), 0xc8 }, //  MIPI.datarate
+	{ CCI_REG8(0x20b5), 0x0 }, //  MIPI.datarate
+	{ CCI_REG8(0x2078), 0x1e }, //  MIPI.datarate
+	{ CCI_REG8(0x2079), 0x4 }, //  MIPI.datarate
+	{ CCI_REG8(0x20b6), 0x1e }, //  MIPI.datarate
+	{ CCI_REG8(0x20b7), 0x4 }, //  MIPI.datarate
+	{ CCI_REG8(0x207a), 0xd4 }, //  MIPI.datarate
+	{ CCI_REG8(0x207b), 0x4 }, //  MIPI.datarate
+	{ CCI_REG8(0x20b8), 0xd4 }, //  MIPI.datarate
+	{ CCI_REG8(0x20b9), 0x4 }, //  MIPI.datarate
+	{ CCI_REG8(0x207c), 0x0 }, //  MIPI.VC_ID(0)
+	{ CCI_REG8(0x6001), 0x7 }, //  MIPI.TINIT(0)
+	{ CCI_REG8(0x6002), 0xd8 }, //  MIPI.TINIT(1)
+	{ CCI_REG8(0x6010), 0x0 }, //  MIPI.FRAME_MODE(0)
+	{ CCI_REG8(0x6010), 0x0 }, //  MIPI.EMBEDDED_FRAME_MODE(0)
+	{ CCI_REG8(0x6011), 0x0 }, //  MIPI.DATA_ENABLE_POLARITY(0)
+	{ CCI_REG8(0x6011), 0x0 }, //  MIPI.HSYNC_POLARITY(0)
+	{ CCI_REG8(0x6011), 0x0 }, //  MIPI.VSYNC_POLARITY(0)
+	{ CCI_REG8(0x6016), 0x0 }, //  MIPI.FRAME_COUNTER(0)
+	{ CCI_REG8(0x6017), 0x0 }, //  MIPI.FRAME_COUNTER(1)
+	{ CCI_REG8(0x6037), 0x1 }, //  MIPI.LINE_COUNT_RAW8(0)
+	{ CCI_REG8(0x6037), 0x3 }, //  MIPI.LINE_COUNT_RAW10(0)
+	{ CCI_REG8(0x6037), 0x7 }, //  MIPI.LINE_COUNT_RAW12(0)
+	{ CCI_REG8(0x6039), 0x1 }, //  MIPI.LINE_COUNT_EMB(0)
+	{ CCI_REG8(0x6018), 0x0 }, //  MIPI.CCI_READ_INTERRUPT_EN(0)
+	{ CCI_REG8(0x6018), 0x0 }, //  MIPI.CCI_WRITE_INTERRUPT_EN(0)
+	{ CCI_REG8(0x6065), 0x0 }, //  MIPI.TWAKE_TIMER(0)
+	{ CCI_REG8(0x6066), 0x0 }, //  MIPI.TWAKE_TIMER(1)
+	{ CCI_REG8(0x601c), 0x0 }, //  MIPI.SKEW_CAL_EN(0)
+	{ CCI_REG8(0x601d), 0x0 }, //  MIPI.SKEW_COUNT(0)
+	{ CCI_REG8(0x601e), 0x22 }, //  MIPI.SKEW_COUNT(1)
+	{ CCI_REG8(0x601f), 0x0 }, //  MIPI.SCRAMBLING_EN(0)
+	{ CCI_REG8(0x6003), 0x1 }, //  MIPI.INIT_SKEW_EN(0)
+	{ CCI_REG8(0x6004), 0x7a }, //  MIPI.INIT_SKEW(0)
+	{ CCI_REG8(0x6005), 0x12 }, //  MIPI.INIT_SKEW(1)
+	{ CCI_REG8(0x6006), 0x1 }, //  MIPI.TX_CTRL_EN(0)
+	/* Processing */
+	{ CCI_REG8(0x4006), 0x8 }, //  Processing.BSP(0)
+	{ CCI_REG8(0x2045), 0x1 }, //  Processing.CDS_RNC(0)
+	{ CCI_REG8(0x2048), 0x1 }, //  Processing.CDS_IMG(0)
+	{ CCI_REG8(0x204b), 0x3 }, //  Processing.RNC_EN(0)
+	{ CCI_REG8(0x205b), 0x64 }, //  Processing.RNC_DARK_TARGET(0)
+	{ CCI_REG8(0x205c), 0x0 }, //  Processing.RNC_DARK_TARGET(1)
+	{ CCI_REG8(0x24dc), 0x12 }, //  Defect Pixel Correction.DC_ENABLE(0)
+	{ CCI_REG8(0x24dc), 0x10 }, //  Defect Pixel Correction.DC_MODE(0)
+	{ CCI_REG8(0x24dc), 0x0 }, //  Defect Pixel Correction.DC_REPLACEMENT_VALUE(0)
+	{ CCI_REG8(0x24dd), 0x0 }, //  Defect Pixel Correction.DC_LIMIT_LOW(0)
+	{ CCI_REG8(0x24de), 0x0 }, //  Defect Pixel Correction.DC_LIMIT_HIGH(0)
+	{ CCI_REG8(0x24df), 0x0 }, //  Defect Pixel Correction.DC_LIMIT_HIGH_MODE(0)
+	/* Illumination */
+	{ CCI_REG8(0x10d7), 0x1 }, //  Illumination Trigger.ILLUM_EN(0)
+	{ CCI_REG8(0x10d8), 0x2 }, //  Illumination Trigger.ILLUM_POL(0)
+	/* Histogram */
+	{ CCI_REG8(0x205d), 0x0 }, //  Histogram.HIST_EN(0)
+	{ CCI_REG8(0x205e), 0x0 }, //  Histogram.HIST_USAGE_RATIO(0)
+	{ CCI_REG8(0x2063), 0x0 }, //  Histogram.PIXEL_DATA_SUPP(0)
+	{ CCI_REG8(0x2063), 0x0 }, //  Histogram.PIXEL_TRANSMISSION(0)
+	/* TP */
+	{ CCI_REG8(0x2091), 0x0 }, //  Test Pattern Generator.TPG_EN(0)
+	{ CCI_REG8(0x2091), 0x0 }, //  Test Pattern Generator.TPG_CONFIG(0)
+	/* Reduce Slew Rate - fix for defect line */
+	{ CCI_REG8(0x402d), 0x7b },
+};
+
+static const char *const mira220_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Gradient",
+};
+
+static const int mira220_test_pattern_val[] = {
+	MIRA220_TEST_PATTERN_DISABLE,
+	MIRA220_TEST_PATTERN_VERTICAL_GRADIENT,
+};
+
+/* regulator supplies */
+static const char *const mira220_supplies[] = {
+	/* Supplies can be enabled in any order */
+	"vana", /* Analog (2.8V) supply */
+	"vdig", /* Digital Core (1.8V) supply */
+	"vddl", /* IF (1.2V) supply */
+};
+
+struct mira220 {
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+
+	enum mira220_variants variant;
+
+	struct v4l2_mbus_framefmt fmt;
+
+	struct clk *xclk;
+	u32 xclk_freq;
+
+	struct regulator_bulk_data supplies[ARRAY_SIZE(mira220_supplies)];
+
+	struct gpio_desc *reset_gpio;
+
+	unsigned int lanes;
+	unsigned int row_length;
+
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *exposure;
+
+	struct regmap *regmap;
+};
+
+static inline struct mira220 *to_mira220(struct v4l2_subdev *sd)
+{
+	return container_of_const(sd, struct mira220, sd);
+}
+
+static bool mira220_is_mono(struct mira220 *mira220)
+{
+	return mira220->variant == MIRA220_VARIANT_MONO;
+}
+
+/*
+ * The supported formats. This table contains 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 mira220_mbus_color_formats[] = {
+	MEDIA_BUS_FMT_SGRBG12_1X12,
+	MEDIA_BUS_FMT_SRGGB12_1X12,
+	MEDIA_BUS_FMT_SBGGR12_1X12,
+	MEDIA_BUS_FMT_SGBRG12_1X12,
+
+	MEDIA_BUS_FMT_SGRBG10_1X10,
+	MEDIA_BUS_FMT_SRGGB10_1X10,
+	MEDIA_BUS_FMT_SBGGR10_1X10,
+	MEDIA_BUS_FMT_SGBRG10_1X10,
+
+	MEDIA_BUS_FMT_SGRBG8_1X8,
+	MEDIA_BUS_FMT_SRGGB8_1X8,
+	MEDIA_BUS_FMT_SBGGR8_1X8,
+	MEDIA_BUS_FMT_SGBRG8_1X8,
+};
+
+static const u32 mira220_mbus_mono_formats[] = {
+	MEDIA_BUS_FMT_Y12_1X12,
+	MEDIA_BUS_FMT_Y10_1X10,
+	MEDIA_BUS_FMT_Y8_1X8,
+};
+
+#define MIRA220_FLIP_FORMAT_MASK	0x3
+static inline unsigned int mira220_flip_format_index(struct mira220 *mira220,
+						     unsigned int index)
+{
+	return (index & ~MIRA220_FLIP_FORMAT_MASK)	|
+	       (mira220->vflip->val ? BIT(1) : 0)	|
+	       (mira220->hflip->val ? BIT(0) : 0);
+}
+
+
+/* Power/clock management functions */
+static int mira220_power_on(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct mira220 *mira220 = to_mira220(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(mira220_supplies),
+				    mira220->supplies);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable regulators\n",
+			__func__);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(mira220->xclk);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable clock\n", __func__);
+		goto reg_off;
+	}
+
+	gpiod_set_value_cansleep(mira220->reset_gpio, 0);
+
+	return 0;
+
+reg_off:
+	regulator_bulk_disable(ARRAY_SIZE(mira220_supplies), mira220->supplies);
+	return ret;
+}
+
+static int mira220_power_off(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct mira220 *mira220 = to_mira220(sd);
+
+	gpiod_set_value_cansleep(mira220->reset_gpio, 1);
+	clk_disable_unprepare(mira220->xclk);
+	regulator_bulk_disable(ARRAY_SIZE(mira220_supplies), mira220->supplies);
+
+	return 0;
+}
+
+static int mira220_write_start_streaming_regs(struct mira220 *mira220)
+{
+	int ret = 0;
+
+	/* Setting master control */
+	cci_write(mira220->regmap, MIRA220_IMAGER_STATE_REG,
+		  MIRA220_IMAGER_STATE_MASTER_CONTROL, &ret);
+
+	/* Enable continuous streaming */
+	cci_write(mira220->regmap, MIRA220_IMAGER_RUN_CONT_REG,
+		  MIRA220_IMAGER_RUN_CONT_ENABLE, &ret);
+	cci_write(mira220->regmap, MIRA220_IMAGER_RUN_REG,
+		  MIRA220_IMAGER_RUN_START, &ret);
+
+	return ret;
+}
+
+static int mira220_write_stop_streaming_regs(struct mira220 *mira220)
+{
+	int ret = 0;
+
+	cci_write(mira220->regmap, MIRA220_IMAGER_STATE_REG,
+		  MIRA220_IMAGER_STATE_STOP_AT_ROW, &ret);
+	cci_write(mira220->regmap, MIRA220_IMAGER_RUN_REG,
+		  MIRA220_IMAGER_RUN_STOP, &ret);
+
+	return ret;
+}
+
+/*
+ * Returns the exposure in lines. Calculation is baded on Mira220 datasheet
+ * Section 9.2.
+ */
+static u32 mira220_calc_exposure(struct mira220 *mira220, u32 height, u32 vblank)
+{
+	u32 max_exposure = (height + vblank) -
+			   (MIRA220_GLOB_NUM_CLK_CYCLES / mira220->row_length);
+
+	return min(max_exposure, MIRA220_MAX_EXPOSURE);
+}
+
+static u32 mira220_calc_min_vblank(struct mira220 *mira220)
+{
+	return (MIRA220_GLOB_NUM_CLK_CYCLES / mira220->row_length)
+	       + MIRA220_MIN_VBLANK_MARGIN;
+}
+
+static int mira220_write_exposure_reg(struct mira220 *mira220, u32 exposure)
+{
+	struct i2c_client *const client = v4l2_get_subdevdata(&mira220->sd);
+	int ret;
+
+	ret = cci_write(mira220->regmap, MIRA220_EXP_TIME_REG, exposure, NULL);
+	if (ret) {
+		dev_err_ratelimited(&client->dev,
+				    "Error setting exposure time to %d",
+				    exposure);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Get bayer order based on flip setting. */
+static u32 mira220_get_format_code(struct mira220 *mira220, u32 code)
+{
+	unsigned int i;
+
+	if (mira220_is_mono(mira220)) {
+		for (i = 0; i < ARRAY_SIZE(mira220_mbus_mono_formats); i++) {
+			if (mira220_mbus_mono_formats[i] == code)
+				return mira220_mbus_mono_formats[i];
+		}
+
+		return mira220_mbus_mono_formats[0];
+	}
+
+	for (i = 0; i < ARRAY_SIZE(mira220_mbus_color_formats); i++) {
+		if (mira220_mbus_color_formats[i] == code)
+			break;
+	}
+	if (i >= ARRAY_SIZE(mira220_mbus_color_formats))
+		i = 0;
+
+	i = mira220_flip_format_index(mira220, i);
+
+	return mira220_mbus_color_formats[i];
+}
+
+static int mira220_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct mira220 *mira220 =
+		container_of(ctrl->handler, struct mira220, ctrl_handler);
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+	struct v4l2_mbus_framefmt *format;
+	struct v4l2_subdev_state *state;
+	int ret = 0;
+
+	state = v4l2_subdev_get_locked_active_state(&mira220->sd);
+	format = v4l2_subdev_state_get_format(state, 0);
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		int exposure_max, exposure_def;
+
+		/* Update max exposure while meeting expected vblanking */
+		exposure_max = mira220_calc_exposure(mira220, format->height,
+						     ctrl->val);
+		exposure_def = min(exposure_max, MIRA220_DEFAULT_EXPOSURE);
+		ret = __v4l2_ctrl_modify_range(mira220->exposure,
+					       mira220->exposure->minimum,
+					       exposure_max,
+					       mira220->exposure->step,
+					       exposure_def);
+		if (ret)
+			return ret;
+	}
+
+	/* Update the format code to adjust it to the new flip state. */
+	if (ctrl->id == V4L2_CID_VFLIP || ctrl->id == V4L2_CID_HFLIP)
+		format->code = mira220_get_format_code(mira220, format->code);
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		ret = mira220_write_exposure_reg(mira220, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = cci_write(mira220->regmap, MIRA220_REG_TEST_PATTERN,
+				mira220_test_pattern_val[ctrl->val], NULL);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = cci_write(mira220->regmap, MIRA220_HFLIP_REG,
+				mira220->hflip->val, NULL);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = cci_write(mira220->regmap, MIRA220_VFLIP_REG,
+				mira220->vflip->val, NULL);
+		break;
+	case V4L2_CID_VBLANK:
+		ret = cci_write(mira220->regmap, MIRA220_VBLANK_REG, ctrl->val,
+				NULL);
+		break;
+	default:
+		dev_info(&client->dev,
+			 "ctrl(id:0x%x,val:0x%x) is not handled\n", ctrl->id,
+			 ctrl->val);
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put_autosuspend(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops mira220_ctrl_ops = {
+	.s_ctrl = mira220_set_ctrl,
+};
+
+static void mira220_update_pad_format(struct mira220 *mira220,
+				      struct v4l2_mbus_framefmt *fmt, u32 code)
+{
+	/* Bayer order varies with flips */
+	fmt->code = mira220_get_format_code(mira220, code);
+	/*
+	 * The mira220 sensor doesn't support binning/skipping but only
+	 * cropping to reduce the frame size so the correct API to configure
+	 * windowing is the .set_selection one, while format is fixed to
+	 * the full resolution 1600x1400 one.
+	 */
+	fmt->width = MIRA220_PIXEL_ARRAY_WIDTH;
+	fmt->height = MIRA220_PIXEL_ARRAY_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;
+}
+
+static int mira220_set_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state,
+				  struct v4l2_subdev_format *fmt)
+{
+	struct mira220 *mira220 = to_mira220(sd);
+	struct v4l2_rect *crop;
+	u32 min_vblank;
+	int ret;
+
+	mira220_update_pad_format(mira220, &fmt->format, fmt->format.code);
+	*v4l2_subdev_state_get_format(state, 0) = fmt->format;
+
+	crop = v4l2_subdev_state_get_crop(state, 0);
+	crop->width = fmt->format.width;
+	crop->height = fmt->format.height;
+	crop->left = MIRA220_PIXEL_ARRAY_LEFT;
+	crop->top = MIRA220_PIXEL_ARRAY_TOP;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+		return 0;
+
+	/* Update vblank based on new mode. */
+	min_vblank = mira220_calc_min_vblank(mira220);
+	ret = __v4l2_ctrl_modify_range(mira220->vblank, min_vblank,
+				       MIRA220_MAX_VBLANK, 1, min_vblank);
+	if (ret)
+		return ret;
+
+	return __v4l2_ctrl_s_ctrl(mira220->vblank, min_vblank);
+}
+
+static int mira220_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct mira220 *mira220 = to_mira220(sd);
+	bool mono = mira220_is_mono(mira220);
+	unsigned int num_formats;
+	unsigned int index;
+	const u32 *codes;
+
+	num_formats = mono ? ARRAY_SIZE(mira220_mbus_mono_formats) :
+			     (ARRAY_SIZE(mira220_mbus_color_formats) / 4);
+
+	if (code->index >= num_formats)
+		return -EINVAL;
+
+	index = mono ? code->index : (code->index * 4);
+	codes = mono ? mira220_mbus_mono_formats : mira220_mbus_color_formats;
+	code->code = mira220_get_format_code(mira220, codes[index]);
+
+	return 0;
+}
+
+static int mira220_enum_frame_size(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *state,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct mira220 *mira220 = to_mira220(sd);
+	u32 code;
+
+	if (fse->index)
+		return -EINVAL;
+
+	code = mira220_get_format_code(mira220, fse->code);
+	if (fse->code != code)
+		return -EINVAL;
+
+	fse->min_width = MIRA220_PIXEL_ARRAY_WIDTH;
+	fse->max_width = fse->min_width;
+	fse->min_height = MIRA220_PIXEL_ARRAY_HEIGHT;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int mira220_init_state(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *state)
+{
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.pad = 0,
+		.format = {
+			.code = MEDIA_BUS_FMT_SGRBG12_1X12,
+			.width = MIRA220_PIXEL_ARRAY_WIDTH,
+			.height = MIRA220_PIXEL_ARRAY_HEIGHT,
+		},
+	};
+
+	mira220_set_pad_format(sd, state, &fmt);
+
+	return 0;
+}
+
+static int mira220_set_bus_config(struct mira220 *mira220)
+{
+	int ret = 0;
+
+	cci_write(mira220->regmap, MIRA220_MIPI_LANES_REG,
+		  mira220->lanes - 1, &ret);
+
+	cci_write(mira220->regmap, MIRA220_MIPI_CLK_MODE_REG,
+		  MIRA220_MIPI_CLK_CONTINUOUS, &ret);
+
+	return ret;
+}
+
+static int mira220_set_framefmt(struct mira220 *mira220,
+				struct v4l2_subdev_state *state)
+{
+	const struct v4l2_mbus_framefmt *format;
+	u32 top, left;
+	int ret = 0;
+
+	format = v4l2_subdev_state_get_format(state, 0);
+
+	/* Program the image format bit order, bit depth and CSI-2 data type */
+
+	ret = cci_write(mira220->regmap, MIRA220_BIT_ORDER_REG,
+			MIRA220_BIT_ORDER_NORMAL, NULL);
+	if (ret)
+		return ret;
+
+	switch (format->code) {
+	case MEDIA_BUS_FMT_Y8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+		cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG,
+			  MIRA220_BIT_DEPTH_8_BIT, &ret);
+		cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG,
+			  MIRA220_CSI_DATA_TYPE_8_BIT, &ret);
+		break;
+	case MEDIA_BUS_FMT_Y10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+		cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG,
+			  MIRA220_BIT_DEPTH_10_BIT, &ret);
+		cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG,
+			  MIRA220_CSI_DATA_TYPE_10_BIT, &ret);
+
+		break;
+	case MEDIA_BUS_FMT_Y12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		cci_write(mira220->regmap, MIRA220_BIT_DEPTH_REG,
+			  MIRA220_BIT_DEPTH_12_BIT, &ret);
+		cci_write(mira220->regmap, MIRA220_CSI_DATA_TYPE_REG,
+			  MIRA220_CSI_DATA_TYPE_12_BIT, &ret);
+
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	if (ret)
+		return ret;
+
+	/* Program the sensor's row length. */
+	ret = cci_write(mira220->regmap, MIRA220_REG_ROW_LENGTH,
+			mira220->row_length, NULL);
+	if (ret)
+		return ret;
+
+	/*
+	 * Program the ROI area, centered in the sensor's pixel array.
+	 *
+	 * TODO: Use the crop rectangle sizes once the driver is ported to the
+	 * new RAW camera sensor model.
+	 */
+	top = (MIRA220_PIXEL_ARRAY_HEIGHT - format->height) / 2;
+	left = (MIRA220_PIXEL_ARRAY_WIDTH - format->width) / 2;
+
+	cci_write(mira220->regmap, MIRA220_VSTART_REG, top, &ret);
+	cci_write(mira220->regmap, MIRA220_HSTART_REG,
+		  left & MIRA220_HSTART_MASK, &ret);
+	cci_write(mira220->regmap, MIRA220_VSIZE_REG, format->height, &ret);
+	cci_write(mira220->regmap, MIRA220_HSIZE_REG,
+		  (format->width / 2) & MIRA220_HSIZE_MASK, &ret);
+	cci_write(mira220->regmap, MIRA220_MIPI_HSIZE_REG, format->width, &ret);
+
+	return ret;
+}
+
+static int mira220_get_selection(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state,
+				 struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP:
+		sel->r = *v4l2_subdev_state_get_crop(state, 0);
+		return 0;
+
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.top = 0;
+		sel->r.left = 0;
+		sel->r.width = MIRA220_NATIVE_WIDTH;
+		sel->r.height = MIRA220_NATIVE_HEIGHT;
+		return 0;
+
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r.top = MIRA220_PIXEL_ARRAY_TOP;
+		sel->r.left = MIRA220_PIXEL_ARRAY_LEFT;
+		sel->r.width = MIRA220_PIXEL_ARRAY_WIDTH;
+		sel->r.height = MIRA220_PIXEL_ARRAY_HEIGHT;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* OTP power on */
+static int mira220_otp_power_on(struct mira220 *mira220)
+{
+	int ret = cci_write(mira220->regmap, MIRA220_OTP_CMD_REG,
+			    MIRA220_OTP_CMD_UP, NULL);
+	fsleep(100);
+
+	return ret;
+}
+
+/* OTP power off */
+static int mira220_otp_power_off(struct mira220 *mira220)
+{
+	return cci_write(mira220->regmap, MIRA220_OTP_CMD_REG,
+			 MIRA220_OTP_CMD_DOWN, NULL);
+}
+
+/* OTP power on */
+static int mira220_otp_read(struct mira220 *mira220, u8 addr, u8 offset,
+			    u8 *val, int *err)
+{
+	u64 readback;
+	int ret = 0;
+
+	if (err && *err)
+		return *err;
+
+	cci_write(mira220->regmap, CCI_REG8(0x0086), addr, &ret);
+	cci_write(mira220->regmap, CCI_REG8(0x0080), 0x02, &ret);
+	cci_read(mira220->regmap, CCI_REG8(0x0082 + offset), &readback, &ret);
+	if (ret)
+		goto out;
+
+	*val = readback & 0xFF;
+
+	return 0;
+
+out:
+	if (err)
+		*err = ret;
+	return ret;
+}
+
+/* Verify chip ID, module version, unique ID and variant */
+static int mira220_identify_module(struct mira220 *mira220)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+	u8 b0, b1, b2, b3, b4, b5, b6, b7;
+	u32 variant;
+	int ret;
+	u8 val;
+	u8 id;
+
+	ret = mira220_otp_power_on(mira220);
+	if (ret)
+		return ret;
+
+	/* Log module version checks */
+	mira220_otp_read(mira220, 0x0d, 0, &val, &ret);
+	mira220_otp_read(mira220, 0x1e, 0, &val, &ret);
+	if (ret)
+		goto err_power_off;
+
+	/* Check unique module hardware ID version */
+	ret = mira220_otp_read(mira220, 0x3a, 0, &id, NULL);
+	if (ret)
+		goto err_power_off;
+
+	if (id < 1 || id > 2) {
+		dev_err(&client->dev,
+			"Read OTP 0x3a, id must be 1 or 2, but got: 0x%02x\n", id);
+		ret = -EINVAL;
+		goto err_power_off;
+	}
+
+	/* Read Unique ID bytes sequentially using the correct 4-argument signature */
+	mira220_otp_read(mira220, 0x25, 0, &b0, &ret);
+	mira220_otp_read(mira220, 0x1e, 0, &b1, &ret);
+	mira220_otp_read(mira220, 0x1e, 1, &b2, &ret);
+	mira220_otp_read(mira220, 0x1e, 2, &b3, &ret);
+	mira220_otp_read(mira220, 0x1d, 0, &b4, &ret);
+	mira220_otp_read(mira220, 0x1d, 1, &b5, &ret);
+	mira220_otp_read(mira220, 0x1d, 2, &b6, &ret);
+	mira220_otp_read(mira220, 0x1d, 3, &b7, &ret);
+	if (ret)
+		goto err_power_off;
+
+	dev_dbg(&client->dev, "Unique ID: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+		b7, b6, b5, b4, b3, b2, b1, b0);
+
+	mira220_otp_read(mira220, 0x3e, 0, &b0, &ret);
+	mira220_otp_read(mira220, 0x3e, 1, &b1, &ret);
+	mira220_otp_read(mira220, 0x3e, 2, &b2, &ret);
+	mira220_otp_read(mira220, 0x3e, 3, &b3, &ret);
+	if (ret)
+		goto err_power_off;
+
+	variant = b0 | b1 << 8 | b2 << 16 | b3 << 24;
+
+	switch (variant) {
+	case MIRA220_MONO_PLAIN_PF:
+	case MIRA220_MONO_PLAIN_NO_PF:
+	case MIRA220_MONO_AR_PF:
+		dev_dbg(&client->dev, "Mira220 MONO variant: %x\n", variant);
+		mira220->variant = MIRA220_VARIANT_MONO;
+		break;
+	case MIRA220_RGB_PLAIN_PF:
+	case MIRA220_RGB_PLAIN_NO_PF:
+	case MIRA220_RGB_AR_PF:
+		dev_dbg(&client->dev, "Mira220 RGB variant: %x\n", variant);
+		mira220->variant = MIRA220_VARIANT_RGB;
+		break;
+	default:
+		dev_info(&client->dev,
+			 "Unsupported mira220 variant %x; default to RGB\n",
+			 variant);
+		mira220->variant = MIRA220_VARIANT_RGB;
+		break;
+	}
+
+err_power_off:
+	mira220_otp_power_off(mira220);
+	return ret;
+}
+
+static int mira220_otp_restore(struct mira220 *mira220)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+	static const u16 reg_list[] = {
+		0x4015, 0x4016, 0x4017, 0x4018, 0x403B, 0x4040, 0x4041, 0x4042,
+		0x402a, 0x4029, 0x4009
+	};
+	u8 val;
+	int ret;
+
+	ret = mira220_otp_power_on(mira220);
+	if (ret)
+		return ret;
+
+	/* Write OTP to image sensor when programmed */
+	ret = mira220_otp_read(mira220, 0x1d, 0, &val, NULL);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to read OTP programming status\n");
+		goto out;
+	}
+
+	if (val == 0xff) {
+		dev_info(&client->dev, "OTP not programmed, skipping calibration\n");
+		return mira220_otp_power_off(mira220);
+	}
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(reg_list); i++) {
+		ret = mira220_otp_read(mira220, i, 0, &val, NULL);
+		if (ret < 0) {
+			dev_err(&client->dev, "Failed to read OTP address %d\n", i);
+			goto out;
+		}
+		ret = cci_write(mira220->regmap, CCI_REG8(reg_list[i]), val, NULL);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"Failed to write register 0x%04x\n", reg_list[i]);
+			goto out;
+		}
+		dev_dbg(&client->dev, "OTP CALIBRATION: 0x%04x, val=0x%02x\n",
+			reg_list[i], val);
+	}
+
+	ret = mira220_otp_read(mira220, 0x0d, 0, &val, NULL);
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to read OTP address 0x0d\n");
+		goto out;
+	}
+
+	ret = cci_write(mira220->regmap, CCI_REG8(0x403e), val, NULL);
+	if (ret < 0)
+		dev_err(&client->dev, "Failed to write register 0x403e\n");
+
+out:
+	mira220_otp_power_off(mira220);
+	return ret;
+}
+
+static int mira220_enable_streams(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state, u32 pad,
+				  u64 streams_mask)
+{
+	struct mira220 *mira220 = to_mira220(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(&client->dev);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Apply default values of current mode. Stop streaming before
+	 * uploading register sequence.
+	 */
+	ret = mira220_write_stop_streaming_regs(mira220);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = cci_multi_reg_write(mira220->regmap, mira220_init_reg_list,
+				  ARRAY_SIZE(mira220_init_reg_list), NULL);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = mira220_otp_restore(mira220);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = mira220_set_bus_config(mira220);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = mira220_set_framefmt(mira220, state);
+	if (ret)
+		goto err_rpm_put;
+
+	/* Apply customized values from user */
+	ret = __v4l2_ctrl_handler_setup(mira220->sd.ctrl_handler);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = mira220_write_start_streaming_regs(mira220);
+	if (ret)
+		goto err_rpm_put;
+
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(mira220->hflip, true);
+	__v4l2_ctrl_grab(mira220->vflip, true);
+
+	return 0;
+
+err_rpm_put:
+	pm_runtime_put_autosuspend(&client->dev);
+	return ret;
+}
+
+static int mira220_disable_streams(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *state, u32 pad,
+				   u64 streams_mask)
+{
+	struct mira220 *mira220 = to_mira220(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+
+	mira220_write_stop_streaming_regs(mira220);
+
+	__v4l2_ctrl_grab(mira220->hflip, false);
+	__v4l2_ctrl_grab(mira220->vflip, false);
+
+	pm_runtime_put_autosuspend(&client->dev);
+
+	return 0;
+}
+
+static int mira220_get_regulators(struct mira220 *mira220)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(mira220_supplies); i++)
+		mira220->supplies[i].supply = mira220_supplies[i];
+
+	return devm_regulator_bulk_get(&client->dev,
+				       ARRAY_SIZE(mira220_supplies),
+				       mira220->supplies);
+}
+
+static const struct v4l2_subdev_video_ops mira220_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops mira220_pad_ops = {
+	.enum_mbus_code = mira220_enum_mbus_code,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = mira220_set_pad_format,
+	.get_selection = mira220_get_selection,
+	.enum_frame_size = mira220_enum_frame_size,
+	.enable_streams = mira220_enable_streams,
+	.disable_streams = mira220_disable_streams,
+};
+
+static const struct v4l2_subdev_ops mira220_subdev_ops = {
+	.video = &mira220_video_ops,
+	.pad = &mira220_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mira220_internal_ops = {
+	.init_state = mira220_init_state,
+};
+
+/* Initialize control handlers */
+static int mira220_init_controls(struct mira220 *mira220)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&mira220->sd);
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	struct v4l2_fwnode_device_properties props;
+	struct v4l2_ctrl *ctrl;
+	u32 max_exposure = 0;
+	u32 min_vblank;
+	u32 hblank_val;
+	int ret;
+
+	ctrl_hdlr = &mira220->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+	if (ret)
+		return ret;
+
+	/* By default, PIXEL_RATE is read only */
+	v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops, V4L2_CID_PIXEL_RATE,
+			  MIRA220_PIXEL_RATE, MIRA220_PIXEL_RATE, 1,
+			  MIRA220_PIXEL_RATE);
+
+	min_vblank = mira220_calc_min_vblank(mira220);
+	mira220->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops,
+					    V4L2_CID_VBLANK,
+					    min_vblank, MIRA220_MAX_VBLANK, 1,
+					    min_vblank);
+
+	ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, &mira220_link_freqs[0]);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	/*
+	 * Scale hblank according to the number of enabled data lanes to match
+	 * row_length.
+	 */
+	hblank_val = MIRA220_LLP_1600x1400_304 * (2 / mira220->lanes)
+		   - MIRA220_PIXEL_ARRAY_WIDTH;
+	ctrl = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, hblank_val,
+				 hblank_val, 1, hblank_val);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	/* Max exposure is determined by vblank + vsize and Tglob. */
+	max_exposure = mira220_calc_exposure(mira220,
+					     MIRA220_PIXEL_ARRAY_HEIGHT,
+					     min_vblank);
+
+	mira220->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops,
+					      V4L2_CID_EXPOSURE,
+					      MIRA220_EXPOSURE_MIN,
+					      max_exposure, 1,
+					      MIRA220_DEFAULT_EXPOSURE);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_ANALOGUE_GAIN,
+			  MIRA220_ANALOG_GAIN_MIN, MIRA220_ANALOG_GAIN_MAX,
+			  MIRA220_ANALOG_GAIN_STEP,
+			  MIRA220_ANALOG_GAIN_DEFAULT);
+
+	mira220->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops,
+					   V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (mira220->hflip)
+		mira220->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	mira220->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &mira220_ctrl_ops,
+					   V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (mira220->vflip)
+		mira220->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &mira220_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(mira220_test_pattern_menu) - 1,
+				     0, 0, mira220_test_pattern_menu);
+
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		goto error;
+	}
+
+	ret = v4l2_fwnode_device_parse(&client->dev, &props);
+	if (ret)
+		goto error;
+
+	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &mira220_ctrl_ops,
+					      &props);
+	if (ret)
+		goto error;
+
+	mira220->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+
+error:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+
+	return ret;
+}
+
+static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira220)
+{
+	struct fwnode_handle *endpoint;
+	struct v4l2_fwnode_endpoint ep_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	int ret = 0;
+
+	endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+	if (!endpoint) {
+		dev_err(dev, "Endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
+		ret = -EINVAL;
+		dev_err(dev, "Failed to parse endpoint\n");
+		goto error_out;
+	}
+
+	/* Non-continuous mode not implemented. */
+	if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) {
+		dev_warn(dev, "clock non-continuous mode not supported\n");
+		ret = -EINVAL;
+		goto error_out;
+	}
+
+	if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) {
+		ret = -EINVAL;
+		dev_err(dev, "Unsupported number of link_frequencies: %u\n",
+			ep_cfg.nr_of_link_frequencies);
+		goto error_out;
+	}
+
+	if (ep_cfg.link_frequencies[0] != MIRA220_LINK_FREQ_750M) {
+		ret = -EINVAL;
+		dev_err(dev, "Unsupported link_frequency: %llu\n",
+			ep_cfg.link_frequencies[0]);
+		goto error_out;
+	}
+
+	/* Check the number of MIPI CSI2 data lanes */
+	if (ep_cfg.bus.mipi_csi2.num_data_lanes != 1 &&
+	    ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
+		ret = -EINVAL;
+		dev_err(dev, "%u data lanes are not supported\n",
+			ep_cfg.bus.mipi_csi2.num_data_lanes);
+		goto error_out;
+	}
+
+	mira220->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
+	mira220->row_length = MIRA220_ROW_LENGTH_MIN * (2 / mira220->lanes);
+
+error_out:
+	v4l2_fwnode_endpoint_free(&ep_cfg);
+	fwnode_handle_put(endpoint);
+
+	return ret;
+}
+
+static int mira220_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct mira220 *mira220;
+	int ret;
+
+	mira220 = devm_kzalloc(&client->dev, sizeof(*mira220), GFP_KERNEL);
+	if (!mira220)
+		return -ENOMEM;
+
+	v4l2_i2c_subdev_init(&mira220->sd, client, &mira220_subdev_ops);
+	mira220->sd.internal_ops = &mira220_internal_ops;
+
+	ret = mira220_parse_endpoint(dev, mira220);
+	if (ret)
+		return ret;
+
+	mira220->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(mira220->regmap))
+		return dev_err_probe(dev, PTR_ERR(mira220->regmap),
+				     "failed to initialize CCI\n");
+
+	/* Get system clock (xclk) */
+	mira220->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
+	if (IS_ERR(mira220->xclk))
+		return dev_err_probe(dev, PTR_ERR(mira220->xclk),
+				     "failed to get xclk\n");
+
+	mira220->xclk_freq = clk_get_rate(mira220->xclk);
+	if (mira220->xclk_freq != MIRA220_SUPPORTED_XCLK_FREQ) {
+		dev_err(dev, "xclk frequency not supported: %d Hz\n",
+			mira220->xclk_freq);
+		return -EINVAL;
+	}
+
+	ret = mira220_get_regulators(mira220);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+	mira220->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						      GPIOD_OUT_HIGH);
+	if (IS_ERR(mira220->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(mira220->reset_gpio),
+				     "failed to get reset gpio\n");
+
+	ret = mira220_power_on(dev);
+	if (ret)
+		return ret;
+
+	/* Enable runtime PM and power on the device */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	ret = mira220_identify_module(mira220);
+	if (ret)
+		goto error_power_off;
+
+	ret = mira220_init_controls(mira220);
+	if (ret)
+		goto error_power_off;
+
+	/* Initialize subdev */
+	mira220->sd.internal_ops = &mira220_internal_ops;
+	mira220->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	mira220->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+	/* Initialize source pads */
+	mira220->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&mira220->sd.entity, 1, &mira220->pad);
+	if (ret) {
+		dev_err_probe(dev, ret, "failed to init entity pads\n");
+		goto error_handler_free;
+	}
+
+	mira220->sd.state_lock = mira220->ctrl_handler.lock;
+	ret = v4l2_subdev_init_finalize(&mira220->sd);
+	if (ret < 0) {
+		dev_err_probe(dev, ret, "subdev init error\n");
+		goto error_media_entity;
+	}
+
+	ret = v4l2_async_register_subdev_sensor(&mira220->sd);
+	if (ret < 0) {
+		dev_err_probe(dev, ret,
+			      "failed to register sensor sub-device\n");
+		goto error_subdev_cleanup;
+	}
+
+	pm_runtime_idle(dev);
+	pm_runtime_set_autosuspend_delay(dev, 1000);
+	pm_runtime_use_autosuspend(dev);
+
+	return 0;
+
+error_subdev_cleanup:
+	v4l2_subdev_cleanup(&mira220->sd);
+error_media_entity:
+	media_entity_cleanup(&mira220->sd.entity);
+error_handler_free:
+	v4l2_ctrl_handler_free(mira220->sd.ctrl_handler);
+error_power_off:
+	pm_runtime_disable(dev);
+	mira220_power_off(dev);
+	pm_runtime_set_suspended(dev);
+	return ret;
+}
+
+static void mira220_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct mira220 *mira220 = to_mira220(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	v4l2_subdev_cleanup(&mira220->sd);
+	media_entity_cleanup(&sd->entity);
+
+	v4l2_ctrl_handler_free(mira220->sd.ctrl_handler);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		mira220_power_off(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct dev_pm_ops mira220_pm_ops = {
+	SET_RUNTIME_PM_OPS(mira220_power_off, mira220_power_on, NULL)
+};
+
+static const struct of_device_id mira220_dt_ids[] = {
+	{ .compatible = "ams,mira220" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mira220_dt_ids);
+
+static struct i2c_driver mira220_i2c_driver = {
+	.driver = {
+		.name = "mira220",
+		.of_match_table	= mira220_dt_ids,
+		.pm = pm_ptr(&mira220_pm_ops),
+	},
+	.probe = mira220_probe,
+	.remove = mira220_remove,
+};
+
+module_i2c_driver(mira220_i2c_driver);
+
+MODULE_AUTHOR("Philippe Baetens <philippe.baetens@ams-osram.com>");
+MODULE_DESCRIPTION("ams MIRA220 sensor driver");
+MODULE_LICENSE("GPL");

-- 
2.54.0


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

* Re: [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220
  2026-07-24 15:37 ` [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220 Jacopo Mondi
@ 2026-07-24 18:57   ` Jai Luthra
  2026-07-27  6:46     ` Jacopo Mondi
  0 siblings, 1 reply; 7+ messages in thread
From: Jai Luthra @ 2026-07-24 18:57 UTC (permalink / raw)
  To: Conor Dooley, Jacopo Mondi, Krzysztof Kozlowski,
	Mauro Carvalho Chehab, Philippe Baetens, Rob Herring,
	Sakari Ailus
  Cc: Kieran Bingham, linux-media, devicetree, linux-kernel,
	Jacopo Mondi

Hi Jacopo,

Thank you for the fixes!

Quoting Jacopo Mondi (2026-07-24 21:07:01)
> From: Philippe Baetens <philippebaetens@gmail.com>
> 
> Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image
> sensor.
> 
> Mira220 is a global shutter image sensor with a resolution of 1600x1400
> pixels.
> 
> The driver implements support for mono and RGB 12, 10 and 8 bits
> formats. The output data-rate per lane is 1500Mbit/s, with a maximum
> frame rate up to 90 fps.
> 
> Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> 

[...]

> +static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira220)
> +{
> +       struct fwnode_handle *endpoint;
> +       struct v4l2_fwnode_endpoint ep_cfg = {
> +               .bus_type = V4L2_MBUS_CSI2_DPHY
> +       };
> +       int ret = 0;
> +
> +       endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> +       if (!endpoint) {
> +               dev_err(dev, "Endpoint node not found\n");
> +               return -EINVAL;
> +       }
> +
> +       if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> +               ret = -EINVAL;
> +               dev_err(dev, "Failed to parse endpoint\n");
> +               goto error_out;
> +       }
> +
> +       /* Non-continuous mode not implemented. */
> +       if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) {
> +               dev_warn(dev, "clock non-continuous mode not supported\n");
> +               ret = -EINVAL;
> +               goto error_out;
> +       }
> +
> +       if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) {
> +               ret = -EINVAL;
> +               dev_err(dev, "Unsupported number of link_frequencies: %u\n",
> +                       ep_cfg.nr_of_link_frequencies);
> +               goto error_out;
> +       }
> +
> +       if (ep_cfg.link_frequencies[0] != MIRA220_LINK_FREQ_750M) {

The LLM-bot says link_frequencies[0] might be invalid if DT omitted the
link-frequencies property, and I think that's a valid concern.

Any particular reason for not using the v4l2_link_freq_to_bitmap() helper I
had suggested? It can handles all of these cases and error msgs too.

With the edge case fixed (with or without the helper):

Reviewed-by: Jai Luthra <jai.luthra@ideasonboard.com>

Thanks,
    Jai
> +               ret = -EINVAL;
> +               dev_err(dev, "Unsupported link_frequency: %llu\n",
> +                       ep_cfg.link_frequencies[0]);
> +               goto error_out;
> +       }
> +
> +       /* Check the number of MIPI CSI2 data lanes */
> +       if (ep_cfg.bus.mipi_csi2.num_data_lanes != 1 &&
> +           ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
> +               ret = -EINVAL;
> +               dev_err(dev, "%u data lanes are not supported\n",
> +                       ep_cfg.bus.mipi_csi2.num_data_lanes);
> +               goto error_out;
> +       }
> +
> +       mira220->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
> +       mira220->row_length = MIRA220_ROW_LENGTH_MIN * (2 / mira220->lanes);
> +
> +error_out:
> +       v4l2_fwnode_endpoint_free(&ep_cfg);
> +       fwnode_handle_put(endpoint);
> +
> +       return ret;
> +}
> +

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

* Re: [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220
  2026-07-24 18:57   ` Jai Luthra
@ 2026-07-27  6:46     ` Jacopo Mondi
  2026-07-27  7:43       ` Jai Luthra
  0 siblings, 1 reply; 7+ messages in thread
From: Jacopo Mondi @ 2026-07-27  6:46 UTC (permalink / raw)
  To: Jai Luthra
  Cc: Conor Dooley, Jacopo Mondi, Krzysztof Kozlowski,
	Mauro Carvalho Chehab, Philippe Baetens, Rob Herring,
	Sakari Ailus, Kieran Bingham, linux-media, devicetree,
	linux-kernel

Hi Jai

On Sat, Jul 25, 2026 at 12:27:25AM +0530, Jai Luthra wrote:
> Hi Jacopo,
>
> Thank you for the fixes!
>
> Quoting Jacopo Mondi (2026-07-24 21:07:01)
> > From: Philippe Baetens <philippebaetens@gmail.com>
> >
> > Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image
> > sensor.
> >
> > Mira220 is a global shutter image sensor with a resolution of 1600x1400
> > pixels.
> >
> > The driver implements support for mono and RGB 12, 10 and 8 bits
> > formats. The output data-rate per lane is 1500Mbit/s, with a maximum
> > frame rate up to 90 fps.
> >
> > Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
> > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> >
>
> [...]
>
> > +static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira220)
> > +{
> > +       struct fwnode_handle *endpoint;
> > +       struct v4l2_fwnode_endpoint ep_cfg = {
> > +               .bus_type = V4L2_MBUS_CSI2_DPHY
> > +       };
> > +       int ret = 0;
> > +
> > +       endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> > +       if (!endpoint) {
> > +               dev_err(dev, "Endpoint node not found\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> > +               ret = -EINVAL;
> > +               dev_err(dev, "Failed to parse endpoint\n");
> > +               goto error_out;
> > +       }
> > +
> > +       /* Non-continuous mode not implemented. */
> > +       if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) {
> > +               dev_warn(dev, "clock non-continuous mode not supported\n");
> > +               ret = -EINVAL;
> > +               goto error_out;
> > +       }
> > +
> > +       if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) {
> > +               ret = -EINVAL;
> > +               dev_err(dev, "Unsupported number of link_frequencies: %u\n",
> > +                       ep_cfg.nr_of_link_frequencies);
> > +               goto error_out;
> > +       }
> > +
> > +       if (ep_cfg.link_frequencies[0] != MIRA220_LINK_FREQ_750M) {
>
> The LLM-bot says link_frequencies[0] might be invalid if DT omitted the
> link-frequencies property, and I think that's a valid concern.

Yeah the bot is right, my bad

I thought the property was required by fw, but it's not.

>
> Any particular reason for not using the v4l2_link_freq_to_bitmap() helper I

I think I missed it from your previous comment, sorry :)

Now that I look at it, the function wants link_frequencies from fw

drivers/media/v4l2-core/v4l2-common.c-  if (!num_of_fw_link_freqs) {
drivers/media/v4l2-core/v4l2-common.c-          dev_err(dev, "no link frequencies in firmware\n");
drivers/media/v4l2-core/v4l2-common.c-          return -ENODATA;
drivers/media/v4l2-core/v4l2-common.c-  }

Should I:

- make link_frequencies required in bindings (something that doesn't
  seem that useful considering a single freq is supported)

- only call v4l2_link_freq_to_bitmap() if the property is specified ?

?

> had suggested? It can handles all of these cases and error msgs too.
>
> With the edge case fixed (with or without the helper):
>
> Reviewed-by: Jai Luthra <jai.luthra@ideasonboard.com>

Thanks
  j

>
> Thanks,
>     Jai
> > +               ret = -EINVAL;
> > +               dev_err(dev, "Unsupported link_frequency: %llu\n",
> > +                       ep_cfg.link_frequencies[0]);
> > +               goto error_out;
> > +       }
> > +
> > +       /* Check the number of MIPI CSI2 data lanes */
> > +       if (ep_cfg.bus.mipi_csi2.num_data_lanes != 1 &&
> > +           ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
> > +               ret = -EINVAL;
> > +               dev_err(dev, "%u data lanes are not supported\n",
> > +                       ep_cfg.bus.mipi_csi2.num_data_lanes);
> > +               goto error_out;
> > +       }
> > +
> > +       mira220->lanes = ep_cfg.bus.mipi_csi2.num_data_lanes;
> > +       mira220->row_length = MIRA220_ROW_LENGTH_MIN * (2 / mira220->lanes);
> > +
> > +error_out:
> > +       v4l2_fwnode_endpoint_free(&ep_cfg);
> > +       fwnode_handle_put(endpoint);
> > +
> > +       return ret;
> > +}
> > +

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

* Re: [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220
  2026-07-27  6:46     ` Jacopo Mondi
@ 2026-07-27  7:43       ` Jai Luthra
  2026-07-27  7:47         ` Jacopo Mondi
  0 siblings, 1 reply; 7+ messages in thread
From: Jai Luthra @ 2026-07-27  7:43 UTC (permalink / raw)
  To: Jacopo Mondi
  Cc: Conor Dooley, Jacopo Mondi, Krzysztof Kozlowski,
	Mauro Carvalho Chehab, Philippe Baetens, Rob Herring,
	Sakari Ailus, Kieran Bingham, linux-media, devicetree,
	linux-kernel

Hi Jacopo,

Quoting Jacopo Mondi (2026-07-27 12:16:53)
> Hi Jai
> 
> On Sat, Jul 25, 2026 at 12:27:25AM +0530, Jai Luthra wrote:
> > Hi Jacopo,
> >
> > Thank you for the fixes!
> >
> > Quoting Jacopo Mondi (2026-07-24 21:07:01)
> > > From: Philippe Baetens <philippebaetens@gmail.com>
> > >
> > > Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image
> > > sensor.
> > >
> > > Mira220 is a global shutter image sensor with a resolution of 1600x1400
> > > pixels.
> > >
> > > The driver implements support for mono and RGB 12, 10 and 8 bits
> > > formats. The output data-rate per lane is 1500Mbit/s, with a maximum
> > > frame rate up to 90 fps.
> > >
> > > Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
> > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > >
> >
> > [...]
> >
> > > +static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira220)
> > > +{
> > > +       struct fwnode_handle *endpoint;
> > > +       struct v4l2_fwnode_endpoint ep_cfg = {
> > > +               .bus_type = V4L2_MBUS_CSI2_DPHY
> > > +       };
> > > +       int ret = 0;
> > > +
> > > +       endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> > > +       if (!endpoint) {
> > > +               dev_err(dev, "Endpoint node not found\n");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> > > +               ret = -EINVAL;
> > > +               dev_err(dev, "Failed to parse endpoint\n");
> > > +               goto error_out;
> > > +       }
> > > +
> > > +       /* Non-continuous mode not implemented. */
> > > +       if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) {
> > > +               dev_warn(dev, "clock non-continuous mode not supported\n");
> > > +               ret = -EINVAL;
> > > +               goto error_out;
> > > +       }
> > > +
> > > +       if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) {
> > > +               ret = -EINVAL;
> > > +               dev_err(dev, "Unsupported number of link_frequencies: %u\n",
> > > +                       ep_cfg.nr_of_link_frequencies);
> > > +               goto error_out;
> > > +       }
> > > +
> > > +       if (ep_cfg.link_frequencies[0] != MIRA220_LINK_FREQ_750M) {
> >
> > The LLM-bot says link_frequencies[0] might be invalid if DT omitted the
> > link-frequencies property, and I think that's a valid concern.
> 
> Yeah the bot is right, my bad
> 
> I thought the property was required by fw, but it's not.
> 
> >
> > Any particular reason for not using the v4l2_link_freq_to_bitmap() helper I
> 
> I think I missed it from your previous comment, sorry :)
> 
> Now that I look at it, the function wants link_frequencies from fw
> 
> drivers/media/v4l2-core/v4l2-common.c-  if (!num_of_fw_link_freqs) {
> drivers/media/v4l2-core/v4l2-common.c-          dev_err(dev, "no link frequencies in firmware\n");
> drivers/media/v4l2-core/v4l2-common.c-          return -ENODATA;
> drivers/media/v4l2-core/v4l2-common.c-  }
> 

Ah thanks for checking, I missed that requirement for using the helper.

> Should I:
> 
> - make link_frequencies required in bindings (something that doesn't
>   seem that useful considering a single freq is supported)
> 

I have a slight preference towards this option.

Many receiver drivers use v4l2_get_link_freq() to configure the PHY, so
even a single freq in the endpoint is useful. Plus it would keep the driver
logic simple too.

Thanks,
    Jai

> - only call v4l2_link_freq_to_bitmap() if the property is specified ?
> 
> ?
> 
> > had suggested? It can handles all of these cases and error msgs too.
> >
> > With the edge case fixed (with or without the helper):
> >
> > Reviewed-by: Jai Luthra <jai.luthra@ideasonboard.com>
> 
> Thanks
>   j
> 
> >
> > Thanks,
> >     Jai

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

* Re: [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220
  2026-07-27  7:43       ` Jai Luthra
@ 2026-07-27  7:47         ` Jacopo Mondi
  0 siblings, 0 replies; 7+ messages in thread
From: Jacopo Mondi @ 2026-07-27  7:47 UTC (permalink / raw)
  To: Jai Luthra
  Cc: Jacopo Mondi, Conor Dooley, Krzysztof Kozlowski,
	Mauro Carvalho Chehab, Philippe Baetens, Rob Herring,
	Sakari Ailus, Kieran Bingham, linux-media, devicetree,
	linux-kernel

Hi Jai

On Mon, Jul 27, 2026 at 01:13:24PM +0530, Jai Luthra wrote:
> Hi Jacopo,
>
> Quoting Jacopo Mondi (2026-07-27 12:16:53)
> > Hi Jai
> >
> > On Sat, Jul 25, 2026 at 12:27:25AM +0530, Jai Luthra wrote:
> > > Hi Jacopo,
> > >
> > > Thank you for the fixes!
> > >
> > > Quoting Jacopo Mondi (2026-07-24 21:07:01)
> > > > From: Philippe Baetens <philippebaetens@gmail.com>
> > > >
> > > > Add a V4L2 subdev driver for driver for the AMS-OSRAM Mira220 image
> > > > sensor.
> > > >
> > > > Mira220 is a global shutter image sensor with a resolution of 1600x1400
> > > > pixels.
> > > >
> > > > The driver implements support for mono and RGB 12, 10 and 8 bits
> > > > formats. The output data-rate per lane is 1500Mbit/s, with a maximum
> > > > frame rate up to 90 fps.
> > > >
> > > > Signed-off-by: Philippe Baetens <philippebaetens@gmail.com>
> > > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > >
> > >
> > > [...]
> > >
> > > > +static int mira220_parse_endpoint(struct device *dev, struct mira220 *mira220)
> > > > +{
> > > > +       struct fwnode_handle *endpoint;
> > > > +       struct v4l2_fwnode_endpoint ep_cfg = {
> > > > +               .bus_type = V4L2_MBUS_CSI2_DPHY
> > > > +       };
> > > > +       int ret = 0;
> > > > +
> > > > +       endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> > > > +       if (!endpoint) {
> > > > +               dev_err(dev, "Endpoint node not found\n");
> > > > +               return -EINVAL;
> > > > +       }
> > > > +
> > > > +       if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
> > > > +               ret = -EINVAL;
> > > > +               dev_err(dev, "Failed to parse endpoint\n");
> > > > +               goto error_out;
> > > > +       }
> > > > +
> > > > +       /* Non-continuous mode not implemented. */
> > > > +       if (ep_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) {
> > > > +               dev_warn(dev, "clock non-continuous mode not supported\n");
> > > > +               ret = -EINVAL;
> > > > +               goto error_out;
> > > > +       }
> > > > +
> > > > +       if (ep_cfg.nr_of_link_frequencies > ARRAY_SIZE(mira220_link_freqs)) {
> > > > +               ret = -EINVAL;
> > > > +               dev_err(dev, "Unsupported number of link_frequencies: %u\n",
> > > > +                       ep_cfg.nr_of_link_frequencies);
> > > > +               goto error_out;
> > > > +       }
> > > > +
> > > > +       if (ep_cfg.link_frequencies[0] != MIRA220_LINK_FREQ_750M) {
> > >
> > > The LLM-bot says link_frequencies[0] might be invalid if DT omitted the
> > > link-frequencies property, and I think that's a valid concern.
> >
> > Yeah the bot is right, my bad
> >
> > I thought the property was required by fw, but it's not.
> >
> > >
> > > Any particular reason for not using the v4l2_link_freq_to_bitmap() helper I
> >
> > I think I missed it from your previous comment, sorry :)
> >
> > Now that I look at it, the function wants link_frequencies from fw
> >
> > drivers/media/v4l2-core/v4l2-common.c-  if (!num_of_fw_link_freqs) {
> > drivers/media/v4l2-core/v4l2-common.c-          dev_err(dev, "no link frequencies in firmware\n");
> > drivers/media/v4l2-core/v4l2-common.c-          return -ENODATA;
> > drivers/media/v4l2-core/v4l2-common.c-  }
> >
>
> Ah thanks for checking, I missed that requirement for using the helper.
>
> > Should I:
> >
> > - make link_frequencies required in bindings (something that doesn't
> >   seem that useful considering a single freq is supported)
> >
>
> I have a slight preference towards this option.
>
> Many receiver drivers use v4l2_get_link_freq() to configure the PHY, so
> even a single freq in the endpoint is useful. Plus it would keep the driver
> logic simple too.

Note that the driver registers V4L2_CID_LINK_FREQUENCY regardless of
the presence of the dts property.

I can make it required if it's simpler.

>
> Thanks,
>     Jai
>
> > - only call v4l2_link_freq_to_bitmap() if the property is specified ?
> >
> > ?
> >
> > > had suggested? It can handles all of these cases and error msgs too.
> > >
> > > With the edge case fixed (with or without the helper):
> > >
> > > Reviewed-by: Jai Luthra <jai.luthra@ideasonboard.com>
> >
> > Thanks
> >   j
> >
> > >
> > > Thanks,
> > >     Jai

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

end of thread, other threads:[~2026-07-27  7:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 15:36 [PATCH v8 0/2] media: i2c: Add driver for Mira220 Jacopo Mondi
2026-07-24 15:37 ` [PATCH v8 1/2] dt-bindings: media: i2c: Add mira220 image sensor Jacopo Mondi
2026-07-24 15:37 ` [PATCH v8 2/2] media: i2c: Add driver for AMS-OSRAM Mira220 Jacopo Mondi
2026-07-24 18:57   ` Jai Luthra
2026-07-27  6:46     ` Jacopo Mondi
2026-07-27  7:43       ` Jai Luthra
2026-07-27  7:47         ` Jacopo Mondi

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