All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] media: Add OmniVision OV32C4 sensor driver
@ 2026-08-26  7:19 Robert Bozik
  2026-08-26  7:20 ` [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4 Robert Bozik
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Robert Bozik @ 2026-08-26  7:19 UTC (permalink / raw)
  To: linux-media
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel,
	Robert Bozik

Hi,

this series adds a driver for the OmniVision OV32C4, a 32 megapixel RGBC
CMOS image sensor. It ships as the under-display camera in the Lenovo
Yoga Slim 9 14ILL10, where it is enumerated through ACPI (_HID
"OVTI32C4") behind an INT3472 discrete control logic node and feeds an
Intel IPU7. The last patch adds the sensor to ipu-bridge; without it the
bridge builds no fwnode graph for the sensor and the driver never binds.

The driver supports 3264x1840 at 30 fps, 10-bit Bayer, 4 CSI-2 lanes at
a 400 MHz link frequency, with exposure, analogue gain, digital gain,
vblank, hblank and flip controls, runtime PM and .get_selection.

Tested on the machine above: the sensor probes, streams continuously at
a measured 30.00 fps, and the frames arrive complete (60 frames =
720691200 bytes = 60 * 3264 * 1840 * 2, V4L2_PIX_FMT_SGRBG10 carrying
one 16-bit sample per pixel). The full path up to a processed image was
exercised with libcamera's software ISP.

There is no public datasheet for this sensor, so a note on where the
numbers come from, since that is the first thing a reviewer will want to
know:

  - The mode register table is the verbatim initialisation sequence from
    the vendor Windows driver: 1787 writes, strictly ascending, copied
    1:1 with nothing added or reordered.
  - The register meanings the controls depend on (exposure 0x3500,
    analogue gain 0x3508, digital gain 0x350a, VTS 0x380e, and the rule
    exposure_max = VTS - 32) were read out of the same binary and then
    confirmed against the values the chip reports.
  - The derived timings were checked against reality: the computed
    320000000 / (4080 * 2614) = 30.005 fps matches the measured 30.00.
  - The 6560x4928 pixel array and the 6528x4896 active area reported by
    .get_selection follow from the window registers of the mode table
    and agree with the vendor's published product brief.

The gain ranges in this series were measured on the sensor, not
inherited: the vendor driver clamps gain a layer above and carries no
limits of its own, and the obvious donor - ov13b10, same registers -
puts analogue unity at 0x80, which turned out to be wrong here. Analogue
response is exactly proportional between 0x100 and 0x7c0 (1x to 7.75x,
0x100 also being the power-up value); digital gain is proportional with
1024 as unity and clips to black one step above 16383. I mention it
because these are the kind of constants that get copied between
OmniVision drivers unchecked.

Flip handling was measured the same way. This sensor preserves the Bayer
order across mirror and flip, so the driver only toggles the bits and the
media bus code never changes. I mention it because ov13b10 compensates the
crop window by one pixel on the same registers to undo a Bayer shift; doing
that here introduces one rather than removing it, which is easy to copy
across by accident.

Two things I would like reviewers to look at, because I am not confident
they are right:

1) The sensor core rail is gated by a companion chip that ACPI lists as
   the second I2C resource of _CRS and that ipu-bridge instantiates as a
   VCM. Without a single write of 0x04 to register 0x1001 on that chip,
   the sensor does not answer on I2C at all. The driver currently does
   that write itself with a bare i2c_transfer(), deliberately not
   claiming the address so the VCM driver can still have it.

   I am aware this bypasses both the I2C device model and the regulator
   framework, and that the architecturally correct answer is probably a
   regulator provided by the VCM driver, consumed here as dvdd-supply.
   I did not want to redesign ipu-bridge's VCM handling as part of an
   initial sensor submission, so I am asking rather than assuming. If
   the bare write is not acceptable, I am happy to do it properly -
   I would just like guidance on the shape.

2) While streaming, the IPU7 receiver reports exactly one

     csi2-0 error: Received packet is too long

   per frame. It is not a lost or corrupted frame: bit 2 of the D-PHY
   error register ("unrecognised data type") never fires, the image data
   is complete, and the byte count above is exact.

   I could not make it go away from the sensor side. All of 0x4800-0x48FF
   and 0x3800-0x38FF were swept register by register - 512 measurements -
   and no value silences it while streaming continues. The vendor driver
   does not suppress it either; it exposes EnableEmbeddedData next to the
   MIPI link parameters and the Windows IPU stack has a matching
   GetEmbeddedData path, i.e. Windows receives that extra packet rather
   than turning it off.

   Receiving it on Linux would need metadata capture in the IPU7 driver,
   which is marked as a TODO there today, plus a second frame descriptor
   entry - and adding that entry alone makes the IPU7 wait for a second
   capture node that does not exist in the graph, so streaming stops.
   I therefore left the driver reporting a single stream and am
   documenting the warning here rather than papering over it.

Tooling disclosure, as asked for by Documentation/process/generated-content.rst:
this series was written with the help of an AI coding assistant (Claude,
Anthropic; claude-opus-5 for the early work, claude-fable-5 for the rest)
in an extended interactive session. The assistant drafted the driver
source, the binding and this cover letter from my descriptions of the
hardware and of the vendor driver; every register meaning, gain range and
timing in it was measured by me on the sensor as described above, I ran
all of the tests, and I have reviewed and understand all of the code and
take responsibility for it. The mode register table was copied 1:1 from
the vendor driver, not generated. Static checks used: checkpatch.pl
--strict, sparse (C=1), W=1 and dt_binding_check. The individual patches
carry Assisted-by tags.

The series applies to media_stage.git; base-commit is below.

Thanks,
Robert

Robert Bozik (3):
  dt-bindings: media: i2c: Add OmniVision OV32C4
  media: i2c: Add driver for OmniVision OV32C4
  media: ipu-bridge: Add OmniVision OV32C4

 .../bindings/media/i2c/ovti,ov32c4.yaml       |  109 +
 MAINTAINERS                                   |    8 +
 drivers/media/i2c/Kconfig                     |   11 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/ov32c4.c                    | 2839 +++++++++++++++++
 drivers/media/pci/intel/ipu-bridge.c          |   10 +
 6 files changed, 2978 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
 create mode 100644 drivers/media/i2c/ov32c4.c


base-commit: 4900cad020c0580dfb1be27776ff10a4ef110cfa
-- 
2.53.0


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

* [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4
  2026-08-26  7:19 [PATCH 0/3] media: Add OmniVision OV32C4 sensor driver Robert Bozik
@ 2026-08-26  7:20 ` Robert Bozik
  2026-08-26 16:13   ` Conor Dooley
  2026-08-26  7:20 ` [PATCH 2/3] media: i2c: Add driver for " Robert Bozik
  2026-08-26  7:20 ` [PATCH 3/3] media: ipu-bridge: Add " Robert Bozik
  2 siblings, 1 reply; 7+ messages in thread
From: Robert Bozik @ 2026-08-26  7:20 UTC (permalink / raw)
  To: linux-media
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel,
	Robert Bozik

Add a binding for the OmniVision OV32C4, a 32 megapixel RGBC CMOS image
sensor with on-chip 4-cell fusion, a 4-lane MIPI CSI-2 D-PHY transmitter
and the standard SCCB command interface.

The sensor ships in laptops as an under-display camera, where it is
described by ACPI; the binding follows ovti,ov08x40.yaml, which covers
the closest relative already in tree.

Signed-off-by: Robert Bozik <robertbozik@gmail.com>
Assisted-by: Claude:claude-opus-5 sparse
Assisted-by: Claude:claude-fable-5 sparse
---
 .../bindings/media/i2c/ovti,ov32c4.yaml       | 109 ++++++++++++++++++
 1 file changed, 109 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
new file mode 100644
index 000000000..be6067c75
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,ov32c4.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OV32C4 Image Sensor
+
+maintainers:
+  - Robert Bozik <robertbozik@gmail.com>
+
+description:
+  The OmniVision OV32C4 is a 32 megapixel RGBC (red, green, blue and clear)
+  CMOS image sensor in a 4-cell arrangement, with on-chip 4-cell fusion and
+  binning. It has a 6528x4896 active pixel array, a 4-lane MIPI CSI-2 D-PHY
+  transmitter and the standard SCCB command interface. The driver supports
+  3264x1840 at 30 fps, 10-bit Bayer, at a link frequency of 400 MHz.
+
+allOf:
+  - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+  compatible:
+    const: ovti,ov32c4
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: Input clock (XVCLK), 19.2 MHz.
+
+  avdd-supply:
+    description: Analogue circuit voltage supply.
+
+  dovdd-supply:
+    description: I/O circuit voltage supply.
+
+  dvdd-supply:
+    description: Digital core voltage supply.
+
+  reset-gpios:
+    maxItems: 1
+    description: Active low GPIO connected to the XSHUTDOWN pad of the sensor.
+
+  port:
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        additionalProperties: false
+
+        properties:
+          data-lanes:
+            items:
+              - const: 1
+              - const: 2
+              - const: 3
+              - const: 4
+          link-frequencies: true
+          remote-endpoint: true
+
+        required:
+          - data-lanes
+          - link-frequencies
+          - remote-endpoint
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - port
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        camera@36 {
+            compatible = "ovti,ov32c4";
+            reg = <0x36>;
+
+            clocks = <&ov32c4_clk>;
+            assigned-clocks = <&ov32c4_clk>;
+            assigned-clock-rates = <19200000>;
+
+            avdd-supply = <&vreg_2p8>;
+            dovdd-supply = <&vreg_1p8>;
+            dvdd-supply = <&vreg_1p1>;
+
+            reset-gpios = <&tlmm 111 GPIO_ACTIVE_LOW>;
+
+            port {
+                ov32c4_ep: endpoint {
+                    remote-endpoint = <&csiphy0_ep>;
+                    data-lanes = <1 2 3 4>;
+                    link-frequencies = /bits/ 64 <400000000>;
+                };
+            };
+        };
+    };
+...
-- 
2.53.0


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

* [PATCH 2/3] media: i2c: Add driver for OmniVision OV32C4
  2026-08-26  7:19 [PATCH 0/3] media: Add OmniVision OV32C4 sensor driver Robert Bozik
  2026-08-26  7:20 ` [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4 Robert Bozik
@ 2026-08-26  7:20 ` Robert Bozik
  2026-08-26  7:31   ` sashiko-bot
  2026-08-26  7:20 ` [PATCH 3/3] media: ipu-bridge: Add " Robert Bozik
  2 siblings, 1 reply; 7+ messages in thread
From: Robert Bozik @ 2026-08-26  7:20 UTC (permalink / raw)
  To: linux-media
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel,
	Robert Bozik

The OV32C4 is a 32 megapixel RGBC CMOS image sensor with on-chip 4-cell
fusion and binning, connected over 4 MIPI CSI-2 D-PHY lanes. It is used
as an under-display camera in laptops, where it is enumerated through
ACPI (_HID "OVTI32C4") and paired with an Intel IPU.

The driver supports 3264x1840 at 30 fps in 10-bit Bayer at a link
frequency of 400 MHz, with exposure, analogue gain, digital gain,
vblank, hblank and horizontal and vertical flip controls, runtime PM
and .get_selection reporting the 6560x4928 pixel array.

There is no public datasheet for this sensor. The mode register table is
the verbatim initialisation sequence from the vendor driver; the meaning
of the individual registers used by the controls was established by
correlating that binary with the values read back from the chip, and the
derived timings were verified against a measured 30.00 fps.

Three things were measured on the sensor rather than inherited from the
OmniVision drivers already in tree, because they turned out to differ:

 - The analogue gain range. Response is proportional to the register
   value from 0x100 to 0x7c0, so 0x100 is unity gain, which is also what
   the chip powers up with. Below 0x100 the output stops tracking the
   value written - 0xe0 and 0xff give the same result - and above 0x7c0
   it no longer increases. ov13b10 uses the same register with unity at
   0x80; that does not hold here.

 - The digital gain range. The register is proportional with 1024 as
   unity - writing 2x, 4x and 8x of the power-up value gives 1.99x,
   3.86x and 7.62x, the shortfall at the top being sensor clipping - and
   the field is 14 bits wide: one step above 16383 produces a black
   frame, so the control stops there.

 - The effect of flipping on the Bayer order. This sensor keeps the
   order across both mirror and flip, so the driver only toggles the
   bits and the media bus code stays SGRBG10. ov13b10 shifts the crop
   window by one pixel to undo a Bayer shift on the same registers;
   doing that here introduces one instead of removing it.

Signed-off-by: Robert Bozik <robertbozik@gmail.com>
Assisted-by: Claude:claude-opus-5 sparse
Assisted-by: Claude:claude-fable-5 sparse
---
 MAINTAINERS                |    8 +
 drivers/media/i2c/Kconfig  |   11 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/ov32c4.c | 2839 ++++++++++++++++++++++++++++++++++++
 4 files changed, 2859 insertions(+)
 create mode 100644 drivers/media/i2c/ov32c4.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3785b8c1d..7b28717fc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20008,6 +20008,14 @@ S:	Maintained
 T:	git git://linuxtv.org/media.git
 F:	drivers/media/i2c/ov2740.c
 
+OMNIVISION OV32C4 SENSOR DRIVER
+M:	Robert Bozik <robertbozik@gmail.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+T:	git git://linuxtv.org/media.git
+F:	Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
+F:	drivers/media/i2c/ov32c4.c
+
 OMNIVISION OV4689 SENSOR DRIVER
 M:	Mikhail Rudenko <mike.rudenko@gmail.com>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5c52007f9..1de1ff762 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -547,6 +547,17 @@ config VIDEO_OV2740
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov2740.
 
+config VIDEO_OV32C4
+	tristate "OmniVision OV32C4 sensor support"
+	depends on ACPI || COMPILE_TEST
+	select V4L2_CCI_I2C
+	help
+	  This is a Video4Linux2 sensor driver for the OmniVision
+	  OV32C4 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov32c4.
+
 config VIDEO_OV4689
 	tristate "OmniVision OV4689 sensor support"
 	depends on GPIOLIB
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d04bd5724..79bf2e393 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
 obj-$(CONFIG_VIDEO_OV2732) += ov2732.o
 obj-$(CONFIG_VIDEO_OV2735) += ov2735.o
 obj-$(CONFIG_VIDEO_OV2740) += ov2740.o
+obj-$(CONFIG_VIDEO_OV32C4) += ov32c4.o
 obj-$(CONFIG_VIDEO_OV4689) += ov4689.o
 obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
 obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
diff --git a/drivers/media/i2c/ov32c4.c b/drivers/media/i2c/ov32c4.c
new file mode 100644
index 000000000..4039aa85c
--- /dev/null
+++ b/drivers/media/i2c/ov32c4.c
@@ -0,0 +1,2839 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OmniVision OV32C4 image sensor driver.
+ *
+ * Built for the Lenovo Yoga Slim 9 14ILL10 (83CX, Intel Lunar Lake / IPU7),
+ * where the sensor sits on i2c-0 at 0x36 behind ACPI _HID "OVTI32C4" and an
+ * INT3472 discrete control logic node providing "avdd" and "reset".
+ *
+ * Structure follows drivers/media/i2c/ov02c10.c (v4l2-cci + regmap,
+ * .enable_streams/.disable_streams, v4l2_subdev_state).
+ *
+ * Provenance of the numbers in this file:
+ *
+ *  - the mode register table below is the verbatim init sequence of mode
+ *    "c09" from the Windows driver ov32c4.sys (file offset 0x00031300,
+ *    1787 entries, strictly ascending, 16-byte stride, ends cleanly at
+ *    0x7150). It is reproduced 1:1, nothing added or reordered.
+ *  - chip id: 3-byte read at 0x300a, expected 0x563243 -- read straight out
+ *    of ov32c4.sys (identify routine at 0x14000a454).
+ *  - exposure 0x3500 (24 bit) + second context 0x3540, gain 0x3508 (16 bit)
+ *    + second context 0x3548, VTS 0x380e, and the rule
+ *    exposure_max = VTS - 32 -- from ov32c4.sys (0x14000bee0).
+ *  - HTS/VTS/window: registers of the same table (HTS 1224, VTS 2614,
+ *    3264x1840 out of a 6560x3712 window, 2x2 binning).
+ *
+ * The link frequency is 400 MHz (800 Mbps per lane). ov32c4.sys carries it
+ * as MipiBps -- bits per second, not hertz -- and it is confirmed on the
+ * machine: the IPU7 D-PHY locks and the sensor streams at a measured
+ * 30.00 fps, which matches pixel_rate / (ppl * vts) exactly.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/minmax.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#define OV32C4_LINK_FREQ_400MHZ		400000000ULL	/* 800 Mbps/lane */
+#define OV32C4_MCLK			19200000
+#define OV32C4_RGB_DEPTH		10
+#define OV32C4_DATA_LANES		4
+
+/*
+ * Internal sensor clock the timing registers are counted in.
+ * 96 MHz = 5 * MCLK; derived as HTS_REG * VTS * fps = 1224 * 2614 * 30.
+ */
+#define OV32C4_SCLK			96000000ULL
+
+/* Line length as the sensor counts it, in SCLK cycles (register 0x380c). */
+#define OV32C4_HTS_REG			1224
+
+/*
+ * Pixel array geometry, all of it derived from the mode registers rather than
+ * assumed.
+ *
+ * 0x3800..0x3807 place the readout window at x 0..6559, y 608..4319, so the
+ * full array is 6560 x 4928. The product brief gives the active area as
+ * 6528 x 4896, which is 32 columns and 32 rows less -- 16 blind ones on each
+ * side, hence the (16, 16) origin.
+ *
+ * The mode reads that window with 2x2 binning into 3280 x 1856 and then takes
+ * 3264 x 1840 out of it at offset (8, 9) (0x3810..0x3813). Mapping that back
+ * to array coordinates:
+ *
+ *   columns:  binned 8..3271  ->  16 .. 6543   = 6528 wide
+ *   rows:     binned 9..1848  ->  626 .. 4305  = 3680 high
+ *
+ * The horizontal crop therefore lands exactly on the active width: the mode
+ * reads the blind columns and throws them away again.
+ */
+#define OV32C4_NATIVE_WIDTH		6560U
+#define OV32C4_NATIVE_HEIGHT		4928U
+#define OV32C4_ACTIVE_LEFT		16U
+#define OV32C4_ACTIVE_TOP		16U
+#define OV32C4_ACTIVE_WIDTH		6528U
+#define OV32C4_ACTIVE_HEIGHT		4896U
+#define OV32C4_CROP_LEFT		16U
+#define OV32C4_CROP_TOP			626U
+#define OV32C4_CROP_WIDTH		6528U
+#define OV32C4_CROP_HEIGHT		3680U
+
+#define OV32C4_REG_CHIP_ID		CCI_REG24(0x300a)
+#define OV32C4_CHIP_ID			0x563243
+
+#define OV32C4_REG_STREAM_CONTROL	CCI_REG8(0x0100)
+
+/*
+ * The module carries a companion chip on the second I2C address of the
+ * sensor's ACPI _CRS (0x3e on this machine). The sensor does not answer I2C
+ * at all until this register is written: ov32c4.sys issues exactly this write
+ * to _CRS resource index 1 both before reading the chip id and before every
+ * mode set, and there is no matching "off" write anywhere in the binary.
+ * Confirmed on the machine: without it 0x36 NACKs everything, with it
+ * 0x300a reads back 0x563243 straight away.
+ *
+ * ACPI declares that address as the VCM (ipu_bridge tries to instantiate a
+ * VCM client on it), so the chip is most likely a VCM driver with an
+ * integrated LDO and this register switches the sensor's core rail. The
+ * write is therefore issued as a plain i2c_transfer and the address is
+ * deliberately NOT claimed with i2c_new_dummy_device() -- claiming it makes
+ * ipu_bridge fail with -EBUSY and the VCM driver never binds.
+ */
+#define OV32C4_COMPANION_ENABLE_REG	0x1001
+#define OV32C4_COMPANION_ENABLE_VAL	0x04
+
+/* Vertical timing */
+#define OV32C4_REG_VTS			CCI_REG16(0x380e)
+#define OV32C4_VTS_MAX			0xffff
+
+/* Exposure; the sensor has two exposure contexts and ov32c4.sys writes both */
+#define OV32C4_REG_EXPOSURE		CCI_REG24(0x3500)
+#define OV32C4_REG_EXPOSURE_CTX2	CCI_REG24(0x3540)
+#define OV32C4_EXPOSURE_MIN		4
+#define OV32C4_EXPOSURE_STEP		1
+/* VTS = max(vts, exposure + 32) in ov32c4.sys -> keep 32 lines of margin */
+#define OV32C4_EXPOSURE_MAX_MARGIN	32
+
+/*
+ * Analogue gain. The register address and width come from ov32c4.sys; the
+ * range was measured on the sensor, because the vendor driver clamps gain
+ * one layer up and carries no limits of its own.
+ *
+ * Response is exactly proportional to the register value from 0x100 to
+ * 0x7c0, so 0x100 is unity gain and 0x7c0 is 7.75x. That is also the value
+ * the chip powers up with. Below 0x100 the output no longer tracks what was
+ * written at all - 0xe0 and 0xff give the same result - and above 0x7c0 it
+ * stops increasing, so neither region is exposed.
+ *
+ * Note for anyone copying from ov13b10, which uses the same register: there
+ * unity is 0x80. That does not hold for this sensor.
+ */
+/*
+ * Mirror and flip. Bit 2 in each, but with opposite polarity: the mode table
+ * leaves 0x3821 with the bit set and 0x3820 with it clear, and that is the
+ * un-flipped state.
+ *
+ * Measured on the sensor: flipping either way leaves the Bayer
+ * order alone, so nothing else has to change. Do not copy the window-offset
+ * compensation ov13b10 does for the same registers - here it does not undo a
+ * Bayer shift, it introduces one.
+ */
+#define OV32C4_REG_FLIP			CCI_REG8(0x3820)
+#define OV32C4_REG_MIRROR		CCI_REG8(0x3821)
+#define OV32C4_FLIP_BIT			BIT(2)
+
+#define OV32C4_REG_ANALOG_GAIN		CCI_REG16(0x3508)
+#define OV32C4_REG_ANALOG_GAIN_CTX2	CCI_REG16(0x3548)
+#define OV32C4_ANA_GAIN_MIN		0x100
+#define OV32C4_ANA_GAIN_MAX		0x07c0
+#define OV32C4_ANA_GAIN_STEP		1
+#define OV32C4_ANA_GAIN_DEFAULT		0x100
+
+/*
+ * Digital gain, 24 bit at 0x350a with a second context at 0x354a. The vendor
+ * driver writes it as the internal value shifted left by 6 (ov32c4.sys
+ * 0x14000bfd7) and reads it back shifted right by 6, so the control here
+ * carries the internal value and the driver does the shift.
+ *
+ * Measured on the sensor: 1024 is unity and the response is
+ * proportional - writing 2048, 4096 and 8192 gives 1.99x, 3.86x and 7.62x,
+ * the shortfall at the top being sensor clipping, not non-linearity. 16384
+ * is outside the field and produces a black frame, so the range stops one
+ * step below it.
+ */
+#define OV32C4_REG_DIGITAL_GAIN		CCI_REG24(0x350a)
+#define OV32C4_REG_DIGITAL_GAIN_CTX2	CCI_REG24(0x354a)
+#define OV32C4_DGAIN_SHIFT		6
+#define OV32C4_DGAIN_MIN		1024
+#define OV32C4_DGAIN_MAX		16383
+#define OV32C4_DGAIN_STEP		1
+#define OV32C4_DGAIN_DEFAULT		1024
+
+struct ov32c4_mode {
+	u32 width;
+	u32 height;
+	/* Line length as written to 0x380c, in sensor clock cycles */
+	u32 hts_reg;
+	/* Frame length as written to 0x380e, in lines */
+	u32 vts_min;
+
+	const struct reg_sequence *reg_sequence;
+	int sequence_length;
+};
+
+static const char * const ov32c4_supply_names[] = {
+	"avdd",		/* Analog power; the only one INT3472 provides here */
+};
+
+/*
+ * Power-up timings. There is no public datasheet for this sensor, so these
+ * are values that proved reliable during bring-up, not datasheet minima.
+ */
+#define OV32C4_AVDD_SETTLE_US		5000
+#define OV32C4_RESET_SETTLE_US		20000
+
+/* Chip id read attempts, 20 ms apart, before giving up. */
+#define OV32C4_ID_RETRIES		5
+
+/*
+ * Mode 3264x1840 @ 30 fps, 4 lanes, SGRBG10.
+ *
+ * Verbatim from ov32c4.sys, table "c09" at file offset 0x00031300:
+ * 1787 register writes, addresses 0x0100..0x7150, strictly ascending.
+ * Relevant values inside: window 0..6559 x 608..4319 (6560x3712),
+ * output 3264x1840 (0x3808/0x380a), HTS 1224 (0x380c), VTS 2614 (0x380e),
+ * 2x2 binning, PLL 0x0301=0xc8 0x0303=0x04 0x0304/05=0x0271 0x0316=0x3c,
+ * MIPI global timing 0x4837=0x10.
+ *
+ * The 15 fps variant of the same table ("c10") differs only in VTS
+ * (5228 = 2 x 2614), which is why it is not carried as a separate mode --
+ * V4L2_CID_VBLANK covers it.
+ */
+static const struct reg_sequence ov32c4_mode_3264x1840_regs[] = {
+	{ 0x0100, 0x00 },
+	{ 0x0103, 0x01, 10000 },	/* sw reset + settle */
+	{ 0x0301, 0xc8 },
+	{ 0x0303, 0x04 },
+	{ 0x0304, 0x02 },
+	{ 0x0305, 0x71 },
+	{ 0x0306, 0x04 },
+	{ 0x0307, 0x01 },
+	{ 0x0308, 0x03 },
+	{ 0x0316, 0x3c },
+	{ 0x0325, 0xd0 },
+	{ 0x0343, 0x04 },
+	{ 0x0344, 0x02 },
+	{ 0x0345, 0x32 },
+	{ 0x034b, 0x03 },
+	{ 0x034f, 0xe4 },
+	{ 0x3012, 0x41 },
+	{ 0x3025, 0x89 },
+	{ 0x3107, 0x86 },
+	{ 0x3400, 0x00 },
+	{ 0x3402, 0x8c },
+	{ 0x3404, 0x1a },
+	{ 0x3405, 0xb6 },
+	{ 0x3406, 0x88 },
+	{ 0x3407, 0x01 },
+	{ 0x3408, 0xb6 },
+	{ 0x3409, 0x88 },
+	{ 0x340a, 0x01 },
+	{ 0x340c, 0x50 },
+	{ 0x340d, 0x52 },
+	{ 0x3419, 0x15 },
+	{ 0x341a, 0xfc },
+	{ 0x341b, 0x30 },
+	{ 0x3420, 0x00 },
+	{ 0x3421, 0x00 },
+	{ 0x3422, 0x00 },
+	{ 0x3423, 0x00 },
+	{ 0x3424, 0x00 },
+	{ 0x3425, 0x00 },
+	{ 0x3426, 0x00 },
+	{ 0x3427, 0x00 },
+	{ 0x3428, 0xff },
+	{ 0x342a, 0x00 },
+	{ 0x342b, 0x00 },
+	{ 0x342c, 0x00 },
+	{ 0x342d, 0x0f },
+	{ 0x342e, 0x00 },
+	{ 0x342f, 0x00 },
+	{ 0x3430, 0x00 },
+	{ 0x3431, 0x00 },
+	{ 0x3432, 0x00 },
+	{ 0x3436, 0x10 },
+	{ 0x3437, 0x00 },
+	{ 0x3438, 0x00 },
+	{ 0x3439, 0x00 },
+	{ 0x3452, 0x14 },
+	{ 0x3453, 0x88 },
+	{ 0x3458, 0x02 },
+	{ 0x3459, 0x02 },
+	{ 0x3504, 0x4c },
+	{ 0x3506, 0x30 },
+	{ 0x3507, 0x00 },
+	{ 0x3544, 0x4c },
+	{ 0x3546, 0x30 },
+	{ 0x3547, 0x00 },
+	{ 0x3602, 0x24 },
+	{ 0x3605, 0xd0 },
+	{ 0x3608, 0x21 },
+	{ 0x3609, 0x90 },
+	{ 0x360c, 0x08 },
+	{ 0x3616, 0x31 },
+	{ 0x361a, 0x0a },
+	{ 0x3682, 0x80 },
+	{ 0x368e, 0x71 },
+	{ 0x3696, 0xd1 },
+	{ 0x36af, 0x00 },
+	{ 0x3714, 0x22 },
+	{ 0x3718, 0x02 },
+	{ 0x37b0, 0x00 },
+	{ 0x37b1, 0x8a },
+	{ 0x37b2, 0x01 },
+	{ 0x37b3, 0x5f },
+	{ 0x37b4, 0x00 },
+	{ 0x37b5, 0xd3 },
+	{ 0x37b6, 0x01 },
+	{ 0x37b7, 0xbe },
+	{ 0x3800, 0x00 },
+	{ 0x3801, 0x00 },
+	{ 0x3802, 0x02 },
+	{ 0x3803, 0x60 },
+	{ 0x3804, 0x19 },
+	{ 0x3805, 0x9f },
+	{ 0x3806, 0x10 },
+	{ 0x3807, 0xdf },
+	{ 0x3808, 0x0c },
+	{ 0x3809, 0xc0 },
+	{ 0x380a, 0x07 },
+	{ 0x380b, 0x30 },
+	{ 0x380c, 0x04 },
+	{ 0x380d, 0xc8 },
+	{ 0x380e, 0x0a },
+	{ 0x380f, 0x36 },
+	{ 0x3810, 0x00 },
+	{ 0x3811, 0x08 },
+	{ 0x3812, 0x00 },
+	{ 0x3813, 0x09 },
+	{ 0x3814, 0x11 },
+	{ 0x3815, 0x11 },
+	{ 0x3820, 0x02 },
+	{ 0x3821, 0x06 },
+	{ 0x3828, 0x0d },
+	{ 0x382a, 0x80 },
+	{ 0x3837, 0x08 },
+	{ 0x383a, 0x81 },
+	{ 0x383b, 0x81 },
+	{ 0x383c, 0x11 },
+	{ 0x383d, 0x11 },
+	{ 0x3840, 0x00 },
+	{ 0x3902, 0x12 },
+	{ 0x3903, 0x12 },
+	{ 0x3904, 0x12 },
+	{ 0x3905, 0x12 },
+	{ 0x3906, 0x12 },
+	{ 0x3907, 0x12 },
+	{ 0x3908, 0x12 },
+	{ 0x3909, 0x12 },
+	{ 0x390a, 0x03 },
+	{ 0x390b, 0x0d },
+	{ 0x390c, 0x12 },
+	{ 0x390d, 0x14 },
+	{ 0x390e, 0x03 },
+	{ 0x390f, 0x0d },
+	{ 0x3910, 0x12 },
+	{ 0x3911, 0x14 },
+	{ 0x3913, 0x00 },
+	{ 0x3914, 0x00 },
+	{ 0x3915, 0x00 },
+	{ 0x3916, 0x00 },
+	{ 0x391a, 0x78 },
+	{ 0x391c, 0x78 },
+	{ 0x391e, 0x78 },
+	{ 0x3920, 0x78 },
+	{ 0x3922, 0x78 },
+	{ 0x3924, 0x78 },
+	{ 0x3926, 0x78 },
+	{ 0x3928, 0x78 },
+	{ 0x392a, 0x04 },
+	{ 0x3930, 0x55 },
+	{ 0x3937, 0x09 },
+	{ 0x3938, 0x13 },
+	{ 0x3939, 0x25 },
+	{ 0x393a, 0x4b },
+	{ 0x393b, 0x05 },
+	{ 0x393f, 0xa6 },
+	{ 0x3940, 0x03 },
+	{ 0x3941, 0x01 },
+	{ 0x3942, 0x03 },
+	{ 0x3943, 0x01 },
+	{ 0x3944, 0x03 },
+	{ 0x3945, 0x02 },
+	{ 0x3946, 0x07 },
+	{ 0x3947, 0x06 },
+	{ 0x394e, 0xaa },
+	{ 0x394f, 0xaa },
+	{ 0x3950, 0x03 },
+	{ 0x395a, 0x60 },
+	{ 0x3a05, 0x03 },
+	{ 0x3a10, 0x84 },
+	{ 0x3a11, 0x20 },
+	{ 0x3a14, 0x13 },
+	{ 0x3a1a, 0x78 },
+	{ 0x3a1d, 0x48 },
+	{ 0x3ae1, 0x7b },
+	{ 0x3b21, 0x71 },
+	{ 0x3b25, 0x71 },
+	{ 0x3b29, 0x71 },
+	{ 0x3b2d, 0x71 },
+	{ 0x3b31, 0x71 },
+	{ 0x3b35, 0x71 },
+	{ 0x3b39, 0x71 },
+	{ 0x3b3d, 0x71 },
+	{ 0x3b64, 0x20 },
+	{ 0x3b68, 0x7f },
+	{ 0x3b69, 0xe3 },
+	{ 0x3b6c, 0x7f },
+	{ 0x3b6d, 0xe3 },
+	{ 0x3b71, 0x83 },
+	{ 0x3b73, 0xbf },
+	{ 0x3ba9, 0x9d },
+	{ 0x3bab, 0xe2 },
+	{ 0x3baf, 0x9d },
+	{ 0x3bb1, 0xe2 },
+	{ 0x3bb5, 0x9d },
+	{ 0x3bb7, 0xe2 },
+	{ 0x3bbb, 0x9d },
+	{ 0x3bbd, 0xe2 },
+	{ 0x3f00, 0x11 },
+	{ 0x4010, 0xe8 },
+	{ 0x4011, 0x01 },
+	{ 0x4012, 0x0c },
+	{ 0x4015, 0x00 },
+	{ 0x4016, 0x0f },
+	{ 0x4017, 0x00 },
+	{ 0x4018, 0x07 },
+	{ 0x401a, 0x40 },
+	{ 0x402e, 0x00 },
+	{ 0x4040, 0x00 },
+	{ 0x4041, 0x00 },
+	{ 0x4042, 0x00 },
+	{ 0x4043, 0x00 },
+	{ 0x4044, 0x00 },
+	{ 0x4045, 0x00 },
+	{ 0x4046, 0x00 },
+	{ 0x4047, 0x00 },
+	{ 0x4048, 0x00 },
+	{ 0x4049, 0x00 },
+	{ 0x404a, 0x00 },
+	{ 0x404b, 0x00 },
+	{ 0x404c, 0x00 },
+	{ 0x404d, 0x00 },
+	{ 0x404e, 0x00 },
+	{ 0x404f, 0x00 },
+	{ 0x40dc, 0x02 },
+	{ 0x4501, 0x11 },
+	{ 0x4504, 0x80 },
+	{ 0x4603, 0x15 },
+	{ 0x4609, 0x20 },
+	{ 0x460e, 0x00 },
+	{ 0x4680, 0x01 },
+	{ 0x4686, 0x00 },
+	{ 0x4687, 0x00 },
+	{ 0x4691, 0x00 },
+	{ 0x4800, 0x64 },
+	{ 0x4802, 0x00 },
+	{ 0x4806, 0x40 },
+	{ 0x481b, 0x3c },
+	{ 0x481f, 0x30 },
+	{ 0x4829, 0x70 },
+	{ 0x4837, 0x10 },
+	{ 0x4850, 0x42 },
+	{ 0x4853, 0x04 },
+	{ 0x4860, 0x00 },
+	{ 0x4861, 0xec },
+	{ 0x4862, 0x04 },
+	{ 0x4883, 0x00 },
+	{ 0x4888, 0x10 },
+	{ 0x4889, 0x03 },
+	{ 0x488a, 0x04 },
+	{ 0x48f8, 0x05 },
+	{ 0x48f9, 0xdc },
+	{ 0x4910, 0xe8 },
+	{ 0x4911, 0x01 },
+	{ 0x4912, 0x0c },
+	{ 0x4915, 0x00 },
+	{ 0x4916, 0x0f },
+	{ 0x4917, 0x00 },
+	{ 0x4918, 0x07 },
+	{ 0x491a, 0x40 },
+	{ 0x4940, 0x00 },
+	{ 0x4941, 0x00 },
+	{ 0x4942, 0x00 },
+	{ 0x4943, 0x00 },
+	{ 0x4944, 0x00 },
+	{ 0x4945, 0x00 },
+	{ 0x4946, 0x00 },
+	{ 0x4947, 0x00 },
+	{ 0x4948, 0x00 },
+	{ 0x4949, 0x00 },
+	{ 0x494a, 0x00 },
+	{ 0x494b, 0x00 },
+	{ 0x494c, 0x00 },
+	{ 0x494d, 0x00 },
+	{ 0x494e, 0x00 },
+	{ 0x494f, 0x00 },
+	{ 0x49dc, 0x02 },
+	{ 0x4d00, 0x04 },
+	{ 0x4d01, 0xea },
+	{ 0x4d02, 0xb9 },
+	{ 0x4d03, 0x40 },
+	{ 0x4d04, 0x72 },
+	{ 0x4d05, 0xf7 },
+	{ 0x5000, 0x1f },
+	{ 0x5001, 0x10 },
+	{ 0x5004, 0x17 },
+	{ 0x5007, 0x06 },
+	{ 0x5008, 0x04 },
+	{ 0x50c1, 0x00 },
+	{ 0x5110, 0x00 },
+	{ 0x5111, 0x14 },
+	{ 0x5112, 0x03 },
+	{ 0x5113, 0x93 },
+	{ 0x5152, 0xa3 },
+	{ 0x5180, 0xc1 },
+	{ 0x518a, 0x04 },
+	{ 0x51b0, 0x30 },
+	{ 0x52c1, 0x00 },
+	{ 0x52c2, 0x00 },
+	{ 0x5310, 0x00 },
+	{ 0x5311, 0x14 },
+	{ 0x5312, 0x03 },
+	{ 0x5313, 0x93 },
+	{ 0x5352, 0x63 },
+	{ 0x5380, 0xc1 },
+	{ 0x538a, 0x04 },
+	{ 0x53b0, 0x00 },
+	{ 0x5400, 0x3f },
+	{ 0x5401, 0x10 },
+	{ 0x5408, 0x0c },
+	{ 0x5409, 0xc0 },
+	{ 0x540a, 0x09 },
+	{ 0x540b, 0x90 },
+	{ 0x6ad0, 0x00 },
+	{ 0x6ad1, 0x75 },
+	{ 0x6ad2, 0x00 },
+	{ 0x6ad3, 0x75 },
+	{ 0x6ad4, 0x00 },
+	{ 0x6ad5, 0x75 },
+	{ 0x6ad6, 0x00 },
+	{ 0x6ad7, 0x75 },
+	{ 0x6ad8, 0x00 },
+	{ 0x6ad9, 0x75 },
+	{ 0x6ada, 0x00 },
+	{ 0x6adb, 0x75 },
+	{ 0x6adc, 0x00 },
+	{ 0x6add, 0x75 },
+	{ 0x6ade, 0x00 },
+	{ 0x6adf, 0x75 },
+	{ 0x6ae0, 0x00 },
+	{ 0x6ae1, 0x75 },
+	{ 0x6ae2, 0x00 },
+	{ 0x6ae3, 0x75 },
+	{ 0x6ae4, 0x00 },
+	{ 0x6ae5, 0x75 },
+	{ 0x6ae6, 0x00 },
+	{ 0x6ae7, 0x75 },
+	{ 0x6ae8, 0x00 },
+	{ 0x6ae9, 0x75 },
+	{ 0x6aea, 0x00 },
+	{ 0x6aeb, 0x75 },
+	{ 0x6aec, 0x00 },
+	{ 0x6aed, 0x75 },
+	{ 0x6aee, 0x00 },
+	{ 0x6aef, 0x75 },
+	{ 0x6af0, 0x00 },
+	{ 0x6af1, 0x75 },
+	{ 0x6af2, 0x00 },
+	{ 0x6af3, 0x75 },
+	{ 0x6af4, 0x00 },
+	{ 0x6af5, 0x75 },
+	{ 0x6af6, 0x00 },
+	{ 0x6af7, 0x75 },
+	{ 0x6af8, 0x00 },
+	{ 0x6af9, 0x75 },
+	{ 0x6afa, 0x00 },
+	{ 0x6afb, 0x75 },
+	{ 0x6afc, 0x00 },
+	{ 0x6afd, 0x75 },
+	{ 0x6afe, 0x00 },
+	{ 0x6aff, 0x75 },
+	{ 0x6b00, 0x00 },
+	{ 0x6b01, 0x75 },
+	{ 0x6b02, 0x00 },
+	{ 0x6b03, 0x75 },
+	{ 0x6b04, 0x00 },
+	{ 0x6b05, 0x75 },
+	{ 0x6b06, 0x00 },
+	{ 0x6b07, 0x75 },
+	{ 0x6b08, 0x00 },
+	{ 0x6b09, 0x75 },
+	{ 0x6b0a, 0x00 },
+	{ 0x6b0b, 0x75 },
+	{ 0x6b0c, 0x00 },
+	{ 0x6b0d, 0x75 },
+	{ 0x6b0e, 0x00 },
+	{ 0x6b0f, 0x75 },
+	{ 0x6b10, 0x00 },
+	{ 0x6b11, 0x75 },
+	{ 0x6b12, 0x00 },
+	{ 0x6b13, 0x75 },
+	{ 0x6b14, 0x00 },
+	{ 0x6b15, 0x75 },
+	{ 0x6b16, 0x00 },
+	{ 0x6b17, 0x75 },
+	{ 0x6b18, 0x00 },
+	{ 0x6b19, 0x75 },
+	{ 0x6b1a, 0x00 },
+	{ 0x6b1b, 0x75 },
+	{ 0x6b1c, 0x00 },
+	{ 0x6b1d, 0x75 },
+	{ 0x6b1e, 0x00 },
+	{ 0x6b1f, 0x75 },
+	{ 0x6b20, 0x00 },
+	{ 0x6b21, 0x75 },
+	{ 0x6b22, 0x00 },
+	{ 0x6b23, 0x75 },
+	{ 0x6b24, 0x00 },
+	{ 0x6b25, 0x75 },
+	{ 0x6b26, 0x00 },
+	{ 0x6b27, 0x75 },
+	{ 0x6b28, 0x00 },
+	{ 0x6b29, 0x75 },
+	{ 0x6b2a, 0x00 },
+	{ 0x6b2b, 0x75 },
+	{ 0x6b2c, 0x00 },
+	{ 0x6b2d, 0x75 },
+	{ 0x6b2e, 0x00 },
+	{ 0x6b2f, 0x75 },
+	{ 0x6b30, 0x00 },
+	{ 0x6b31, 0x75 },
+	{ 0x6b32, 0x00 },
+	{ 0x6b33, 0x75 },
+	{ 0x6b34, 0x00 },
+	{ 0x6b35, 0x75 },
+	{ 0x6b36, 0x00 },
+	{ 0x6b37, 0x75 },
+	{ 0x6b38, 0x00 },
+	{ 0x6b39, 0x75 },
+	{ 0x6b3a, 0x00 },
+	{ 0x6b3b, 0x75 },
+	{ 0x6b3c, 0x00 },
+	{ 0x6b3d, 0x75 },
+	{ 0x6b3e, 0x00 },
+	{ 0x6b3f, 0x75 },
+	{ 0x6b40, 0x00 },
+	{ 0x6b41, 0x75 },
+	{ 0x6b42, 0x00 },
+	{ 0x6b43, 0x75 },
+	{ 0x6b44, 0x00 },
+	{ 0x6b45, 0x75 },
+	{ 0x6b46, 0x00 },
+	{ 0x6b47, 0x75 },
+	{ 0x6b48, 0x00 },
+	{ 0x6b49, 0x75 },
+	{ 0x6b4a, 0x00 },
+	{ 0x6b4b, 0x75 },
+	{ 0x6b4c, 0x00 },
+	{ 0x6b4d, 0x75 },
+	{ 0x6b4e, 0x00 },
+	{ 0x6b4f, 0x75 },
+	{ 0x6b50, 0x00 },
+	{ 0x6b51, 0x75 },
+	{ 0x6b52, 0x00 },
+	{ 0x6b53, 0x75 },
+	{ 0x6b54, 0x00 },
+	{ 0x6b55, 0x75 },
+	{ 0x6b56, 0x00 },
+	{ 0x6b57, 0x75 },
+	{ 0x6b58, 0x00 },
+	{ 0x6b59, 0x75 },
+	{ 0x6b5a, 0x00 },
+	{ 0x6b5b, 0x75 },
+	{ 0x6b5c, 0x00 },
+	{ 0x6b5d, 0x75 },
+	{ 0x6b5e, 0x00 },
+	{ 0x6b5f, 0x75 },
+	{ 0x6b60, 0x00 },
+	{ 0x6b61, 0x75 },
+	{ 0x6b62, 0x00 },
+	{ 0x6b63, 0x75 },
+	{ 0x6b64, 0x00 },
+	{ 0x6b65, 0x75 },
+	{ 0x6b66, 0x00 },
+	{ 0x6b67, 0x75 },
+	{ 0x6b68, 0x00 },
+	{ 0x6b69, 0x75 },
+	{ 0x6b6a, 0x00 },
+	{ 0x6b6b, 0x75 },
+	{ 0x6b6c, 0x00 },
+	{ 0x6b6d, 0x75 },
+	{ 0x6b6e, 0x00 },
+	{ 0x6b6f, 0x75 },
+	{ 0x6b70, 0x00 },
+	{ 0x6b71, 0x75 },
+	{ 0x6b72, 0x00 },
+	{ 0x6b73, 0x75 },
+	{ 0x6b74, 0x00 },
+	{ 0x6b75, 0x75 },
+	{ 0x6b76, 0x00 },
+	{ 0x6b77, 0x75 },
+	{ 0x6b78, 0x00 },
+	{ 0x6b79, 0x75 },
+	{ 0x6b7a, 0x00 },
+	{ 0x6b7b, 0x75 },
+	{ 0x6b7c, 0x00 },
+	{ 0x6b7d, 0x75 },
+	{ 0x6b7e, 0x00 },
+	{ 0x6b7f, 0x75 },
+	{ 0x6b80, 0x00 },
+	{ 0x6b81, 0x75 },
+	{ 0x6b82, 0x00 },
+	{ 0x6b83, 0x75 },
+	{ 0x6b84, 0x00 },
+	{ 0x6b85, 0x75 },
+	{ 0x6b86, 0x00 },
+	{ 0x6b87, 0x75 },
+	{ 0x6b88, 0x00 },
+	{ 0x6b89, 0x75 },
+	{ 0x6b8a, 0x00 },
+	{ 0x6b8b, 0x75 },
+	{ 0x6b8c, 0x00 },
+	{ 0x6b8d, 0x75 },
+	{ 0x6b8e, 0x00 },
+	{ 0x6b8f, 0x75 },
+	{ 0x6b90, 0x00 },
+	{ 0x6b91, 0x75 },
+	{ 0x6b92, 0x00 },
+	{ 0x6b93, 0x75 },
+	{ 0x6b94, 0x00 },
+	{ 0x6b95, 0x75 },
+	{ 0x6b96, 0x00 },
+	{ 0x6b97, 0x75 },
+	{ 0x6b98, 0x00 },
+	{ 0x6b99, 0x75 },
+	{ 0x6b9a, 0x00 },
+	{ 0x6b9b, 0x75 },
+	{ 0x6b9c, 0x00 },
+	{ 0x6b9d, 0x75 },
+	{ 0x6b9e, 0x00 },
+	{ 0x6b9f, 0x75 },
+	{ 0x6ba0, 0x00 },
+	{ 0x6ba1, 0x75 },
+	{ 0x6ba2, 0x00 },
+	{ 0x6ba3, 0x75 },
+	{ 0x6ba4, 0x00 },
+	{ 0x6ba5, 0x75 },
+	{ 0x6ba6, 0x00 },
+	{ 0x6ba7, 0x75 },
+	{ 0x6ba8, 0x00 },
+	{ 0x6ba9, 0x75 },
+	{ 0x6baa, 0x00 },
+	{ 0x6bab, 0x75 },
+	{ 0x6bac, 0x00 },
+	{ 0x6bad, 0x75 },
+	{ 0x6bae, 0x00 },
+	{ 0x6baf, 0x75 },
+	{ 0x6bb0, 0x00 },
+	{ 0x6bb1, 0x75 },
+	{ 0x6bb2, 0x00 },
+	{ 0x6bb3, 0x75 },
+	{ 0x6bb4, 0x00 },
+	{ 0x6bb5, 0x75 },
+	{ 0x6bb6, 0x00 },
+	{ 0x6bb7, 0x75 },
+	{ 0x6bb8, 0x00 },
+	{ 0x6bb9, 0x75 },
+	{ 0x6bba, 0x00 },
+	{ 0x6bbb, 0x75 },
+	{ 0x6bbc, 0x00 },
+	{ 0x6bbd, 0x75 },
+	{ 0x6bbe, 0x00 },
+	{ 0x6bbf, 0x75 },
+	{ 0x6bc0, 0x00 },
+	{ 0x6bc1, 0x75 },
+	{ 0x6bc2, 0x00 },
+	{ 0x6bc3, 0x75 },
+	{ 0x6bc4, 0x00 },
+	{ 0x6bc5, 0x75 },
+	{ 0x6bc6, 0x00 },
+	{ 0x6bc7, 0x75 },
+	{ 0x6bc8, 0x00 },
+	{ 0x6bc9, 0x75 },
+	{ 0x6bca, 0x00 },
+	{ 0x6bcb, 0x75 },
+	{ 0x6bcc, 0x00 },
+	{ 0x6bcd, 0x75 },
+	{ 0x6bce, 0x00 },
+	{ 0x6bcf, 0x75 },
+	{ 0x6bd0, 0x00 },
+	{ 0x6bd1, 0x75 },
+	{ 0x6bd2, 0x00 },
+	{ 0x6bd3, 0x75 },
+	{ 0x6bd4, 0x00 },
+	{ 0x6bd5, 0x75 },
+	{ 0x6bd6, 0x00 },
+	{ 0x6bd7, 0x75 },
+	{ 0x6bd8, 0x00 },
+	{ 0x6bd9, 0x75 },
+	{ 0x6bda, 0x00 },
+	{ 0x6bdb, 0x75 },
+	{ 0x6bdc, 0x00 },
+	{ 0x6bdd, 0x75 },
+	{ 0x6bde, 0x00 },
+	{ 0x6bdf, 0x75 },
+	{ 0x6be0, 0x00 },
+	{ 0x6be1, 0x75 },
+	{ 0x6be2, 0x00 },
+	{ 0x6be3, 0x75 },
+	{ 0x6be4, 0x00 },
+	{ 0x6be5, 0x75 },
+	{ 0x6be6, 0x00 },
+	{ 0x6be7, 0x75 },
+	{ 0x6be8, 0x00 },
+	{ 0x6be9, 0x75 },
+	{ 0x6bea, 0x00 },
+	{ 0x6beb, 0x75 },
+	{ 0x6bec, 0x00 },
+	{ 0x6bed, 0x75 },
+	{ 0x6bee, 0x00 },
+	{ 0x6bef, 0x75 },
+	{ 0x6c00, 0x00 },
+	{ 0x6c01, 0x75 },
+	{ 0x6c02, 0x00 },
+	{ 0x6c03, 0x75 },
+	{ 0x6c04, 0x00 },
+	{ 0x6c05, 0x75 },
+	{ 0x6c06, 0x00 },
+	{ 0x6c07, 0x75 },
+	{ 0x6c08, 0x00 },
+	{ 0x6c09, 0x75 },
+	{ 0x6c0a, 0x00 },
+	{ 0x6c0b, 0x75 },
+	{ 0x6c0c, 0x00 },
+	{ 0x6c0d, 0x75 },
+	{ 0x6c0e, 0x00 },
+	{ 0x6c0f, 0x75 },
+	{ 0x6c10, 0x00 },
+	{ 0x6c11, 0x75 },
+	{ 0x6c12, 0x00 },
+	{ 0x6c13, 0x75 },
+	{ 0x6c14, 0x00 },
+	{ 0x6c15, 0x75 },
+	{ 0x6c16, 0x00 },
+	{ 0x6c17, 0x75 },
+	{ 0x6c18, 0x00 },
+	{ 0x6c19, 0x75 },
+	{ 0x6c1a, 0x00 },
+	{ 0x6c1b, 0x75 },
+	{ 0x6c1c, 0x00 },
+	{ 0x6c1d, 0x75 },
+	{ 0x6c1e, 0x00 },
+	{ 0x6c1f, 0x75 },
+	{ 0x6c20, 0x00 },
+	{ 0x6c21, 0x75 },
+	{ 0x6c22, 0x00 },
+	{ 0x6c23, 0x75 },
+	{ 0x6c24, 0x00 },
+	{ 0x6c25, 0x75 },
+	{ 0x6c26, 0x00 },
+	{ 0x6c27, 0x75 },
+	{ 0x6c28, 0x00 },
+	{ 0x6c29, 0x75 },
+	{ 0x6c2a, 0x00 },
+	{ 0x6c2b, 0x75 },
+	{ 0x6c2c, 0x00 },
+	{ 0x6c2d, 0x75 },
+	{ 0x6c2e, 0x00 },
+	{ 0x6c2f, 0x75 },
+	{ 0x6c30, 0x00 },
+	{ 0x6c31, 0x75 },
+	{ 0x6c32, 0x00 },
+	{ 0x6c33, 0x75 },
+	{ 0x6c34, 0x00 },
+	{ 0x6c35, 0x75 },
+	{ 0x6c36, 0x00 },
+	{ 0x6c37, 0x75 },
+	{ 0x6c38, 0x00 },
+	{ 0x6c39, 0x75 },
+	{ 0x6c3a, 0x00 },
+	{ 0x6c3b, 0x75 },
+	{ 0x6c3c, 0x00 },
+	{ 0x6c3d, 0x75 },
+	{ 0x6c3e, 0x00 },
+	{ 0x6c3f, 0x75 },
+	{ 0x6c40, 0x00 },
+	{ 0x6c41, 0x75 },
+	{ 0x6c42, 0x00 },
+	{ 0x6c43, 0x75 },
+	{ 0x6c44, 0x00 },
+	{ 0x6c45, 0x75 },
+	{ 0x6c46, 0x00 },
+	{ 0x6c47, 0x75 },
+	{ 0x6c48, 0x00 },
+	{ 0x6c49, 0x75 },
+	{ 0x6c4a, 0x00 },
+	{ 0x6c4b, 0x75 },
+	{ 0x6c4c, 0x00 },
+	{ 0x6c4d, 0x75 },
+	{ 0x6c4e, 0x00 },
+	{ 0x6c4f, 0x75 },
+	{ 0x6c50, 0x00 },
+	{ 0x6c51, 0x75 },
+	{ 0x6c52, 0x00 },
+	{ 0x6c53, 0x75 },
+	{ 0x6c54, 0x00 },
+	{ 0x6c55, 0x75 },
+	{ 0x6c56, 0x00 },
+	{ 0x6c57, 0x75 },
+	{ 0x6c58, 0x00 },
+	{ 0x6c59, 0x75 },
+	{ 0x6c5a, 0x00 },
+	{ 0x6c5b, 0x75 },
+	{ 0x6c5c, 0x00 },
+	{ 0x6c5d, 0x75 },
+	{ 0x6c5e, 0x00 },
+	{ 0x6c5f, 0x75 },
+	{ 0x6c60, 0x00 },
+	{ 0x6c61, 0x75 },
+	{ 0x6c62, 0x00 },
+	{ 0x6c63, 0x75 },
+	{ 0x6c64, 0x00 },
+	{ 0x6c65, 0x75 },
+	{ 0x6c66, 0x00 },
+	{ 0x6c67, 0x75 },
+	{ 0x6c68, 0x00 },
+	{ 0x6c69, 0x75 },
+	{ 0x6c6a, 0x00 },
+	{ 0x6c6b, 0x75 },
+	{ 0x6c6c, 0x00 },
+	{ 0x6c6d, 0x75 },
+	{ 0x6c6e, 0x00 },
+	{ 0x6c6f, 0x75 },
+	{ 0x6c70, 0x00 },
+	{ 0x6c71, 0x75 },
+	{ 0x6c72, 0x00 },
+	{ 0x6c73, 0x75 },
+	{ 0x6c74, 0x00 },
+	{ 0x6c75, 0x75 },
+	{ 0x6c76, 0x00 },
+	{ 0x6c77, 0x75 },
+	{ 0x6c78, 0x00 },
+	{ 0x6c79, 0x75 },
+	{ 0x6c7a, 0x00 },
+	{ 0x6c7b, 0x75 },
+	{ 0x6c7c, 0x00 },
+	{ 0x6c7d, 0x75 },
+	{ 0x6c7e, 0x00 },
+	{ 0x6c7f, 0x75 },
+	{ 0x6c80, 0x00 },
+	{ 0x6c81, 0x75 },
+	{ 0x6c82, 0x00 },
+	{ 0x6c83, 0x75 },
+	{ 0x6c84, 0x00 },
+	{ 0x6c85, 0x75 },
+	{ 0x6c86, 0x00 },
+	{ 0x6c87, 0x75 },
+	{ 0x6c88, 0x00 },
+	{ 0x6c89, 0x75 },
+	{ 0x6c8a, 0x00 },
+	{ 0x6c8b, 0x75 },
+	{ 0x6c8c, 0x00 },
+	{ 0x6c8d, 0x75 },
+	{ 0x6c8e, 0x00 },
+	{ 0x6c8f, 0x75 },
+	{ 0x6c90, 0x00 },
+	{ 0x6c91, 0x75 },
+	{ 0x6c92, 0x00 },
+	{ 0x6c93, 0x75 },
+	{ 0x6c94, 0x00 },
+	{ 0x6c95, 0x75 },
+	{ 0x6c96, 0x00 },
+	{ 0x6c97, 0x75 },
+	{ 0x6c98, 0x00 },
+	{ 0x6c99, 0x75 },
+	{ 0x6c9a, 0x00 },
+	{ 0x6c9b, 0x75 },
+	{ 0x6c9c, 0x00 },
+	{ 0x6c9d, 0x75 },
+	{ 0x6c9e, 0x00 },
+	{ 0x6c9f, 0x75 },
+	{ 0x6ca0, 0x00 },
+	{ 0x6ca1, 0x75 },
+	{ 0x6ca2, 0x00 },
+	{ 0x6ca3, 0x75 },
+	{ 0x6ca4, 0x00 },
+	{ 0x6ca5, 0x75 },
+	{ 0x6ca6, 0x00 },
+	{ 0x6ca7, 0x75 },
+	{ 0x6ca8, 0x00 },
+	{ 0x6ca9, 0x75 },
+	{ 0x6caa, 0x00 },
+	{ 0x6cab, 0x75 },
+	{ 0x6cac, 0x00 },
+	{ 0x6cad, 0x75 },
+	{ 0x6cae, 0x00 },
+	{ 0x6caf, 0x75 },
+	{ 0x6cb0, 0x00 },
+	{ 0x6cb1, 0x75 },
+	{ 0x6cb2, 0x00 },
+	{ 0x6cb3, 0x75 },
+	{ 0x6cb4, 0x00 },
+	{ 0x6cb5, 0x75 },
+	{ 0x6cb6, 0x00 },
+	{ 0x6cb7, 0x75 },
+	{ 0x6cb8, 0x00 },
+	{ 0x6cb9, 0x75 },
+	{ 0x6cba, 0x00 },
+	{ 0x6cbb, 0x75 },
+	{ 0x6cbc, 0x00 },
+	{ 0x6cbd, 0x75 },
+	{ 0x6cbe, 0x00 },
+	{ 0x6cbf, 0x75 },
+	{ 0x6cc0, 0x00 },
+	{ 0x6cc1, 0x75 },
+	{ 0x6cc2, 0x00 },
+	{ 0x6cc3, 0x75 },
+	{ 0x6cc4, 0x00 },
+	{ 0x6cc5, 0x75 },
+	{ 0x6cc6, 0x00 },
+	{ 0x6cc7, 0x75 },
+	{ 0x6cc8, 0x00 },
+	{ 0x6cc9, 0x75 },
+	{ 0x6cca, 0x00 },
+	{ 0x6ccb, 0x75 },
+	{ 0x6ccc, 0x00 },
+	{ 0x6ccd, 0x75 },
+	{ 0x6cce, 0x00 },
+	{ 0x6ccf, 0x75 },
+	{ 0x6cd0, 0x00 },
+	{ 0x6cd1, 0x75 },
+	{ 0x6cd2, 0x00 },
+	{ 0x6cd3, 0x75 },
+	{ 0x6cd4, 0x00 },
+	{ 0x6cd5, 0x75 },
+	{ 0x6cd6, 0x00 },
+	{ 0x6cd7, 0x75 },
+	{ 0x6cd8, 0x00 },
+	{ 0x6cd9, 0x75 },
+	{ 0x6cda, 0x00 },
+	{ 0x6cdb, 0x75 },
+	{ 0x6cdc, 0x00 },
+	{ 0x6cdd, 0x75 },
+	{ 0x6cde, 0x00 },
+	{ 0x6cdf, 0x75 },
+	{ 0x6ce0, 0x00 },
+	{ 0x6ce1, 0x75 },
+	{ 0x6ce2, 0x00 },
+	{ 0x6ce3, 0x75 },
+	{ 0x6ce4, 0x00 },
+	{ 0x6ce5, 0x75 },
+	{ 0x6ce6, 0x00 },
+	{ 0x6ce7, 0x75 },
+	{ 0x6ce8, 0x00 },
+	{ 0x6ce9, 0x75 },
+	{ 0x6cea, 0x00 },
+	{ 0x6ceb, 0x75 },
+	{ 0x6cec, 0x00 },
+	{ 0x6ced, 0x75 },
+	{ 0x6cee, 0x00 },
+	{ 0x6cef, 0x75 },
+	{ 0x6cf0, 0x00 },
+	{ 0x6cf1, 0x75 },
+	{ 0x6cf2, 0x00 },
+	{ 0x6cf3, 0x75 },
+	{ 0x6cf4, 0x00 },
+	{ 0x6cf5, 0x75 },
+	{ 0x6cf6, 0x00 },
+	{ 0x6cf7, 0x75 },
+	{ 0x6cf8, 0x00 },
+	{ 0x6cf9, 0x75 },
+	{ 0x6cfa, 0x00 },
+	{ 0x6cfb, 0x75 },
+	{ 0x6cfc, 0x00 },
+	{ 0x6cfd, 0x75 },
+	{ 0x6cfe, 0x00 },
+	{ 0x6cff, 0x75 },
+	{ 0x6d00, 0x00 },
+	{ 0x6d01, 0x75 },
+	{ 0x6d02, 0x00 },
+	{ 0x6d03, 0x75 },
+	{ 0x6d04, 0x00 },
+	{ 0x6d05, 0x75 },
+	{ 0x6d06, 0x00 },
+	{ 0x6d07, 0x75 },
+	{ 0x6d08, 0x00 },
+	{ 0x6d09, 0x75 },
+	{ 0x6d0a, 0x00 },
+	{ 0x6d0b, 0x75 },
+	{ 0x6d0c, 0x00 },
+	{ 0x6d0d, 0x75 },
+	{ 0x6d0e, 0x00 },
+	{ 0x6d0f, 0x75 },
+	{ 0x6d10, 0x00 },
+	{ 0x6d11, 0x75 },
+	{ 0x6d12, 0x00 },
+	{ 0x6d13, 0x75 },
+	{ 0x6d14, 0x00 },
+	{ 0x6d15, 0x75 },
+	{ 0x6d16, 0x00 },
+	{ 0x6d17, 0x75 },
+	{ 0x6d18, 0x00 },
+	{ 0x6d19, 0x75 },
+	{ 0x6d1a, 0x00 },
+	{ 0x6d1b, 0x75 },
+	{ 0x6d1c, 0x00 },
+	{ 0x6d1d, 0x75 },
+	{ 0x6d1e, 0x00 },
+	{ 0x6d1f, 0x75 },
+	{ 0x6d20, 0x00 },
+	{ 0x6d21, 0x75 },
+	{ 0x6d22, 0x00 },
+	{ 0x6d23, 0x75 },
+	{ 0x6d24, 0x00 },
+	{ 0x6d25, 0x75 },
+	{ 0x6d26, 0x00 },
+	{ 0x6d27, 0x75 },
+	{ 0x6d28, 0x00 },
+	{ 0x6d29, 0x75 },
+	{ 0x6d2a, 0x00 },
+	{ 0x6d2b, 0x75 },
+	{ 0x6d2c, 0x00 },
+	{ 0x6d2d, 0x75 },
+	{ 0x6d2e, 0x00 },
+	{ 0x6d2f, 0x75 },
+	{ 0x6d30, 0x00 },
+	{ 0x6d31, 0x75 },
+	{ 0x6d32, 0x00 },
+	{ 0x6d33, 0x75 },
+	{ 0x6d34, 0x00 },
+	{ 0x6d35, 0x75 },
+	{ 0x6d36, 0x00 },
+	{ 0x6d37, 0x75 },
+	{ 0x6d38, 0x00 },
+	{ 0x6d39, 0x75 },
+	{ 0x6d3a, 0x00 },
+	{ 0x6d3b, 0x75 },
+	{ 0x6d3c, 0x00 },
+	{ 0x6d3d, 0x75 },
+	{ 0x6d3e, 0x00 },
+	{ 0x6d3f, 0x75 },
+	{ 0x6d40, 0x00 },
+	{ 0x6d41, 0x75 },
+	{ 0x6d42, 0x00 },
+	{ 0x6d43, 0x75 },
+	{ 0x6d44, 0x00 },
+	{ 0x6d45, 0x75 },
+	{ 0x6d46, 0x00 },
+	{ 0x6d47, 0x75 },
+	{ 0x6d48, 0x00 },
+	{ 0x6d49, 0x75 },
+	{ 0x6d4a, 0x00 },
+	{ 0x6d4b, 0x75 },
+	{ 0x6d4c, 0x00 },
+	{ 0x6d4d, 0x75 },
+	{ 0x6d4e, 0x00 },
+	{ 0x6d4f, 0x75 },
+	{ 0x6d50, 0x00 },
+	{ 0x6d51, 0x75 },
+	{ 0x6d52, 0x00 },
+	{ 0x6d53, 0x75 },
+	{ 0x6d54, 0x00 },
+	{ 0x6d55, 0x75 },
+	{ 0x6d56, 0x00 },
+	{ 0x6d57, 0x75 },
+	{ 0x6d58, 0x00 },
+	{ 0x6d59, 0x75 },
+	{ 0x6d5a, 0x00 },
+	{ 0x6d5b, 0x75 },
+	{ 0x6d5c, 0x00 },
+	{ 0x6d5d, 0x75 },
+	{ 0x6d5e, 0x00 },
+	{ 0x6d5f, 0x75 },
+	{ 0x6d60, 0x00 },
+	{ 0x6d61, 0x75 },
+	{ 0x6d62, 0x00 },
+	{ 0x6d63, 0x75 },
+	{ 0x6d64, 0x00 },
+	{ 0x6d65, 0x75 },
+	{ 0x6d66, 0x00 },
+	{ 0x6d67, 0x75 },
+	{ 0x6d68, 0x00 },
+	{ 0x6d69, 0x75 },
+	{ 0x6d6a, 0x00 },
+	{ 0x6d6b, 0x75 },
+	{ 0x6d6c, 0x00 },
+	{ 0x6d6d, 0x75 },
+	{ 0x6d6e, 0x00 },
+	{ 0x6d6f, 0x75 },
+	{ 0x6d70, 0x00 },
+	{ 0x6d71, 0x75 },
+	{ 0x6d72, 0x00 },
+	{ 0x6d73, 0x75 },
+	{ 0x6d74, 0x00 },
+	{ 0x6d75, 0x75 },
+	{ 0x6d76, 0x00 },
+	{ 0x6d77, 0x75 },
+	{ 0x6d78, 0x00 },
+	{ 0x6d79, 0x75 },
+	{ 0x6d7a, 0x00 },
+	{ 0x6d7b, 0x75 },
+	{ 0x6d7c, 0x00 },
+	{ 0x6d7d, 0x75 },
+	{ 0x6d7e, 0x00 },
+	{ 0x6d7f, 0x75 },
+	{ 0x6d80, 0x00 },
+	{ 0x6d81, 0x75 },
+	{ 0x6d82, 0x00 },
+	{ 0x6d83, 0x75 },
+	{ 0x6d84, 0x00 },
+	{ 0x6d85, 0x75 },
+	{ 0x6d86, 0x00 },
+	{ 0x6d87, 0x75 },
+	{ 0x6d88, 0x00 },
+	{ 0x6d89, 0x75 },
+	{ 0x6d8a, 0x00 },
+	{ 0x6d8b, 0x75 },
+	{ 0x6d8c, 0x00 },
+	{ 0x6d8d, 0x75 },
+	{ 0x6d8e, 0x00 },
+	{ 0x6d8f, 0x75 },
+	{ 0x6d90, 0x00 },
+	{ 0x6d91, 0x75 },
+	{ 0x6d92, 0x00 },
+	{ 0x6d93, 0x75 },
+	{ 0x6d94, 0x00 },
+	{ 0x6d95, 0x75 },
+	{ 0x6d96, 0x00 },
+	{ 0x6d97, 0x75 },
+	{ 0x6d98, 0x00 },
+	{ 0x6d99, 0x75 },
+	{ 0x6d9a, 0x00 },
+	{ 0x6d9b, 0x75 },
+	{ 0x6d9c, 0x00 },
+	{ 0x6d9d, 0x75 },
+	{ 0x6d9e, 0x00 },
+	{ 0x6d9f, 0x75 },
+	{ 0x6da0, 0x00 },
+	{ 0x6da1, 0x75 },
+	{ 0x6da2, 0x00 },
+	{ 0x6da3, 0x75 },
+	{ 0x6da4, 0x00 },
+	{ 0x6da5, 0x75 },
+	{ 0x6da6, 0x00 },
+	{ 0x6da7, 0x75 },
+	{ 0x6da8, 0x00 },
+	{ 0x6da9, 0x75 },
+	{ 0x6daa, 0x00 },
+	{ 0x6dab, 0x75 },
+	{ 0x6dac, 0x00 },
+	{ 0x6dad, 0x75 },
+	{ 0x6dae, 0x00 },
+	{ 0x6daf, 0x75 },
+	{ 0x6db0, 0x00 },
+	{ 0x6db1, 0x75 },
+	{ 0x6db2, 0x00 },
+	{ 0x6db3, 0x75 },
+	{ 0x6db4, 0x00 },
+	{ 0x6db5, 0x75 },
+	{ 0x6db6, 0x00 },
+	{ 0x6db7, 0x75 },
+	{ 0x6db8, 0x00 },
+	{ 0x6db9, 0x75 },
+	{ 0x6dba, 0x00 },
+	{ 0x6dbb, 0x75 },
+	{ 0x6dbc, 0x00 },
+	{ 0x6dbd, 0x75 },
+	{ 0x6dbe, 0x00 },
+	{ 0x6dbf, 0x75 },
+	{ 0x6dc0, 0x00 },
+	{ 0x6dc1, 0x75 },
+	{ 0x6dc2, 0x00 },
+	{ 0x6dc3, 0x75 },
+	{ 0x6dc4, 0x00 },
+	{ 0x6dc5, 0x75 },
+	{ 0x6dc6, 0x00 },
+	{ 0x6dc7, 0x75 },
+	{ 0x6dc8, 0x00 },
+	{ 0x6dc9, 0x75 },
+	{ 0x6dca, 0x00 },
+	{ 0x6dcb, 0x75 },
+	{ 0x6dcc, 0x00 },
+	{ 0x6dcd, 0x75 },
+	{ 0x6dce, 0x00 },
+	{ 0x6dcf, 0x75 },
+	{ 0x6dd0, 0x00 },
+	{ 0x6dd1, 0x75 },
+	{ 0x6dd2, 0x00 },
+	{ 0x6dd3, 0x75 },
+	{ 0x6dd4, 0x00 },
+	{ 0x6dd5, 0x75 },
+	{ 0x6dd6, 0x00 },
+	{ 0x6dd7, 0x75 },
+	{ 0x6dd8, 0x00 },
+	{ 0x6dd9, 0x75 },
+	{ 0x6dda, 0x00 },
+	{ 0x6ddb, 0x75 },
+	{ 0x6ddc, 0x00 },
+	{ 0x6ddd, 0x75 },
+	{ 0x6dde, 0x00 },
+	{ 0x6ddf, 0x75 },
+	{ 0x6de0, 0x00 },
+	{ 0x6de1, 0x75 },
+	{ 0x6de2, 0x00 },
+	{ 0x6de3, 0x75 },
+	{ 0x6de4, 0x00 },
+	{ 0x6de5, 0x75 },
+	{ 0x6de6, 0x00 },
+	{ 0x6de7, 0x75 },
+	{ 0x6de8, 0x00 },
+	{ 0x6de9, 0x75 },
+	{ 0x6dea, 0x00 },
+	{ 0x6deb, 0x75 },
+	{ 0x6dec, 0x00 },
+	{ 0x6ded, 0x75 },
+	{ 0x6dee, 0x00 },
+	{ 0x6def, 0x75 },
+	{ 0x6df0, 0x00 },
+	{ 0x6df1, 0x75 },
+	{ 0x6df2, 0x00 },
+	{ 0x6df3, 0x75 },
+	{ 0x6df4, 0x00 },
+	{ 0x6df5, 0x75 },
+	{ 0x6df6, 0x00 },
+	{ 0x6df7, 0x75 },
+	{ 0x6df8, 0x00 },
+	{ 0x6df9, 0x75 },
+	{ 0x6dfa, 0x00 },
+	{ 0x6dfb, 0x75 },
+	{ 0x6dfc, 0x00 },
+	{ 0x6dfd, 0x75 },
+	{ 0x6dfe, 0x00 },
+	{ 0x6dff, 0x75 },
+	{ 0x6e00, 0x00 },
+	{ 0x6e01, 0x75 },
+	{ 0x6e02, 0x00 },
+	{ 0x6e03, 0x75 },
+	{ 0x6e04, 0x00 },
+	{ 0x6e05, 0x75 },
+	{ 0x6e06, 0x00 },
+	{ 0x6e07, 0x75 },
+	{ 0x6e08, 0x00 },
+	{ 0x6e09, 0x75 },
+	{ 0x6e0a, 0x00 },
+	{ 0x6e0b, 0x75 },
+	{ 0x6e0c, 0x00 },
+	{ 0x6e0d, 0x75 },
+	{ 0x6e0e, 0x00 },
+	{ 0x6e0f, 0x75 },
+	{ 0x6e10, 0x00 },
+	{ 0x6e11, 0x75 },
+	{ 0x6e12, 0x00 },
+	{ 0x6e13, 0x75 },
+	{ 0x6e14, 0x00 },
+	{ 0x6e15, 0x75 },
+	{ 0x6e16, 0x00 },
+	{ 0x6e17, 0x75 },
+	{ 0x6e18, 0x00 },
+	{ 0x6e19, 0x75 },
+	{ 0x6e1a, 0x00 },
+	{ 0x6e1b, 0x75 },
+	{ 0x6e1c, 0x00 },
+	{ 0x6e1d, 0x75 },
+	{ 0x6e1e, 0x00 },
+	{ 0x6e1f, 0x75 },
+	{ 0x6e20, 0x00 },
+	{ 0x6e21, 0x75 },
+	{ 0x6e22, 0x00 },
+	{ 0x6e23, 0x75 },
+	{ 0x6e24, 0x00 },
+	{ 0x6e25, 0x75 },
+	{ 0x6e26, 0x00 },
+	{ 0x6e27, 0x75 },
+	{ 0x6e28, 0x00 },
+	{ 0x6e29, 0x75 },
+	{ 0x6e2a, 0x00 },
+	{ 0x6e2b, 0x75 },
+	{ 0x6e2c, 0x00 },
+	{ 0x6e2d, 0x75 },
+	{ 0x6e2e, 0x00 },
+	{ 0x6e2f, 0x75 },
+	{ 0x6e30, 0x00 },
+	{ 0x6e31, 0x75 },
+	{ 0x6e32, 0x00 },
+	{ 0x6e33, 0x75 },
+	{ 0x6e34, 0x00 },
+	{ 0x6e35, 0x75 },
+	{ 0x6e36, 0x00 },
+	{ 0x6e37, 0x75 },
+	{ 0x6e38, 0x00 },
+	{ 0x6e39, 0x75 },
+	{ 0x6e3a, 0x00 },
+	{ 0x6e3b, 0x75 },
+	{ 0x6e3c, 0x00 },
+	{ 0x6e3d, 0x75 },
+	{ 0x6e3e, 0x00 },
+	{ 0x6e3f, 0x75 },
+	{ 0x6e40, 0x00 },
+	{ 0x6e41, 0x75 },
+	{ 0x6e42, 0x00 },
+	{ 0x6e43, 0x75 },
+	{ 0x6e44, 0x00 },
+	{ 0x6e45, 0x75 },
+	{ 0x6e46, 0x00 },
+	{ 0x6e47, 0x75 },
+	{ 0x6e48, 0x00 },
+	{ 0x6e49, 0x75 },
+	{ 0x6e4a, 0x00 },
+	{ 0x6e4b, 0x75 },
+	{ 0x6e4c, 0x00 },
+	{ 0x6e4d, 0x75 },
+	{ 0x6e4e, 0x00 },
+	{ 0x6e4f, 0x75 },
+	{ 0x6e50, 0x00 },
+	{ 0x6e51, 0x75 },
+	{ 0x6e52, 0x00 },
+	{ 0x6e53, 0x75 },
+	{ 0x6e54, 0x00 },
+	{ 0x6e55, 0x75 },
+	{ 0x6e56, 0x00 },
+	{ 0x6e57, 0x75 },
+	{ 0x6e58, 0x00 },
+	{ 0x6e59, 0x75 },
+	{ 0x6e5a, 0x00 },
+	{ 0x6e5b, 0x75 },
+	{ 0x6e5c, 0x00 },
+	{ 0x6e5d, 0x75 },
+	{ 0x6e5e, 0x00 },
+	{ 0x6e5f, 0x75 },
+	{ 0x6e60, 0x00 },
+	{ 0x6e61, 0x75 },
+	{ 0x6e62, 0x00 },
+	{ 0x6e63, 0x75 },
+	{ 0x6e64, 0x00 },
+	{ 0x6e65, 0x75 },
+	{ 0x6e66, 0x00 },
+	{ 0x6e67, 0x75 },
+	{ 0x6e68, 0x00 },
+	{ 0x6e69, 0x75 },
+	{ 0x6e6a, 0x00 },
+	{ 0x6e6b, 0x75 },
+	{ 0x6e6c, 0x00 },
+	{ 0x6e6d, 0x75 },
+	{ 0x6e6e, 0x00 },
+	{ 0x6e6f, 0x75 },
+	{ 0x6e70, 0x00 },
+	{ 0x6e71, 0x75 },
+	{ 0x6e72, 0x00 },
+	{ 0x6e73, 0x75 },
+	{ 0x6e74, 0x00 },
+	{ 0x6e75, 0x75 },
+	{ 0x6e76, 0x00 },
+	{ 0x6e77, 0x75 },
+	{ 0x6e78, 0x00 },
+	{ 0x6e79, 0x75 },
+	{ 0x6e7a, 0x00 },
+	{ 0x6e7b, 0x75 },
+	{ 0x6e7c, 0x00 },
+	{ 0x6e7d, 0x75 },
+	{ 0x6e7e, 0x00 },
+	{ 0x6e7f, 0x75 },
+	{ 0x6e80, 0x00 },
+	{ 0x6e81, 0x75 },
+	{ 0x6e82, 0x00 },
+	{ 0x6e83, 0x75 },
+	{ 0x6e84, 0x00 },
+	{ 0x6e85, 0x75 },
+	{ 0x6e86, 0x00 },
+	{ 0x6e87, 0x75 },
+	{ 0x6e88, 0x00 },
+	{ 0x6e89, 0x75 },
+	{ 0x6e8a, 0x00 },
+	{ 0x6e8b, 0x75 },
+	{ 0x6e8c, 0x00 },
+	{ 0x6e8d, 0x75 },
+	{ 0x6e8e, 0x00 },
+	{ 0x6e8f, 0x75 },
+	{ 0x6e90, 0x00 },
+	{ 0x6e91, 0x75 },
+	{ 0x6e92, 0x00 },
+	{ 0x6e93, 0x75 },
+	{ 0x6e94, 0x00 },
+	{ 0x6e95, 0x75 },
+	{ 0x6e96, 0x00 },
+	{ 0x6e97, 0x75 },
+	{ 0x6e98, 0x00 },
+	{ 0x6e99, 0x75 },
+	{ 0x6e9a, 0x00 },
+	{ 0x6e9b, 0x75 },
+	{ 0x6e9c, 0x00 },
+	{ 0x6e9d, 0x75 },
+	{ 0x6e9e, 0x00 },
+	{ 0x6e9f, 0x75 },
+	{ 0x6ea0, 0x00 },
+	{ 0x6ea1, 0x75 },
+	{ 0x6ea2, 0x00 },
+	{ 0x6ea3, 0x75 },
+	{ 0x6ea4, 0x00 },
+	{ 0x6ea5, 0x75 },
+	{ 0x6ea6, 0x00 },
+	{ 0x6ea7, 0x75 },
+	{ 0x6ea8, 0x00 },
+	{ 0x6ea9, 0x75 },
+	{ 0x6eaa, 0x00 },
+	{ 0x6eab, 0x75 },
+	{ 0x6eac, 0x00 },
+	{ 0x6ead, 0x75 },
+	{ 0x6eae, 0x00 },
+	{ 0x6eaf, 0x75 },
+	{ 0x6eb0, 0x00 },
+	{ 0x6eb1, 0x75 },
+	{ 0x6eb2, 0x00 },
+	{ 0x6eb3, 0x75 },
+	{ 0x6eb4, 0x00 },
+	{ 0x6eb5, 0x75 },
+	{ 0x6eb6, 0x00 },
+	{ 0x6eb7, 0x75 },
+	{ 0x6eb8, 0x00 },
+	{ 0x6eb9, 0x75 },
+	{ 0x6eba, 0x00 },
+	{ 0x6ebb, 0x75 },
+	{ 0x6ebc, 0x00 },
+	{ 0x6ebd, 0x75 },
+	{ 0x6ebe, 0x00 },
+	{ 0x6ebf, 0x75 },
+	{ 0x6ec0, 0x00 },
+	{ 0x6ec1, 0x75 },
+	{ 0x6ec2, 0x00 },
+	{ 0x6ec3, 0x75 },
+	{ 0x6ec4, 0x00 },
+	{ 0x6ec5, 0x75 },
+	{ 0x6ec6, 0x00 },
+	{ 0x6ec7, 0x75 },
+	{ 0x6ec8, 0x00 },
+	{ 0x6ec9, 0x75 },
+	{ 0x6eca, 0x00 },
+	{ 0x6ecb, 0x75 },
+	{ 0x6ecc, 0x00 },
+	{ 0x6ecd, 0x75 },
+	{ 0x6ece, 0x00 },
+	{ 0x6ecf, 0x75 },
+	{ 0x6ed0, 0x00 },
+	{ 0x6ed1, 0x75 },
+	{ 0x6ed2, 0x00 },
+	{ 0x6ed3, 0x75 },
+	{ 0x6ed4, 0x00 },
+	{ 0x6ed5, 0x75 },
+	{ 0x6ed6, 0x00 },
+	{ 0x6ed7, 0x75 },
+	{ 0x6ed8, 0x00 },
+	{ 0x6ed9, 0x75 },
+	{ 0x6eda, 0x00 },
+	{ 0x6edb, 0x75 },
+	{ 0x6edc, 0x00 },
+	{ 0x6edd, 0x75 },
+	{ 0x6ede, 0x00 },
+	{ 0x6edf, 0x75 },
+	{ 0x6ee0, 0x00 },
+	{ 0x6ee1, 0x75 },
+	{ 0x6ee2, 0x00 },
+	{ 0x6ee3, 0x75 },
+	{ 0x6ee4, 0x00 },
+	{ 0x6ee5, 0x75 },
+	{ 0x6ee6, 0x00 },
+	{ 0x6ee7, 0x75 },
+	{ 0x6ee8, 0x00 },
+	{ 0x6ee9, 0x75 },
+	{ 0x6eea, 0x00 },
+	{ 0x6eeb, 0x75 },
+	{ 0x6eec, 0x00 },
+	{ 0x6eed, 0x75 },
+	{ 0x6eee, 0x00 },
+	{ 0x6eef, 0x75 },
+	{ 0x6ef0, 0x00 },
+	{ 0x6ef1, 0x75 },
+	{ 0x6ef2, 0x00 },
+	{ 0x6ef3, 0x75 },
+	{ 0x6ef4, 0x00 },
+	{ 0x6ef5, 0x75 },
+	{ 0x6ef6, 0x00 },
+	{ 0x6ef7, 0x75 },
+	{ 0x6ef8, 0x00 },
+	{ 0x6ef9, 0x75 },
+	{ 0x6efa, 0x00 },
+	{ 0x6efb, 0x75 },
+	{ 0x6efc, 0x00 },
+	{ 0x6efd, 0x75 },
+	{ 0x6efe, 0x00 },
+	{ 0x6eff, 0x75 },
+	{ 0x6f00, 0x00 },
+	{ 0x6f01, 0x75 },
+	{ 0x6f02, 0x00 },
+	{ 0x6f03, 0x75 },
+	{ 0x6f04, 0x00 },
+	{ 0x6f05, 0x75 },
+	{ 0x6f06, 0x00 },
+	{ 0x6f07, 0x75 },
+	{ 0x6f08, 0x00 },
+	{ 0x6f09, 0x75 },
+	{ 0x6f0a, 0x00 },
+	{ 0x6f0b, 0x75 },
+	{ 0x6f0c, 0x00 },
+	{ 0x6f0d, 0x75 },
+	{ 0x6f0e, 0x00 },
+	{ 0x6f0f, 0x75 },
+	{ 0x6f10, 0x00 },
+	{ 0x6f11, 0x75 },
+	{ 0x6f12, 0x00 },
+	{ 0x6f13, 0x75 },
+	{ 0x6f14, 0x00 },
+	{ 0x6f15, 0x75 },
+	{ 0x6f16, 0x00 },
+	{ 0x6f17, 0x75 },
+	{ 0x6f18, 0x00 },
+	{ 0x6f19, 0x75 },
+	{ 0x6f1a, 0x00 },
+	{ 0x6f1b, 0x75 },
+	{ 0x6f1c, 0x00 },
+	{ 0x6f1d, 0x75 },
+	{ 0x6f1e, 0x00 },
+	{ 0x6f1f, 0x75 },
+	{ 0x6f20, 0x00 },
+	{ 0x6f21, 0x75 },
+	{ 0x6f22, 0x00 },
+	{ 0x6f23, 0x75 },
+	{ 0x6f24, 0x00 },
+	{ 0x6f25, 0x75 },
+	{ 0x6f26, 0x00 },
+	{ 0x6f27, 0x75 },
+	{ 0x6f28, 0x00 },
+	{ 0x6f29, 0x75 },
+	{ 0x6f2a, 0x00 },
+	{ 0x6f2b, 0x75 },
+	{ 0x6f2c, 0x00 },
+	{ 0x6f2d, 0x75 },
+	{ 0x6f2e, 0x00 },
+	{ 0x6f2f, 0x75 },
+	{ 0x6f30, 0x00 },
+	{ 0x6f31, 0x75 },
+	{ 0x6f32, 0x00 },
+	{ 0x6f33, 0x75 },
+	{ 0x6f34, 0x00 },
+	{ 0x6f35, 0x75 },
+	{ 0x6f36, 0x00 },
+	{ 0x6f37, 0x75 },
+	{ 0x6f38, 0x00 },
+	{ 0x6f39, 0x75 },
+	{ 0x6f3a, 0x00 },
+	{ 0x6f3b, 0x75 },
+	{ 0x6f3c, 0x00 },
+	{ 0x6f3d, 0x75 },
+	{ 0x6f3e, 0x00 },
+	{ 0x6f3f, 0x75 },
+	{ 0x6f40, 0x00 },
+	{ 0x6f41, 0x75 },
+	{ 0x6f42, 0x00 },
+	{ 0x6f43, 0x75 },
+	{ 0x6f44, 0x00 },
+	{ 0x6f45, 0x75 },
+	{ 0x6f46, 0x00 },
+	{ 0x6f47, 0x75 },
+	{ 0x6f48, 0x00 },
+	{ 0x6f49, 0x75 },
+	{ 0x6f4a, 0x00 },
+	{ 0x6f4b, 0x75 },
+	{ 0x6f4c, 0x00 },
+	{ 0x6f4d, 0x75 },
+	{ 0x6f4e, 0x00 },
+	{ 0x6f4f, 0x75 },
+	{ 0x6f50, 0x00 },
+	{ 0x6f51, 0x75 },
+	{ 0x6f52, 0x00 },
+	{ 0x6f53, 0x75 },
+	{ 0x6f54, 0x00 },
+	{ 0x6f55, 0x75 },
+	{ 0x6f56, 0x00 },
+	{ 0x6f57, 0x75 },
+	{ 0x6f58, 0x00 },
+	{ 0x6f59, 0x75 },
+	{ 0x6f5a, 0x00 },
+	{ 0x6f5b, 0x75 },
+	{ 0x6f5c, 0x00 },
+	{ 0x6f5d, 0x75 },
+	{ 0x6f5e, 0x00 },
+	{ 0x6f5f, 0x75 },
+	{ 0x6f60, 0x00 },
+	{ 0x6f61, 0x75 },
+	{ 0x6f62, 0x00 },
+	{ 0x6f63, 0x75 },
+	{ 0x6f64, 0x00 },
+	{ 0x6f65, 0x75 },
+	{ 0x6f66, 0x00 },
+	{ 0x6f67, 0x75 },
+	{ 0x6f68, 0x00 },
+	{ 0x6f69, 0x75 },
+	{ 0x6f6a, 0x00 },
+	{ 0x6f6b, 0x75 },
+	{ 0x6f6c, 0x00 },
+	{ 0x6f6d, 0x75 },
+	{ 0x6f6e, 0x00 },
+	{ 0x6f6f, 0x75 },
+	{ 0x6f70, 0x00 },
+	{ 0x6f71, 0x75 },
+	{ 0x6f72, 0x00 },
+	{ 0x6f73, 0x75 },
+	{ 0x6f74, 0x00 },
+	{ 0x6f75, 0x75 },
+	{ 0x6f76, 0x00 },
+	{ 0x6f77, 0x75 },
+	{ 0x6f78, 0x00 },
+	{ 0x6f79, 0x75 },
+	{ 0x6f7a, 0x00 },
+	{ 0x6f7b, 0x75 },
+	{ 0x6f7c, 0x00 },
+	{ 0x6f7d, 0x75 },
+	{ 0x6f7e, 0x00 },
+	{ 0x6f7f, 0x75 },
+	{ 0x6f80, 0x00 },
+	{ 0x6f81, 0x75 },
+	{ 0x6f82, 0x00 },
+	{ 0x6f83, 0x75 },
+	{ 0x6f84, 0x00 },
+	{ 0x6f85, 0x75 },
+	{ 0x6f86, 0x00 },
+	{ 0x6f87, 0x75 },
+	{ 0x6f88, 0x00 },
+	{ 0x6f89, 0x75 },
+	{ 0x6f8a, 0x00 },
+	{ 0x6f8b, 0x75 },
+	{ 0x6f8c, 0x00 },
+	{ 0x6f8d, 0x75 },
+	{ 0x6f8e, 0x00 },
+	{ 0x6f8f, 0x75 },
+	{ 0x6f90, 0x00 },
+	{ 0x6f91, 0x75 },
+	{ 0x6f92, 0x00 },
+	{ 0x6f93, 0x75 },
+	{ 0x6f94, 0x00 },
+	{ 0x6f95, 0x75 },
+	{ 0x6f96, 0x00 },
+	{ 0x6f97, 0x75 },
+	{ 0x6f98, 0x00 },
+	{ 0x6f99, 0x75 },
+	{ 0x6f9a, 0x00 },
+	{ 0x6f9b, 0x75 },
+	{ 0x6f9c, 0x00 },
+	{ 0x6f9d, 0x75 },
+	{ 0x6f9e, 0x00 },
+	{ 0x6f9f, 0x75 },
+	{ 0x6fa0, 0x00 },
+	{ 0x6fa1, 0x75 },
+	{ 0x6fa2, 0x00 },
+	{ 0x6fa3, 0x75 },
+	{ 0x6fa4, 0x00 },
+	{ 0x6fa5, 0x75 },
+	{ 0x6fa6, 0x00 },
+	{ 0x6fa7, 0x75 },
+	{ 0x6fa8, 0x00 },
+	{ 0x6fa9, 0x75 },
+	{ 0x6faa, 0x00 },
+	{ 0x6fab, 0x75 },
+	{ 0x6fac, 0x00 },
+	{ 0x6fad, 0x75 },
+	{ 0x6fae, 0x00 },
+	{ 0x6faf, 0x75 },
+	{ 0x6fb0, 0x00 },
+	{ 0x6fb1, 0x75 },
+	{ 0x6fb2, 0x00 },
+	{ 0x6fb3, 0x75 },
+	{ 0x6fb4, 0x00 },
+	{ 0x6fb5, 0x75 },
+	{ 0x6fb6, 0x00 },
+	{ 0x6fb7, 0x75 },
+	{ 0x6fb8, 0x00 },
+	{ 0x6fb9, 0x75 },
+	{ 0x6fba, 0x00 },
+	{ 0x6fbb, 0x75 },
+	{ 0x6fbc, 0x00 },
+	{ 0x6fbd, 0x75 },
+	{ 0x6fbe, 0x00 },
+	{ 0x6fbf, 0x75 },
+	{ 0x6fc0, 0x00 },
+	{ 0x6fc1, 0x75 },
+	{ 0x6fc2, 0x00 },
+	{ 0x6fc3, 0x75 },
+	{ 0x6fc4, 0x00 },
+	{ 0x6fc5, 0x75 },
+	{ 0x6fc6, 0x00 },
+	{ 0x6fc7, 0x75 },
+	{ 0x6fc8, 0x00 },
+	{ 0x6fc9, 0x75 },
+	{ 0x6fca, 0x00 },
+	{ 0x6fcb, 0x75 },
+	{ 0x6fcc, 0x00 },
+	{ 0x6fcd, 0x75 },
+	{ 0x6fce, 0x00 },
+	{ 0x6fcf, 0x75 },
+	{ 0x6fd0, 0x00 },
+	{ 0x6fd1, 0x75 },
+	{ 0x6fd2, 0x00 },
+	{ 0x6fd3, 0x75 },
+	{ 0x6fd4, 0x00 },
+	{ 0x6fd5, 0x75 },
+	{ 0x6fd6, 0x00 },
+	{ 0x6fd7, 0x75 },
+	{ 0x6fd8, 0x00 },
+	{ 0x6fd9, 0x75 },
+	{ 0x6fda, 0x00 },
+	{ 0x6fdb, 0x75 },
+	{ 0x6fdc, 0x00 },
+	{ 0x6fdd, 0x75 },
+	{ 0x6fde, 0x00 },
+	{ 0x6fdf, 0x75 },
+	{ 0x6fe0, 0x00 },
+	{ 0x6fe1, 0x75 },
+	{ 0x6fe2, 0x00 },
+	{ 0x6fe3, 0x75 },
+	{ 0x6fe4, 0x00 },
+	{ 0x6fe5, 0x75 },
+	{ 0x6fe6, 0x00 },
+	{ 0x6fe7, 0x75 },
+	{ 0x6fe8, 0x00 },
+	{ 0x6fe9, 0x75 },
+	{ 0x6fea, 0x00 },
+	{ 0x6feb, 0x75 },
+	{ 0x6fec, 0x00 },
+	{ 0x6fed, 0x75 },
+	{ 0x6fee, 0x00 },
+	{ 0x6fef, 0x75 },
+	{ 0x6ff0, 0x00 },
+	{ 0x6ff1, 0x75 },
+	{ 0x6ff2, 0x00 },
+	{ 0x6ff3, 0x75 },
+	{ 0x6ff4, 0x00 },
+	{ 0x6ff5, 0x75 },
+	{ 0x6ff6, 0x00 },
+	{ 0x6ff7, 0x75 },
+	{ 0x6ff8, 0x00 },
+	{ 0x6ff9, 0x75 },
+	{ 0x6ffa, 0x00 },
+	{ 0x6ffb, 0x75 },
+	{ 0x6ffc, 0x00 },
+	{ 0x6ffd, 0x75 },
+	{ 0x6ffe, 0x00 },
+	{ 0x6fff, 0x75 },
+	{ 0x7000, 0x00 },
+	{ 0x7001, 0x75 },
+	{ 0x7002, 0x00 },
+	{ 0x7003, 0x75 },
+	{ 0x7004, 0x00 },
+	{ 0x7005, 0x75 },
+	{ 0x7006, 0x00 },
+	{ 0x7007, 0x75 },
+	{ 0x7008, 0x00 },
+	{ 0x7009, 0x75 },
+	{ 0x700a, 0x00 },
+	{ 0x700b, 0x75 },
+	{ 0x700c, 0x00 },
+	{ 0x700d, 0x75 },
+	{ 0x700e, 0x00 },
+	{ 0x700f, 0x75 },
+	{ 0x7010, 0x00 },
+	{ 0x7011, 0x75 },
+	{ 0x7012, 0x00 },
+	{ 0x7013, 0x75 },
+	{ 0x7014, 0x00 },
+	{ 0x7015, 0x75 },
+	{ 0x7016, 0x00 },
+	{ 0x7017, 0x75 },
+	{ 0x7018, 0x00 },
+	{ 0x7019, 0x75 },
+	{ 0x701a, 0x00 },
+	{ 0x701b, 0x75 },
+	{ 0x701c, 0x00 },
+	{ 0x701d, 0x75 },
+	{ 0x701e, 0x00 },
+	{ 0x701f, 0x75 },
+	{ 0x7020, 0x00 },
+	{ 0x7021, 0x75 },
+	{ 0x7022, 0x00 },
+	{ 0x7023, 0x75 },
+	{ 0x7024, 0x00 },
+	{ 0x7025, 0x75 },
+	{ 0x7026, 0x00 },
+	{ 0x7027, 0x75 },
+	{ 0x7028, 0x00 },
+	{ 0x7029, 0x75 },
+	{ 0x702a, 0x00 },
+	{ 0x702b, 0x75 },
+	{ 0x702c, 0x00 },
+	{ 0x702d, 0x75 },
+	{ 0x702e, 0x00 },
+	{ 0x702f, 0x75 },
+	{ 0x7030, 0x00 },
+	{ 0x7031, 0x75 },
+	{ 0x7032, 0x00 },
+	{ 0x7033, 0x75 },
+	{ 0x7034, 0x00 },
+	{ 0x7035, 0x75 },
+	{ 0x7036, 0x00 },
+	{ 0x7037, 0x75 },
+	{ 0x7038, 0x00 },
+	{ 0x7039, 0x75 },
+	{ 0x703a, 0x00 },
+	{ 0x703b, 0x75 },
+	{ 0x703c, 0x00 },
+	{ 0x703d, 0x75 },
+	{ 0x703e, 0x00 },
+	{ 0x703f, 0x75 },
+	{ 0x7040, 0x00 },
+	{ 0x7041, 0x75 },
+	{ 0x7042, 0x00 },
+	{ 0x7043, 0x75 },
+	{ 0x7044, 0x00 },
+	{ 0x7045, 0x75 },
+	{ 0x7046, 0x00 },
+	{ 0x7047, 0x75 },
+	{ 0x7048, 0x00 },
+	{ 0x7049, 0x75 },
+	{ 0x704a, 0x00 },
+	{ 0x704b, 0x75 },
+	{ 0x704c, 0x00 },
+	{ 0x704d, 0x75 },
+	{ 0x704e, 0x00 },
+	{ 0x704f, 0x75 },
+	{ 0x7050, 0x00 },
+	{ 0x7051, 0x75 },
+	{ 0x7052, 0x00 },
+	{ 0x7053, 0x75 },
+	{ 0x7054, 0x00 },
+	{ 0x7055, 0x75 },
+	{ 0x7056, 0x00 },
+	{ 0x7057, 0x75 },
+	{ 0x7058, 0x00 },
+	{ 0x7059, 0x75 },
+	{ 0x705a, 0x00 },
+	{ 0x705b, 0x75 },
+	{ 0x705c, 0x00 },
+	{ 0x705d, 0x75 },
+	{ 0x705e, 0x00 },
+	{ 0x705f, 0x75 },
+	{ 0x7060, 0x00 },
+	{ 0x7061, 0x75 },
+	{ 0x7062, 0x00 },
+	{ 0x7063, 0x75 },
+	{ 0x7064, 0x00 },
+	{ 0x7065, 0x75 },
+	{ 0x7066, 0x00 },
+	{ 0x7067, 0x75 },
+	{ 0x7068, 0x00 },
+	{ 0x7069, 0x75 },
+	{ 0x706a, 0x00 },
+	{ 0x706b, 0x75 },
+	{ 0x706c, 0x00 },
+	{ 0x706d, 0x75 },
+	{ 0x706e, 0x00 },
+	{ 0x706f, 0x75 },
+	{ 0x7070, 0x00 },
+	{ 0x7071, 0x75 },
+	{ 0x7072, 0x00 },
+	{ 0x7073, 0x75 },
+	{ 0x7074, 0x00 },
+	{ 0x7075, 0x75 },
+	{ 0x7076, 0x00 },
+	{ 0x7077, 0x75 },
+	{ 0x7078, 0x00 },
+	{ 0x7079, 0x75 },
+	{ 0x707a, 0x00 },
+	{ 0x707b, 0x75 },
+	{ 0x707c, 0x00 },
+	{ 0x707d, 0x75 },
+	{ 0x707e, 0x00 },
+	{ 0x707f, 0x75 },
+	{ 0x70e4, 0x02 },
+	{ 0x70e6, 0x06 },
+	{ 0x70e7, 0x08 },
+	{ 0x70e8, 0x0a },
+	{ 0x70e9, 0x0c },
+	{ 0x70ea, 0x10 },
+	{ 0x70eb, 0x16 },
+	{ 0x70ec, 0x1a },
+	{ 0x70ed, 0x20 },
+	{ 0x70ee, 0x04 },
+	{ 0x70ef, 0x06 },
+	{ 0x70f0, 0x08 },
+	{ 0x70f1, 0x0a },
+	{ 0x70f2, 0x10 },
+	{ 0x70f3, 0x16 },
+	{ 0x70f4, 0x1a },
+	{ 0x70f5, 0x20 },
+	{ 0x7119, 0x16 },
+	{ 0x711b, 0x1a },
+	{ 0x713a, 0x19 },
+	{ 0x713b, 0x17 },
+	{ 0x713c, 0x15 },
+	{ 0x713d, 0x13 },
+	{ 0x713e, 0x11 },
+	{ 0x713f, 0x11 },
+	{ 0x7140, 0x11 },
+	{ 0x7141, 0x11 },
+	{ 0x7142, 0x12 },
+	{ 0x7143, 0x0d },
+	{ 0x7144, 0x0b },
+	{ 0x7145, 0x0b },
+	{ 0x7146, 0x09 },
+	{ 0x7147, 0x09 },
+	{ 0x7148, 0x09 },
+	{ 0x7149, 0x08 },
+	{ 0x7150, 0x00 },
+};
+
+static const s64 link_freq_menu_items[] = {
+	OV32C4_LINK_FREQ_400MHZ,
+};
+
+static const struct ov32c4_mode supported_modes[] = {
+	{
+		.width = 3264,
+		.height = 1840,
+		.hts_reg = OV32C4_HTS_REG,
+		.vts_min = 2614,
+		.reg_sequence = ov32c4_mode_3264x1840_regs,
+		.sequence_length = ARRAY_SIZE(ov32c4_mode_3264x1840_regs),
+	},
+};
+
+struct ov32c4 {
+	struct device *dev;
+
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct regmap *regmap;
+
+	/* Companion chip that gates the sensor, see OV32C4_COMPANION_ENABLE_REG */
+	u16 companion_addr;
+
+	/* V4L2 controls */
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *exposure;
+
+	struct clk *img_clk;
+	struct gpio_desc *reset;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(ov32c4_supply_names)];
+
+	u32 link_freq_index;
+	u8 mipi_lanes;
+};
+
+/*
+ * Walk the sensor's ACPI _CRS and return the address of the n-th
+ * I2cSerialBus resource. Index 0 is the sensor itself (the kernel binds the
+ * i2c client to it), index 1 is the companion, index 2 the module EEPROM.
+ */
+struct ov32c4_i2c_res_ctx {
+	int index;
+	int want;
+	u16 addr;
+};
+
+static int ov32c4_i2c_res_cb(struct acpi_resource *ares, void *data)
+{
+	struct ov32c4_i2c_res_ctx *ctx = data;
+	struct acpi_resource_i2c_serialbus *sb;
+
+	if (i2c_acpi_get_i2c_resource(ares, &sb)) {
+		if (ctx->index == ctx->want)
+			ctx->addr = sb->slave_address;
+		ctx->index++;
+	}
+
+	return 1;
+}
+
+static u16 ov32c4_acpi_i2c_addr(struct device *dev, int index)
+{
+	struct ov32c4_i2c_res_ctx ctx = { .want = index };
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	LIST_HEAD(resources);
+	int ret;
+
+	if (!adev)
+		return 0;
+
+	ret = acpi_dev_get_resources(adev, &resources, ov32c4_i2c_res_cb, &ctx);
+	acpi_dev_free_resource_list(&resources);
+
+	return ret < 0 ? 0 : ctx.addr;
+}
+
+static inline struct ov32c4 *to_ov32c4(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct ov32c4, sd);
+}
+
+/* MIPI D-PHY is DDR -> link frequency * 2 */
+static u64 ov32c4_pixel_rate(struct ov32c4 *ov32c4)
+{
+	return div_u64(link_freq_menu_items[ov32c4->link_freq_index] *
+		       2 * ov32c4->mipi_lanes, OV32C4_RGB_DEPTH);
+}
+
+/*
+ * 0x380c/0x380d is the line length in sensor clock cycles, not in pixels on
+ * the bus. What V4L2 wants (width + hblank) is that length expressed in bus
+ * pixels, so scale it by pixel_rate / SCLK. Proof that this is the right
+ * relation, and not a fixed "HTS is in units of N pixels": ov13b10 has the
+ * same register value 1176 in its 4-lane and 2-lane tables but reports
+ * ppl 4704 and 2352 respectively.
+ */
+static u32 ov32c4_ppl(struct ov32c4 *ov32c4, const struct ov32c4_mode *mode)
+{
+	return div_u64(mode->hts_reg * ov32c4_pixel_rate(ov32c4), OV32C4_SCLK);
+}
+
+static int ov32c4_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov32c4 *ov32c4 = container_of(ctrl->handler,
+					     struct ov32c4, ctrl_handler);
+	const u32 height = supported_modes[0].height;
+	s64 exposure_max;
+	int ret = 0;
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		exposure_max = height + ctrl->val - OV32C4_EXPOSURE_MAX_MARGIN;
+		__v4l2_ctrl_modify_range(ov32c4->exposure,
+					 ov32c4->exposure->minimum,
+					 exposure_max, ov32c4->exposure->step,
+					 exposure_max);
+	}
+
+	if (!pm_runtime_get_if_in_use(ov32c4->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ANALOGUE_GAIN:
+		cci_write(ov32c4->regmap, OV32C4_REG_ANALOG_GAIN,
+			  ctrl->val, &ret);
+		cci_write(ov32c4->regmap, OV32C4_REG_ANALOG_GAIN_CTX2,
+			  ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_EXPOSURE:
+		cci_write(ov32c4->regmap, OV32C4_REG_EXPOSURE,
+			  ctrl->val, &ret);
+		cci_write(ov32c4->regmap, OV32C4_REG_EXPOSURE_CTX2,
+			  ctrl->val, &ret);
+		break;
+
+	case V4L2_CID_VBLANK:
+		cci_write(ov32c4->regmap, OV32C4_REG_VTS, height + ctrl->val,
+			  &ret);
+		break;
+
+	case V4L2_CID_DIGITAL_GAIN:
+		cci_write(ov32c4->regmap, OV32C4_REG_DIGITAL_GAIN,
+			  (u64)ctrl->val << OV32C4_DGAIN_SHIFT, &ret);
+		cci_write(ov32c4->regmap, OV32C4_REG_DIGITAL_GAIN_CTX2,
+			  (u64)ctrl->val << OV32C4_DGAIN_SHIFT, &ret);
+		break;
+
+	case V4L2_CID_HFLIP:
+		cci_update_bits(ov32c4->regmap, OV32C4_REG_MIRROR,
+				OV32C4_FLIP_BIT,
+				ctrl->val ? 0 : OV32C4_FLIP_BIT, &ret);
+		break;
+
+	case V4L2_CID_VFLIP:
+		cci_update_bits(ov32c4->regmap, OV32C4_REG_FLIP,
+				OV32C4_FLIP_BIT,
+				ctrl->val ? OV32C4_FLIP_BIT : 0, &ret);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	pm_runtime_put(ov32c4->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov32c4_ctrl_ops = {
+	.s_ctrl = ov32c4_set_ctrl,
+};
+
+static int ov32c4_init_controls(struct ov32c4 *ov32c4)
+{
+	struct v4l2_ctrl_handler *ctrl_hdlr = &ov32c4->ctrl_handler;
+	const struct ov32c4_mode *mode = &supported_modes[0];
+	u32 vblank_min, vblank_max, vblank_default;
+	struct v4l2_fwnode_device_properties props;
+	s64 exposure_max, h_blank, pixel_rate;
+	int ret;
+
+	v4l2_ctrl_handler_init(ctrl_hdlr, 13);
+
+	ov32c4->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov32c4_ctrl_ops,
+						   V4L2_CID_LINK_FREQ,
+						   ARRAY_SIZE(link_freq_menu_items) - 1,
+						   ov32c4->link_freq_index,
+						   link_freq_menu_items);
+	if (ov32c4->link_freq)
+		ov32c4->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	pixel_rate = ov32c4_pixel_rate(ov32c4);
+	ov32c4->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops,
+					       V4L2_CID_PIXEL_RATE, 0,
+					       pixel_rate, 1, pixel_rate);
+
+	vblank_min = mode->vts_min - mode->height;
+	vblank_max = OV32C4_VTS_MAX - mode->height;
+	vblank_default = vblank_min;
+	ov32c4->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops,
+					   V4L2_CID_VBLANK, vblank_min,
+					   vblank_max, 1, vblank_default);
+
+	h_blank = ov32c4_ppl(ov32c4, mode) - mode->width;
+	ov32c4->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops,
+					   V4L2_CID_HBLANK, h_blank, h_blank,
+					   1, h_blank);
+	if (ov32c4->hblank)
+		ov32c4->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+			  OV32C4_ANA_GAIN_MIN, OV32C4_ANA_GAIN_MAX,
+			  OV32C4_ANA_GAIN_STEP, OV32C4_ANA_GAIN_DEFAULT);
+
+	exposure_max = mode->vts_min - OV32C4_EXPOSURE_MAX_MARGIN;
+	ov32c4->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops,
+					     V4L2_CID_EXPOSURE,
+					     OV32C4_EXPOSURE_MIN, exposure_max,
+					     OV32C4_EXPOSURE_STEP,
+					     exposure_max);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+			  OV32C4_DGAIN_MIN, OV32C4_DGAIN_MAX,
+			  OV32C4_DGAIN_STEP, OV32C4_DGAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops, V4L2_CID_HFLIP,
+			  0, 1, 1, 0);
+	v4l2_ctrl_new_std(ctrl_hdlr, &ov32c4_ctrl_ops, V4L2_CID_VFLIP,
+			  0, 1, 1, 0);
+
+	ret = v4l2_fwnode_device_parse(ov32c4->dev, &props);
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov32c4_ctrl_ops, &props);
+
+	if (ctrl_hdlr->error)
+		return ctrl_hdlr->error;
+
+	ov32c4->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+}
+
+static void ov32c4_update_pad_format(const struct ov32c4_mode *mode,
+				     struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->width = mode->width;
+	fmt->height = mode->height;
+	/* graph_settings_OV32C4_*_LNL.bin advertise this mode as GR10 */
+	fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov32c4_enable_streams(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *state,
+				 u32 pad, u64 streams_mask)
+{
+	const struct ov32c4_mode *mode = &supported_modes[0];
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(ov32c4->dev);
+	if (ret)
+		return ret;
+
+	ret = regmap_multi_reg_write(ov32c4->regmap, mode->reg_sequence,
+				     mode->sequence_length);
+	if (ret) {
+		dev_err(ov32c4->dev, "failed to set mode: %d\n", ret);
+		goto out;
+	}
+
+	ret = __v4l2_ctrl_handler_setup(ov32c4->sd.ctrl_handler);
+	if (ret)
+		goto out;
+
+	ret = cci_write(ov32c4->regmap, OV32C4_REG_STREAM_CONTROL, 1, NULL);
+out:
+	if (ret)
+		pm_runtime_put(ov32c4->dev);
+
+	return ret;
+}
+
+static int ov32c4_disable_streams(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state,
+				  u32 pad, u64 streams_mask)
+{
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+
+	cci_write(ov32c4->regmap, OV32C4_REG_STREAM_CONTROL, 0, NULL);
+	pm_runtime_put(ov32c4->dev);
+
+	return 0;
+}
+
+static int ov32c4_get_pm_resources(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+	int i;
+
+	/*
+	 * INT3472 registers the reset pin as GPIO_ACTIVE_LOW, so a logical
+	 * 1 here means "held in reset".
+	 */
+	ov32c4->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(ov32c4->reset))
+		return dev_err_probe(dev, PTR_ERR(ov32c4->reset),
+				     "failed to get reset gpio\n");
+
+	for (i = 0; i < ARRAY_SIZE(ov32c4_supply_names); i++)
+		ov32c4->supplies[i].supply = ov32c4_supply_names[i];
+
+	return devm_regulator_bulk_get(dev, ARRAY_SIZE(ov32c4_supply_names),
+				       ov32c4->supplies);
+}
+
+/*
+ * Ungate the sensor through the companion chip. Deliberately a bare
+ * i2c_transfer: the address belongs to the VCM and must stay free for it.
+ */
+static int ov32c4_companion_enable(struct ov32c4 *ov32c4)
+{
+	struct i2c_client *client = to_i2c_client(ov32c4->dev);
+	u8 wr[3] = {
+		OV32C4_COMPANION_ENABLE_REG >> 8,
+		OV32C4_COMPANION_ENABLE_REG & 0xff,
+		OV32C4_COMPANION_ENABLE_VAL,
+	};
+	struct i2c_msg msg = {
+		.addr = ov32c4->companion_addr,
+		.flags = 0,
+		.len = sizeof(wr),
+		.buf = wr,
+	};
+	int ret;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret == 1)
+		return 0;
+
+	return ret < 0 ? ret : -EIO;
+}
+
+static int ov32c4_power_off(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+
+	gpiod_set_value_cansleep(ov32c4->reset, 1);
+	regulator_bulk_disable(ARRAY_SIZE(ov32c4_supply_names),
+			       ov32c4->supplies);
+	clk_disable_unprepare(ov32c4->img_clk);
+
+	return 0;
+}
+
+static int ov32c4_power_on(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+	int ret;
+
+	ret = clk_prepare_enable(ov32c4->img_clk);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable imaging clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(ov32c4_supply_names),
+				    ov32c4->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable regulators: %d\n", ret);
+		clk_disable_unprepare(ov32c4->img_clk);
+		return ret;
+	}
+
+	fsleep(OV32C4_AVDD_SETTLE_US);
+
+	if (ov32c4->reset) {
+		gpiod_set_value_cansleep(ov32c4->reset, 0);
+		fsleep(OV32C4_RESET_SETTLE_US);
+	}
+
+	if (ov32c4->companion_addr) {
+		ret = ov32c4_companion_enable(ov32c4);
+		if (ret)
+			dev_err(dev, "companion at 0x%02x did not accept the enable write: %d\n",
+				ov32c4->companion_addr, ret);
+		fsleep(1000);
+	}
+
+	dev_dbg(dev,
+		"powered: clk %lu Hz (enabled), avdd on, reset logical %d%s\n",
+		 clk_get_rate(ov32c4->img_clk),
+		 ov32c4->reset ? gpiod_get_value_cansleep(ov32c4->reset) : -1,
+		 ov32c4->reset ? "" : " (no reset gpio!)");
+
+	return 0;
+}
+
+static int ov32c4_set_format(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *sd_state,
+			     struct v4l2_subdev_format *fmt)
+{
+	const struct ov32c4_mode *mode = &supported_modes[0];
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+	s32 vblank_def, h_blank;
+
+	ov32c4_update_pad_format(mode, &fmt->format);
+	*v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+		return 0;
+
+	vblank_def = mode->vts_min - mode->height;
+	__v4l2_ctrl_modify_range(ov32c4->vblank, vblank_def,
+				 OV32C4_VTS_MAX - mode->height, 1, vblank_def);
+	__v4l2_ctrl_s_ctrl(ov32c4->vblank, vblank_def);
+
+	h_blank = ov32c4_ppl(ov32c4, mode) - mode->width;
+	__v4l2_ctrl_modify_range(ov32c4->hblank, h_blank, h_blank, 1, h_blank);
+
+	return 0;
+}
+
+static int ov32c4_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+	return 0;
+}
+
+static int ov32c4_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+		return -EINVAL;
+
+	fse->min_width = supported_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = supported_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov32c4_init_state(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_state *sd_state)
+{
+	ov32c4_update_pad_format(&supported_modes[0],
+				 v4l2_subdev_state_get_format(sd_state, 0));
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops ov32c4_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+/* CSI-2 data type of the image stream: RAW10. */
+#define OV32C4_CSI2_DT_RAW10	0x2b
+
+/*
+ * Tell the receiver which CSI-2 data types actually come down the link.
+ *
+ * IPU7 asks for this (ipu7_isys_setup_video, "Framedesc: stream %u, len %u,
+ * vc %u, dt %#x"). Without the op the call returns -ENOIOCTLCMD, the receiver
+ * derives a single data type from the media bus code and ends up configured
+ * with one input pin at dt 0x2b -- which is why it reports one oversized
+ * packet per frame.
+ *
+ * The sensor also emits one extra long packet per frame that the receiver
+ * reports as oversized. It is deliberately not described here: its data type
+ * could not be determined, and IPU7 has no metadata capture to route it to
+ * anyway.
+ */
+static int ov32c4_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+				 struct v4l2_mbus_frame_desc *fd)
+{
+	struct v4l2_mbus_frame_desc_entry *e;
+
+	if (pad != 0)
+		return -EINVAL;
+
+	memset(fd, 0, sizeof(*fd));
+	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+
+	e = &fd->entry[fd->num_entries++];
+	e->stream = 0;
+	e->pixelcode = MEDIA_BUS_FMT_SGRBG10_1X10;
+	e->bus.csi2.vc = 0;
+	e->bus.csi2.dt = OV32C4_CSI2_DT_RAW10;
+
+	return 0;
+}
+
+/*
+ * libcamera asks for these and says so plainly when they are missing:
+ *
+ *   'ov32c4 0-0036': Failed to retrieve the sensor crop rectangle
+ *   'ov32c4 0-0036': The sensor kernel driver needs to be fixed
+ *
+ * Without them it defaults PixelArraySize to the output size (3264x1840),
+ * which is wrong by nearly a factor of four in area and makes every
+ * field-of-view and pixel-pitch calculation on top of it wrong too.
+ */
+static int ov32c4_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_state *state,
+				struct v4l2_subdev_selection *sel)
+{
+	if (sel->pad != 0)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP:
+		sel->r.left = OV32C4_CROP_LEFT;
+		sel->r.top = OV32C4_CROP_TOP;
+		sel->r.width = OV32C4_CROP_WIDTH;
+		sel->r.height = OV32C4_CROP_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = OV32C4_NATIVE_WIDTH;
+		sel->r.height = OV32C4_NATIVE_HEIGHT;
+		return 0;
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r.left = OV32C4_ACTIVE_LEFT;
+		sel->r.top = OV32C4_ACTIVE_TOP;
+		sel->r.width = OV32C4_ACTIVE_WIDTH;
+		sel->r.height = OV32C4_ACTIVE_HEIGHT;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct v4l2_subdev_pad_ops ov32c4_pad_ops = {
+	.set_fmt = ov32c4_set_format,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.get_selection = ov32c4_get_selection,
+	.enum_mbus_code = ov32c4_enum_mbus_code,
+	.enum_frame_size = ov32c4_enum_frame_size,
+	.get_frame_desc = ov32c4_get_frame_desc,
+	.enable_streams = ov32c4_enable_streams,
+	.disable_streams = ov32c4_disable_streams,
+};
+
+static const struct v4l2_subdev_ops ov32c4_subdev_ops = {
+	.video = &ov32c4_video_ops,
+	.pad = &ov32c4_pad_ops,
+};
+
+static const struct media_entity_operations ov32c4_subdev_entity_ops = {
+	.link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops ov32c4_internal_ops = {
+	.init_state = ov32c4_init_state,
+};
+
+static int ov32c4_identify_module(struct ov32c4 *ov32c4)
+{
+	struct i2c_client *client = to_i2c_client(ov32c4->dev);
+	unsigned int try;
+	u64 chip_id;
+	int ret = -ENODEV;
+
+	/*
+	 * The sensor does not answer right after reset is released, so allow
+	 * a few attempts before declaring it absent.
+	 */
+	for (try = 0; try < OV32C4_ID_RETRIES; try++) {
+		if (try)
+			fsleep(20000);
+
+		ret = cci_read(ov32c4->regmap, OV32C4_REG_CHIP_ID, &chip_id,
+			       NULL);
+		if (!ret)
+			break;
+	}
+
+	if (ret)
+		return dev_err_probe(ov32c4->dev, ret,
+				     "no answer at i2c addr 0x%02x after %u tries\n",
+				     client->addr, try);
+
+	if (chip_id != OV32C4_CHIP_ID)
+		return dev_err_probe(ov32c4->dev, -ENXIO,
+				     "chip id mismatch: %x != %llx\n",
+				     OV32C4_CHIP_ID, chip_id);
+
+	return 0;
+}
+
+static int ov32c4_check_hwcfg(struct ov32c4 *ov32c4)
+{
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY
+	};
+	struct device *dev = ov32c4->dev;
+	struct fwnode_handle *ep, *fwnode = dev_fwnode(dev);
+	unsigned long link_freq_bitmap;
+	int ret;
+
+	/* The fwnode graph is built by ipu-bridge; wait for it. */
+	ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
+	if (!ep)
+		return dev_err_probe(dev, -EPROBE_DEFER,
+				     "waiting for fwnode graph endpoint\n");
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+	fwnode_handle_put(ep);
+	if (ret)
+		return dev_err_probe(dev, ret, "parsing endpoint failed\n");
+
+	ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+				       bus_cfg.nr_of_link_frequencies,
+				       link_freq_menu_items,
+				       ARRAY_SIZE(link_freq_menu_items),
+				       &link_freq_bitmap);
+	if (ret)
+		goto check_hwcfg_error;
+
+	/* v4l2_link_freq_to_bitmap() guarantees at least one bit is set */
+	ov32c4->link_freq_index = ffs(link_freq_bitmap) - 1;
+
+	if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV32C4_DATA_LANES) {
+		ret = dev_err_probe(dev, -EINVAL,
+				    "number of CSI2 data lanes %u is not supported\n",
+				    bus_cfg.bus.mipi_csi2.num_data_lanes);
+		goto check_hwcfg_error;
+	}
+
+	ov32c4->mipi_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+check_hwcfg_error:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+	return ret;
+}
+
+static void ov32c4_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov32c4 *ov32c4 = to_ov32c4(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	v4l2_subdev_cleanup(sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	pm_runtime_disable(ov32c4->dev);
+	if (!pm_runtime_status_suspended(ov32c4->dev)) {
+		ov32c4_power_off(ov32c4->dev);
+		pm_runtime_set_suspended(ov32c4->dev);
+	}
+}
+
+static int ov32c4_probe(struct i2c_client *client)
+{
+	struct ov32c4 *ov32c4;
+	unsigned long freq;
+	int ret;
+
+	ov32c4 = devm_kzalloc(&client->dev, sizeof(*ov32c4), GFP_KERNEL);
+	if (!ov32c4)
+		return -ENOMEM;
+
+	ov32c4->dev = &client->dev;
+
+	ov32c4->img_clk = devm_v4l2_sensor_clk_get(ov32c4->dev, NULL);
+	if (IS_ERR(ov32c4->img_clk))
+		return dev_err_probe(ov32c4->dev, PTR_ERR(ov32c4->img_clk),
+				     "failed to get imaging clock\n");
+
+	freq = clk_get_rate(ov32c4->img_clk);
+	if (freq != OV32C4_MCLK)
+		return dev_err_probe(ov32c4->dev, -EINVAL,
+				     "external clock %lu is not supported\n",
+				     freq);
+
+	v4l2_i2c_subdev_init(&ov32c4->sd, client, &ov32c4_subdev_ops);
+
+	ret = ov32c4_check_hwcfg(ov32c4);
+	if (ret)
+		return ret;
+
+	ret = ov32c4_get_pm_resources(ov32c4->dev);
+	if (ret)
+		return ret;
+
+	ov32c4->regmap = devm_cci_regmap_init_i2c(client, 16);
+	if (IS_ERR(ov32c4->regmap))
+		return PTR_ERR(ov32c4->regmap);
+
+	/*
+	 * On the ACPI machines this sensor ships in, the core rail is gated by
+	 * a companion chip listed as the second I2C resource of _CRS; without
+	 * it the sensor does not answer at all. Elsewhere the rail is expected
+	 * to come from a regulator, so its absence is not an error here.
+	 */
+	ov32c4->companion_addr = ov32c4_acpi_i2c_addr(ov32c4->dev, 1);
+	if (ov32c4->companion_addr)
+		dev_dbg(ov32c4->dev, "companion chip at i2c 0x%02x\n",
+			ov32c4->companion_addr);
+	else if (ACPI_COMPANION(ov32c4->dev))
+		dev_warn(ov32c4->dev,
+			 "no second I2C address in _CRS, core rail may stay off\n");
+
+	ret = ov32c4_power_on(ov32c4->dev);
+	if (ret) {
+		dev_err_probe(ov32c4->dev, ret, "failed to power on\n");
+		return ret;
+	}
+
+	ret = ov32c4_identify_module(ov32c4);
+	if (ret)
+		goto probe_error_power_off;
+
+	dev_dbg(ov32c4->dev,
+		"link freq %lld Hz, %u lanes, pixel rate %llu, ppl %u\n",
+		link_freq_menu_items[ov32c4->link_freq_index],
+		ov32c4->mipi_lanes, ov32c4_pixel_rate(ov32c4),
+		ov32c4_ppl(ov32c4, &supported_modes[0]));
+
+	ret = ov32c4_init_controls(ov32c4);
+	if (ret) {
+		dev_err_probe(ov32c4->dev, ret, "failed to init controls\n");
+		goto probe_error_v4l2_ctrl_handler_free;
+	}
+
+	ov32c4->sd.internal_ops = &ov32c4_internal_ops;
+	ov32c4->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	ov32c4->sd.entity.ops = &ov32c4_subdev_entity_ops;
+	ov32c4->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ov32c4->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_pads_init(&ov32c4->sd.entity, 1, &ov32c4->pad);
+	if (ret) {
+		dev_err_probe(ov32c4->dev, ret, "failed to init entity pads\n");
+		goto probe_error_v4l2_ctrl_handler_free;
+	}
+
+	ov32c4->sd.state_lock = ov32c4->ctrl_handler.lock;
+	ret = v4l2_subdev_init_finalize(&ov32c4->sd);
+	if (ret < 0) {
+		dev_err_probe(ov32c4->dev, ret, "failed to init subdev\n");
+		goto probe_error_media_entity_cleanup;
+	}
+
+	pm_runtime_set_active(ov32c4->dev);
+	pm_runtime_enable(ov32c4->dev);
+
+	ret = v4l2_async_register_subdev_sensor(&ov32c4->sd);
+	if (ret < 0) {
+		dev_err_probe(ov32c4->dev, ret, "failed to register subdev\n");
+		goto probe_error_v4l2_subdev_cleanup;
+	}
+
+	pm_runtime_idle(ov32c4->dev);
+	return 0;
+
+probe_error_v4l2_subdev_cleanup:
+	pm_runtime_disable(ov32c4->dev);
+	pm_runtime_set_suspended(ov32c4->dev);
+	v4l2_subdev_cleanup(&ov32c4->sd);
+
+probe_error_media_entity_cleanup:
+	media_entity_cleanup(&ov32c4->sd.entity);
+
+probe_error_v4l2_ctrl_handler_free:
+	v4l2_ctrl_handler_free(ov32c4->sd.ctrl_handler);
+
+probe_error_power_off:
+	ov32c4_power_off(ov32c4->dev);
+
+	return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ov32c4_pm_ops, ov32c4_power_off,
+				 ov32c4_power_on, NULL);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ov32c4_acpi_ids[] = {
+	{ "OVTI32C4" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, ov32c4_acpi_ids);
+#endif
+
+static const struct of_device_id ov32c4_of_match[] = {
+	{ .compatible = "ovti,ov32c4" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov32c4_of_match);
+
+static struct i2c_driver ov32c4_i2c_driver = {
+	.driver = {
+		.name = "ov32c4",
+		.pm = pm_sleep_ptr(&ov32c4_pm_ops),
+		.acpi_match_table = ACPI_PTR(ov32c4_acpi_ids),
+		.of_match_table = ov32c4_of_match,
+	},
+	.probe = ov32c4_probe,
+	.remove = ov32c4_remove,
+};
+module_i2c_driver(ov32c4_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision OV32C4 sensor driver");
+MODULE_LICENSE("GPL");
-- 
2.53.0


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

* [PATCH 3/3] media: ipu-bridge: Add OmniVision OV32C4
  2026-08-26  7:19 [PATCH 0/3] media: Add OmniVision OV32C4 sensor driver Robert Bozik
  2026-08-26  7:20 ` [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4 Robert Bozik
  2026-08-26  7:20 ` [PATCH 2/3] media: i2c: Add driver for " Robert Bozik
@ 2026-08-26  7:20 ` Robert Bozik
  2 siblings, 0 replies; 7+ messages in thread
From: Robert Bozik @ 2026-08-26  7:20 UTC (permalink / raw)
  To: linux-media
  Cc: Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel,
	Robert Bozik

Add the sensor configuration for the OV32C4, matching the _HID the
sensor enumerates with. Without this entry the bridge does not build a
software node graph for the sensor, so its driver defers probing on the
missing fwnode endpoint forever and the camera never binds.

Signed-off-by: Robert Bozik <robertbozik@gmail.com>
Assisted-by: Claude:claude-opus-5 sparse
Assisted-by: Claude:claude-fable-5 sparse
---
 drivers/media/pci/intel/ipu-bridge.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 1bb3a3e98..7d3c7e09f 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -93,6 +93,16 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
 	IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
 	/* Omnivision OV2680 */
 	IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
+	/*
+	 * OmniVision OV32C4.
+	 *
+	 * 400 MHz link frequency, i.e. 800 Mbps per lane. There is no public
+	 * datasheet; the value comes from the vendor Windows driver, which
+	 * carries it as MipiBps, and it is confirmed on hardware: the IPU7
+	 * D-PHY locks and the sensor streams 3264x1840 at a measured
+	 * 30.00 fps, matching pixel_rate / (ppl * vts) exactly.
+	 */
+	IPU_SENSOR_CONFIG("OVTI32C4", 1, 400000000),
 	/* Omnivision OV5675 */
 	IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
 	/* Omnivision OV8856 */
-- 
2.53.0


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

* Re: [PATCH 2/3] media: i2c: Add driver for OmniVision OV32C4
  2026-08-26  7:20 ` [PATCH 2/3] media: i2c: Add driver for " Robert Bozik
@ 2026-08-26  7:31   ` sashiko-bot
  0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-08-26  7:31 UTC (permalink / raw)
  To: Robert Bozik; +Cc: robh, conor+dt, devicetree, media-ci

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Incorrect use of pm_sleep_ptr() for the dev_pm_ops structure pointer disables runtime PM when CONFIG_PM_SLEEP is disabled.
- [Medium] Memory leak of V4L2 control handler resources on the probe error path if ov32c4_init_controls() fails.
--

commit 86444baf0012dbc98352979b151e14a3e84e5a51
Author: Robert Bozik <robertbozik@gmail.com>
Subject: media: i2c: Add driver for OmniVision OV32C4

This commit adds a new V4L2 I2C sensor driver for the OmniVision OV32C4
camera. It configures the sensor for a 3264x1840 at 30 fps mode and includes
controls for exposure, gain, blanking, along with runtime PM support.

> diff --git a/drivers/media/i2c/ov32c4.c b/drivers/media/i2c/ov32c4.c
> new file mode 100644
> index 0000000000000..4039aa85cf5e8
> --- /dev/null
> +++ b/drivers/media/i2c/ov32c4.c
[ ... ]
> +static int ov32c4_init_controls(struct ov32c4 *ov32c4)
> +{
> +	struct v4l2_ctrl_handler *ctrl_hdlr = &ov32c4->ctrl_handler;
[ ... ]
> +	ret = v4l2_fwnode_device_parse(ov32c4->dev, &props);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov32c4_ctrl_ops, &props);
> +
> +	if (ctrl_hdlr->error)
> +		return ctrl_hdlr->error;
> +
> +	ov32c4->sd.ctrl_handler = ctrl_hdlr;
> +
> +	return 0;
> +}
[ ... ]
> +static int ov32c4_probe(struct i2c_client *client)
> +{
[ ... ]
> +	ret = ov32c4_init_controls(ov32c4);
> +	if (ret) {
> +		dev_err_probe(ov32c4->dev, ret, "failed to init controls\n");
> +		goto probe_error_v4l2_ctrl_handler_free;
> +	}
[ ... ]
> +probe_error_v4l2_ctrl_handler_free:
> +	v4l2_ctrl_handler_free(ov32c4->sd.ctrl_handler);

[Severity: Medium]
If ov32c4_init_controls() fails (e.g. because v4l2_fwnode_device_parse()
fails or ctrl_hdlr->error is set), it returns early without assigning
ov32c4->sd.ctrl_handler = ctrl_hdlr.

Since the pointer remains NULL, does the probe cleanup path leak the
allocated V4L2 controls when it calls v4l2_ctrl_handler_free(NULL)? Should
this explicitly free the embedded &ov32c4->ctrl_handler instead?

[ ... ]
> +static DEFINE_RUNTIME_DEV_PM_OPS(ov32c4_pm_ops, ov32c4_power_off,
> +				 ov32c4_power_on, NULL);
[ ... ]
> +static struct i2c_driver ov32c4_i2c_driver = {
> +	.driver = {
> +		.name = "ov32c4",
> +		.pm = pm_sleep_ptr(&ov32c4_pm_ops),

[Severity: Medium]
Is pm_sleep_ptr() the correct wrapper for &ov32c4_pm_ops here?

The ov32c4_pm_ops structure is defined using DEFINE_RUNTIME_DEV_PM_OPS()
which relies on CONFIG_PM. By wrapping it with pm_sleep_ptr(), the entire
dev_pm_ops structure pointer evaluates to NULL when CONFIG_PM_SLEEP is
disabled, even if CONFIG_PM is enabled.

Does this unintentionally disable runtime PM callbacks in configurations
without sleep support? Should this use pm_ptr() instead?

> +		.acpi_match_table = ACPI_PTR(ov32c4_acpi_ids),
> +		.of_match_table = ov32c4_of_match,
> +	},
> +	.probe = ov32c4_probe,
> +	.remove = ov32c4_remove,
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826072002.14357-1-robertbozik@gmail.com?part=2

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

* Re: [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4
  2026-08-26  7:20 ` [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4 Robert Bozik
@ 2026-08-26 16:13   ` Conor Dooley
       [not found]     ` <CALSBEvTvrhniBrM29Y7sery15W-onz7t_8ZQhg1TW8nBDgYKxg@mail.gmail.com>
  0 siblings, 1 reply; 7+ messages in thread
From: Conor Dooley @ 2026-08-26 16:13 UTC (permalink / raw)
  To: Robert Bozik
  Cc: linux-media, Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4445 bytes --]

On Wed, Aug 26, 2026 at 09:20:00AM +0200, Robert Bozik wrote:
> Add a binding for the OmniVision OV32C4, a 32 megapixel RGBC CMOS image
> sensor with on-chip 4-cell fusion, a 4-lane MIPI CSI-2 D-PHY transmitter
> and the standard SCCB command interface.
> 
> The sensor ships in laptops as an under-display camera, where it is
> described by ACPI; the binding follows ovti,ov08x40.yaml, which covers
> the closest relative already in tree.
> 
> Signed-off-by: Robert Bozik <robertbozik@gmail.com>
> Assisted-by: Claude:claude-opus-5 sparse
> Assisted-by: Claude:claude-fable-5 sparse
> ---
>  .../bindings/media/i2c/ovti,ov32c4.yaml       | 109 ++++++++++++++++++
>  1 file changed, 109 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
> new file mode 100644
> index 000000000..be6067c75
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov32c4.yaml
> @@ -0,0 +1,109 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/i2c/ovti,ov32c4.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: OmniVision OV32C4 Image Sensor
> +
> +maintainers:
> +  - Robert Bozik <robertbozik@gmail.com>
> +
> +description:
> +  The OmniVision OV32C4 is a 32 megapixel RGBC (red, green, blue and clear)
> +  CMOS image sensor in a 4-cell arrangement, with on-chip 4-cell fusion and
> +  binning. It has a 6528x4896 active pixel array, a 4-lane MIPI CSI-2 D-PHY
> +  transmitter and the standard SCCB command interface. The driver supports
> +  3264x1840 at 30 fps, 10-bit Bayer, at a link frequency of 400 MHz.
> +
> +allOf:
> +  - $ref: /schemas/media/video-interface-devices.yaml#
> +
> +properties:
> +  compatible:
> +    const: ovti,ov32c4
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +    description: Input clock (XVCLK), 19.2 MHz.

Where do this name and frequency come from? pin name I presume for the
former? Is 19.2 MHz a mandatory frequency?

> +
> +  avdd-supply:
> +    description: Analogue circuit voltage supply.
> +
> +  dovdd-supply:
> +    description: I/O circuit voltage supply.
> +
> +  dvdd-supply:
> +    description: Digital core voltage supply.

These 3 can just be :true, what they do should be obvious, provided they
match the pin names of the device.

Generally this looks fine to me though..

Cheers,
Conor.

> +
> +  reset-gpios:
> +    maxItems: 1
> +    description: Active low GPIO connected to the XSHUTDOWN pad of the sensor.
> +
> +  port:
> +    $ref: /schemas/graph.yaml#/$defs/port-base
> +    additionalProperties: false
> +
> +    properties:
> +      endpoint:
> +        $ref: /schemas/media/video-interfaces.yaml#
> +        additionalProperties: false
> +
> +        properties:
> +          data-lanes:
> +            items:
> +              - const: 1
> +              - const: 2
> +              - const: 3
> +              - const: 4
> +          link-frequencies: true
> +          remote-endpoint: true
> +
> +        required:
> +          - data-lanes
> +          - link-frequencies
> +          - remote-endpoint
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - port
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        camera@36 {
> +            compatible = "ovti,ov32c4";
> +            reg = <0x36>;
> +
> +            clocks = <&ov32c4_clk>;
> +            assigned-clocks = <&ov32c4_clk>;
> +            assigned-clock-rates = <19200000>;
> +
> +            avdd-supply = <&vreg_2p8>;
> +            dovdd-supply = <&vreg_1p8>;
> +            dvdd-supply = <&vreg_1p1>;
> +
> +            reset-gpios = <&tlmm 111 GPIO_ACTIVE_LOW>;
> +
> +            port {
> +                ov32c4_ep: endpoint {
> +                    remote-endpoint = <&csiphy0_ep>;
> +                    data-lanes = <1 2 3 4>;
> +                    link-frequencies = /bits/ 64 <400000000>;
> +                };
> +            };
> +        };
> +    };
> +...
> -- 
> 2.53.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

* Re: [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4
       [not found]     ` <CALSBEvTvrhniBrM29Y7sery15W-onz7t_8ZQhg1TW8nBDgYKxg@mail.gmail.com>
@ 2026-08-26 22:47       ` Conor Dooley
  0 siblings, 0 replies; 7+ messages in thread
From: Conor Dooley @ 2026-08-26 22:47 UTC (permalink / raw)
  To: Róbert Božik
  Cc: linux-media, Sakari Ailus, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1033 bytes --]

On Wed, Aug 26, 2026 at 08:34:01PM +0200, Róbert Božik wrote:
> Hi Conor,
> 
> > Where do this name and frequency come from? pin name I presume for the
> > former? Is 19.2 MHz a mandatory frequency?

Please do normal inline replies, instead of this which removes some
context.

> The name is the sensor's input clock pin (XVCLK), as in the other
> OmniVision bindings. 19.2 MHz is not a requirement of the sensor but of
> the driver: it comes from the platform (the ACPI SSDB of the laptop this
> was developed on provides 19.2 MHz) and the register tables in the
> driver are computed for it (the internal 96 MHz clock the timing
> registers count in is 5 x 19.2 MHz). The driver refuses other rates
> because I have no verified PLL settings for them. I'll drop the
> frequency from the description and keep it only in the example and in
> the driver. I've also dropped the sentence about what the driver
> supports from the description, that doesn't describe the hardware.

Great, sounds good to me. Thanks.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

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

end of thread, other threads:[~2026-08-26 22:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26  7:19 [PATCH 0/3] media: Add OmniVision OV32C4 sensor driver Robert Bozik
2026-08-26  7:20 ` [PATCH 1/3] dt-bindings: media: i2c: Add OmniVision OV32C4 Robert Bozik
2026-08-26 16:13   ` Conor Dooley
     [not found]     ` <CALSBEvTvrhniBrM29Y7sery15W-onz7t_8ZQhg1TW8nBDgYKxg@mail.gmail.com>
2026-08-26 22:47       ` Conor Dooley
2026-08-26  7:20 ` [PATCH 2/3] media: i2c: Add driver for " Robert Bozik
2026-08-26  7:31   ` sashiko-bot
2026-08-26  7:20 ` [PATCH 3/3] media: ipu-bridge: Add " Robert Bozik

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.