* [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor
@ 2026-10-19 16:27 Sergey Lebedev
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:27 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
The ST VD55G0 is a 644x604 monochrome global shutter sensor. It is the
infrared camera of the Microsoft Surface Pro 11 for Business with Intel
(Lunar Lake, IPU7), ACPI device SMO55F0, and other machines use it under
the same ID. Without a driver the camera does not appear at all.
1/4 dt-bindings: media: i2c: Add ST VD55G0 camera sensor
2/4 media: i2c: Add ST VD55G0 global shutter sensor driver
3/4 media: ipu-bridge: Add ST VD55G0
4/4 media: ipu-bridge: Add the Surface Pro 11 infrared illuminator
The driver is a port of ST's own downstream driver, firmware patch
included, as vd55g1 carries its own:
https://github.com/STMicroelectronics/vd55g0-linux-driver
Benjamin reviewed every version of it off-list and ran the previous one
on a Raspberry Pi 5; 1/4 and 2/4 carry his Co-developed-by, and the clock
tree in 2/4 is based on his. Peter Marshall's series adds the VD55G0 to
vd55g1 instead, and Benjamin preferred a separate driver:
https://lore.kernel.org/all/0bb08d07-bd06-4adb-851b-6b45d296b328@foss.st.com/
Peter's series also adds SMO55F0 to ipu-bridge, at a link frequency this
driver refuses from a 24 MHz clock; 3/4 says why it uses 396 MHz.
4/4 gives the sensor st,leds from a DMI match, as this machine's ACPI
tables do not say which sensor GPIO drives the illuminator. Sakari
preferred this in ipu-bridge to a quirk in the sensor driver:
https://lore.kernel.org/all/arY4HDSQZYAGJwYR@kekkonen.localdomain/
On this machine the sensor needs Hao Yao's int3472 fix, reviewed and
waiting for its v3:
https://lore.kernel.org/all/20251208035122.2147678-1-hao.yao@intel.com/
The SSDB asks for 24 MHz, but INT3472 enables the clock at 19.2 MHz, so
without the fix the sensor runs at four fifths of every rate this series
computes, its MIPI output below the 750 Mbps minimum.
While it streams, the IPU7 CSI-2 receiver logs "Received packet is too
long" in Y10 at 644x604 and Y8 at 640x480, though not in Y8 at 644x604 or
320x240, and the frames are complete. With the staging ipu7 driver, the
machine's two other cameras, an IMX681 and an OV13858, log it too, so it
does not look specific to this sensor.
Tested on the Surface Pro 11 for Business, on media next d7e7bca7169 with
Hao Yao's int3472 v2, KASAN and lockdep, IPU7 run by the ipu6 driver:
- probe defers until ipu-bridge has made the endpoint, then binds;
- every mode runs at 59.14 fps, as the controls give;
- Y8 and Y10 at all four sizes, and Y10 packed at full size;
- exposure, gains, flips and test patterns, vertical blanking changed
during a stream, set_fmt refused while streaming, runtime suspend after
it;
- under auto exposure, exposure and the gains read back the values the
sensor applies, as its registers show, and carry over to manual mode;
- the illuminator through led_mode, on and off during a stream;
- v4l2-compliance 46 of 46, and 54 of 54 through the media device, with
two warnings for the read-only crop;
- no KASAN report, and no lockdep report from vd55g0; the ipu6 driver's
lockdep_assert_held(&stream->mutex) in ipu6_isys_buffer_list_get()
fires at every stream start, from ipu6_isys_csi2_enable_streams();
- W=1 and sparse clean, dt_binding_check clean.
Sergey Lebedev (4):
dt-bindings: media: i2c: Add ST VD55G0 camera sensor
media: i2c: Add ST VD55G0 global shutter sensor driver
media: ipu-bridge: Add ST VD55G0
media: ipu-bridge: Add the Surface Pro 11 infrared illuminator
.../bindings/media/i2c/st,vd55g0.yaml | 137 +
MAINTAINERS | 10 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2434 +++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 62 +-
include/media/ipu-bridge.h | 2 +-
7 files changed, 2654 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
create mode 100644 drivers/media/i2c/vd55g0.c
base-commit: d7e7bca7169873b46e857c025c10f3336c1258d8
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
@ 2026-10-19 16:27 ` Sergey Lebedev
2026-10-20 8:27 ` Krzysztof Kozlowski
2026-10-19 16:27 ` [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
` (2 subsequent siblings)
3 siblings, 1 reply; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:27 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
The VD55G0 is a monochrome global shutter sensor with a 644x604 active
array, sending over a single MIPI CSI-2 lane.
Merged from ST's own binding in STMicroelectronics/vd55g0-linux-driver,
which gives the supply voltages, and st,vd55g1.yaml, whose conventions and
property list it follows. ST's st,out-sync and st,in-sync are left out, as
in vd55g1. Link frequencies run from 375 to 600 MHz, for the sensor's MIPI
rate of 750 to 1200 Mbps (UM2829, 13.1).
Co-developed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
The properties follow st,vd55g1.yaml nearly line for line. If a
compatible in that file is preferred, with an if/then for the
link-frequency minimum, I can fold it in there.
.../bindings/media/i2c/st,vd55g0.yaml | 137 ++++++++++++++++++
1 file changed, 137 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
new file mode 100644
index 00000000000..be03d7e8b9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (c) 2026 STMicroelectronics SA.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/st,vd55g0.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STMicroelectronics VD55G0 Global Shutter Image Sensor
+
+maintainers:
+ - Benjamin Mugnier <benjamin.mugnier@foss.st.com>
+ - Sylvain Petinot <sylvain.petinot@foss.st.com>
+
+description: |-
+ The STMicroelectronics VD55G0 is a monochrome global shutter image sensor
+ with an active array size of 644H x 604V. It is programmable through an I2C
+ interface.
+
+ Image data is sent through MIPI CSI-2, over a single data lane. The sensor
+ provides 4 GPIOs, which can drive a strobe light source synchronised with the
+ exposure period.
+
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+ compatible:
+ const: st,vd55g0
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ vcore-supply:
+ description: Digital core power supply (1.2V)
+
+ vddio-supply:
+ description: Digital IO power supply (1.8V)
+
+ vana-supply:
+ description: Analog power supply (2.8V)
+
+ reset-gpios:
+ description: Sensor reset active low GPIO (XSHUTDOWN)
+ maxItems: 1
+
+ st,leds:
+ description:
+ List of sensor GPIOs used to control strobe light sources during the
+ exposure time. The numbers identify the sensor pin on which the
+ illumination system is connected. GPIOs are active-high.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 4
+ items:
+ minimum: 0
+ maximum: 3
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ items:
+ - const: 1
+
+ link-frequencies:
+ maxItems: 1
+ items:
+ minimum: 375000000
+ maximum: 600000000
+ description:
+ The MIPI output runs at twice the link frequency, 750 to 1200
+ Mbps, and at an integer multiple of the input clock frequency.
+
+ lane-polarities:
+ minItems: 1
+ maxItems: 2
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - vcore-supply
+ - vddio-supply
+ - vana-supply
+ - reset-gpios
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera-sensor@60 {
+ compatible = "st,vd55g0";
+ reg = <0x60>;
+
+ clocks = <&camera_clk_12M>;
+
+ vcore-supply = <&camera_vcore_v1v2>;
+ vddio-supply = <&camera_vddio_v1v8>;
+ vana-supply = <&camera_vana_v2v8>;
+
+ reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
+ st,leds = <1>;
+
+ orientation = <MEDIA_ORIENTATION_FRONT>;
+ rotation = <0>;
+
+ port {
+ endpoint {
+ data-lanes = <1>;
+ link-frequencies = /bits/ 64 <402000000>;
+ remote-endpoint = <&csiphy0_ep>;
+ };
+ };
+ };
+ };
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
@ 2026-10-19 16:27 ` Sergey Lebedev
2026-10-19 18:24 ` George Neacsu
2026-10-19 16:28 ` [PATCH 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-10-19 16:28 ` [PATCH 4/4] media: ipu-bridge: Add the Surface Pro 11 infrared illuminator Sergey Lebedev
3 siblings, 1 reply; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:27 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
The VD55G0 is a 644x604 monochrome global shutter sensor from
STMicroelectronics. It streams Y8 or Y10 over one CSI-2 lane, runs its own
auto exposure, can strobe an illuminator from its GPIOs, and needs a
firmware patch at every power-on, carried in the driver as vd55g1 does.
The driver is ported from ST's own, GPL-2.0, to current interfaces: CCI
register access, .enable_streams with the subdev state, fwnode properties,
devm_v4l2_sensor_clk_get(), and an ACPI match for x86 machines where the
sensor sits behind ipu-bridge:
https://github.com/STMicroelectronics/vd55g0-linux-driver
ST's st,out-sync and st,in-sync and its two private controls are left out,
as vd55g1 has none of them. With the ACPI ID the supplies take the names
INT3472 registers, avdd and dovdd; a PRP0001 device keeps the DT names.
The clock tree is computed from the input clock and the one link frequency
the endpoint gives, as in vd55g1. The MIPI rate must be an integer multiple
of the input clock, or the sensor would run at another one, so any other
link frequency fails probe.
Exposure and the two gains form an auto cluster with auto exposure, as in
vd55g1: while auto exposure runs, they read back what the sensor applies.
Co-developed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
On ACPI the supplies take the names INT3472 registers, avdd and dovdd,
through acpi_match_device(); vd55g1 is served by an entry in int3472's
map instead. I did not add one, as it would have to say which rail each
pin feeds, and that is not known here: INT3472 calls pin type 0x10
DOVDD, while Windows' camera driver calls it Avdd. If a map entry is
preferred, I can move the names there.
MAINTAINERS | 10 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2434 ++++++++++++++++++++++++++++++++++++
4 files changed, 2456 insertions(+)
create mode 100644 drivers/media/i2c/vd55g0.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 760791e3635..7745ad9ef81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26036,6 +26036,15 @@ F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
F: Documentation/hwmon/tsc1641.rst
F: drivers/hwmon/tsc1641.c
+ST VD55G0 DRIVER
+M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
+M: Sergey Lebedev <lsa.uz@pm.me>
+M: Sylvain Petinot <sylvain.petinot@foss.st.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
+F: drivers/media/i2c/vd55g0.c
+
ST VD55G1 DRIVER
M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
M: Sylvain Petinot <sylvain.petinot@foss.st.com>
@@ -28683,6 +28692,7 @@ F: drivers/media/i2c/mt*
F: drivers/media/i2c/og*
F: drivers/media/i2c/ov*
F: drivers/media/i2c/s5*
+F: drivers/media/i2c/vd55g0.c
F: drivers/media/i2c/vd55g1.c
F: drivers/media/i2c/vd56g3.c
F: drivers/media/i2c/vgxy61.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 7de2d095c96..05343f82ec6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -891,6 +891,17 @@ config VIDEO_T4KA3
To compile this driver as a module, choose M here: the
module will be called t4ka3.
+config VIDEO_VD55G0
+ tristate "ST VD55G0 sensor support"
+ select V4L2_CCI_I2C
+ depends on GPIOLIB
+ help
+ This is a Video4Linux2 sensor driver for the ST VD55G0
+ camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vd55g0.
+
config VIDEO_VD55G1
tristate "ST VD55G1 sensor support"
select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 59e31af457c..a7e990ec650 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -171,6 +171,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o
obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_VD55G0) += vd55g0.o
obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o
obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o
obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o
diff --git a/drivers/media/i2c/vd55g0.c b/drivers/media/i2c/vd55g0.c
new file mode 100644
index 00000000000..3b51a87e740
--- /dev/null
+++ b/drivers/media/i2c/vd55g0.c
@@ -0,0 +1,2434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the ST VD55G0 global shutter sensor
+ *
+ * Copyright (C) 2026 STMicroelectronics SA
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define VD55G0_REG_MODEL_ID CCI_REG16_LE(0x0000)
+#define VD55G0_MODEL_ID 0x4730
+#define VD55G0_REG_REVISION CCI_REG16_LE(0x0004)
+#define VD55G0_REVISION_CUT1 0x1111
+#define VD55G0_REVISION_CUT2 0x1120
+#define VD55G0_REG_FWPATCH_REVISION CCI_REG16_LE(0x0022)
+#define VD55G0_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
+#define VD55G0_REG_SYSTEM_FSM CCI_REG8(0x002c)
+#define VD55G0_SYSTEM_FSM_READY_TO_BOOT 0x01
+#define VD55G0_SYSTEM_FSM_SW_STBY 0x02
+#define VD55G0_SYSTEM_FSM_STREAMING 0x03
+#define VD55G0_REG_BOOT CCI_REG8(0x0200)
+#define VD55G0_BOOT_BOOT 1
+#define VD55G0_BOOT_PATCH_SETUP 2
+#define VD55G0_REG_SW_STBY CCI_REG8(0x0201)
+#define VD55G0_SW_STBY_START_STREAM 1
+#define VD55G0_REG_STREAMING CCI_REG8(0x0202)
+#define VD55G0_STREAMING_STOP_STREAM 1
+#define VD55G0_REG_EXT_CLOCK CCI_REG32_LE(0x0220)
+#define VD55G0_REG_LINE_LENGTH CCI_REG16_LE(0x0300)
+#define VD55G0_REG_ORIENTATION CCI_REG8(0x0302)
+#define VD55G0_REG_FORMAT_CTRL CCI_REG8(0x030a)
+#define VD55G0_REG_OIF_CTRL CCI_REG16_LE(0x030c)
+#define VD55G0_REG_OIF_IMG_CTRL CCI_REG8(0x030f)
+#define VD55G0_REG_ISL_ENABLE CCI_REG8(0x0329)
+#define VD55G0_REG_CLK_PLL_MIPI CCI_REG32_LE(0x0224)
+#define VD55G0_REG_PATGEN_CTRL CCI_REG16_LE(0x0400)
+#define VD55G0_PATGEN_TYPE_SHIFT 4
+#define VD55G0_PATGEN_ENABLE BIT(0)
+#define VD55G0_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x044d)
+#define VD55G0_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x044e)
+#define VD55G0_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0450)
+#define VD55G0_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x0064)
+#define VD55G0_REG_APPLIED_ANALOG_GAIN CCI_REG8(0x0066)
+#define VD55G0_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x0068)
+#define VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE CCI_REG16_LE(0x042e)
+#define VD55G0_REG_AE_COLDSTART_ANALOG_GAIN CCI_REG8(0x0430)
+#define VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN CCI_REG16_LE(0x0432)
+#define VD55G0_REG_EXP_MODE CCI_REG8(0x044c)
+#define VD55G0_REG_FRAME_LENGTH CCI_REG16_LE(0x0458)
+#define VD55G0_REG_ROI_X_START CCI_REG16_LE(0x045e)
+#define VD55G0_REG_ROI_X_END CCI_REG16_LE(0x0460)
+#define VD55G0_REG_ROI_Y_START CCI_REG16_LE(0x0462)
+#define VD55G0_REG_ROI_Y_END CCI_REG16_LE(0x0464)
+#define VD55G0_REG_AE_ROI_START_H CCI_REG16_LE(0x0436)
+#define VD55G0_REG_AE_ROI_START_V CCI_REG16_LE(0x0438)
+#define VD55G0_REG_AE_ROI_END_H CCI_REG16_LE(0x043a)
+#define VD55G0_REG_AE_ROI_END_V CCI_REG16_LE(0x043c)
+#define VD55G0_REG_GPIO_0_CTRL CCI_REG8(0x0467)
+#define VD55G0_REG_READOUT_CTRL CCI_REG8(0x047a)
+#define VD55G0_REG_DARKCAL_CTRL CCI_REG8(0x032c)
+#define VD55G0_DARKCAL_BYPASS 0
+#define VD55G0_DARKCAL_AUTO 1
+#define VD55G0_REG_DUSTER_CTRL CCI_REG8(0x0316)
+#define VD55G0_DUSTER_ENABLE BIT(0)
+#define VD55G0_DUSTER_DISABLE 0
+#define VD55G0_DUSTER_DYN_ENABLE BIT(1)
+#define VD55G0_DUSTER_RING_ENABLE BIT(4)
+#define VD55G0_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0440)
+
+#define VD55G0_WIDTH 644
+#define VD55G0_HEIGHT 604
+#define VD55G0_DEFAULT_MODE 1
+#define VD55G0_WRITE_MULTIPLE_CHUNK_MAX 16
+#define VD55G0_NB_GPIOS 4
+#define VD55G0_MIN_FRAME_LENGTH (605 + 76)
+#define VD55G0_FRAME_LENGTH_DEF 1860
+#define VD55G0_TIMEOUT_MS 500
+#define VD55G0_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
+#define VD55G0_EXPO_MAX_TERM 64
+#define VD55G0_EXPO_DEF 200
+#define VD55G0_EXPO_TARGET_DEF 27
+#define VD55G0_LINE_LENGTH_SLOW 1200
+#define VD55G0_LINE_LENGTH_FAST 1128
+#define VD55G0_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
+#define VD55G0_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
+#define VD55G0_MIPI_RATE_MIN (750 * MEGA)
+#define VD55G0_MIPI_RATE_MAX (1200 * MEGA)
+#define VD55G0_MIPI_RATE_FAST (900 * MEGA)
+
+static const u8 cut1_patch[] = {
+0x81, 0x06, 0x0b, 0x02, 0x00, 0x09, 0x42, 0x00, 0xd2, 0x08, 0x42, 0x00, 0xe6,
+0x08, 0x42, 0x00, 0x12, 0x09, 0x42, 0x00, 0xe0, 0x3f, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x40, 0x01, 0x60, 0x10, 0x40, 0x11,
+0x81, 0x16, 0x80, 0x93, 0xdd, 0x4c, 0x04, 0x04, 0xfa, 0x06, 0x4c, 0x94, 0x60,
+0xe6, 0x0e, 0xdc, 0xe0, 0x04, 0x18, 0x70, 0x02, 0x08, 0xe7, 0xce, 0x44, 0x01,
+0x60, 0x10, 0x40, 0x31, 0x85, 0x1e, 0x08, 0x4e, 0x43, 0x01, 0x60, 0x10, 0xc0,
+0x31, 0x40, 0x20, 0xe0, 0x04, 0x98, 0x29, 0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24,
+0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84, 0x2b, 0xa1, 0x15, 0xfe, 0xd8, 0x41,
+0x00, 0xe8, 0xf8, 0x47, 0x00, 0xe8, 0x78, 0x44, 0x40, 0xef, 0x0a, 0x9c, 0x1a,
+0x9e, 0x2a, 0xdc, 0x3a, 0xde, 0x03, 0x4e, 0x7c, 0xf9, 0x13, 0x4e, 0x78, 0xf9,
+0x23, 0x4e, 0x74, 0xf9, 0x33, 0x4e, 0x70, 0xf9, 0x18, 0x50, 0x40, 0xef, 0x38,
+0x49, 0x44, 0xef, 0x04, 0x18, 0x11, 0x83, 0x11, 0x40, 0x20, 0xe0, 0x4c, 0x0c,
+0x04, 0xf2, 0x93, 0xdd, 0x86, 0x44, 0x88, 0xe0, 0x05, 0x04, 0x40, 0xf8, 0x09,
+0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0x60, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80,
+0x65, 0x86, 0x31, 0x01, 0x06, 0x02, 0x0c, 0xe1, 0x33, 0x40, 0xc8, 0xe2, 0x33,
+0x40, 0x70, 0xe2, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0x55, 0x8a, 0x0a, 0x02,
+0x08, 0x70, 0x16, 0x43, 0x58, 0xe0, 0x26, 0x08, 0x00, 0x40, 0x16, 0x0a, 0x00,
+0xc0, 0x41, 0x44, 0x08, 0x40, 0x06, 0x46, 0x08, 0xe0, 0x51, 0x44, 0x04, 0xc0,
+0x45, 0x85, 0x66, 0x02, 0x04, 0xe0, 0x13, 0x40, 0x70, 0x62, 0x06, 0x02, 0x0c,
+0xe1, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0xb5, 0x86, 0x0a, 0x02, 0x08, 0x70,
+0xa6, 0x02, 0x68, 0xe0, 0x26, 0x0a, 0x00, 0x40, 0x16, 0x18, 0x00, 0xc0, 0x31,
+0x01, 0xc6, 0x09, 0x0c, 0xe0, 0x51, 0x44, 0x08, 0xc0, 0xc1, 0x44, 0x04, 0xc0,
+0x95, 0x83, 0x0a, 0x02, 0x04, 0xf0, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x44, 0x08,
+0xc0, 0x11, 0x44, 0x04, 0x40, 0x11, 0x81, 0x33, 0x40, 0x18, 0xe3, 0x15, 0x83,
+0x0a, 0x02, 0x04, 0x70, 0x11, 0x81, 0x16, 0x0a, 0x00, 0x40, 0x31, 0x9d, 0x51,
+0x44, 0x08, 0x40, 0x06, 0x09, 0x04, 0xe0, 0x51, 0x44, 0x04, 0xc0, 0x43, 0x40,
+0x18, 0xe3, 0x33, 0x40, 0xc8, 0xe2, 0x13, 0x40, 0xc0, 0x62, 0xa6, 0x05, 0x78,
+0xe0, 0x23, 0x40, 0xd0, 0xe3, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x7c, 0xfa,
+0x86, 0x42, 0x84, 0x60, 0x06, 0x00, 0x0c, 0xe1, 0x14, 0x04, 0xf6, 0x50, 0x78,
+0xe3, 0x04, 0x00, 0x26, 0x0f, 0x28, 0xe2, 0x16, 0x15, 0x2c, 0x64, 0xb1, 0xbb,
+0x6a, 0x84, 0x0b, 0xa3, 0x36, 0x10, 0x10, 0x41, 0x2e, 0x54, 0x08, 0xd8, 0x16,
+0x4f, 0x44, 0x43, 0x06, 0x16, 0x00, 0xc0, 0x0b, 0x10, 0x1b, 0x8c, 0xb8, 0x4f,
+0x00, 0x6f, 0xa4, 0x12, 0x80, 0xfb, 0x06, 0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc,
+0x73, 0x03, 0xc0, 0x83, 0x22, 0x0e, 0x02, 0xc1, 0x7f, 0x1f, 0xc0, 0x87, 0xd4,
+0x8f, 0x02, 0x1b, 0x8c, 0x8f, 0x20, 0x0b, 0x8e, 0x98, 0x4e, 0x00, 0xef, 0x06,
+0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc, 0xf0, 0x73, 0xa2, 0x77, 0xcc, 0x70, 0x42,
+0x01, 0x73, 0xff, 0xc7, 0x12, 0x4e, 0x80, 0x79, 0x0b, 0x92, 0xb8, 0x4d, 0x00,
+0x6f, 0x1b, 0x8c, 0x06, 0x42, 0x94, 0x60, 0x06, 0x46, 0x1a, 0xe0, 0x24, 0x04,
+0x00, 0x40, 0xfd, 0x73, 0x03, 0xc0, 0xa3, 0xa6, 0xa7, 0xd4, 0xb3, 0x44, 0x60,
+0xe3, 0x85, 0x44, 0x9c, 0xe0, 0x24, 0x84, 0x75, 0x44, 0xa0, 0x60, 0x0e, 0x04,
+0xc0, 0x7f, 0x1f, 0xc0, 0x14, 0x04, 0xaf, 0x84, 0xaf, 0xa0, 0xa5, 0x42, 0xa4,
+0xe0, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x0c, 0x04, 0x0c, 0xfa, 0x46, 0x0d,
+0xcc, 0xe0, 0x09, 0x0c, 0xbc, 0xee, 0x0a, 0x40, 0x80, 0xe0, 0x45, 0x82, 0xa6,
+0x40, 0x08, 0xe0, 0x06, 0x98, 0x08, 0x7f, 0x74, 0xef, 0x0b, 0xa1, 0xb5, 0xfe,
+0x01, 0x87, 0x06, 0x98, 0x06, 0x4c, 0x94, 0x60, 0x06, 0x42, 0x0c, 0xe0, 0x05,
+0x0c, 0x14, 0xe0, 0x11, 0x40, 0x30, 0x62, 0x26, 0x42, 0x00, 0xe0, 0x05, 0x0c,
+0x14, 0xe0, 0x11, 0x40, 0xa0, 0x62, 0x56, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14,
+0xe0, 0x11, 0x40, 0x40, 0x62, 0x76, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14, 0xe0,
+0x11, 0x40, 0x50, 0xe2, 0x04, 0x98, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x42, 0x88,
+0xe0, 0xc5, 0x80, 0x28, 0x67, 0x54, 0x6f, 0x66, 0x00, 0xa0, 0xe0, 0x04, 0x98,
+0x19, 0x00, 0x30, 0xe0, 0x19, 0xc4, 0x11, 0x40, 0x30, 0x60, 0x11, 0x83, 0x04,
+0x9a, 0x11, 0x40, 0x10, 0xe0, 0x04, 0x98, 0x11, 0x40, 0x20, 0x60, 0x11, 0x81,
+0x04, 0x98, 0x11, 0x40, 0xb0, 0xe7, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x02, 0x0c,
+0xfc, 0x85, 0xfe, 0xc6, 0x4c, 0xb4, 0xe0, 0x60, 0x10, 0xb1, 0x64, 0x01, 0xc0,
+0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8c, 0x04, 0xe0, 0x0b, 0xa1, 0x25, 0x8c, 0xd8,
+0x43, 0x5c, 0xef, 0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8a, 0x04, 0xe0, 0x0b, 0xa1,
+0x25, 0x8a, 0xc6, 0x4e, 0x84, 0xe0, 0x04, 0x9c, 0x09, 0x00, 0x20, 0xe1, 0x4a,
+0x40, 0x80, 0xe0, 0x25, 0x86, 0x08, 0x22, 0x46, 0x43, 0x00, 0xe0, 0x06, 0x40,
+0x06, 0xe0, 0x63, 0x80, 0x09, 0x0c, 0x40, 0xe2, 0x09, 0x86, 0x0b, 0xa5, 0xd5,
+0x82, 0x68, 0x4e, 0x48, 0x6f, 0x01, 0x85, 0x78, 0x44, 0x48, 0x6f, 0x06, 0x00,
+0x80, 0xe1, 0xf8, 0x75, 0x4c, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x78, 0x48, 0x48,
+0xef, 0x09, 0x0e, 0x48, 0xe0, 0x0b, 0xa1, 0xb5, 0x80, 0x58, 0x7b, 0x3c, 0xef,
+0x78, 0x74, 0x40, 0xef, 0xa6, 0x40, 0x08, 0xe0, 0x0c, 0x0c, 0x0c, 0xf2, 0x18,
+0x12, 0x50, 0xff, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x7b, 0x00, 0x06, 0x01,
+0x08, 0xe1, 0x08, 0x00, 0x6b, 0x82, 0x0b, 0xa1, 0x15, 0x82, 0xa6, 0x41, 0x84,
+0xe0, 0x18, 0x00, 0x01, 0x89, 0x18, 0x42, 0x00, 0xef, 0x63, 0x80, 0x04, 0x1c,
+0x64, 0x02, 0x84, 0xe0, 0x78, 0x40, 0x00, 0x68, 0x00, 0x00, 0x40, 0xe6, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x1e, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x3c, 0xfa,
+0xf0, 0x1e, 0x40, 0x6c, 0x86, 0x0f, 0xe0, 0xe0, 0x01, 0x05, 0x91, 0x81, 0x06,
+0x02, 0x00, 0x62, 0x81, 0xb1, 0x01, 0x4e, 0xd8, 0x69, 0x0e, 0x00, 0x00, 0x60,
+0x01, 0xc0, 0x0e, 0x7c, 0x6b, 0x9e, 0x93, 0x4e, 0x14, 0x65, 0x86, 0x00, 0x30,
+0xe1, 0x93, 0x4e, 0x18, 0x65, 0x2b, 0x8c, 0x91, 0x4e, 0x50, 0xe1, 0x81, 0x4e,
+0xc8, 0xe9, 0x1e, 0xbe, 0x1e, 0x7e, 0x11, 0x8f, 0x9e, 0xbc, 0xd8, 0x55, 0x30,
+0xef, 0x06, 0x00, 0x00, 0x64, 0x46, 0x02, 0x54, 0xe1, 0x0e, 0x3e, 0x06, 0x00,
+0x20, 0xe0, 0x0e, 0x7e, 0x86, 0x00, 0x30, 0xe1, 0x1e, 0x7c, 0x11, 0x91, 0x9e,
+0x3c, 0x2b, 0x8c, 0xd8, 0x54, 0x30, 0xef, 0x06, 0x00, 0x00, 0x66, 0xa1, 0x83,
+0x0e, 0x3e, 0xf6, 0x41, 0xfc, 0xe9, 0x0e, 0x7e, 0x06, 0x00, 0x01, 0x60, 0x20,
+0xc0, 0x0e, 0x7c, 0x86, 0x00, 0x30, 0xe1, 0xae, 0x3c, 0x11, 0x93, 0xc8, 0x53,
+0x30, 0x6f, 0x2b, 0x8c, 0x06, 0x40, 0x88, 0x60, 0xf6, 0x45, 0xfc, 0x63, 0xff,
+0xc3, 0x14, 0x00, 0xf1, 0xa0, 0x95, 0x4e, 0xa8, 0xe2, 0xa1, 0x4e, 0x44, 0xea,
+0x81, 0x4e, 0xf0, 0x69, 0x10, 0x42, 0x00, 0x7c, 0x00, 0xcc, 0x1f, 0x04, 0x21,
+0x8b, 0x1e, 0x00, 0x86, 0x40, 0xfc, 0xe7, 0x05, 0x4e, 0xa4, 0x62, 0xc6, 0x01,
+0x5c, 0xe0, 0x05, 0x4e, 0xac, 0x62, 0x9e, 0x00, 0x00, 0x60, 0x02, 0xc0, 0x05,
+0x4e, 0x94, 0x62, 0x86, 0x40, 0x3c, 0x60, 0x10, 0xe7, 0x0e, 0x9c, 0x09, 0x0e,
+0x0c, 0xe1, 0x15, 0x4e, 0x3c, 0x79, 0x06, 0x02, 0x80, 0xe0, 0x13, 0x4e, 0xf4,
+0x64, 0x86, 0x02, 0x00, 0x60, 0x02, 0xc0, 0x21, 0x4e, 0x5c, 0x69, 0x00, 0x40,
+0xc0, 0xf3, 0x1e, 0x9e, 0x01, 0x4e, 0x0c, 0xe1, 0x0c, 0x0c, 0x3c, 0xf2, 0x93,
+0xdd, 0xc6, 0x01, 0xcc, 0x60, 0x06, 0x42, 0x88, 0xe0, 0x29, 0x00, 0x98, 0xee,
+0x34, 0x84, 0x4a, 0x44, 0x80, 0x60, 0x86, 0x04, 0x30, 0xe1, 0x24, 0x08, 0x30,
+0x46, 0xfd, 0x73, 0xff, 0xc3, 0x3e, 0x04, 0xa6, 0x08, 0xa0, 0x80, 0x34, 0x06,
+0xc8, 0x9c, 0x45, 0x44, 0xa0, 0x80, 0x35, 0x42, 0x00, 0x00, 0x06, 0x06, 0x00,
+0xc0, 0x35, 0x44, 0xa0, 0xc0, 0x35, 0x02, 0x00, 0xc0, 0x3e, 0x80, 0x93, 0xdd,
+0x4c, 0x00, 0x00, 0xfa, 0x88, 0x40, 0x00, 0xe8, 0x86, 0x02, 0x0c, 0xe1, 0x0e,
+0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04,
+0x00, 0xf8, 0xc6, 0x40, 0xb4, 0xe0, 0x00, 0x02, 0xb1, 0x64, 0x01, 0xc0, 0x2a,
+0xc4, 0x2b, 0xa1, 0x35, 0x92, 0x24, 0xc4, 0x2b, 0xa1, 0xc5, 0x90, 0x38, 0x06,
+0x46, 0x45, 0x00, 0xe0, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0x49, 0x06, 0xf0,
+0xe2, 0x4d, 0xe6, 0xc6, 0x40, 0x48, 0x41, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xd5, 0x8c, 0xc9, 0x06, 0x44, 0x62, 0x06, 0x4b, 0x00, 0xe0, 0x46,
+0x48, 0x16, 0x60, 0x86, 0x4a, 0xc8, 0xe0, 0x53, 0x08, 0xc0, 0x58, 0x80, 0xf3,
+0x49, 0x0a, 0xa4, 0xe7, 0x6b, 0x0a, 0xd8, 0xe3, 0x49, 0xbe, 0xcf, 0xa8, 0xc1,
+0x46, 0x44, 0xe2, 0x48, 0x86, 0xc9, 0x0a, 0xa0, 0xe7, 0x46, 0x48, 0x0a, 0xe0,
+0xc1, 0x46, 0x40, 0xe2, 0xcb, 0x0a, 0xe0, 0x63, 0x43, 0x80, 0x63, 0x48, 0x28,
+0xe1, 0x38, 0x86, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0xc3, 0x46, 0x30, 0xe1,
+0x18, 0x86, 0xcb, 0x0a, 0xd4, 0xe3, 0x2b, 0x0a, 0xdc, 0x63, 0x16, 0x42, 0x0a,
+0xe0, 0x5b, 0x0a, 0xe4, 0xe3, 0xc3, 0x48, 0x24, 0x61, 0x03, 0x82, 0x23, 0x46,
+0x2c, 0xe1, 0x53, 0x40, 0x34, 0xe1, 0x4c, 0x0c, 0x00, 0xf0, 0x93, 0xdd, 0xc3,
+0xc1, 0x0c, 0x00, 0x80, 0xfa, 0x86, 0x43, 0xcc, 0x60, 0x0b, 0xa3, 0x29, 0x02,
+0xc4, 0xee, 0xb5, 0x84, 0x4a, 0x04, 0xfc, 0xfb, 0x45, 0x84, 0xc6, 0x40, 0x84,
+0xe0, 0x14, 0x82, 0x18, 0x84, 0x19, 0x86, 0x1b, 0xa5, 0x85, 0x8c, 0x04, 0x80,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0xc5, 0x8a, 0x0c, 0x08, 0x80,
+0xf2, 0x08, 0x34, 0x44, 0xff, 0x0b, 0xa5, 0x15, 0x8a, 0x0a, 0x04, 0x04, 0xf0,
+0xb5, 0x88, 0xc6, 0x40, 0x84, 0xe0, 0x24, 0x82, 0x28, 0x88, 0x29, 0x86, 0x2b,
+0xa5, 0xc5, 0x86, 0x04, 0x80, 0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0,
+0x05, 0x86, 0x09, 0x02, 0x24, 0xee, 0x0b, 0xa3, 0xd5, 0x80, 0x08, 0x84, 0x0a,
+0x40, 0x80, 0xe0, 0x75, 0xf8, 0xf5, 0x81, 0x38, 0x72, 0x44, 0xef, 0xf8, 0x51,
+0xfc, 0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0xa5,
+0x80, 0x0c, 0x08, 0x80, 0xf2, 0xb8, 0x11, 0xfc, 0xff, 0x0c, 0x08, 0x80, 0xf2,
+0x93, 0xdd, 0xcc, 0x04, 0x0c, 0xfa, 0x06, 0x4f, 0x84, 0x60, 0x86, 0x50, 0x4c,
+0xe1, 0x04, 0x1c, 0x80, 0x0c, 0x21, 0x6f, 0x01, 0xc0, 0xf0, 0x04, 0x50, 0x64,
+0x11, 0x81, 0x09, 0x00, 0x6c, 0xe0, 0x06, 0x29, 0x0b, 0x8c, 0x58, 0x4c, 0x58,
+0xef, 0x04, 0x9c, 0x19, 0x00, 0x6c, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3,
+0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f,
+0xa2, 0x21, 0x40, 0x6c, 0xe0, 0x86, 0x43, 0xcc, 0xe0, 0x29, 0x02, 0x24, 0xee,
+0x2b, 0xa3, 0xd5, 0x80, 0x18, 0x84, 0x4a, 0x42, 0x80, 0x60, 0x11, 0x8b, 0x65,
+0x80, 0x19, 0x00, 0x70, 0xe0, 0x16, 0x29, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x03,
+0x0b, 0x8c, 0x28, 0x4a, 0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x70, 0xe0, 0x10,
+0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3,
+0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40, 0x70, 0xe0, 0x09, 0x00, 0x74,
+0x60, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x85, 0x06, 0x29, 0x0b, 0x8c, 0xa8, 0x48,
+0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x74, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b,
+0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0,
+0x2f, 0xa2, 0x21, 0x40, 0x74, 0xe0, 0x09, 0x00, 0x78, 0x60, 0xf0, 0x04, 0x50,
+0xe4, 0x11, 0x87, 0x06, 0x29, 0x0b, 0x8c, 0x28, 0x47, 0x58, 0xef, 0x04, 0x9c,
+0x19, 0x00, 0x78, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28,
+0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40,
+0x78, 0xe0, 0x19, 0x0e, 0xd0, 0xe0, 0x1b, 0xa1, 0x85, 0x86, 0x05, 0x00, 0x24,
+0xe0, 0x1a, 0xa2, 0x04, 0x04, 0xc0, 0xe1, 0x2b, 0x83, 0xd5, 0x80, 0x19, 0x10,
+0x10, 0x60, 0x09, 0x9e, 0x0b, 0x83, 0x85, 0x84, 0x08, 0xa0, 0x0b, 0xa1, 0x38,
+0x61, 0x74, 0xcf, 0x58, 0x49, 0x34, 0xef, 0x14, 0x9c, 0x25, 0x02, 0x24, 0xe0,
+0x24, 0x02, 0xc0, 0x61, 0x29, 0x9e, 0x18, 0x58, 0x74, 0xef, 0x55, 0x81, 0x08,
+0xa0, 0x0b, 0xa1, 0x48, 0x60, 0x74, 0xcf, 0x28, 0x72, 0x4c, 0x6f, 0x46, 0x01,
+0x08, 0xe0, 0xcc, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x65, 0x5e, 0x04,
+0xfa, 0xf0, 0x0c, 0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc,
+0xfc, 0x07, 0x80, 0xfe, 0xf0, 0x1e, 0x20, 0x6c, 0xe6, 0x40, 0x00, 0xe0, 0xc8,
+0x70, 0x4c, 0xef, 0x0b, 0xa1, 0xb5, 0x88, 0xc6, 0x0d, 0xdc, 0xe0, 0x09, 0x0c,
+0x94, 0xea, 0x0b, 0xa3, 0xf5, 0x80, 0x09, 0x0c, 0x91, 0x79, 0x01, 0xc0, 0x0a,
+0x40, 0x80, 0xe0, 0xe5, 0x82, 0xc8, 0x71, 0x44, 0xef, 0x04, 0x18, 0x86, 0x02,
+0x84, 0xe0, 0x24, 0x04, 0x31, 0x83, 0x01, 0x82, 0x0e, 0x18, 0xe6, 0x00, 0x48,
+0xe1, 0x36, 0x00, 0x21, 0x82, 0x2e, 0x84, 0x46, 0x00, 0x2c, 0x61, 0x21, 0x83,
+0x14, 0x00, 0x46, 0x40, 0x34, 0xe1, 0x25, 0x42, 0x44, 0xe0, 0x28, 0x51, 0x28,
+0xef, 0x88, 0x40, 0x00, 0xe8, 0xf1, 0x90, 0xfc, 0x0f, 0x80, 0xf6, 0xf4, 0xbc,
+0x64, 0xfd, 0x53, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x01, 0x03, 0x11, 0x81, 0x48,
+0x79, 0x08, 0xef, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00,
+0x00, 0xfa, 0x0d, 0x62, 0x8e, 0x45, 0xf0, 0xff, 0x75, 0x82, 0x4e, 0x05, 0x01,
+0x60, 0x10, 0x40, 0x07, 0xc8, 0x24, 0x88, 0x23, 0x80, 0x1e, 0x8a, 0x78, 0x40,
+0x00, 0xe8, 0x21, 0x81, 0x0b, 0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x83,
+0xc1, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x41, 0xb9, 0x06, 0x48, 0x12, 0xe0,
+0x33, 0x88, 0x45, 0x06, 0x14, 0xe0, 0x4b, 0xa3, 0xc5, 0x80, 0x35, 0x06, 0x18,
+0xe0, 0x3b, 0x23, 0x31, 0x81, 0x55, 0x80, 0x31, 0x83, 0x0d, 0xe6, 0x04, 0x00,
+0x88, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x10, 0x80, 0x05, 0x00, 0x10, 0x9c, 0x08,
+0x40, 0x00, 0x98, 0x25, 0x0b, 0x8e, 0x40, 0xf0, 0xff, 0x3b, 0xa1, 0x45, 0x8a,
+0x2b, 0x23, 0x06, 0x4c, 0x00, 0xe0, 0x45, 0x90, 0x2b, 0x25, 0x6e, 0x40, 0xf0,
+0xff, 0x25, 0x87, 0x3b, 0xa1, 0x25, 0x8a, 0x2b, 0x23, 0x06, 0x0c, 0x08, 0xe0,
+0x05, 0x8e, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x0c, 0x06, 0x0c, 0x04,
+0xe0, 0x95, 0x87, 0x3b, 0xa1, 0xc5, 0x88, 0x2b, 0x23, 0x61, 0x91, 0x85, 0x8a,
+0x2b, 0x25, 0x7e, 0x40, 0xf0, 0xff, 0x25, 0x83, 0x2b, 0xa3, 0xc5, 0x08, 0x06,
+0x0c, 0x08, 0x80, 0x2b, 0x25, 0x4e, 0x40, 0xf0, 0xff, 0x15, 0x8a, 0xf5, 0x03,
+0x06, 0x0c, 0x04, 0xe0, 0x2b, 0x23, 0x61, 0x91, 0x45, 0x86, 0x2b, 0x25, 0x6e,
+0x40, 0xf0, 0xff, 0xc5, 0x04, 0x61, 0x89, 0xc5, 0x83, 0x2b, 0x23, 0x06, 0x4c,
+0x00, 0xe0, 0x05, 0x84, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x02, 0x61,
+0xa1, 0x25, 0x83, 0x2b, 0x23, 0x61, 0x85, 0xe5, 0x80, 0x2b, 0x25, 0x7e, 0x40,
+0xf0, 0xff, 0x65, 0x00, 0x61, 0x83, 0x95, 0x81, 0x0b, 0x02, 0x1b, 0x8c, 0x48,
+0x78, 0xfc, 0xef, 0x0b, 0xa1, 0x00, 0x4c, 0x00, 0xdc, 0x0b, 0x8c, 0x0c, 0x0c,
+0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x04, 0xfa, 0x6b, 0x00, 0xc6,
+0x40, 0xdc, 0xe0, 0x04, 0x00, 0xf0, 0x1e, 0x30, 0xec, 0x48, 0x55, 0xfc, 0x6e,
+0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0xb8, 0x68, 0xfc, 0xee, 0x58, 0x71, 0x08,
+0xef, 0x78, 0x68, 0xfc, 0x6e, 0x0b, 0x8c, 0x71, 0x01, 0x5b, 0x80, 0x7e, 0x3c,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x11, 0x03, 0x21, 0x81, 0x31, 0x05, 0x41,
+0x81, 0xa8, 0x77, 0x08, 0xef, 0x0e, 0x3e, 0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0,
+0x48, 0x4b, 0x0c, 0xef, 0x0e, 0x7c, 0x11, 0x81, 0x04, 0x3e, 0x21, 0x83, 0x68,
+0x76, 0xfc, 0x6f, 0x0e, 0x06, 0x04, 0x60, 0x10, 0xc0, 0xce, 0x0d, 0x01, 0x60,
+0x10, 0xc0, 0x76, 0x98, 0x04, 0xfc, 0xc8, 0x59, 0x0c, 0xef, 0x13, 0xc3, 0x08,
+0x98, 0x0b, 0xa1, 0xa5, 0xfe, 0x04, 0xfc, 0x18, 0x5c, 0x0c, 0xef, 0x14, 0x3e,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x38, 0x46, 0x0c, 0xef, 0x01, 0x01, 0xf1,
+0x98, 0x0c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x65, 0x5e, 0x04, 0xfa, 0xf0, 0x0c,
+0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc, 0xfc, 0x03, 0x80,
+0xfe, 0x26, 0x00, 0x4c, 0x61, 0x11, 0x83, 0x16, 0x00, 0xce, 0x01, 0x01, 0x60,
+0x10, 0xc0, 0x16, 0x00, 0x46, 0x00, 0x2c, 0xe1, 0x04, 0x00, 0xf0, 0x1e, 0x30,
+0xec, 0x14, 0xc0, 0x14, 0x02, 0x20, 0xfc, 0x1e, 0xc0, 0x98, 0x40, 0x00, 0xe8,
+0xf1, 0x98, 0xfc, 0x0b, 0x80, 0xf6, 0xf4, 0xbc, 0x64, 0xfd, 0x53, 0xc1, 0xc3,
+0xc1, 0x06, 0x00, 0x01, 0x60, 0x00, 0xe0, 0x06, 0x00, 0x62, 0xf4, 0x93, 0xdd,
+0x4c, 0x04, 0x7c, 0xfa, 0xc6, 0x54, 0x84, 0x60, 0x06, 0x0f, 0xdc, 0xe0, 0x04,
+0x2a, 0xae, 0x4d, 0x01, 0x60, 0x10, 0xc0, 0x29, 0x14, 0x70, 0x60, 0x8e, 0x51,
+0x00, 0x60, 0x10, 0xc0, 0x3b, 0x0e, 0x9c, 0x65, 0x06, 0x52, 0x94, 0xe0, 0x1b,
+0x00, 0x20, 0x60, 0x0e, 0x57, 0x08, 0x60, 0x10, 0xc0, 0x0b, 0x00, 0x1c, 0xe0,
+0x13, 0x20, 0x01, 0x81, 0x13, 0x84, 0x13, 0x86, 0x11, 0x9e, 0x1c, 0x98, 0x0c,
+0xa0, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0xac, 0x0a, 0x98, 0x1a, 0xac,
+0x0d, 0xc3, 0xf5, 0x82, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0x2c, 0x01,
+0x8b, 0x08, 0x56, 0x2c, 0xef, 0x0a, 0xa0, 0x01, 0x82, 0x0c, 0xa0, 0x0a, 0xa0,
+0x8a, 0x00, 0x24, 0xe7, 0xf5, 0xfa, 0x05, 0x0e, 0xbc, 0x61, 0x06, 0x04, 0x00,
+0x60, 0x00, 0xf0, 0x13, 0x0e, 0xb8, 0x79, 0x06, 0x06, 0x00, 0x60, 0x00, 0xe0,
+0x2f, 0x80, 0x2b, 0x07, 0x0e, 0x45, 0x01, 0x60, 0x10, 0xc0, 0x1c, 0x88, 0x95,
+0x86, 0x07, 0xe1, 0x09, 0xbe, 0x0b, 0xa1, 0x05, 0x86, 0x21, 0xff, 0x1d, 0x84,
+0x85, 0x02, 0x00, 0x00, 0x08, 0x8c, 0x10, 0x04, 0x18, 0xec, 0x8a, 0x04, 0x14,
+0xed, 0xb5, 0x80, 0x8a, 0x02, 0x40, 0xe1, 0x75, 0x82, 0x01, 0x84, 0x14, 0x24,
+0x09, 0xbe, 0x29, 0x02, 0x00, 0xe3, 0x20, 0x44, 0x80, 0xf3, 0x2f, 0xa0, 0x21,
+0x42, 0x00, 0xe3, 0x8e, 0x41, 0x09, 0x60, 0x10, 0x40, 0xc6, 0x0d, 0x30, 0xe1,
+0x04, 0x80, 0x0b, 0xa3, 0x75, 0xa0, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0xc5, 0x9e, 0xa8, 0x54, 0x00, 0x68, 0x01, 0x81, 0x04, 0x24, 0x11, 0x85,
+0x11, 0x40, 0x20, 0x60, 0x4e, 0x43, 0x00, 0x60, 0x10, 0xc0, 0x04, 0xa4, 0x29,
+0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24, 0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84,
+0x2b, 0xa1, 0x15, 0xfe, 0xa8, 0x52, 0x44, 0xef, 0x04, 0xaa, 0x08, 0x80, 0x0b,
+0xa5, 0xe5, 0x80, 0x04, 0xa8, 0x09, 0x00, 0x20, 0xe1, 0x0b, 0xa1, 0x48, 0x4a,
+0x44, 0x8f, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0x2c,
+0xfc, 0x1e, 0xc0, 0x14, 0xc0, 0x14, 0x02, 0x08, 0xfc, 0x1e, 0x40, 0x11, 0x83,
+0x04, 0xa4, 0x11, 0x40, 0xb0, 0x67, 0x11, 0x81, 0x04, 0xa6, 0x11, 0x40, 0x10,
+0xe0, 0xb8, 0x73, 0x74, 0xef, 0x84, 0x20, 0x70, 0x06, 0x00, 0xe7, 0x8e, 0x17,
+0x01, 0x60, 0x10, 0x40, 0xce, 0x54, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xe0, 0x04,
+0x00, 0x04, 0xfc, 0x0e, 0xe0, 0x04, 0xe0, 0x04, 0x00, 0x0c, 0xfc, 0x0e, 0xe0,
+0x04, 0xe0, 0x04, 0x00, 0x10, 0xfc, 0x0e, 0xe0, 0x45, 0x10, 0x2c, 0xe0, 0x44,
+0x00, 0x00, 0xfc, 0x05, 0x50, 0x2c, 0x60, 0x4e, 0x41, 0x08, 0x60, 0x10, 0xc0,
+0x1b, 0x0e, 0xa0, 0xe1, 0x04, 0x80, 0x2b, 0x0e, 0x9c, 0xe1, 0x1b, 0xc2, 0x03,
+0x02, 0xce, 0x42, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x04, 0x03, 0x84, 0x00, 0x00,
+0xf4, 0xec, 0x0e, 0xac, 0x09, 0x0e, 0x00, 0xe3, 0x4e, 0x8f, 0x0b, 0xa5, 0x0b,
+0x0e, 0x10, 0xd8, 0x95, 0x96, 0x0b, 0x0e, 0x84, 0xe1, 0x15, 0x0e, 0x10, 0xe0,
+0x03, 0x4e, 0x10, 0xf8, 0x1d, 0xc1, 0x13, 0x4e, 0x10, 0x58, 0x10, 0x40, 0x00,
+0xdc, 0x1a, 0x5e, 0x04, 0x04, 0x80, 0xfb, 0x2d, 0xc3, 0x13, 0x4e, 0x10, 0x58,
+0x10, 0x40, 0x00, 0xdc, 0x85, 0x89, 0x0b, 0xa1, 0x98, 0x0c, 0x00, 0xc0, 0x68,
+0x7b, 0x68, 0x6f, 0x01, 0x85, 0x60, 0x00, 0x10, 0x64, 0x11, 0x81, 0xc8, 0x71,
+0x54, 0xef, 0x28, 0x45, 0x70, 0xef, 0x09, 0x0e, 0xe8, 0xea, 0x4a, 0x40, 0x90,
+0xe0, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x4b, 0x00, 0xc8, 0x09, 0x0e, 0x5c, 0xeb,
+0x0a, 0x40, 0x80, 0xe0, 0x25, 0xac, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0x85, 0xaa, 0x0a, 0x40, 0x90, 0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1,
+0x04, 0x80, 0x14, 0xc0, 0x14, 0x02, 0x2c, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c,
+0xeb, 0x0a, 0x40, 0x84, 0xe0, 0x25, 0x82, 0xb8, 0x68, 0x30, 0x6f, 0x01, 0x81,
+0xc8, 0x58, 0x2c, 0x6f, 0x0b, 0x8c, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x94,
+0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1, 0x04, 0x80, 0x14, 0xc0, 0x14, 0x02,
+0x08, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x98, 0xe0, 0x18,
+0x49, 0x00, 0xc8, 0x45, 0x91, 0x14, 0x2c, 0x04, 0x00, 0x80, 0xfb, 0x13, 0x20,
+0x06, 0x41, 0x2c, 0xe0, 0x16, 0x40, 0x02, 0x60, 0x46, 0x42, 0x04, 0xe0, 0xc8,
+0x7a, 0xf8, 0xee, 0x0e, 0x2c, 0x0e, 0x4c, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xac,
+0x00, 0x00, 0xd1, 0x6f, 0x0f, 0xc0, 0x0e, 0xac, 0x05, 0x10, 0x40, 0xe0, 0x04,
+0x00, 0x38, 0xfc, 0x0e, 0x98, 0x05, 0x50, 0x40, 0xe0, 0x04, 0xac, 0xd8, 0x63,
+0xfc, 0xef, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x15, 0x00, 0x40, 0xe0, 0x14,
+0x02, 0xb8, 0xfc, 0x1e, 0x98, 0x15, 0x40, 0x40, 0xe0, 0x98, 0x52, 0x30, 0xef,
+0x0b, 0xa1, 0xa5, 0xfe, 0x34, 0x28, 0xc6, 0x0d, 0x30, 0xe1, 0x08, 0x8c, 0x0b,
+0xa5, 0x0b, 0x0e, 0x04, 0xd8, 0x1b, 0x0e, 0x00, 0xc0, 0x2b, 0x0e, 0x04, 0xc0,
+0x3b, 0x0e, 0x08, 0xc0, 0x75, 0x84, 0x08, 0x8e, 0x19, 0x0e, 0x10, 0xeb, 0x12,
+0x40, 0x80, 0xfb, 0x01, 0x4e, 0x10, 0xf8, 0x0a, 0xcc, 0x03, 0x4e, 0x04, 0xf8,
+0x1a, 0xce, 0x1c, 0x9c, 0x2b, 0x06, 0x10, 0xe0, 0x2c, 0x9e, 0x3b, 0x06, 0x14,
+0xe0, 0x3c, 0xdc, 0xa4, 0x8f, 0x03, 0x4e, 0x80, 0xf9, 0x13, 0x4e, 0x7c, 0xf9,
+0x23, 0x4e, 0x78, 0xf9, 0x33, 0x4e, 0x74, 0xf9, 0xc8, 0x6d, 0x38, 0xef, 0xe8,
+0x66, 0x3c, 0xef, 0x04, 0x20, 0xa9, 0xc2, 0xa5, 0x40, 0x2c, 0xe0, 0x19, 0x0c,
+0x40, 0xe6, 0x1b, 0xa1, 0x55, 0x88, 0x14, 0xc0, 0x19, 0xd0, 0x1e, 0xc0, 0x14,
+0xc0, 0x19, 0xe0, 0x1e, 0xc0, 0x14, 0xc0, 0x19, 0xc4, 0x1e, 0x40, 0x01, 0x83,
+0xa8, 0x41, 0x00, 0xe8, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0xac, 0xfc, 0x1e,
+0xc0, 0x14, 0xc0, 0x19, 0xc8, 0x1e, 0xc0, 0x48, 0x51, 0x74, 0xef, 0x04, 0x26,
+0x11, 0x83, 0x11, 0x40, 0x10, 0xe0, 0x04, 0xa4, 0x11, 0x40, 0x20, 0x60, 0x11,
+0x81, 0x04, 0xa4, 0x11, 0x40, 0xb0, 0xe7, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd,
+0x1b, 0x00, 0x06, 0x01, 0x38, 0xe1, 0x04, 0x80, 0xc8, 0x20, 0x04, 0x60, 0x01,
+0xb8, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04,
+0x00, 0xfa, 0xe6, 0x41, 0x84, 0x60, 0x8e, 0x42, 0x00, 0x60, 0x10, 0xc0, 0x08,
+0x80, 0x14, 0x84, 0x0b, 0x21, 0xa6, 0x41, 0x04, 0xe1, 0x1c, 0x80, 0x05, 0x82,
+0x28, 0x47, 0x64, 0xef, 0x88, 0x7b, 0x28, 0x6f, 0x46, 0x40, 0x04, 0xe0, 0x35,
+0x81, 0x38, 0x56, 0x40, 0xef, 0xd8, 0x67, 0x64, 0xef, 0x38, 0x63, 0x64, 0xef,
+0xd8, 0x44, 0x44, 0xef, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80, 0x09, 0x00, 0x50,
+0xeb, 0x0a, 0x40, 0x88, 0xe0, 0x76, 0x00, 0xa0, 0xc0, 0x68, 0x4e, 0x50, 0xcf,
+0xc6, 0x0d, 0x30, 0xe1, 0x09, 0x0c, 0x30, 0xe7, 0x0b, 0xa1, 0x78, 0x6c, 0x70,
+0xcf, 0x98, 0x7f, 0x6c, 0xef, 0x38, 0x4b, 0x2c, 0x6f, 0x0b, 0x8c, 0xc8, 0x5a,
+0x30, 0x6f, 0x01, 0x81, 0x58, 0x49, 0x30, 0xef, 0xb8, 0x5f, 0x70, 0xef, 0x01,
+0x83, 0x01, 0x4c, 0xc0, 0xe5, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1,
+0x0c, 0x04, 0x84, 0xfa, 0x06, 0x4e, 0x88, 0x60, 0x06, 0x0c, 0x0c, 0xe1, 0x05,
+0x0e, 0x38, 0x78, 0x06, 0x04, 0x00, 0x70, 0x40, 0xc0, 0x19, 0x0c, 0x94, 0x78,
+0x06, 0x06, 0x10, 0x60, 0x40, 0xc0, 0x08, 0xc0, 0x4a, 0x02, 0x00, 0xe0, 0x09,
+0x86, 0x07, 0xc6, 0x2f, 0x61, 0x3f, 0xe1, 0x21, 0x4e, 0x10, 0xf8, 0x0c, 0x00,
+0x00, 0xe2, 0x31, 0x4e, 0x0c, 0xf8, 0x18, 0x62, 0x30, 0xef, 0x05, 0x0e, 0x38,
+0xf8, 0x14, 0x98, 0x09, 0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0xe0, 0x65, 0x82,
+0x0b, 0x0c, 0x28, 0x78, 0x1b, 0xa1, 0x0c, 0xde, 0x05, 0x84, 0x0a, 0x01, 0x20,
+0xec, 0xf5, 0x84, 0x55, 0x03, 0x01, 0x93, 0x0b, 0x0c, 0x24, 0x78, 0x1b, 0xa1,
+0x0c, 0xde, 0x65, 0x82, 0x0a, 0x01, 0x24, 0xec, 0xb5, 0x82, 0xb5, 0x01, 0x01,
+0x95, 0x0a, 0x01, 0x48, 0xec, 0x15, 0x82, 0x65, 0x01, 0x01, 0xa7, 0x0a, 0x01,
+0x50, 0xec, 0x75, 0x80, 0x01, 0xab, 0x0c, 0xde, 0x15, 0x0c, 0x20, 0x79, 0x06,
+0x04, 0x00, 0x60, 0x00, 0xf0, 0x49, 0x0c, 0x64, 0x78, 0x06, 0x06, 0x00, 0x60,
+0x00, 0xe0, 0x2f, 0x82, 0x3b, 0x0c, 0x8c, 0x78, 0x2b, 0x87, 0x30, 0x42, 0x00,
+0x5c, 0x4a, 0x08, 0x14, 0xfc, 0x14, 0x04, 0x80, 0x7a, 0xce, 0x43, 0x00, 0x60,
+0x10, 0xc0, 0x2c, 0x04, 0x8e, 0x48, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x90, 0x85,
+0x88, 0x45, 0x0e, 0x30, 0xf8, 0x3b, 0x08, 0x18, 0xe0, 0x5b, 0x08, 0x20, 0xe0,
+0x4b, 0x08, 0x1c, 0xe0, 0x32, 0x4a, 0x94, 0xf1, 0x53, 0x88, 0x8a, 0x0a, 0x89,
+0x63, 0x05, 0xc0, 0xb5, 0x84, 0x54, 0x1a, 0x8e, 0x59, 0x09, 0x60, 0x10, 0xc0,
+0x71, 0x83, 0x7e, 0x30, 0x2e, 0x59, 0x09, 0x60, 0x10, 0xc0, 0x5c, 0x30, 0x54,
+0x08, 0x80, 0xfb, 0x4d, 0x44, 0x4e, 0x45, 0x09, 0x60, 0x10, 0xc0, 0x3e, 0x88,
+0x43, 0x42, 0x00, 0xc0, 0x65, 0x81, 0x8e, 0x43, 0x09, 0x60, 0x10, 0x40, 0x21,
+0x81, 0x2e, 0x84, 0x03, 0x4c, 0xd5, 0x79, 0x02, 0x40, 0xd6, 0x41, 0x00, 0xe0,
+0x0c, 0x0c, 0x84, 0xf2, 0x18, 0x34, 0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0xfc,
+0xfa, 0x6b, 0x00, 0x06, 0x41, 0x08, 0xe0, 0x98, 0x73, 0x48, 0xef, 0x0b, 0xa1,
+0x78, 0x14, 0x00, 0xc0, 0x86, 0x52, 0x84, 0xe0, 0x74, 0xe4, 0x08, 0x9c, 0x09,
+0x86, 0x0b, 0xa3, 0x36, 0x40, 0x98, 0x40, 0x06, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xf5, 0x80, 0x21, 0x01, 0x36, 0x40, 0x98, 0xe0, 0x2e, 0x18, 0x11,
+0x83, 0x16, 0x80, 0x84, 0x26, 0x16, 0x44, 0x98, 0xe0, 0x18, 0x9c, 0x09, 0x10,
+0xb4, 0xe0, 0x0a, 0x02, 0x0c, 0xfc, 0x0c, 0x02, 0x00, 0xe2, 0x01, 0x4c, 0x10,
+0xe1, 0x0b, 0x10, 0x80, 0xe0, 0x16, 0x88, 0xe8, 0x75, 0xf8, 0xee, 0x05, 0x4c,
+0x48, 0xe0, 0x08, 0x9c, 0x00, 0x42, 0x08, 0xfc, 0x19, 0x86, 0x1b, 0xa5, 0xd5,
+0x80, 0x4a, 0x00, 0x0c, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x0e, 0x98, 0x09, 0x10,
+0xb0, 0xe0, 0x98, 0x51, 0x00, 0x68, 0x09, 0x82, 0x01, 0x4c, 0x18, 0x60, 0x11,
+0x9f, 0x0b, 0x10, 0x88, 0xe0, 0x78, 0x51, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x4c, 0xe0, 0x0b, 0x02, 0x84, 0x60, 0x11, 0x91, 0xf8, 0x50, 0x00, 0xe8, 0xb4,
+0xa6, 0x05, 0x4c, 0x50, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x0a, 0x40, 0x84, 0x60,
+0x06, 0x00, 0x00, 0x60, 0x48, 0xd0, 0x56, 0x01, 0x55, 0x15, 0x41, 0xd0, 0x05,
+0x4c, 0x34, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x68, 0x4f, 0x00, 0x68, 0x09, 0x82,
+0x14, 0x64, 0x06, 0x54, 0xdc, 0xe0, 0x01, 0x4c, 0x10, 0x60, 0xce, 0x41, 0x00,
+0x60, 0x10, 0xc0, 0x0a, 0x80, 0x2b, 0x02, 0x18, 0xe0, 0x23, 0xa0, 0x23, 0x4c,
+0x60, 0xe0, 0x09, 0x14, 0x70, 0xea, 0x22, 0x40, 0x80, 0xf1, 0x03, 0x4c, 0x60,
+0xe0, 0x25, 0x14, 0xe0, 0xe2, 0x2b, 0xa3, 0x35, 0x88, 0x24, 0xa4, 0x3b, 0x02,
+0x18, 0xe0, 0x4b, 0x02, 0x20, 0xe0, 0x1b, 0x02, 0x1c, 0xe0, 0x59, 0x04, 0x24,
+0xe0, 0x33, 0xa8, 0x32, 0x44, 0x84, 0x70, 0x2e, 0x47, 0x08, 0x60, 0x10, 0xc0,
+0x20, 0x02, 0xfc, 0x6c, 0x0a, 0x4a, 0x88, 0xe0, 0x1c, 0x0c, 0x20, 0x02, 0x14,
+0xcc, 0x10, 0x02, 0xf0, 0xcc, 0x13, 0x46, 0x00, 0x40, 0x0a, 0x41, 0x04, 0xec,
+0x13, 0x4c, 0x60, 0x40, 0x10, 0x40, 0x00, 0xdc, 0x15, 0x14, 0xdc, 0x62, 0x04,
+0x00, 0x80, 0xfb, 0x86, 0x05, 0x74, 0xe1, 0x1d, 0xc1, 0x13, 0x4c, 0x60, 0x40,
+0x14, 0x00, 0x80, 0xdb, 0x14, 0xa4, 0x0e, 0xa8, 0x04, 0x88, 0x7a, 0x84, 0x73,
+0x4c, 0x64, 0xe0, 0xe8, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x8e, 0x40, 0xb1, 0x66,
+0x9b, 0xd3, 0x08, 0x6d, 0xf8, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x85, 0x4c, 0x3c,
+0xe0, 0x28, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0x28, 0x6d, 0xf8, 0xee,
+0x48, 0x6c, 0xf8, 0x6e, 0x0e, 0x02, 0x00, 0x60, 0x1e, 0xd1, 0x05, 0x4c, 0x40,
+0x60, 0x11, 0x91, 0x0b, 0x16, 0x64, 0xe0, 0x7b, 0x16, 0x5c, 0xe0, 0x89, 0x16,
+0xc0, 0xe0, 0x08, 0x49, 0x00, 0xe8, 0x3b, 0x00, 0x80, 0x44, 0x7c, 0xf0, 0x81,
+0x01, 0x11, 0x81, 0xb8, 0x40, 0x40, 0x6f, 0x0b, 0x8e, 0x4b, 0x0c, 0x60, 0x60,
+0x06, 0x0e, 0x00, 0x60, 0xe0, 0xcf, 0x0e, 0x5a, 0x11, 0x81, 0x21, 0x01, 0x06,
+0x06, 0x01, 0x60, 0xe0, 0xcf, 0xd8, 0x7f, 0x3c, 0x6f, 0x0b, 0x88, 0x14, 0x64,
+0x86, 0x57, 0x94, 0xe1, 0x05, 0x4c, 0x2c, 0xe0, 0x0a, 0x86, 0x83, 0x4c, 0xe4,
+0xe0, 0x03, 0x4c, 0xe0, 0xe0, 0x08, 0x86, 0x09, 0xbe, 0x03, 0x4c, 0xe8, 0xe0,
+0x08, 0x86, 0x09, 0xbe, 0x07, 0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x54,
+0xe0, 0x0a, 0xc4, 0x03, 0x4c, 0xec, 0xe0, 0x0a, 0x44, 0x11, 0x91, 0xe8, 0x45,
+0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x58, 0xe0, 0x0a, 0x46, 0x11, 0x91, 0x78,
+0x45, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x5c, 0xe0, 0x0b, 0x02, 0x10, 0x60,
+0x11, 0x91, 0xf8, 0x44, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x60, 0xe0, 0x0b,
+0x02, 0x14, 0x60, 0x11, 0x91, 0x78, 0x44, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x64, 0xe0, 0x85, 0x4c, 0x14, 0xe0, 0x85, 0x4c, 0x18, 0xe0, 0x0b, 0x02, 0x7c,
+0x60, 0x11, 0x91, 0xb8, 0x43, 0x00, 0xe8, 0x05, 0x4c, 0x10, 0x60, 0x11, 0x91,
+0x81, 0x4c, 0x14, 0xe0, 0x75, 0x4c, 0x1c, 0xe0, 0x09, 0x14, 0xd0, 0xea, 0x07,
+0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x20, 0xe0, 0x0b, 0x14, 0x94, 0xe5,
+0x98, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x38, 0x60, 0x11, 0x91, 0x0b, 0x14, 0x64,
+0xe5, 0x28, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x28, 0x60, 0x11, 0x81, 0x09, 0x14,
+0x8c, 0x6a, 0x21, 0x81, 0x0b, 0x12, 0x7c, 0x60, 0x0a, 0x40, 0x88, 0xe0, 0xd6,
+0x0e, 0xcd, 0x0c, 0x73, 0xcf, 0x75, 0x4c, 0x24, 0x60, 0x3b, 0x8e, 0x28, 0x79,
+0x3c, 0xef, 0x0e, 0xd8, 0x16, 0x41, 0x08, 0xe0, 0x0c, 0x0c, 0xfc, 0xf2, 0xb8,
+0x1e, 0x48, 0xff, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2,
+0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x88, 0x61, 0x28, 0xef, 0x4c,
+0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x88, 0x5d,
+0x48, 0x6f, 0xf6, 0x40, 0x04, 0xe0, 0x0b, 0xa1, 0x85, 0x80, 0x0c, 0x0c, 0x00,
+0xf2, 0x93, 0xdd, 0xb8, 0x6b, 0x54, 0xef, 0xc6, 0x4c, 0x84, 0xe0, 0x04, 0x98,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0x85, 0x88, 0xc6, 0x40, 0xb4,
+0x60, 0x46, 0x45, 0x00, 0xe0, 0x19, 0x00, 0xb4, 0xe4, 0x16, 0x42, 0x0a, 0xe0,
+0x03, 0x82, 0x09, 0x00, 0x40, 0xe2, 0x0b, 0xa5, 0xf5, 0x84, 0x48, 0x56, 0x40,
+0x6f, 0x01, 0x85, 0x58, 0x4c, 0x40, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0xd8, 0x7d,
+0x44, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x58, 0x50, 0x40, 0xef, 0x18, 0x70, 0xf4,
+0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x70,
+0xf4, 0xcf, 0x09, 0x0c, 0x48, 0xe0, 0x0b, 0xa1, 0x95, 0x84, 0xe8, 0x70, 0x38,
+0xef, 0x06, 0x40, 0x94, 0x60, 0x16, 0x07, 0x04, 0xe1, 0x04, 0x80, 0x1b, 0x00,
+0x18, 0xe1, 0x2b, 0x00, 0x50, 0xe1, 0x48, 0x8c, 0x39, 0x06, 0xac, 0xe0, 0x13,
+0xa4, 0x13, 0xa8, 0x13, 0xa6, 0x15, 0x40, 0x2c, 0xe0, 0xc6, 0x00, 0x2c, 0x61,
+0x11, 0xa1, 0x04, 0x80, 0x13, 0x40, 0xd0, 0x65, 0xe6, 0x40, 0xe0, 0xe0, 0x0a,
+0x00, 0x06, 0x42, 0x94, 0xe0, 0x14, 0x84, 0x03, 0x42, 0x10, 0x62, 0x06, 0x41,
+0x04, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x98, 0x17, 0x48, 0xff, 0xc3, 0xc1, 0x66,
+0x01, 0xcc, 0xe0, 0x18, 0x80, 0x0a, 0x42, 0x84, 0xe0, 0x10, 0x42, 0xf8, 0xd3,
+0x11, 0x40, 0x00, 0xc0, 0x93, 0xdd, 0x4a, 0x40, 0x80, 0xe0, 0x55, 0x84, 0x14,
+0x04, 0xa0, 0x60, 0x0e, 0x06, 0x00, 0x70, 0x3f, 0xc0, 0x2f, 0x06, 0x14, 0x06,
+0xa0, 0xe1, 0x14, 0x00, 0xe0, 0x61, 0x30, 0x46, 0x00, 0x70, 0xff, 0xc0, 0x17,
+0x70, 0x3f, 0xa0, 0x1f, 0xa4, 0x1f, 0xa6, 0x93, 0x5d, 0x0b, 0x82, 0x4c, 0x04,
+0x04, 0xfa, 0x86, 0x0d, 0xcc, 0x60, 0x71, 0x81, 0x7c, 0x98, 0x88, 0x55, 0x68,
+0xef, 0x0a, 0x98, 0x00, 0x42, 0xf1, 0x73, 0xff, 0xc0, 0x19, 0xc2, 0x1c, 0x18,
+0x60, 0x02, 0xe1, 0x65, 0x02, 0xc0, 0x65, 0x81, 0x24, 0x84, 0x2b, 0xa1, 0xf5,
+0x80, 0x71, 0x02, 0x11, 0x88, 0x0a, 0x0e, 0x00, 0xe1, 0x35, 0xfe, 0x75, 0x81,
+0x77, 0x50, 0x00, 0x40, 0xf0, 0xf3, 0x0f, 0xae, 0x09, 0xc4, 0x0c, 0x98, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x4c, 0x04, 0x7c, 0xfa, 0xbb, 0x06, 0x6b, 0x82,
+0x14, 0x2c, 0x9b, 0x80, 0x74, 0x24, 0xab, 0x84, 0xa4, 0x00, 0xa0, 0x61, 0x8b,
+0x88, 0x1b, 0x81, 0x65, 0x82, 0x0e, 0x2c, 0x0b, 0x8e, 0x78, 0x56, 0x30, 0x6f,
+0x1b, 0x8c, 0x24, 0x2c, 0x0b, 0x8e, 0x28, 0x59, 0x30, 0x6f, 0x1b, 0x8c, 0x04,
+0x18, 0xb1, 0x83, 0x11, 0x01, 0x21, 0x83, 0x38, 0x44, 0x00, 0x68, 0x31, 0x85,
+0xa4, 0x02, 0x80, 0x7a, 0x21, 0x8b, 0xd8, 0x43, 0x00, 0x68, 0x06, 0x46, 0xfc,
+0xe1, 0xab, 0x00, 0x0b, 0x8e, 0x78, 0x40, 0xf8, 0x6f, 0x1b, 0x94, 0x09, 0x12,
+0x10, 0x60, 0x1b, 0x90, 0x98, 0x79, 0xfc, 0x6f, 0x70, 0x12, 0x10, 0xe4, 0x1b,
+0x00, 0x0b, 0x92, 0xb8, 0x7f, 0xf4, 0xef, 0x11, 0x03, 0x21, 0x85, 0x31, 0x09,
+0x0b, 0x94, 0x58, 0x42, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x18, 0x7f, 0xf4,
+0x6f, 0x1b, 0x90, 0x70, 0x12, 0x50, 0x64, 0x36, 0x54, 0x48, 0xe1, 0x08, 0x42,
+0x00, 0x68, 0x0b, 0x92, 0x0a, 0x00, 0x00, 0xfb, 0xb1, 0x54, 0x00, 0x40, 0x0a,
+0x40, 0xa0, 0xe0, 0xe5, 0xfc, 0x11, 0x01, 0x21, 0x85, 0x31, 0x09, 0x0b, 0x90,
+0xc8, 0x40, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x88, 0x7d, 0xf4, 0x6f, 0x1b,
+0x90, 0x8e, 0x98, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1f, 0x25,
+0x32, 0x40, 0x80, 0xf8, 0x1f, 0x86, 0x93, 0x5d, 0x0f, 0xa2, 0x04, 0x80, 0x93,
+0xdd, 0x0c, 0x00, 0x80, 0xfa, 0xa8, 0x4c, 0x48, 0x6f, 0xe6, 0x40, 0x08, 0xe0,
+0x0b, 0xa1, 0x95, 0x84, 0xc8, 0x46, 0x40, 0xef, 0x86, 0x02, 0x84, 0x60, 0xc6,
+0x04, 0x84, 0xe0, 0x14, 0x04, 0x5a, 0x00, 0x08, 0xe0, 0x24, 0x88, 0x4a, 0x42,
+0x08, 0x60, 0x46, 0x02, 0x84, 0xe0, 0x0a, 0x42, 0x80, 0xe1, 0x0c, 0x00, 0x00,
+0xe2, 0x06, 0x84, 0xf6, 0x40, 0x08, 0xe0, 0x0c, 0x08, 0x80, 0xf2, 0xd8, 0x0a,
+0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0xf0, 0x1e, 0x60, 0x6c, 0xa6,
+0x01, 0x04, 0xe0, 0x48, 0x4a, 0x48, 0xef, 0x0b, 0xa1, 0x15, 0x90, 0xc8, 0x5d,
+0x2c, 0xef, 0xe8, 0x7a, 0xf4, 0x6e, 0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0x86,
+0x50, 0x84, 0xe0, 0x69, 0x10, 0x44, 0xe0, 0x58, 0x7a, 0xf4, 0x6e, 0x1b, 0x8c,
+0x04, 0x60, 0x7b, 0x80, 0x1b, 0x00, 0x28, 0xe0, 0x0b, 0x00, 0x24, 0xe0, 0x13,
+0xa0, 0x10, 0x00, 0x04, 0x64, 0x1b, 0x8c, 0x88, 0x79, 0xf4, 0xee, 0x14, 0xa0,
+0x19, 0x02, 0x28, 0xe0, 0x19, 0xbe, 0x06, 0x44, 0x06, 0x60, 0x06, 0x41, 0xdc,
+0xe0, 0x04, 0x00, 0x16, 0x42, 0x1e, 0xe0, 0x21, 0xbe, 0x1e, 0x7c, 0x27, 0xcb,
+0x2c, 0x7c, 0x0e, 0x02, 0x25, 0x74, 0x03, 0xc0, 0x48, 0x78, 0xf4, 0xee, 0x0e,
+0x7e, 0x11, 0x81, 0x1e, 0x09, 0xf0, 0x00, 0x10, 0xe4, 0x1c, 0x95, 0x88, 0x7e,
+0x1c, 0xef, 0x0a, 0x95, 0x18, 0x41, 0x00, 0x68, 0x01, 0xa0, 0x06, 0x00, 0x38,
+0x61, 0x11, 0x83, 0x78, 0x7c, 0x1c, 0xef, 0x18, 0x46, 0x48, 0x6f, 0xb6, 0x01,
+0x04, 0xe0, 0xf1, 0xb0, 0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1b,
+0x00, 0x06, 0x00, 0x38, 0xe1, 0x04, 0x80, 0x08, 0x35, 0xf4, 0x7f, 0x01, 0xc8,
+0xc3, 0xc1, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x4e, 0x84, 0x60, 0xf0, 0x1e, 0x20,
+0xee, 0x84, 0x9c, 0x19, 0x0e, 0x48, 0xe0, 0x0a, 0xe2, 0x2a, 0xe0, 0x03, 0xa4,
+0x01, 0x82, 0x38, 0x75, 0xf4, 0x6e, 0x04, 0x00, 0x80, 0xfb, 0x0b, 0x10, 0x64,
+0x60, 0x6b, 0x80, 0x0b, 0xa1, 0x05, 0x0e, 0x04, 0xc0, 0x99, 0x00, 0xa0, 0xc0,
+0xd5, 0x84, 0x09, 0x10, 0x24, 0x60, 0x06, 0x03, 0xd0, 0xe0, 0x1a, 0x84, 0x07,
+0xc5, 0x09, 0x82, 0x1f, 0x21, 0x06, 0x00, 0x04, 0xe0, 0x10, 0x42, 0x01, 0x70,
+0xff, 0xc0, 0x17, 0xd1, 0x1b, 0xa1, 0x78, 0x73, 0xf4, 0x6e, 0x16, 0x02, 0x00,
+0x80, 0x00, 0x12, 0x04, 0xec, 0x18, 0x42, 0x48, 0x6f, 0x36, 0x01, 0x04, 0xe0,
+0x0b, 0xa1, 0xb5, 0xb0, 0x04, 0x1c, 0x06, 0x11, 0xd0, 0xe0, 0x19, 0x10, 0x24,
+0x6e, 0x46, 0x05, 0xb8, 0xe0, 0xa4, 0x9e, 0x09, 0x00, 0xb0, 0xe0, 0x18, 0x08,
+0x5a, 0x02, 0x00, 0xe0, 0x1c, 0x06, 0x00, 0xe2, 0x36, 0x3e, 0x09, 0x86, 0x0b,
+0x21, 0x19, 0x82, 0x0c, 0x04, 0x00, 0x62, 0x0b, 0xa5, 0x16, 0x7c, 0x64, 0x02,
+0x80, 0xfb, 0x26, 0x3c, 0x6b, 0x9e, 0x1e, 0xbe, 0x19, 0x0e, 0x60, 0xe0, 0x2b,
+0x10, 0x30, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x16, 0x1b, 0x11, 0x81, 0x06, 0x7e,
+0x2b, 0xa3, 0x19, 0x14, 0x5c, 0xc0, 0x09, 0x0e, 0x80, 0xe0, 0x25, 0x10, 0xa4,
+0xe3, 0x35, 0x10, 0xa8, 0x63, 0x10, 0x42, 0x3c, 0xd0, 0x0c, 0x0b, 0x01, 0x81,
+0x16, 0x1d, 0x11, 0x81, 0x26, 0x1f, 0x46, 0x05, 0x04, 0xe0, 0x0c, 0x89, 0x06,
+0x99, 0x06, 0xa5, 0x0c, 0x91, 0x3e, 0x8b, 0x0e, 0x91, 0x0e, 0x8f, 0x0e, 0x0d,
+0x60, 0x00, 0xa0, 0xe4, 0x91, 0x5e, 0x90, 0xe0, 0xc8, 0x7a, 0xf4, 0xee, 0x09,
+0x14, 0x58, 0xe0, 0x19, 0x10, 0xf0, 0xf8, 0x0e, 0x3f, 0xfe, 0x41, 0x3d, 0x70,
+0xff, 0xc0, 0x1b, 0x25, 0xf6, 0x43, 0x3c, 0xe0, 0x05, 0x5e, 0x80, 0x60, 0x16,
+0x00, 0x00, 0x80, 0x15, 0x5e, 0x84, 0xe0, 0x01, 0x5e, 0x34, 0x80, 0x05, 0x14,
+0x18, 0xe0, 0x38, 0x47, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a,
+0x20, 0x11, 0x91, 0x0e, 0x97, 0x78, 0x5d, 0xfc, 0x6f, 0x0b, 0x84, 0x19, 0x10,
+0xc0, 0xe9, 0x0e, 0xb7, 0x4a, 0x42, 0x80, 0xe0, 0xc5, 0x82, 0x60, 0x00, 0xc0,
+0x65, 0x11, 0xfb, 0x75, 0x81, 0x24, 0x37, 0x11, 0x82, 0x2e, 0xc1, 0x24, 0x97,
+0x25, 0x40, 0x44, 0xf8, 0x1b, 0xa1, 0x35, 0xfe, 0xe5, 0x85, 0x04, 0x9e, 0x05,
+0x00, 0x1c, 0xe0, 0x28, 0x45, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7,
+0x14, 0x9e, 0x0e, 0x99, 0x05, 0x02, 0x20, 0xe0, 0x98, 0x44, 0x00, 0x68, 0x00,
+0x40, 0xfc, 0x73, 0xff, 0xc7, 0x14, 0x9e, 0x0e, 0x9b, 0x05, 0x02, 0x24, 0xe0,
+0x08, 0x44, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a, 0x22, 0x11,
+0x91, 0x0e, 0x9d, 0x48, 0x5a, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x60, 0x11, 0x91,
+0x0e, 0xb9, 0xe8, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x62, 0x11, 0x91, 0x0e,
+0xbb, 0x88, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x0e, 0xbd, 0x04, 0x9c, 0x09, 0x00,
+0x24, 0xe0, 0x0a, 0x40, 0x88, 0xe0, 0xa5, 0x84, 0x04, 0xb7, 0xf8, 0x7a, 0xf4,
+0x6e, 0x1b, 0x80, 0x14, 0xb9, 0x0e, 0xb7, 0xa8, 0x7a, 0xf4, 0x6e, 0x0b, 0x82,
+0x14, 0xbb, 0x0e, 0xb9, 0x58, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x14, 0xbd, 0x0e,
+0xbb, 0x08, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x0e, 0xbd, 0x86, 0x41, 0x30, 0x61,
+0x1b, 0x9e, 0xd8, 0x4c, 0x08, 0xef, 0x78, 0x75, 0x44, 0x6f, 0x46, 0x01, 0x04,
+0xe0, 0xf0, 0x1e, 0x20, 0xe6, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0x4c, 0x00,
+0x00, 0xfa, 0x08, 0x74, 0x24, 0x6f, 0x11, 0x8f, 0x4c, 0x08, 0x00, 0xf2, 0x93,
+0xdd, 0x0c, 0x04, 0x7c, 0xfe, 0x6b, 0x04, 0xf0, 0x1e, 0x60, 0xec, 0xb5, 0x0c,
+0x10, 0xe0, 0x85, 0x0c, 0x18, 0xe0, 0x79, 0x0c, 0x24, 0xe0, 0xa4, 0xda, 0x1e,
+0x0b, 0x11, 0x81, 0x0e, 0x7e, 0x0b, 0x96, 0x68, 0x79, 0xf4, 0xee, 0x0b, 0xa1,
+0x15, 0x82, 0x14, 0x18, 0x0b, 0x96, 0x88, 0x79, 0xf4, 0xee, 0x0b, 0xa1, 0xb5,
+0x4c, 0x00, 0xc0, 0x11, 0x01, 0x0b, 0x94, 0x98, 0x78, 0xf4, 0xee, 0x94, 0x18,
+0x0b, 0xa1, 0x05, 0x1e, 0x14, 0xc0, 0xbe, 0xbe, 0x75, 0x82, 0x0b, 0x14, 0x1b,
+0x92, 0xf8, 0x78, 0xf4, 0xee, 0x04, 0x0b, 0x0b, 0xa1, 0xa5, 0x4c, 0x00, 0x40,
+0xa0, 0x52, 0x00, 0xdc, 0xae, 0x7c, 0xab, 0x80, 0x77, 0x42, 0x0b, 0x92, 0x15,
+0x14, 0x1c, 0xfe, 0x58, 0x75, 0xf4, 0xee, 0x0b, 0x14, 0x44, 0x60, 0x9b, 0x80,
+0x88, 0x75, 0xf4, 0xee, 0xbb, 0x00, 0x0b, 0x92, 0x08, 0x77, 0xf4, 0x6e, 0x1b,
+0x96, 0x0b, 0xa1, 0x90, 0x56, 0x00, 0x1c, 0xa0, 0x12, 0x88, 0xe4, 0xc8, 0x75,
+0xf4, 0x6e, 0x0b, 0x96, 0xab, 0x00, 0x01, 0x81, 0x01, 0x4c, 0x20, 0x60, 0x8b,
+0xa1, 0x04, 0x7e, 0x76, 0x03, 0x71, 0x7b, 0x34, 0xce, 0xac, 0xd8, 0x03, 0x8e,
+0x00, 0x00, 0x70, 0xe7, 0x0e, 0x89, 0x65, 0x88, 0x8b, 0xa3, 0x35, 0x08, 0x06,
+0x02, 0x01, 0x00, 0xc0, 0xcf, 0x58, 0x74, 0xf4, 0x6e, 0x0b, 0x96, 0x28, 0x73,
+0xf4, 0x6e, 0x8b, 0x80, 0x1b, 0x00, 0x0b, 0x96, 0x58, 0x73, 0xf4, 0xee, 0xd8,
+0x49, 0x00, 0xe8, 0x18, 0x74, 0xf4, 0x6e, 0x76, 0x03, 0x70, 0x7b, 0x34, 0xce,
+0x0b, 0xa1, 0x25, 0x84, 0x11, 0x01, 0x0b, 0x96, 0x48, 0x74, 0xf4, 0xee, 0x0b,
+0xa1, 0x80, 0x14, 0x04, 0x04, 0x80, 0x54, 0x00, 0xdc, 0x75, 0x81, 0xc8, 0x70,
+0xf4, 0x6e, 0x0b, 0x96, 0x58, 0x72, 0xf4, 0xee, 0xab, 0x80, 0xac, 0xd8, 0x0a,
+0xa4, 0x08, 0x71, 0xf4, 0xee, 0x04, 0x0b, 0x9b, 0x80, 0x73, 0x80, 0x0b, 0x0e,
+0x40, 0xe0, 0x98, 0x70, 0xf4, 0xee, 0xbb, 0x00, 0xa4, 0x00, 0x80, 0xfb, 0x48,
+0x70, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x96, 0x68, 0x49, 0x00, 0xe8, 0x1b, 0x00,
+0x0b, 0x92, 0xc8, 0x47, 0x00, 0xe8, 0xa8, 0x70, 0xf4, 0xee, 0x04, 0x09, 0xab,
+0x80, 0xac, 0xd8, 0x9a, 0x80, 0x9c, 0xda, 0x0b, 0x0e, 0x4c, 0xe0, 0x18, 0x6f,
+0xf4, 0xee, 0x0b, 0x0e, 0x48, 0x60, 0xbb, 0x80, 0xc8, 0x6e, 0xf4, 0xee, 0x8b,
+0x00, 0x0b, 0x92, 0x88, 0x6e, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0xa8, 0x47,
+0x00, 0xe8, 0x1b, 0x00, 0x0b, 0x96, 0x08, 0x46, 0x00, 0xe8, 0xe8, 0x6e, 0xf4,
+0xee, 0xb4, 0x3e, 0x9b, 0x80, 0x84, 0xfc, 0x9c, 0xda, 0x1b, 0x96, 0xf8, 0x6d,
+0xf4, 0x6e, 0x0b, 0x90, 0x68, 0x44, 0x00, 0xe8, 0xe8, 0x6e, 0xf4, 0x6e, 0x11,
+0x81, 0x0b, 0xa1, 0x85, 0x8e, 0x11, 0x01, 0x0b, 0x96, 0xf8, 0x6d, 0xf4, 0xee,
+0x2a, 0x00, 0x00, 0x60, 0x0b, 0x94, 0x24, 0x7e, 0x1b, 0x92, 0x34, 0x8b, 0xf8,
+0x74, 0x18, 0xef, 0x58, 0x01, 0x01, 0x40, 0x00, 0x40, 0x9b, 0x80, 0x0b, 0x16,
+0x1b, 0x92, 0x78, 0x6d, 0xf4, 0xee, 0x0b, 0xa1, 0x85, 0x82, 0x1a, 0xd8, 0x09,
+0x0c, 0x20, 0xe0, 0x11, 0x82, 0x04, 0x09, 0x0b, 0xa1, 0x1c, 0x58, 0x70, 0x00,
+0x90, 0xc4, 0x0a, 0x80, 0x0c, 0xda, 0x11, 0x01, 0x0b, 0x90, 0xe8, 0x6b, 0xf4,
+0xee, 0x0b, 0xa1, 0xb5, 0x84, 0x0b, 0x10, 0x1b, 0x92, 0x88, 0x6c, 0xf4, 0xee,
+0x0b, 0xa1, 0xe5, 0x82, 0x09, 0x0c, 0x20, 0xe0, 0x1a, 0xd8, 0x0b, 0xa1, 0x70,
+0x0e, 0x98, 0x44, 0x10, 0x00, 0x04, 0xec, 0x75, 0x5e, 0x10, 0xc0, 0x0c, 0xd8,
+0x04, 0x89, 0x0a, 0x80, 0x0c, 0xda, 0xf1, 0xb0, 0x0c, 0x0c, 0x7c, 0xf6, 0x93,
+0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x00, 0x11, 0x81, 0xe8, 0x6a, 0xf4, 0xee,
+0x0b, 0xa1, 0x64, 0x0c, 0x7c, 0xdd, 0x0b, 0x8c, 0x0c, 0x0c, 0x00, 0xf2, 0x93,
+0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x02, 0x7b, 0x80, 0x08, 0x6a,
+0xf4, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x48, 0x69, 0xf4, 0x6e, 0x1b, 0x8c, 0x0b,
+0xa1, 0x70, 0x4c, 0x00, 0x1c, 0x8b, 0xa1, 0x70, 0x4c, 0x00, 0xdc, 0x0b, 0x8c,
+0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x6b,
+0x02, 0x7b, 0x80, 0xa8, 0x68, 0xf4, 0xee, 0x0b, 0xa1, 0x70, 0x4c, 0x00, 0x9c,
+0x0b, 0x8c, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00,
+0xfa, 0x6b, 0x00, 0x21, 0x81, 0x04, 0x18, 0x31, 0x83, 0x68, 0x53, 0xfc, 0xef,
+0x0e, 0x98, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x18, 0xa9, 0x00, 0x00, 0x38,
+0x90, 0x40, 0x00, 0x08, 0x14, 0xb8, 0xe0, 0x80, 0x2c, 0x40, 0x00, 0xa8, 0x3a,
+0xe8, 0xe0, 0x08, 0x9f, 0x40, 0x00, 0x28, 0x28, 0xb0, 0xe0, 0x18, 0x72, 0x40,
+0x00, 0x88, 0x19, 0xc8, 0xe0, 0xe0, 0xbb, 0x40, 0x00, 0x08, 0x0c, 0xa4, 0xe0,
+0xa8, 0xe3, 0x40, 0x00, 0x28, 0x13, 0x90, 0xe0, 0xf0, 0xec, 0x40, 0x00, 0x68,
+0x0d, 0x8c, 0xe0, 0x48, 0xa8, 0x40, 0x00, 0xa8, 0x3c, 0xac, 0xe0, 0x28, 0xe4,
+0x40, 0x00, 0x08, 0x0e, 0x94, 0xe0, 0xb8, 0xe2, 0x40, 0x00, 0x28, 0x3d, 0x90,
+0xe0, 0x90, 0xa7, 0x40, 0x00, 0xc8, 0x13, 0xb0, 0xe0, 0x18, 0xc1, 0x40, 0x00,
+0xa8, 0x2a, 0xa4, 0xe0, 0xa8, 0x91, 0x40, 0x00, 0x68, 0x2f, 0xbc, 0xe0, 0x88,
+0xf1, 0x40, 0x00, 0x08, 0x1c, 0x88, 0xe0, 0xd0, 0x8e, 0x40, 0x00, 0xa8, 0x19,
+0xb8, 0xe0, 0x38, 0x82, 0x40, 0x00, 0x28, 0x38, 0xbc, 0xe0, 0x88, 0xe0, 0x40,
+0x00, 0xa8, 0x0e, 0x98, 0xe0, 0xd8, 0xe4, 0x40, 0x00, 0x68, 0x32, 0x94, 0xe0,
+0x18, 0xd7, 0x40, 0x00, 0x28, 0x2d, 0x9c, 0xe0, 0x40, 0xe8, 0x40, 0x00, 0x28,
+0x19, 0x94, 0xe0, 0xa8, 0x78, 0x40, 0x00, 0x28, 0x18, 0xcc, 0xe0, 0xf0, 0x95,
+0x40, 0x00, 0x48, 0x34, 0xbc, 0xe0, 0xa8, 0xd2, 0x40, 0x00, 0x48, 0x16, 0xa0,
+0xe0, 0x18, 0x52, 0x40, 0x00, 0x68, 0x2b, 0xe0, 0xe0, 0x00, 0xb9, 0x00, 0x00,
+0x23, 0xdb, 0x00, 0x00
+};
+
+static const u8 cut2_patch[] = {
+0x37, 0x00, 0x05, 0x00, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x0e, 0x84, 0x60, 0x06,
+0x11, 0x84, 0xe0, 0x04, 0x1c, 0x06, 0x13, 0x88, 0xe1, 0x14, 0xa0, 0x0d, 0xc2,
+0x95, 0x80, 0x04, 0xa4, 0x0b, 0xa3, 0xb5, 0x88, 0x78, 0x6c, 0x40, 0xef, 0x6b,
+0x00, 0x46, 0x01, 0x84, 0xe0, 0x88, 0x53, 0x48, 0x6f, 0x46, 0x15, 0x84, 0xe0,
+0x6b, 0x25, 0xc6, 0x0d, 0x84, 0xe0, 0x95, 0x84, 0x08, 0xa8, 0x4a, 0x00, 0xfc,
+0xfb, 0x05, 0x84, 0x09, 0x12, 0x1c, 0xe0, 0xa8, 0x46, 0x48, 0xef, 0x04, 0x1c,
+0x11, 0x83, 0x16, 0x18, 0xc6, 0x04, 0x84, 0xe0, 0x0e, 0x08, 0x01, 0x87, 0xf8,
+0x6a, 0x40, 0xef, 0x46, 0x0c, 0x84, 0xe0, 0x04, 0x1c, 0x11, 0x81, 0x16, 0x98,
+0x0e, 0xa0, 0x06, 0x41, 0x84, 0x60, 0xc6, 0x02, 0xd8, 0xe0, 0x04, 0x80, 0x08,
+0x80, 0x06, 0x84, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x00,
+0x80, 0xfa, 0xc6, 0x40, 0x84, 0x60, 0x86, 0x43, 0xd8, 0xe0, 0x04, 0x80, 0x09,
+0x00, 0x18, 0xe1, 0x06, 0x84, 0x88, 0x41, 0x68, 0xef, 0x0c, 0x08, 0x80, 0xf2,
+0x48, 0x20, 0x68, 0xff, 0x1e, 0xc0, 0xb5, 0x00, 0x2c, 0xe0, 0xb4, 0x02, 0x00,
+0xfc, 0x15, 0x40, 0x2c, 0xe0, 0x19, 0x10, 0x31, 0x78, 0x01, 0x40, 0x86, 0x04,
+0x0c, 0xe0, 0x4a, 0x04, 0x00, 0x64, 0x31, 0x81, 0x30, 0x0a, 0x04, 0x64, 0x4b,
+0x82, 0x1b, 0x06, 0x3b, 0x8a, 0xc8, 0x44, 0xfc, 0x87, 0x41, 0x50, 0x31, 0x78,
+0x01, 0x40, 0xa1, 0xe1, 0xe6, 0x43, 0x99, 0x6c, 0x10, 0xc0, 0x08, 0x42, 0x00,
+0xf8, 0x00, 0x00, 0x03, 0xa9, 0x00, 0x00, 0x30, 0x85, 0x40, 0x00, 0xa8, 0x16,
+0xbc, 0xe0, 0xcc, 0xc1, 0x40, 0x00, 0xa8, 0x35, 0x9c, 0xe0, 0xb0, 0xc9, 0x40,
+0x00, 0x88, 0x37, 0x98, 0xe0, 0x00, 0xb9, 0x00, 0x00, 0x49, 0xa7, 0x00, 0x00,
+};
+
+struct vd55g0_patch {
+ const u8 *bin;
+ const uint size;
+ const u16 major;
+ const u16 minor;
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut1 = {
+ .bin = cut1_patch,
+ .size = ARRAY_SIZE(cut1_patch),
+ .major = 2,
+ .minor = 11,
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut2 = {
+ .bin = cut2_patch,
+ .size = ARRAY_SIZE(cut2_patch),
+ .major = 0,
+ .minor = 5,
+};
+
+static const char * const vd55g0_test_pattern_menu[] = {
+ "Disabled",
+ "Solid",
+ "Colorbar",
+ "Gradbar",
+ "Hgrey",
+ "Vgrey",
+ "Dgrey",
+ "PN28",
+};
+
+static const s64 vd55g0_ev_bias_menu[] = {
+ -3000, -2500, -2000, -1500, -1000, -500,
+ 0,
+ 500, 1000, 1500, 2000, 2500, 3000,
+};
+
+/*
+ * On ACPI, the INT3472 registers the rails the platform wired under its own
+ * names; the DT names would get dummy regulators there.
+ */
+static const char * const vd55g0_dt_supply_name[] = {
+ "vcore",
+ "vddio",
+ "vana",
+};
+
+static const char * const vd55g0_acpi_supply_name[] = {
+ "avdd",
+ "dovdd",
+};
+
+static const struct acpi_device_id vd55g0_acpi_ids[] = {
+ { "SMO55F0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, vd55g0_acpi_ids);
+
+#define VD55G0_NUM_SUPPLIES ARRAY_SIZE(vd55g0_dt_supply_name)
+
+enum vd55g0_bin_mode {
+ VD55G0_BIN_MODE_NORMAL,
+ VD55G0_BIN_MODE_DIGITAL_X2,
+};
+
+enum vd55g0_gpio_modes {
+ VD55G0_GPIO_MODE_DISABLED,
+ VD55G0_GPIO_MODE_STROBE,
+};
+
+struct vd55g0_mode_info {
+ u32 width;
+ u32 height;
+ enum vd55g0_bin_mode bin_mode;
+ struct v4l2_rect crop;
+};
+
+struct vd55g0_fmt_desc {
+ u32 code;
+ u8 bpp;
+ u8 data_type;
+};
+
+static const struct vd55g0_fmt_desc vd55g0_supported_codes[] = {
+ {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .bpp = 8,
+ .data_type = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .bpp = 10,
+ .data_type = MIPI_CSI2_DT_RAW10,
+ },
+};
+
+static const struct vd55g0_mode_info vd55g0_mode_data[] = {
+ {
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 0,
+ .top = 0,
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ },
+ },
+ {
+ .width = 640,
+ .height = 600,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 2,
+ .width = 640,
+ .height = 600,
+ },
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ {
+ .width = 320,
+ .height = 240,
+ .bin_mode = VD55G0_BIN_MODE_DIGITAL_X2,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+};
+
+enum vd55g0_expo_state {
+ VD55G0_EXP_AUTO,
+ VD55G0_EXP_FREEZE,
+ VD55G0_EXP_MANUAL
+};
+
+struct vd55g0_dev {
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[VD55G0_NUM_SUPPLIES];
+ unsigned int nb_supplies;
+ struct gpio_desc *reset_gpio;
+ struct clk *xclk;
+ u32 clk_freq;
+ u16 oif_ctrl;
+ u32 mipi_rate;
+ u32 pixel_rate;
+ u64 link_freq;
+ u16 line_length;
+ u16 revision;
+ /* Lock to protect all members below */
+ struct mutex lock;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vflip_ctrl;
+ struct v4l2_ctrl *hflip_ctrl;
+ struct v4l2_ctrl *pattern_ctrl;
+ /* The auto exposure cluster, the auto control first */
+ struct {
+ struct v4l2_ctrl *ae_ctrl;
+ struct v4l2_ctrl *expo_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ struct v4l2_ctrl *dgain_ctrl;
+ };
+ bool streaming;
+ u32 code;
+ const struct vd55g0_mode_info *current_mode;
+ bool hflip;
+ bool vflip;
+ u16 manual_expo;
+ enum vd55g0_expo_state expo_state;
+ u16 digital_gain;
+ u8 analog_gain;
+ u16 vblank;
+ u16 vblank_min;
+ u16 frame_length;
+ bool ae_frozen;
+ u32 pattern;
+ u16 exposure_target;
+ u32 leds[VD55G0_NB_GPIOS];
+ bool flash_en;
+ unsigned int nb_leds;
+ struct {
+ u16 expo;
+ u16 digital_gain;
+ u8 analog_gain;
+ } cold_start;
+};
+
+static inline struct vd55g0_dev *to_vd55g0_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vd55g0_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vd55g0_dev,
+ ctrl_handler)->sd;
+}
+
+static u8 get_bpp_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].bpp;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to 8 bpp\n", code);
+ return 8;
+}
+
+static u8 get_data_type_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].data_type;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type\n",
+ code);
+ return MIPI_CSI2_DT_RAW8;
+}
+
+#define vd55g0_read(sensor, reg, val, err) \
+ cci_read((sensor)->regmap, reg, val, err)
+
+#define vd55g0_write(sensor, reg, val, err) \
+ cci_write((sensor)->regmap, reg, val, err)
+
+static int get_chunk_size(struct vd55g0_dev *sensor)
+{
+ int max_write_len = VD55G0_WRITE_MULTIPLE_CHUNK_MAX;
+ struct i2c_adapter *adapter = sensor->i2c_client->adapter;
+
+ if (adapter->quirks && adapter->quirks->max_write_len)
+ max_write_len = adapter->quirks->max_write_len - 2;
+
+ max_write_len = min(max_write_len, VD55G0_WRITE_MULTIPLE_CHUNK_MAX);
+
+ return max(max_write_len, 1);
+}
+
+static int vd55g0_write_array(struct vd55g0_dev *sensor, u32 reg,
+ unsigned int nb, const u8 *array)
+{
+ const unsigned int chunk_size = get_chunk_size(sensor);
+ unsigned int addr = CCI_REG_ADDR(reg);
+ unsigned int sz;
+ int ret;
+
+ while (nb) {
+ sz = min(nb, chunk_size);
+ ret = regmap_bulk_write(sensor->regmap, addr, array, sz);
+ if (ret < 0)
+ return ret;
+ nb -= sz;
+ addr += sz;
+ array += sz;
+ }
+
+ return 0;
+}
+
+static int vd55g0_poll_reg(struct vd55g0_dev *sensor, u32 reg, u8 poll_val,
+ unsigned int timeout_ms)
+{
+ unsigned int val = 0;
+
+ return regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
+ (val == poll_val), 10 * USEC_PER_MSEC,
+ timeout_ms * USEC_PER_MSEC);
+}
+
+static int vd55g0_wait_state(struct vd55g0_dev *sensor, int state,
+ unsigned int timeout_ms)
+{
+ return vd55g0_poll_reg(sensor, VD55G0_REG_SYSTEM_FSM, state,
+ timeout_ms);
+}
+
+static int vd55g0_prepare_clock_tree(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ u32 pixel_div;
+
+ if (sensor->clk_freq < VD55G0_XCLK_FREQ_MIN ||
+ sensor->clk_freq > VD55G0_XCLK_FREQ_MAX) {
+ dev_err(dev,
+ "Only %luMHz-%luMHz clock range supported. Provided %lu MHz\n",
+ VD55G0_XCLK_FREQ_MIN / HZ_PER_MHZ,
+ VD55G0_XCLK_FREQ_MAX / HZ_PER_MHZ,
+ sensor->clk_freq / HZ_PER_MHZ);
+ return -EINVAL;
+ }
+
+ /*
+ * The MIPI bus is double data rate, and its rate is an integer multiple
+ * of the input clock. The sensor would round any other rate down
+ * (UM2829 13.6), so it is refused rather than used.
+ */
+ if (sensor->link_freq < VD55G0_MIPI_RATE_MIN / 2 ||
+ sensor->link_freq > VD55G0_MIPI_RATE_MAX / 2) {
+ dev_err(dev,
+ "Only %luMbps-%luMbps data rate range supported. Provided link frequency %llu Hz\n",
+ VD55G0_MIPI_RATE_MIN / MEGA, VD55G0_MIPI_RATE_MAX / MEGA,
+ sensor->link_freq);
+ return -EINVAL;
+ }
+ sensor->mipi_rate = sensor->link_freq * 2;
+ if (sensor->mipi_rate % sensor->clk_freq) {
+ dev_err(dev,
+ "Link frequency %llu Hz is not a multiple of half the %u Hz input clock\n",
+ sensor->link_freq, sensor->clk_freq);
+ return -EINVAL;
+ }
+
+ pixel_div = sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST ? 6 : 8;
+ sensor->pixel_rate = sensor->mipi_rate / pixel_div;
+
+ return 0;
+}
+
+static int vd55g0_update_gpio_mode(struct vd55g0_dev *sensor, u32 mode,
+ int gpio)
+{
+ static const u8 index2val[] = {0x01, 0x02};
+
+ return vd55g0_write(sensor, VD55G0_REG_GPIO_0_CTRL + gpio,
+ index2val[mode], NULL);
+}
+
+static int vd55g0_set_gpios_array(struct vd55g0_dev *sensor, u32 *array,
+ int size, enum vd55g0_gpio_modes mode)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < size; i++) {
+ if (array[i] == ~0)
+ break;
+ ret = vd55g0_update_gpio_mode(sensor, mode, array[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vd55g0_apply_exposure_auto(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_expo_state exp = sensor->expo_state;
+
+ if (sensor->ae_frozen && sensor->expo_state == VD55G0_EXP_AUTO)
+ exp = VD55G0_EXP_FREEZE;
+
+ return vd55g0_write(sensor, VD55G0_REG_EXP_MODE, exp, NULL);
+}
+
+static int vd55g0_get_regulators(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ const char * const *names;
+ unsigned int i;
+
+ /* A PRP0001 device has DT names; only the ACPI ID has INT3472's */
+ if (acpi_match_device(vd55g0_acpi_ids, dev)) {
+ names = vd55g0_acpi_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_acpi_supply_name);
+ } else {
+ names = vd55g0_dt_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_dt_supply_name);
+ }
+
+ for (i = 0; i < sensor->nb_supplies; i++)
+ sensor->supplies[i].supply = names[i];
+
+ return devm_regulator_bulk_get(dev, sensor->nb_supplies,
+ sensor->supplies);
+}
+
+static int vd55g0_apply_patgen(struct vd55g0_dev *sensor)
+{
+ static const u8 index2val[] = {
+ 0x0, 0x0, 0x8, 0x10, 0x20, 0x21, 0x22, 0x28
+ };
+ u32 pattern = index2val[sensor->pattern];
+ u32 reg = pattern << VD55G0_PATGEN_TYPE_SHIFT;
+ u8 darkcal = VD55G0_DARKCAL_AUTO;
+ u8 duster = VD55G0_DUSTER_RING_ENABLE | VD55G0_DUSTER_DYN_ENABLE |
+ VD55G0_DUSTER_ENABLE;
+ int ret = 0;
+
+ if (sensor->pattern != 0) {
+ reg |= VD55G0_PATGEN_ENABLE;
+ /*
+ * Take care of dark calibaration and duster to not mess up the
+ * test pattern output.
+ */
+ darkcal = VD55G0_DARKCAL_BYPASS;
+ duster = VD55G0_DUSTER_DISABLE;
+ }
+
+ vd55g0_write(sensor, VD55G0_REG_DARKCAL_CTRL, darkcal, &ret);
+ vd55g0_write(sensor, VD55G0_REG_DUSTER_CTRL, duster, &ret);
+ if (ret)
+ return ret;
+
+ return vd55g0_write(sensor, VD55G0_REG_PATGEN_CTRL, reg, NULL);
+}
+
+static int vd55g0_apply_flash(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_gpio_modes mode = sensor->flash_en ?
+ VD55G0_GPIO_MODE_STROBE :
+ VD55G0_GPIO_MODE_DISABLED;
+
+ return vd55g0_set_gpios_array(sensor, sensor->leds,
+ ARRAY_SIZE(sensor->leds), mode);
+}
+
+/* The helpers below cache a value only once the sensor has taken it */
+static int vd55g0_update_exposure_auto(struct vd55g0_dev *sensor, u32 index)
+{
+ enum vd55g0_expo_state old = sensor->expo_state;
+ int ret;
+
+ switch (index) {
+ case V4L2_EXPOSURE_AUTO:
+ sensor->expo_state = VD55G0_EXP_AUTO;
+ break;
+ case V4L2_EXPOSURE_MANUAL:
+ sensor->expo_state = VD55G0_EXP_MANUAL;
+ break;
+ default:
+ /* Should never happen */
+ return -EINVAL;
+ }
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->expo_state = old;
+
+ return ret;
+}
+
+static int vd55g0_lock_exposure(struct vd55g0_dev *sensor,
+ struct v4l2_ctrl *ctrl)
+{
+ /* Only exposure lock is supported */
+ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
+ bool old = sensor->ae_frozen;
+ int ret;
+
+ sensor->ae_frozen = ae_lock;
+
+ /* Cap control value to reflect the hardware state */
+ ctrl->val = ae_lock;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->ae_frozen = old;
+
+ return ret;
+}
+
+static int vd55g0_apply_framelength(struct vd55g0_dev *sensor)
+{
+ return vd55g0_write(sensor, VD55G0_REG_FRAME_LENGTH,
+ sensor->frame_length, NULL);
+}
+
+static int vd55g0_update_vblank(struct vd55g0_dev *sensor, u16 vblank)
+{
+ u16 old_vblank = sensor->vblank, old_frame_length = sensor->frame_length;
+ int ret;
+
+ sensor->vblank_min = VD55G0_MIN_FRAME_LENGTH -
+ sensor->current_mode->crop.height;
+ sensor->vblank = vblank;
+ sensor->frame_length = sensor->current_mode->crop.height +
+ sensor->vblank;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret) {
+ sensor->vblank = old_vblank;
+ sensor->frame_length = old_frame_length;
+ }
+
+ return ret;
+}
+
+static int vd55g0_update_analog_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->analog_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_digital_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->digital_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure(struct vd55g0_dev *sensor, int expo_ms)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ expo_ms, NULL);
+ if (!ret)
+ sensor->manual_expo = expo_ms;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure_target(struct vd55g0_dev *sensor, int index)
+{
+ /*
+ * Find auto exposure target with: default target exposure * 2^EV,
+ * the default being VD55G0_EXPO_TARGET_DEF at index 6.
+ */
+ static const unsigned int index2exposure_target[] = {
+ 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
+ };
+ unsigned int target = index2exposure_target[index];
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ target, NULL);
+ if (!ret)
+ sensor->exposure_target = target;
+
+ return ret;
+}
+
+static int vd55g0_update_flash(struct vd55g0_dev *sensor, int flash_en)
+{
+ bool old = sensor->flash_en;
+ int ret;
+
+ sensor->flash_en = flash_en;
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ sensor->flash_en = old;
+
+ return ret;
+}
+
+static void vd55g0_apply_reset(struct vd55g0_dev *sensor)
+{
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+}
+
+static void vd55g0_fill_framefmt(struct vd55g0_dev *sensor,
+ const struct vd55g0_mode_info *mode,
+ struct v4l2_mbus_framefmt *fmt, u32 code)
+{
+ fmt->code = code;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int vd55g0_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ const struct vd55g0_mode_info **new_mode)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *mode = vd55g0_mode_data;
+ unsigned int index;
+
+ for (index = 0; index < ARRAY_SIZE(vd55g0_supported_codes); index++) {
+ if (vd55g0_supported_codes[index].code == fmt->code)
+ break;
+ }
+ if (index == ARRAY_SIZE(vd55g0_supported_codes))
+ index = 0;
+
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data), width,
+ height, fmt->width, fmt->height);
+ if (new_mode)
+ *new_mode = mode;
+
+ vd55g0_fill_framefmt(sensor, mode, fmt,
+ vd55g0_supported_codes[index].code);
+
+ return 0;
+}
+
+static int vd55g0_apply_cold_start(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE,
+ sensor->cold_start.expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_ANALOG_GAIN,
+ sensor->cold_start.analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN,
+ sensor->cold_start.digital_gain, &ret);
+
+ return ret;
+}
+
+static int vd55g0_apply_settings(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ sensor->manual_expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ sensor->analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ sensor->digital_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ sensor->exposure_target, &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_cold_start(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_ORIENTATION,
+ sensor->hflip | (sensor->vflip << 1), &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_patgen(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_apply_frame_format(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_rect *crop = &sensor->current_mode->crop;
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_READOUT_CTRL,
+ sensor->current_mode->bin_mode, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_START, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_END,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_START, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_END,
+ crop->top + crop->height - 1, &ret);
+
+ /*
+ * Use the same auto exposure crop as the image crop, performing auto
+ * exposure computation only on image boundaries.
+ */
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_H, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_H,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_V, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_V,
+ crop->top + crop->height - 1, &ret);
+
+ return ret;
+}
+
+static int vd55g0_set_gpios(struct vd55g0_dev *sensor)
+{
+ int ret;
+ unsigned int i;
+
+ /* GPIOs in input (disabled) by default */
+ for (i = 0; i < VD55G0_NB_GPIOS; i++) {
+ ret = vd55g0_update_gpio_mode(sensor,
+ VD55G0_GPIO_MODE_DISABLED, i);
+ if (ret)
+ return ret;
+ }
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_stream_enable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = vd55g0_set_gpios(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ vd55g0_write(sensor, VD55G0_REG_FORMAT_CTRL,
+ get_bpp_by_code(sensor->code), &ret);
+ vd55g0_write(sensor, VD55G0_REG_OIF_IMG_CTRL,
+ get_data_type_by_code(sensor->code), &ret);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_frame_format(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_settings(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_SW_STBY,
+ VD55G0_SW_STBY_START_STREAM, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_SW_STBY, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_STREAMING,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+ return ret;
+}
+
+static void vd55g0_save_exposure(struct vd55g0_dev *sensor)
+{
+ u64 val;
+
+ /* On a read error, keep what the previous stream left */
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &val, NULL))
+ sensor->cold_start.expo = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &val, NULL))
+ sensor->cold_start.digital_gain = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &val, NULL))
+ sensor->cold_start.analog_gain = val;
+}
+
+static void vd55g0_stream_disable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ /* Keep exposure values for next cold start boot */
+ vd55g0_save_exposure(sensor);
+
+ ret = vd55g0_write(sensor, VD55G0_REG_STREAMING,
+ VD55G0_STREAMING_STOP_STREAM, NULL);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_STREAMING, 0, 2000);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+
+err_str_dis:
+ if (ret)
+ dev_err(&client->dev, "failed to stop streaming: %d\n", ret);
+ pm_runtime_put_autosuspend(&client->dev);
+}
+
+static int vd55g0_tx_from_ep(struct vd55g0_dev *sensor,
+ struct fwnode_handle *handle)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct v4l2_fwnode_endpoint *ep;
+ struct v4l2_fwnode_endpoint ep_node = { .bus_type =
+ V4L2_MBUS_CSI2_DPHY
+ };
+ int ret;
+
+ ep = &ep_node;
+ ret = v4l2_fwnode_endpoint_alloc_parse(handle, ep);
+ if (ret)
+ return ret;
+
+ /* The sensor has a single data lane */
+ if (ep->bus.mipi_csi2.num_data_lanes != 1) {
+ dev_err(&client->dev, "only one data lane is supported, found %u\n",
+ ep->bus.mipi_csi2.num_data_lanes);
+ goto error_ep;
+ }
+
+ if (ep->bus.mipi_csi2.clock_lane != 0) {
+ dev_err(&client->dev, "clock lane must be lane 0\n");
+ goto error_ep;
+ }
+
+ /* Only the two polarity bits: the rest of OIF_CTRL is reserved */
+ sensor->oif_ctrl = (ep->bus.mipi_csi2.lane_polarities[0] << 3) |
+ (ep->bus.mipi_csi2.lane_polarities[1] << 6);
+
+ if (ep->nr_of_link_frequencies != 1) {
+ dev_err(&client->dev, "one link frequency is needed, found %u\n",
+ ep->nr_of_link_frequencies);
+ goto error_ep;
+ }
+ sensor->link_freq = ep->link_frequencies[0];
+
+ v4l2_fwnode_endpoint_free(ep);
+
+ return 0;
+
+error_ep:
+ v4l2_fwnode_endpoint_free(ep);
+
+ return -EINVAL;
+}
+
+static int vd55g0_patch(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ const struct vd55g0_patch *patch;
+ u64 patch_version;
+ int ret;
+
+ /* Choose appropriate patch for sensor revision */
+ if (sensor->revision == VD55G0_REVISION_CUT1)
+ patch = &vd55g0_patch_cut1;
+ else if (sensor->revision == VD55G0_REVISION_CUT2)
+ patch = &vd55g0_patch_cut2;
+ else
+ return -EINVAL;
+
+ /* Write patch */
+ ret = vd55g0_write_array(sensor, VD55G0_REG_FWPATCH_START_ADDR,
+ patch->size, patch->bin);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT,
+ VD55G0_BOOT_PATCH_SETUP, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ /* Read back patch version */
+ ret = vd55g0_read(sensor, VD55G0_REG_FWPATCH_REVISION, &patch_version,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (patch_version != (patch->major << 8) + patch->minor) {
+ dev_err(&client->dev,
+ "bad patch version expected %d.%d got %llu.%llu\n",
+ patch->major, patch->minor,
+ patch_version >> 8, patch_version & 0xff);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "patch %llu.%llu applied\n",
+ patch_version >> 8, patch_version & 0xff);
+
+ return 0;
+}
+
+static int vd55g0_boot(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT, VD55G0_BOOT_BOOT, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_configure(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ sensor->line_length = VD55G0_LINE_LENGTH_FAST;
+ if (sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST)
+ sensor->line_length = VD55G0_LINE_LENGTH_SLOW;
+ vd55g0_write(sensor, VD55G0_REG_LINE_LENGTH, sensor->line_length,
+ &ret);
+
+ /*
+ * PLL_PREDIV and PLL_MULT are not accessible. We rely on the firmware
+ * to set the correct multiplier for the clock. Hence we don't do
+ * anything more here.
+ */
+ vd55g0_write(sensor, VD55G0_REG_EXT_CLOCK, sensor->clk_freq, &ret);
+
+ vd55g0_write(sensor, VD55G0_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
+ /* The status lines are not exposed, so do not send them, as vd55g1 */
+ vd55g0_write(sensor, VD55G0_REG_ISL_ENABLE, 0, &ret);
+ vd55g0_write(sensor, VD55G0_REG_CLK_PLL_MIPI, sensor->mipi_rate, &ret);
+
+ return ret;
+}
+
+/* Called with sensor->lock held, which is also the control handler's lock */
+static void vd55g0_grab_stream_ctrls(struct vd55g0_dev *sensor, bool grab)
+{
+ __v4l2_ctrl_grab(sensor->vflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->hflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->pattern_ctrl, grab);
+}
+
+static int vd55g0_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = vd55g0_stream_enable(sensor);
+ if (ret)
+ return ret;
+
+ sensor->streaming = true;
+ vd55g0_grab_stream_ctrls(sensor, true);
+
+ return 0;
+}
+
+static int vd55g0_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Powered down either way, so report success as call_s_stream() does */
+ vd55g0_stream_disable(sensor);
+
+ sensor->streaming = false;
+ vd55g0_grab_stream_ctrls(sensor, false);
+
+ return 0;
+}
+
+static int vd55g0_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ const struct vd55g0_mode_info *mode;
+ const struct v4l2_mbus_framefmt *fmt;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ /* From the state asked about, so that TRY gets its own crop */
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data),
+ width, height,
+ fmt->width, fmt->height);
+ sel->r = mode->crop;
+ return 0;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = VD55G0_WIDTH;
+ sel->r.height = VD55G0_HEIGHT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vd55g0_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ code->code = vd55g0_supported_codes[code->index].code;
+
+ return 0;
+}
+
+static int vd55g0_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *new_mode;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int expo_max, hblank;
+ u16 vblank_def;
+ int ret;
+
+ /* The mode cannot change under a running stream */
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && sensor->streaming)
+ return -EBUSY;
+
+ ret = vd55g0_try_fmt_internal(sd, &format->format, &new_mode);
+ if (ret)
+ return ret;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ *fmt = format->format;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return 0;
+
+ if (sensor->current_mode == new_mode && sensor->code == fmt->code)
+ return 0;
+
+ sensor->code = fmt->code;
+ sensor->current_mode = new_mode;
+
+ /* Reset vblank and framelength to default */
+ vblank_def = VD55G0_FRAME_LENGTH_DEF - new_mode->crop.height;
+ ret = vd55g0_update_vblank(sensor, vblank_def);
+ if (ret)
+ return ret;
+
+ /* The range change may clamp and apply the old vblank; set the default */
+ ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, sensor->vblank_min,
+ 0xffff - new_mode->crop.height, 1,
+ vblank_def);
+ if (ret)
+ return ret;
+ ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank_def);
+ if (ret)
+ return ret;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1,
+ VD55G0_EXPO_DEF);
+ if (ret)
+ return ret;
+ /* Blanking counts from the analogue crop, as in vd55g1 */
+ hblank = sensor->line_length - sensor->current_mode->crop.width;
+ ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
+ hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_s_ctrl(sensor->hblank_ctrl, hblank);
+}
+
+static int vd55g0_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ vd55g0_fill_framefmt(sensor, &vd55g0_mode_data[VD55G0_DEFAULT_MODE],
+ &fmt.format, VD55G0_MEDIA_BUS_FMT_DEF);
+
+ return vd55g0_set_fmt(sd, NULL, sd_state, &fmt);
+}
+
+static int vd55g0_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ unsigned int i;
+
+ if (fse->index >= ARRAY_SIZE(vd55g0_mode_data))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++)
+ if (vd55g0_supported_codes[i].code == fse->code)
+ break;
+ if (i == ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ fse->min_width = vd55g0_mode_data[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = vd55g0_mode_data[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops vd55g0_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops vd55g0_pad_ops = {
+ .enum_mbus_code = vd55g0_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = vd55g0_set_fmt,
+ .enable_streams = vd55g0_enable_streams,
+ .disable_streams = vd55g0_disable_streams,
+ .get_selection = vd55g0_get_selection,
+ .enum_frame_size = vd55g0_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops vd55g0_subdev_ops = {
+ .video = &vd55g0_video_ops,
+ .pad = &vd55g0_pad_ops,
+};
+
+static const struct media_entity_operations vd55g0_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops vd55g0_internal_ops = {
+ .init_state = vd55g0_init_state,
+};
+
+/*
+ * The manual values first, so that a switch to manual starts from them; the
+ * sensor ignores them under auto exposure.
+ */
+static int vd55g0_update_expo_cluster(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ if (sensor->ae_ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ /* A range change under auto exposure did not clamp it */
+ sensor->expo_ctrl->val = min(sensor->expo_ctrl->val,
+ sensor->expo_ctrl->maximum);
+ if (sensor->expo_ctrl->is_new)
+ ret = vd55g0_update_exposure(sensor,
+ sensor->expo_ctrl->val);
+ if (!ret && sensor->again_ctrl->is_new)
+ ret = vd55g0_update_analog_gain(sensor,
+ sensor->again_ctrl->val);
+ if (!ret && sensor->dgain_ctrl->is_new)
+ ret = vd55g0_update_digital_gain(sensor,
+ sensor->dgain_ctrl->val);
+ if (ret)
+ return ret;
+ }
+
+ if (sensor->ae_ctrl->is_new)
+ ret = vd55g0_update_exposure_auto(sensor, sensor->ae_ctrl->val);
+
+ return ret;
+}
+
+/* Under auto exposure, the manual controls read what the sensor applies */
+static int vd55g0_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(ctrl_to_sd(ctrl));
+ struct device *dev = &sensor->i2c_client->dev;
+ u64 expo, again, dgain;
+ int ret = 0;
+
+ if (ctrl->id != V4L2_CID_EXPOSURE_AUTO)
+ return -EINVAL;
+
+ /* Only a powered sensor has applied values; keep the last ones */
+ if (!pm_runtime_get_if_in_use(dev))
+ return 0;
+
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &expo, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &again, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret);
+ if (!ret) {
+ sensor->expo_ctrl->val = expo;
+ sensor->again_ctrl->val = again;
+ sensor->dgain_ctrl->val = dgain;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int vd55g0_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ unsigned int expo_max;
+ u16 old_vblank;
+ int ret;
+
+ /* Read-only controls are set by the driver, there is nothing to apply */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ break;
+ }
+ if (ctrl->id == V4L2_CID_VFLIP)
+ sensor->vflip = ctrl->val;
+ if (ctrl->id == V4L2_CID_HFLIP)
+ sensor->hflip = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ /* Can't be done while streaming because of duster disabling */
+ sensor->pattern = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = vd55g0_update_expo_cluster(sensor);
+ break;
+ case V4L2_CID_3A_LOCK:
+ ret = vd55g0_lock_exposure(sensor, ctrl);
+ break;
+ case V4L2_CID_VBLANK:
+ old_vblank = sensor->vblank;
+ ret = vd55g0_update_vblank(sensor, ctrl->val);
+ if (ret)
+ break;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max,
+ 1, VD55G0_EXPO_DEF);
+ /* A clamped exposure failed to apply: put the frame back */
+ if (ret)
+ vd55g0_update_vblank(sensor, old_vblank);
+ break;
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ /*
+ * We use auto exposure target percentage register to control
+ * exposure bias for more precision.
+ */
+ ret = vd55g0_update_exposure_target(sensor, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_LED_MODE:
+ ret = vd55g0_update_flash(sensor, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vd55g0_ctrl_ops = {
+ .g_volatile_ctrl = vd55g0_g_volatile_ctrl,
+ .s_ctrl = vd55g0_s_ctrl,
+};
+
+static int vd55g0_init_controls(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &vd55g0_ctrl_ops;
+ struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
+ const struct vd55g0_mode_info *cur_mode = sensor->current_mode;
+ struct v4l2_fwnode_device_properties fwnode_props;
+ struct v4l2_ctrl *ctrl;
+ unsigned int patgen_size = ARRAY_SIZE(vd55g0_test_pattern_menu) - 1;
+ unsigned int hblank = sensor->line_length - cur_mode->crop.width;
+ unsigned int expo_mode = sensor->expo_state == VD55G0_EXP_AUTO ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 16);
+ /* we can use our own mutex for the ctrl lock */
+ hdl->lock = &sensor->lock;
+ v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) - 1,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) / 2,
+ vd55g0_ev_bias_menu);
+ ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
+ 0, 0, &sensor->link_freq);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, sensor->pixel_rate,
+ sensor->pixel_rate, 1, sensor->pixel_rate);
+ sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO, 1,
+ ~0x3, expo_mode);
+ sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ 0, 24, 1, sensor->analog_gain);
+ sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+ 256, 2048, 1,
+ sensor->digital_gain);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, 1, 0, 0);
+ /* Only when the firmware describes the LED GPIOs */
+ if (sensor->nb_leds)
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_FLASH, ~0x7,
+ sensor->flash_en);
+
+ /*
+ * Keep a pointer to these controls as we need to update them when
+ * setting the format
+ */
+ sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ sensor->vblank_min,
+ 0xffff - cur_mode->crop.height,
+ 1, sensor->vblank);
+ sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ hblank, hblank, 1, hblank);
+ if (sensor->hblank_ctrl)
+ sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
+ 0, 1, 1, sensor->vflip);
+ sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
+ 0, 1, 1, sensor->hflip);
+ sensor->pattern_ctrl =
+ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+ patgen_size, 0, 0,
+ vd55g0_test_pattern_menu);
+ sensor->expo_ctrl =
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
+ sensor->frame_length - VD55G0_EXPO_MAX_TERM,
+ 1, sensor->manual_expo);
+ if (!hdl->error)
+ v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_ctrls;
+ }
+
+ sensor->sd.ctrl_handler = hdl;
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+}
+
+static int vd55g0_detect(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ u64 revision, id = 0;
+ int ret;
+
+ ret = vd55g0_read(sensor, VD55G0_REG_MODEL_ID, &id, NULL);
+ if (ret)
+ return ret;
+
+ if (id != VD55G0_MODEL_ID) {
+ dev_warn(&client->dev, "Unsupported sensor id %llx\n", id);
+ return -ENODEV;
+ }
+
+ ret = vd55g0_read(sensor, VD55G0_REG_REVISION, &revision, NULL);
+ if (ret)
+ return ret;
+ dev_dbg(&client->dev, "Sensor revision 0x%llx\n", revision);
+
+ if (revision != VD55G0_REVISION_CUT1 &&
+ revision != VD55G0_REVISION_CUT2) {
+ dev_err(&client->dev, "Unsupported device revision 0x%llx\n",
+ revision);
+ return -ENODEV;
+ }
+
+ sensor->revision = revision;
+
+ return 0;
+}
+
+/* Power/clock management functions */
+static int vd55g0_power_on(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(sensor->nb_supplies, sensor->supplies);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable regulators %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sensor->xclk);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable clock %d\n", ret);
+ goto disable_bulk;
+ }
+
+ if (sensor->reset_gpio)
+ vd55g0_apply_reset(sensor);
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_READY_TO_BOOT,
+ VD55G0_TIMEOUT_MS);
+ if (ret) {
+ dev_err(&client->dev, "sensor reset failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_detect(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor detect failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_patch(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor patch failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_boot(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor boot failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_configure(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor configuration failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ return 0;
+
+disable_clock:
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+disable_bulk:
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+
+ return ret;
+}
+
+static int vd55g0_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Hold the sensor in reset while its rails are off, as vd55g1 does */
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios_array(struct vd55g0_dev *sensor,
+ char *prop_name, u32 *array, int *nb)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ unsigned int i;
+ int ret;
+
+ *nb = device_property_count_u32(&client->dev, prop_name);
+ if (*nb == -EINVAL) {
+ /* Property not found */
+ *nb = 0;
+ return 0;
+ }
+ if (*nb < 0)
+ return *nb;
+ if (*nb > VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "%s lists %d GPIOs, the sensor has %d\n",
+ prop_name, *nb, VD55G0_NB_GPIOS);
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32_array(&client->dev, prop_name, array,
+ *nb);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read %s prop\n", prop_name);
+ return ret;
+ }
+
+ for (i = 0; i < *nb; i++) {
+ if (array[i] >= VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "invalid GPIO %d for leds\n",
+ array[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios(struct vd55g0_dev *sensor)
+{
+ int nb_gpios_leds;
+ int ret;
+
+ memset(sensor->leds, ~0, sizeof(sensor->leds));
+
+ ret = vd55g0_parse_dt_gpios_array(sensor, "st,leds", sensor->leds,
+ &nb_gpios_leds);
+ if (ret)
+ return ret;
+
+ sensor->nb_leds = nb_gpios_leds;
+
+ return 0;
+}
+
+static int vd55g0_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct fwnode_handle *handle;
+ struct vd55g0_dev *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->analog_gain = 0;
+ sensor->digital_gain = 256;
+
+ sensor->i2c_client = client;
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->regmap),
+ "failed to init CCI\n");
+ sensor->streaming = false;
+ /* Must match what vd55g0_init_state() puts in the subdev state */
+ sensor->code = VD55G0_MEDIA_BUS_FMT_DEF;
+ sensor->manual_expo = VD55G0_EXPO_DEF;
+ sensor->vflip = false;
+ sensor->hflip = false;
+ sensor->flash_en = false;
+ /* Written at every stream start, so it must match the control default */
+ sensor->exposure_target = VD55G0_EXPO_TARGET_DEF;
+ sensor->cold_start.expo = 0x32;
+ sensor->cold_start.digital_gain = 256;
+ sensor->cold_start.analog_gain = 0;
+
+ sensor->current_mode = &vd55g0_mode_data[VD55G0_DEFAULT_MODE];
+
+ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+
+ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+ "Failed to get reset gpio\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_parse_dt_gpios(sensor);
+ if (ret) {
+ dev_err(dev, "Failed to get gpios\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_tx_from_ep(sensor, handle);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to parse endpoint\n");
+ goto error_put_handle;
+ }
+ fwnode_handle_put(handle);
+
+ sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
+ if (IS_ERR(sensor->xclk))
+ return dev_err_probe(dev, PTR_ERR(sensor->xclk),
+ "failed to get xclk\n");
+
+ sensor->clk_freq = clk_get_rate(sensor->xclk);
+ ret = vd55g0_prepare_clock_tree(sensor);
+ if (ret)
+ return ret;
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g0_subdev_ops);
+ sensor->sd.internal_ops = &vd55g0_internal_ops;
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.ops = &vd55g0_subdev_entity_ops;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = vd55g0_get_regulators(sensor);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
+
+ /* Power on once to check that the sensor answers */
+ ret = vd55g0_power_on(dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&sensor->lock);
+
+ ret = vd55g0_update_vblank(sensor, VD55G0_FRAME_LENGTH_DEF -
+ sensor->current_mode->crop.height);
+ if (ret)
+ goto error_power_off;
+
+ ret = vd55g0_init_controls(sensor);
+ if (ret) {
+ dev_err(&client->dev, "controls initialization failed %d\n",
+ ret);
+ goto error_power_off;
+ }
+
+ sensor->sd.state_lock = &sensor->lock;
+
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret) {
+ dev_err(&client->dev, "pads init failed %d\n", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "subdev init error %d\n", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "async subdev register failed %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ /* Runtime PM may power the sensor down only from here on */
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_idle(dev);
+
+ dev_dbg(&client->dev, "vd55g0 probe successfully\n");
+
+ return 0;
+
+error_put_handle:
+ fwnode_handle_put(handle);
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ v4l2_subdev_cleanup(&sensor->sd);
+error_media_entity:
+ media_entity_cleanup(&sensor->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+error_power_off:
+ mutex_destroy(&sensor->lock);
+ vd55g0_power_off(dev);
+
+ return ret;
+}
+
+static void vd55g0_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_subdev_cleanup(&sensor->sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+ mutex_destroy(&sensor->lock);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ vd55g0_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
+}
+
+static const struct of_device_id vd55g0_dt_ids[] = {
+ { .compatible = "st,vd55g0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vd55g0_dt_ids);
+
+static const struct dev_pm_ops vd55g0_pm_ops = {
+ SET_RUNTIME_PM_OPS(vd55g0_power_off, vd55g0_power_on, NULL)
+};
+
+static struct i2c_driver vd55g0_i2c_driver = {
+ .driver = {
+ .name = "vd55g0",
+ .of_match_table = vd55g0_dt_ids,
+ .acpi_match_table = vd55g0_acpi_ids,
+ .pm = &vd55g0_pm_ops,
+ },
+ .probe = vd55g0_probe,
+ .remove = vd55g0_remove,
+};
+
+module_i2c_driver(vd55g0_i2c_driver);
+
+MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>");
+MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>");
+MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>");
+MODULE_AUTHOR("Sergey Lebedev <lsa.uz@pm.me>");
+MODULE_DESCRIPTION("VD55G0 camera subdev driver");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/4] media: ipu-bridge: Add ST VD55G0
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
2026-10-19 16:27 ` [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
@ 2026-10-19 16:28 ` Sergey Lebedev
2026-10-19 16:28 ` [PATCH 4/4] media: ipu-bridge: Add the Surface Pro 11 infrared illuminator Sergey Lebedev
3 siblings, 0 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:28 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
The infrared camera of the Surface Pro 11 with Intel is an ST VD55G0,
enumerated by ACPI as SMO55F0 and wired to the IPU7 over one CSI-2 data
lane.
The vd55g0 driver takes only a link frequency whose MIPI rate, twice it,
is an integer multiple of the input clock. 396 MHz gives 792 Mbps, 33
times the 24 MHz the sensor's SSDB lists on the Surface Pro 8, 9, 10 and
11, and within the sensor's 750 to 1200 Mbps. Peter Marshall's earlier
patch for the Surface Pro 9 used 380 MHz, 760 Mbps, which is not a
multiple of 24 MHz, so the driver would refuse it:
https://lore.kernel.org/all/20260918221705.323510-4-pm@petermarshall.ca/
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
drivers/media/pci/intel/ipu-bridge.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 3739c4a2a8c..cb1d1582fa1 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -120,6 +120,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+ /* STMicroelectronics VD55G0 */
+ IPU_SENSOR_CONFIG("SMO55F0", 1, 396000000),
/* Sony IMX471 */
IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
/* Sony IMX471 (found on Lenovo X1 Carbon G14) */
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/4] media: ipu-bridge: Add the Surface Pro 11 infrared illuminator
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
` (2 preceding siblings ...)
2026-10-19 16:28 ` [PATCH 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
@ 2026-10-19 16:28 ` Sergey Lebedev
3 siblings, 0 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:28 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
On the Surface Pro 11 with Intel the infrared illuminator is driven by the
VD55G0's GPIO 1, which strobes it during the exposure. The ACPI tables do
not describe it, so the sensor driver finds no st,leds and offers no
V4L2_CID_FLASH_LED_MODE.
Give the sensor's software node st,leds from a DMI match, as the
upside-down quirk gives a rotation. dev_properties gains an entry for it,
as a sensor can also have a lens-focus reference.
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
drivers/media/pci/intel/ipu-bridge.c | 60 +++++++++++++++++++++++++++-
include/media/ipu-bridge.h | 2 +-
2 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index cb1d1582fa1..05aa6d92bbf 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -283,6 +283,39 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
{} /* Terminating entry */
};
+/*
+ * Sensor GPIOs that drive an illuminator, for machines whose ACPI tables do
+ * not say which. They reach the sensor driver as its own leds property.
+ */
+struct ipu_sensor_leds {
+ const char *hid;
+ const char *property;
+ const u32 *gpios;
+ unsigned int nr_gpios;
+};
+
+/* The infrared illuminator, strobed by the VD55G0's GPIO 1 */
+static const u32 surface_pro_11_ir_gpios[] = { 1 };
+
+static const struct ipu_sensor_leds surface_pro_11_ir_leds = {
+ .hid = "SMO55F0",
+ .property = "st,leds",
+ .gpios = surface_pro_11_ir_gpios,
+ .nr_gpios = ARRAY_SIZE(surface_pro_11_ir_gpios),
+};
+
+static const struct dmi_system_id sensor_leds_dmi_ids[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME,
+ "Surface Pro for Business 11th Edition with Intel"),
+ },
+ .driver_data = (void *)&surface_pro_11_ir_leds,
+ },
+ {} /* Terminating entry */
+};
+
static const struct ipu_property_names prop_names = {
.clock_frequency = "clock-frequency",
.rotation = "rotation",
@@ -563,6 +596,22 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
}
EXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, "INTEL_IPU_BRIDGE");
+static const struct ipu_sensor_leds *
+ipu_bridge_get_leds(const struct ipu_sensor_config *cfg)
+{
+ const struct dmi_system_id *dmi_id;
+ const struct ipu_sensor_leds *leds;
+
+ for (dmi_id = dmi_first_match(sensor_leds_dmi_ids); dmi_id;
+ dmi_id = dmi_first_match(dmi_id + 1)) {
+ leds = dmi_id->driver_data;
+ if (!strcmp(cfg->hid, leds->hid))
+ return leds;
+ }
+
+ return NULL;
+}
+
static void ipu_bridge_create_fwnode_properties(
struct ipu_sensor *sensor,
struct ipu_bridge *bridge,
@@ -570,7 +619,8 @@ static void ipu_bridge_create_fwnode_properties(
{
struct ipu_property_names *names = &sensor->prop_names;
struct software_node *nodes = sensor->swnodes;
- unsigned int i = 0;
+ const struct ipu_sensor_leds *leds;
+ unsigned int i = 0, d = 3;
sensor->prop_names = prop_names;
@@ -624,10 +674,16 @@ static void ipu_bridge_create_fwnode_properties(
if (sensor->vcm_type) {
sensor->vcm_ref[0] =
SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]);
- sensor->dev_properties[3] =
+ sensor->dev_properties[d++] =
PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
}
+ leds = ipu_bridge_get_leds(cfg);
+ if (leds)
+ sensor->dev_properties[d++] =
+ PROPERTY_ENTRY_U32_ARRAY_LEN(leds->property, leds->gpios,
+ leds->nr_gpios);
+
sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_BUS_TYPE)] =
PROPERTY_ENTRY_U32(names->bus_type,
V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
index 3ef94c2b767..f56ac1c4e96 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -179,7 +179,7 @@ struct ipu_sensor {
struct ipu_property_names prop_names;
struct property_entry ep_properties[IPU_BRIDGE_EP_NUM_ENTRIES];
- struct property_entry dev_properties[5];
+ struct property_entry dev_properties[6];
struct property_entry ipu_properties[3];
struct property_entry ivsc_properties[1];
struct property_entry ivsc_sensor_ep_properties[4];
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-19 16:27 ` [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
@ 2026-10-19 18:24 ` George Neacsu
2026-10-19 19:34 ` Sergey Lebedev
0 siblings, 1 reply; 11+ messages in thread
From: George Neacsu @ 2026-10-19 18:24 UTC (permalink / raw)
To: Sergey Lebedev
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, Hao Yao,
linux-media, devicetree, linux-kernel
On Mon, Oct 19, 2026 at 04:27:50PM +0000, Sergey Lebedev wrote:
> The clock tree is computed from the input clock and the one link frequency
> the endpoint gives, as in vd55g1. The MIPI rate must be an integer multiple
> of the input clock, or the sensor would run at another one, so any other
> link frequency fails probe.
Thank you for posting this. I tested v1 on the Snapdragon Surface Pro 11
(X1E80100), where the endpoint is 420 MHz link frequency (840 Mbps) with
a 19.2 MHz xclk.
As posted, patch 2/4 fails probe at the integer-multiple check because
840 MHz is not an integer multiple of 19.2 MHz.
For a test only, I removed that rejection and left the rest of the driver
unchanged. The sensor then probes, the CAMSS media graph completes, and
capture delivers one complete 644x604 Y10 frame. A continuous five-frame
capture then times out waiting for frame 2.
The 1200-pixel line length below 900 Mbps is already present in this
standalone driver, so this is not the earlier line-length issue. I also
tried the ST one-lane OIF_CTRL value 0x81; the behaviour was unchanged.
For comparison, Peter's generalized vd55g1 series, with the 1200-pixel
line-length fix, streams continuously on this same machine with the same
19.2 MHz xclk and nominal 840 Mbps endpoint setting. Whether the sensor
internally rounds that rate is a separate question, but rejecting the
configuration outright prevents this Snapdragon model from probing.
There also appears to be a second difference to narrow down after the
first frame. I'm continuing to bisect that and will report the exact
cause/delta once I have it, so I can't give this a Tested-by yet.
Best regards,
George
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-19 18:24 ` George Neacsu
@ 2026-10-19 19:34 ` Sergey Lebedev
2026-10-20 6:44 ` George Neacsu
0 siblings, 1 reply; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-19 19:34 UTC (permalink / raw)
To: George Neacsu
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, Hao Yao,
linux-media, devicetree, linux-kernel
Hi George,
Thank you for trying it on the Snapdragon model.
On 19/10/2026 19:24, George Neacsu wrote:
> As posted, patch 2/4 fails probe at the integer-multiple check because
> 840 MHz is not an integer multiple of 19.2 MHz.
That refusal is deliberate. The MIPI rate is an integer multiple of the
input clock, and the sensor rounds a request down (UM2829 13.6): asked
for 840 Mbps from 19.2 MHz, it runs at 825.6 while the receiver is set
up for 840. camera-sensor.rst asks a sensor driver to use only the link
frequencies the firmware gives, so the driver refuses one it cannot
produce. From 19.2 MHz, 403.2 MHz (806.4 Mbps, 42 times the clock) and
384 MHz (768 Mbps, 40 times) are exact, so the change is one value in
your board's DT, in the sensor endpoint:
link-frequencies = /bits/ 64 <403200000>;
On the Intel model I ran it at 403.2 MHz with a 19.2 MHz clock, and every
mode ran at 60.22 fps, as the controls give.
> For a test only, I removed that rejection and left the rest of the driver
> unchanged. The sensor then probes, the CAMSS media graph completes, and
> capture delivers one complete 644x604 Y10 frame. A continuous five-frame
> capture then times out waiting for frame 2.
With the check removed, the sensor runs at 825.6 Mbps against a receiver
set up for 840, and the driver reports a pixel rate of 140 MHz where it
is 137.6. I do not know whether CAMSS minds, but a run at 403.2 MHz with
the driver as posted would take that out of the question. If it still
stops after one frame, I would be glad to compare what the two drivers
write at stream start.
Sergey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-19 19:34 ` Sergey Lebedev
@ 2026-10-20 6:44 ` George Neacsu
2026-10-20 8:52 ` Sergey Lebedev
0 siblings, 1 reply; 11+ messages in thread
From: George Neacsu @ 2026-10-20 6:44 UTC (permalink / raw)
To: Sergey Lebedev
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, Hao Yao,
linux-media, devicetree, linux-kernel
On Mon, Oct 19, 2026 at 07:34:10PM +0000, Sergey Lebedev wrote:
> From 19.2 MHz, 403.2 MHz (806.4 Mbps, 42 times the clock) and
> 384 MHz (768 Mbps, 40 times) are exact, so the change is one value in
> your board's DT, in the sensor endpoint:
>
> link-frequencies = /bits/ 64 <403200000>;
>
> If it still stops after one frame, I would be glad to compare what the
> two drivers write at stream start.
That took the clock mismatch out of the experiment. With 403.2 MHz in
the DT and your v1 otherwise as posted, the sensor probes cleanly and
reports link_frequency=403200000 and pixel_rate=134400000, but capture
still delivers exactly one 644x604 Y10 frame and then times out waiting
for frame 2.
I then went back to the earlier VD55G0 A/B tests on this same Snapdragon
machine. One result was particularly useful: the 1200-pixel line length
alone still stalled, and omitting the 0x0329 write with the old line
length still stalled, but 1200 plus leaving 0x0329 untouched streamed
continuously. Your driver already has the 1200-pixel line length, but it
also has:
vd55g0_write(sensor, VD55G0_REG_ISL_ENABLE, 0, &ret);
I rebuilt v1 with only that single write omitted, keeping the 403.2 MHz
DT setting and everything else unchanged. That fixes continuous capture
here:
- 5/5 frames in the initial Y10 644x604 capture
- 120/120 frames in a continuous run at about 60.2 fps
- immediate stream restart: 10/10 frames
- v4l2-compliance on the sensor subdev: 46/46, 0 failures, 0 warnings
There were no new CAMSS/CSID/VFE errors during the runs.
So the link-frequency correction is needed, but the one-frame stall is a
separate issue caused here by clearing VD55G0_REG_ISL_ENABLE (0x0329).
I have not tried to infer yet why clearing it prevents subsequent frames
on this sensor, but the A/B is repeatable and agrees with the older
VD55G0 results on this machine.
Would you be comfortable dropping that write in the next revision? I can
retest the revision on the Snapdragon model and add a Tested-by once it
is clean.
Best regards,
George
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
@ 2026-10-20 8:27 ` Krzysztof Kozlowski
2026-10-20 9:08 ` Sergey Lebedev
0 siblings, 1 reply; 11+ messages in thread
From: Krzysztof Kozlowski @ 2026-10-20 8:27 UTC (permalink / raw)
To: Sergey Lebedev
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, linux-media, devicetree, linux-kernel
On Mon, Oct 19, 2026 at 04:27:38PM +0000, Sergey Lebedev wrote:
> The VD55G0 is a monochrome global shutter sensor with a 644x604 active
> array, sending over a single MIPI CSI-2 lane.
>
> Merged from ST's own binding in STMicroelectronics/vd55g0-linux-driver,
> which gives the supply voltages, and st,vd55g1.yaml, whose conventions and
> property list it follows. ST's st,out-sync and st,in-sync are left out, as
> in vd55g1. Link frequencies run from 375 to 600 MHz, for the sensor's MIPI
> rate of 750 to 1200 Mbps (UM2829, 13.1).
>
> Co-developed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
> Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
> Assisted-by: Claude:claude-opus-5-5
> Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
> ---
> The properties follow st,vd55g1.yaml nearly line for line. If a
> compatible in that file is preferred, with an if/then for the
> link-frequency minimum, I can fold it in there.
https://lore.kernel.org/all/20260902204549.657228-3-pm@petermarshall.ca/
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-20 6:44 ` George Neacsu
@ 2026-10-20 8:52 ` Sergey Lebedev
0 siblings, 0 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-20 8:52 UTC (permalink / raw)
To: George Neacsu
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, Hao Yao,
linux-media, devicetree, linux-kernel
Hi George,
On 20/10/2026 07:44, George Neacsu wrote:
> Would you be comfortable dropping that write in the next revision? I can
> retest the revision on the Snapdragon model and add a Tested-by once it
> is clean.
Yes, I will drop that write in v2 and leave 0x0329 at its default, and
Cc you on it. Thank you for narrowing it down to one line.
On the Intel model it makes no difference. In one boot, with the sensor
held powered and 0x0329 set to 1, then 0, then 1 again, each state
streamed 150 of 150 frames at 59.14 fps and restarted 10 of 10 times.
The test-pattern frames showed no shift or extra lines in any state, so
IPU7 does not put the status lines into the buffer, and its error
count did not change. The full test run without the write
matches the run with it, v4l2-compliance 46 of 46 included.
One thing I cannot explain: on 16/10 your vd55g1 build streamed 2/2
with 0x0329 = 0 written once per power-on, as v1 does it. So the write
alone may not be what stops CAMSS; something else in v1 may take part.
Leaving the register alone is simpler either way.
Sergey
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor
2026-10-20 8:27 ` Krzysztof Kozlowski
@ 2026-10-20 9:08 ` Sergey Lebedev
0 siblings, 0 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-10-20 9:08 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot,
Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, linux-media, devicetree, linux-kernel
Hi Krzysztof,
On 20/10/2026 10:27, Krzysztof Kozlowski wrote:
>> The properties follow st,vd55g1.yaml nearly line for line. If a
>> compatible in that file is preferred, with an if/then for the
>> link-frequency minimum, I can fold it in there.
>
> https://lore.kernel.org/all/20260902204549.657228-3-pm@petermarshall.ca/
Thank you. I read it as: no new file, the compatible goes into
st,vd55g1.yaml. So in v2, 1/4 becomes one patch to st,vd55g1.yaml that:
- adds st,vd55g0 to the compatible enum;
- moves allOf below required, together with an if/then that sets the
375 MHz link-frequency minimum for st,vd55g0, as in Peter's patches
squashed per your review of his v2;
- says in the description that the VD55G0 array is 644x604 and that
its I2C address comes from its NVM rather than being fixed at 0x10.
If you meant something else, please say so.
Sergey
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-20 9:08 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
2026-10-20 8:27 ` Krzysztof Kozlowski
2026-10-20 9:08 ` Sergey Lebedev
2026-10-19 16:27 ` [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
2026-10-19 18:24 ` George Neacsu
2026-10-19 19:34 ` Sergey Lebedev
2026-10-20 6:44 ` George Neacsu
2026-10-20 8:52 ` Sergey Lebedev
2026-10-19 16:28 ` [PATCH 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-10-19 16:28 ` [PATCH 4/4] media: ipu-bridge: Add the Surface Pro 11 infrared illuminator Sergey Lebedev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®