* [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor
@ 2026-11-10 19:54 Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 1/6] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:54 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, 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) and of the Surface Pro 9 (Alder Lake, IPU6), ACPI
device SMO55F0. Without a driver the camera does not appear at all.
1/6 dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
2/6 media: i2c: Add ST VD55G0 global shutter sensor driver
3/6 platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana"
4/6 media: ipu-bridge: Add ST VD55G0
5/6 media: ipu-bridge: Index the sensor device properties as the endpoint ones
6/6 media: ipu-bridge: Add the Surface Pro 9 and 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
2/6 carries Benjamin's Co-developed-by, and its clock tree is based on
his.
Changes in v4
=============
- 2/6, Sakari's review:
- the controls hold the settings, applied with
__v4l2_ctrl_handler_setup() at stream start and written by s_ctrl
only while the sensor is powered, as in vd55g1;
- the format and crop live in the subdev state, and
media_entity_is_streaming() replaces the driver's own flag;
- one supply list for DT and ACPI; the ACPI table moves to the end;
- and the smaller points.
- 2/6 also: the control handler's lock is the state lock, as in vd55g1;
hflip and vflip form a cluster; AE_TARGET_PERCENTAGE is written as the
16-bit register it is, and LINE_LENGTH at stream start, from the crop
and HBLANK; no pm_runtime_dont_use_autosuspend(), which the core now
does, as in Joshua Crofts's patch for vd55g1.
- 2/6 keeps the bias on AE_TARGET_PERCENTAGE: AE_COMPENSATION sends the
AE to its maximum for any negative value here (Benjamin, on v3):
https://lore.kernel.org/all/20261110194620.46374-1-lsa.uz@pm.me/
- 3/6 is new: int3472 gives SMO55F0's two supplies the driver's names.
- 5/6 is new: dev_properties indexed as ep_properties (Sakari); 6/6
takes its entry that way.
- 1/6 does not change, and carries Krzysztof's Reviewed-by. One file
stays, since the two sensors' bindings are the same (Krzysztof, on v3).
- Fernando's Tested-by stays on 4/6, which does not change, and is off
6/6, which does.
v3: https://lore.kernel.org/all/20261109093353.3757-1-lsa.uz@pm.me/
On the supply names in 3/6: which rail each of the two pins feeds is not
documented. Measuring it here does not tell either: with the power
enable alone, or type 0x10 alone, the sensor never answered on I2C (0
of 6 each), and with both it always did (6 of 6). Type 0x10 maps to
vana because Intel's iactrllogic64.sys calls it Avdd; the power enable
takes vddio, and VCORE stays a dummy. The driver enables all three
together, so the names do not change what the sensor gets. Hans, Hao,
Dan, D. Manresa: if you know what these two pins feed on this machine, I
will change 3/6 to match.
3/6 matches SMO55F0, which is also the Surface Pro 9's IR camera, and v4
has not run there. Fernando, could you try it? If its INT3472 uses other
pins for the sensor, 3/6 needs them too.
On both machines the sensor needs Hao Yao's int3472 fix, which sets the
24 MHz clock the SSDB asks for; without it the sensor runs at four
fifths of every rate this series computes. Its v2 fixes the Surface Pro
11; on the Surface Pro 9 the _DSM wants 0 rather than 3 (Fernando):
https://lore.kernel.org/all/20251208035122.2147678-1-hao.yao@intel.com/
Tested with v4 on the Surface Pro 11 for Business, on v7.4-rc1 with Hao
Yao's int3472 v2, KASAN and lockdep on, IPU7 run by the ipu6 driver, in
four boots:
- 3/6: INT3472 registers vana and vddio for the sensor and nothing else
changes; vcore is a dummy;
- probe defers until ipu-bridge has made the endpoint, then binds; the
sensor suspends about 1 s after probe, with no power-off inside it;
- probe forced to fail at registration: no clock or regulator warning.
As a control, the same failure after a runtime suspend inside probe
gives three warnings per probe;
- every mode runs at 59.14 fps, in Y8 and Y10 at all four sizes and in
Y10 packed at full size;
- read back over I2C: the controls libcamera leaves alone reach the
sensor at stream start when set with it off, and during a stream at
once; the 3A lock freezes auto exposure and holds across manual mode;
an active format change while streaming gets -EBUSY;
- under auto exposure, exposure and the gains read back what the sensor
applies, and after the stream stops, what it last applied;
- the illuminator through led_mode, and the privacy LED lit while
streaming;
- three unload and reload cycles, and ten runtime PM cycles;
- s2idle with the camera idle, and in the middle of a stream, which the
ISYS refuses: the sensor gets no callback and the stream goes on;
- v4l2-compliance 46 of 46, and 54 of 54 through the media device, with
two warnings for the read-only crop;
- no KASAN, lockdep or Oops report; the only other WARNING is the ipu6
driver's lockdep_assert_held() at each stream start;
- after the last change, no pm_runtime_dont_use_autosuspend(), a fifth
boot repeated the probe, the forced failures, the unload cycles and
compliance, with the same results;
- checkpatch --strict, W=1 and sparse clean on x86_64, i386,
allmodconfig and without CONFIG_PM; smatch clean; coccicheck clean
but for three scripts this spatch cannot run; each patch builds on its
own; dt_binding_check clean, and it refuses a VD55G0 example at 125
MHz.
5/6's lens-focus entry did not run: this machine's rear camera has a
VCM type ipu-bridge does not know. The Surface Pro 9 entry in 6/6 was
tested by Fernando on v2, not by me.
On the illuminator in 6/6: it stops lighting past about 70% of the
frame, and in the dark the AE runs past that, to exposure 1796 with both
gains at maximum. v4, as vd55g1, keeps that as the next stream's AE cold
start, so every later stream starts on a black frame and stays there
until the driver is bound again. On a white sheet the AE settles at 51
lines from the default cold start, and from 1796 with no gain, but never
leaves 1796 with full gains. A MAX_EXPOSURE_LINES cap while the strobe
is on should avoid both; I would send it on top of this series.
Benjamin, or should the cold start not carry over until then?
Tool use, per Documentation/process/generated-content.rst (Assisted-by
tags in commits were dropped in 7.4-rc1):
* My contributions to this series were prepared with Claude Code
(claude-opus-5-5); work by others in it is their own.
* This replaces any Assisted-by tags in earlier versions.
Sergey Lebedev (6):
dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
media: i2c: Add ST VD55G0 global shutter sensor driver
platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana"
media: ipu-bridge: Add ST VD55G0
media: ipu-bridge: Index the sensor device properties as the endpoint
ones
media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator
.../bindings/media/i2c/st,vd55g1.yaml | 27 +-
MAINTAINERS | 9 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2232 +++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 85 +-
drivers/platform/x86/intel/int3472/discrete.c | 25 +
include/media/ipu-bridge.h | 16 +-
8 files changed, 2390 insertions(+), 16 deletions(-)
create mode 100644 drivers/media/i2c/vd55g0.c
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 1/6] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 2/6] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, devicetree, linux-kernel
The VD55G0 is a monochrome global shutter sensor with a 644x604 active
array, sending over a single MIPI CSI-2 lane, with the same supplies,
reset and strobe GPIOs as the VD55G1. Its MIPI rate runs from 750 to
1200 Mbps (UM2829, 13.1), so its link frequency starts at 375 MHz, and
its I2C address is set in its NVM rather than fixed.
allOf moves below required to carry the if/then for that minimum.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v4: no change; Krzysztof's Reviewed-by from v3. One file stays, as he
answered Benjamin on v3:
https://lore.kernel.org/all/20261110-normal-ingenious-kakapo-f8c479@quoll/
v2: the VD55G0 joins this file instead of a st,vd55g0.yaml of its own,
with allOf below required for the if/then, as Krzysztof asked of Peter
Marshall's vd55g1 v2, whose 01/11 and 02/11 took this approach:
https://lore.kernel.org/all/20260918221705.323510-3-pm@petermarshall.ca/
.../bindings/media/i2c/st,vd55g1.yaml | 27 ++++++++++++++++---
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
index dfd87beba06..0245673d105 100644
--- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
@@ -16,21 +16,23 @@ description: |-
array size of 804H x 704V. It is programmable through I2C interface. The I2C
address is fixed to 0x10.
+ The VD55G0 is a monochrome global shutter image sensor with an active array
+ size of 644H x 604V, programmable through I2C at an address set in its NVM.
+
Image data is sent through MIPI CSI-2, which is configured as only 1 data
lane. The sensor provides 4 GPIOS that can be used for external LED signal
(synchronized with sensor integration periods).
-allOf:
- - $ref: /schemas/media/video-interface-devices.yaml#
-
properties:
compatible:
enum:
+ - st,vd55g0
- st,vd55g1
- st,vd55g4
- st,vd65g4
description:
- VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one.
+ VD55G0, VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a
+ color one.
reg:
maxItems: 1
@@ -101,6 +103,23 @@ required:
- reset-gpios
- port
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: st,vd55g0
+ then:
+ properties:
+ port:
+ properties:
+ endpoint:
+ properties:
+ link-frequencies:
+ items:
+ minimum: 375000000
+
unevaluatedProperties: false
examples:
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/6] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 1/6] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 3/6] platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana" Sergey Lebedev
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, 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. The supplies keep their DT names on ACPI too,
where int3472 maps them.
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>
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v4: Sakari's review. The controls hold the settings, applied with
__v4l2_ctrl_handler_setup() at stream start and written by s_ctrl only
while powered; the subdev state holds the format and crop;
media_entity_is_streaming() replaces the driver's flag; one supply list,
which 3/6 maps on ACPI; and the smaller points. g_volatile_ctrl() still
sets ->val, as discussed on v3. Also the handler's lock is the state
lock, hflip and vflip form a cluster, AE_TARGET_PERCENTAGE is written as
16 bits, and LINE_LENGTH is written at stream start from the crop and
HBLANK. pm_runtime_dont_use_autosuspend() is no longer called: the core
does it since d1706f9da9f4, as Joshua Crofts's vd55g1 patch says.
v3: Sashiko's three points on v2 (auto exposure readback after a
stream, the runtime PM reference across probe, RUNTIME_PM_OPS() and
pm_ptr()); registration through v4l2_async_register_subdev_sensor()
(Jizhou Tong), with dev_err_probe() on failure.
v2: no code change; MAINTAINERS no longer lists a binding for it.
MAINTAINERS | 9 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2232 ++++++++++++++++++++++++++++++++++++
4 files changed, 2253 insertions(+)
create mode 100644 drivers/media/i2c/vd55g0.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 93abbb31506..dbbc5b05954 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26592,6 +26592,14 @@ 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: drivers/media/i2c/vd55g0.c
+
ST VD55G1 DRIVER
M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
M: Sylvain Petinot <sylvain.petinot@foss.st.com>
@@ -29317,6 +29325,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 11c30252540..f8a653154de 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..99bfb05225d
--- /dev/null
+++ b/drivers/media/i2c/vd55g0.c
@@ -0,0 +1,2232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the ST VD55G0 global shutter sensor
+ *
+ * Copyright (C) 2026 STMicroelectronics SA
+ */
+
+#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-rect.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_REG16_LE(0x0440)
+
+#define VD55G0_WIDTH 644
+#define VD55G0_HEIGHT 604
+#define VD55G0_DEFAULT_MODE 1
+#define VD55G0_WRITE_MULTIPLE_CHUNK_MAX 16U
+#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_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;
+ unsigned int size;
+ /* As FWPATCH_REVISION reads it: major in the high byte */
+ u16 version;
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut1 = {
+ .bin = cut1_patch,
+ .size = ARRAY_SIZE(cut1_patch),
+ .version = 0x020b, /* 2.11 */
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut2 = {
+ .bin = cut2_patch,
+ .size = ARRAY_SIZE(cut2_patch),
+ .version = 0x0005, /* 0.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,
+};
+
+static const char * const vd55g0_supply_name[] = {
+ "vcore",
+ "vddio",
+ "vana",
+};
+
+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;
+ 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,
+ .crop = {
+ .left = 0,
+ .top = 0,
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ },
+ },
+ {
+ .width = 640,
+ .height = 600,
+ .crop = {
+ .left = 2,
+ .top = 2,
+ .width = 640,
+ .height = 600,
+ },
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ {
+ .width = 320,
+ .height = 240,
+ .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[ARRAY_SIZE(vd55g0_supply_name)];
+ struct gpio_desc *reset_gpio;
+ struct clk *xclk;
+ u32 clk_freq;
+ u16 oif_ctrl;
+ u64 link_freq;
+ u16 revision;
+ u32 leds[VD55G0_NB_GPIOS];
+ unsigned int nb_leds;
+ /* Its lock is also the subdev state lock and protects all below */
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ /* The flip cluster */
+ struct {
+ struct v4l2_ctrl *hflip_ctrl;
+ struct v4l2_ctrl *vflip_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;
+ };
+ struct v4l2_ctrl *ae_lock_ctrl;
+ /* What the last stream applied, for the next one to start from */
+ 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)
+{
+ for (unsigned int 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)
+{
+ for (unsigned int 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 unsigned int get_chunk_size(struct vd55g0_dev *sensor)
+{
+ const struct i2c_adapter_quirks *quirks =
+ sensor->i2c_client->adapter->quirks;
+ unsigned int max_write_len = VD55G0_WRITE_MULTIPLE_CHUNK_MAX;
+
+ /* Each write also carries the two address bytes */
+ if (quirks && quirks->max_write_len > 2)
+ max_write_len = min(max_write_len, quirks->max_write_len - 2U);
+
+ return max_write_len;
+}
+
+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);
+}
+
+/* The MIPI bus is double data rate; 32 bits, as the range is checked */
+static u32 vd55g0_mipi_rate(struct vd55g0_dev *sensor)
+{
+ return sensor->link_freq * 2;
+}
+
+static u32 vd55g0_pixel_rate(struct vd55g0_dev *sensor)
+{
+ u32 mipi_rate = vd55g0_mipi_rate(sensor);
+
+ return mipi_rate / (mipi_rate <= VD55G0_MIPI_RATE_FAST ? 6 : 8);
+}
+
+static unsigned int vd55g0_line_length(struct vd55g0_dev *sensor)
+{
+ return vd55g0_mipi_rate(sensor) <= VD55G0_MIPI_RATE_FAST ?
+ VD55G0_LINE_LENGTH_SLOW : VD55G0_LINE_LENGTH_FAST;
+}
+
+static int vd55g0_check_clock_tree(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+
+ if (sensor->clk_freq < VD55G0_XCLK_FREQ_MIN ||
+ sensor->clk_freq > VD55G0_XCLK_FREQ_MAX) {
+ dev_err(dev,
+ "Only %lu Hz-%lu Hz clock range supported. Provided %u Hz\n",
+ VD55G0_XCLK_FREQ_MIN, VD55G0_XCLK_FREQ_MAX,
+ sensor->clk_freq);
+ return -EINVAL;
+ }
+
+ /*
+ * The MIPI 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;
+ }
+ if (vd55g0_mipi_rate(sensor) % 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;
+ }
+
+ return 0;
+}
+
+static int vd55g0_update_gpio_mode(struct vd55g0_dev *sensor, u32 mode,
+ unsigned 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,
+ unsigned int size,
+ enum vd55g0_gpio_modes mode)
+{
+ int ret;
+
+ for (unsigned int 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_mode(struct vd55g0_dev *sensor, bool is_auto,
+ bool locked)
+{
+ enum vd55g0_expo_state exp = VD55G0_EXP_MANUAL;
+
+ if (is_auto)
+ exp = locked ? VD55G0_EXP_FREEZE : VD55G0_EXP_AUTO;
+
+ 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;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(vd55g0_supply_name); i++)
+ sensor->supplies[i].supply = vd55g0_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(vd55g0_supply_name),
+ sensor->supplies);
+}
+
+static int vd55g0_apply_patgen(struct vd55g0_dev *sensor, u32 index)
+{
+ static const u8 index2val[] = {
+ 0x0, 0x0, 0x8, 0x10, 0x20, 0x21, 0x22, 0x28
+ };
+ u32 pattern = index2val[index];
+ 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 (index) {
+ 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, u32 led_mode)
+{
+ enum vd55g0_gpio_modes mode = led_mode == V4L2_FLASH_LED_MODE_NONE ?
+ VD55G0_GPIO_MODE_DISABLED :
+ VD55G0_GPIO_MODE_STROBE;
+
+ return vd55g0_set_gpios_array(sensor, sensor->leds,
+ ARRAY_SIZE(sensor->leds), mode);
+}
+
+static int vd55g0_apply_exposure_target(struct vd55g0_dev *sensor, u32 index)
+{
+ /*
+ * Find auto exposure target with: default target exposure * 2^EV,
+ * the default being the firmware's 27, at index 6.
+ */
+ static const unsigned int index2exposure_target[] = {
+ 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
+ };
+
+ return vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ index2exposure_target[index], NULL);
+}
+
+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(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 const struct vd55g0_mode_info *
+vd55g0_try_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ const struct vd55g0_mode_info *mode;
+ u32 code = vd55g0_supported_codes[0].code;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == fmt->code)
+ code = fmt->code;
+ }
+
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data), width,
+ height, fmt->width, fmt->height);
+ vd55g0_fill_framefmt(mode, fmt, code);
+
+ return mode;
+}
+
+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_frame_format(struct vd55g0_dev *sensor,
+ const struct v4l2_mbus_framefmt *fmt,
+ const struct v4l2_rect *crop)
+{
+ enum vd55g0_bin_mode bin_mode = fmt->width < crop->width ?
+ VD55G0_BIN_MODE_DIGITAL_X2 :
+ VD55G0_BIN_MODE_NORMAL;
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_FORMAT_CTRL, get_bpp_by_code(fmt->code),
+ &ret);
+ vd55g0_write(sensor, VD55G0_REG_OIF_IMG_CTRL,
+ get_data_type_by_code(fmt->code), &ret);
+ vd55g0_write(sensor, VD55G0_REG_READOUT_CTRL, 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;
+}
+
+/* GPIOs in input (disabled) by default; the LED control may strobe some */
+static int vd55g0_disable_gpios(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < VD55G0_NB_GPIOS; i++) {
+ ret = vd55g0_update_gpio_mode(sensor,
+ VD55G0_GPIO_MODE_DISABLED, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vd55g0_stream_enable(struct vd55g0_dev *sensor,
+ struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(state, 0);
+ const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ struct device *dev = &sensor->i2c_client->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = vd55g0_disable_gpios(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_frame_format(sensor, fmt, crop);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_cold_start(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
+ if (ret)
+ goto err_rpm_put;
+
+ /* HBLANK is read-only, so s_ctrl leaves the line length to us */
+ ret = vd55g0_write(sensor, VD55G0_REG_LINE_LENGTH,
+ crop->width + sensor->hblank_ctrl->cur.val, NULL);
+ 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(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 = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ int ret;
+
+ 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->version) {
+ dev_err(&client->dev,
+ "bad patch version expected %u.%u got %llu.%llu\n",
+ patch->version >> 8, patch->version & 0xff,
+ 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;
+
+ /*
+ * 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, vd55g0_mipi_rate(sensor),
+ &ret);
+
+ return ret;
+}
+
+/* Called with the control handler's lock held, which is the state 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, state);
+ if (ret)
+ return ret;
+
+ 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);
+
+ 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)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(sd_state, 0);
+ 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;
+}
+
+/* Max exposure follows the frame length */
+static int vd55g0_update_expo_range(struct vd55g0_dev *sensor,
+ unsigned int frame_length)
+{
+ return __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0,
+ frame_length - VD55G0_EXPO_MAX_TERM, 1,
+ VD55G0_EXPO_DEF);
+}
+
+/* Blanking counts from the analogue crop, as in vd55g1 */
+static int vd55g0_update_blanking(struct vd55g0_dev *sensor,
+ const struct v4l2_rect *crop)
+{
+ unsigned int vblank_def = VD55G0_FRAME_LENGTH_DEF - crop->height;
+ unsigned int hblank = vd55g0_line_length(sensor) - crop->width;
+ int ret;
+
+ ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl,
+ VD55G0_MIN_FRAME_LENGTH - crop->height,
+ 0xffff - crop->height, 1, vblank_def);
+ if (ret)
+ return ret;
+
+ /* The range change may keep the old vblank; set the default */
+ ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank_def);
+ if (ret)
+ return ret;
+
+ /* Also when vblank kept its value and s_ctrl was not called */
+ ret = vd55g0_update_expo_range(sensor, crop->height + vblank_def);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
+ hblank);
+}
+
+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 *mode;
+ struct v4l2_rect *crop;
+ bool crop_changed;
+
+ /* The mode cannot change under a running stream */
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ mode = vd55g0_try_fmt(&format->format);
+ *v4l2_subdev_state_get_format(sd_state, 0) = format->format;
+
+ crop = v4l2_subdev_state_get_crop(sd_state, 0);
+ crop_changed = !v4l2_rect_equal(crop, &mode->crop);
+ *crop = mode->crop;
+
+ /* The code alone leaves the blanking as the user set it */
+ if (format->which != V4L2_SUBDEV_FORMAT_ACTIVE || !crop_changed)
+ return 0;
+
+ return vd55g0_update_blanking(sensor, crop);
+}
+
+static int vd55g0_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_format fmt = { 0 };
+
+ vd55g0_fill_framefmt(&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 v4l2_subdev_internal_ops vd55g0_internal_ops = {
+ .init_state = vd55g0_init_state,
+};
+
+/*
+ * The manual values only in manual mode, written before the mode so that a
+ * switch to manual starts from them. On that switch all three are written:
+ * under auto exposure the sensor never had them.
+ */
+static int vd55g0_update_expo_cluster(struct vd55g0_dev *sensor)
+{
+ bool is_auto = sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO;
+ bool all = sensor->ae_ctrl->is_new;
+ int ret = 0;
+
+ if (!is_auto) {
+ /* A range change under auto exposure did not clamp it */
+ sensor->expo_ctrl->val = min(sensor->expo_ctrl->val,
+ sensor->expo_ctrl->maximum);
+ if (all || sensor->expo_ctrl->is_new)
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ sensor->expo_ctrl->val, &ret);
+ if (all || sensor->again_ctrl->is_new)
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ sensor->again_ctrl->val, &ret);
+ if (all || sensor->dgain_ctrl->is_new)
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ sensor->dgain_ctrl->val, &ret);
+ if (ret)
+ return ret;
+ }
+
+ if (sensor->ae_ctrl->is_new)
+ ret = vd55g0_apply_exposure_mode(sensor, is_auto,
+ sensor->ae_lock_ctrl->cur.val &
+ V4L2_LOCK_EXPOSURE);
+
+ 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;
+
+ /* Unpowered: the last stream's values, the next one's start */
+ if (!pm_runtime_get_if_in_use(dev)) {
+ sensor->expo_ctrl->val = min_t(u32, sensor->cold_start.expo,
+ sensor->expo_ctrl->maximum);
+ sensor->again_ctrl->val = sensor->cold_start.analog_gain;
+ sensor->dgain_ctrl->val = sensor->cold_start.digital_gain;
+ 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 = min_t(u64, expo,
+ sensor->expo_ctrl->maximum);
+ 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);
+ struct device *dev = &sensor->i2c_client->dev;
+ struct v4l2_subdev_state *state =
+ v4l2_subdev_get_locked_active_state(sd);
+ const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ bool is_auto, locked;
+ int ret;
+
+ /* Read-only controls are set by the driver: nothing to apply */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
+ /* The exposure range follows vblank whether the sensor is on or not */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ ret = vd55g0_update_expo_range(sensor,
+ crop->height + ctrl->val);
+ if (ret) {
+ vd55g0_update_expo_range(sensor,
+ crop->height + ctrl->cur.val);
+ return ret;
+ }
+ }
+
+ /* The sensor gets the values at the next stream start */
+ if (!pm_runtime_get_if_in_use(dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ret = vd55g0_write(sensor, VD55G0_REG_ORIENTATION,
+ sensor->hflip_ctrl->val |
+ (sensor->vflip_ctrl->val << 1), NULL);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = vd55g0_apply_patgen(sensor, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = vd55g0_update_expo_cluster(sensor);
+ break;
+ case V4L2_CID_3A_LOCK:
+ is_auto = sensor->ae_ctrl->cur.val == V4L2_EXPOSURE_AUTO;
+ locked = ctrl->val & V4L2_LOCK_EXPOSURE;
+ ret = vd55g0_apply_exposure_mode(sensor, is_auto, locked);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = vd55g0_write(sensor, VD55G0_REG_FRAME_LENGTH,
+ crop->height + ctrl->val, NULL);
+ /* Not applied: put the exposure range back */
+ if (ret)
+ vd55g0_update_expo_range(sensor,
+ crop->height + ctrl->cur.val);
+ break;
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ /*
+ * We use auto exposure target percentage register to control
+ * exposure bias for more precision.
+ */
+ ret = vd55g0_apply_exposure_target(sensor, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_LED_MODE:
+ ret = vd55g0_apply_flash(sensor, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ 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;
+ /* What vd55g0_init_state() puts in the active state */
+ const struct v4l2_rect *crop =
+ &vd55g0_mode_data[VD55G0_DEFAULT_MODE].crop;
+ 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 = vd55g0_line_length(sensor) - crop->width;
+ u32 pixel_rate = vd55g0_pixel_rate(sensor);
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &fwnode_props);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(hdl, 16);
+ 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, pixel_rate,
+ pixel_rate, 1, pixel_rate);
+ sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO, 1,
+ ~0x3, V4L2_EXPOSURE_AUTO);
+ sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ 0, 24, 1, 0);
+ sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+ 256, 2048, 1, 256);
+ sensor->ae_lock_ctrl = 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,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /*
+ * 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,
+ VD55G0_MIN_FRAME_LENGTH - crop->height,
+ 0xffff - crop->height, 1,
+ VD55G0_FRAME_LENGTH_DEF - crop->height);
+ 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->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
+ 0, 1, 1, 0);
+ sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
+ 0, 1, 1, 0);
+ 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,
+ VD55G0_FRAME_LENGTH_DEF -
+ VD55G0_EXPO_MAX_TERM, 1, VD55G0_EXPO_DEF);
+
+ ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ v4l2_ctrl_cluster(2, &sensor->hflip_ctrl);
+ v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true);
+
+ sensor->sd.ctrl_handler = hdl;
+
+ return 0;
+
+free_ctrls:
+ return v4l2_ctrl_handler_free(hdl);
+}
+
+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(ARRAY_SIZE(vd55g0_supply_name),
+ 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(ARRAY_SIZE(vd55g0_supply_name),
+ 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(ARRAY_SIZE(vd55g0_supply_name),
+ 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;
+ 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 (unsigned int 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->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->cold_start.expo = 0x32;
+ sensor->cold_start.digital_gain = 256;
+ sensor->cold_start.analog_gain = 0;
+
+ 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_check_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.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;
+
+ 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->ctrl_handler.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;
+ }
+
+ /* Held until registered, so that runtime PM cannot power it down */
+ pm_runtime_set_active(dev);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
+ if (ret) {
+ dev_err_probe(dev, ret, "async subdev register failed\n");
+ goto error_pm_runtime;
+ }
+
+ pm_runtime_put_autosuspend(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(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(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:
+ 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);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ vd55g0_power_off(&client->dev);
+ pm_runtime_set_suspended(&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 acpi_device_id vd55g0_acpi_ids[] = {
+ { "SMO55F0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, vd55g0_acpi_ids);
+
+static const struct dev_pm_ops vd55g0_pm_ops = {
+ 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 = pm_ptr(&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] 7+ messages in thread
* [PATCH v4 3/6] platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana"
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 1/6] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 2/6] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 4/6] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, devicetree, linux-kernel
The ST VD55G0 IR camera of the Surface Pro 11 with Intel (ACPI HID
SMO55F0) is powered through two GPIOs of an INT3472 discrete device: the
power enable, and type 0x10, which int3472 calls DOVDD. Which rail each
one feeds is not documented, and it cannot be measured here: the sensor
answers on I2C only with both on.
Intel's own control logic driver names type 0x10 Avdd, so map it to
"vana". The power enable takes "vddio"; whether it feeds VDDIO or VCORE
is not known either. VCORE is not described, so the driver gets a dummy
regulator for it. The vd55g0 driver enables all three together, so the
names do not change what the sensor gets.
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
New in v4, so that 2/6 uses the same supply names on DT and ACPI
(Sakari). 2/6 needs it on ACPI.
drivers/platform/x86/intel/int3472/discrete.c | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index b19ea6ee337..a7098b08f8b 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -150,6 +150,17 @@ static const char * const power_enable_hids_vdda[] = {
NULL
};
+static const char * const power_enable_hids_vddio[] = {
+ "SMO55F0", /* vd55g0 on Microsoft Surface Pro 11 */
+ NULL
+};
+
+/* Intel's own name for type 0x10 is Avdd */
+static const char * const dovdd_hids_vana[] = {
+ "SMO55F0", /* vd55g0 on Microsoft Surface Pro 11 */
+ NULL
+};
+
/**
* struct int3472_gpio_map - Map GPIOs to whatever is expected by the
* sensor driver (as in DT bindings)
@@ -199,6 +210,20 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
.con_id = "vana",
.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
},
+ { /* Sensors which expect "vddio" as con_id for power enable */
+ .hids = power_enable_hids_vddio,
+ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
+ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
+ .con_id = "vddio",
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
+ },
+ { /* Sensors which expect "vana" as con_id for DOVDD */
+ .hids = dovdd_hids_vana,
+ .type_from = INT3472_GPIO_TYPE_DOVDD,
+ .type_to = INT3472_GPIO_TYPE_DOVDD,
+ .con_id = "vana",
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
+ },
};
static bool int3472_gpio_map_hids_match(struct acpi_device *adev,
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 4/6] media: ipu-bridge: Add ST VD55G0
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
` (2 preceding siblings ...)
2026-11-10 19:55 ` [PATCH v4 3/6] platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana" Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 5/6] media: ipu-bridge: Index the sensor device properties as the endpoint ones Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 6/6] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, 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/
Tested-by: Fernando Rimoli <fernandorimoli11@gmail.com> # Surface Pro 9, IPU6
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v4: no change.
v3: moved before VD55G1, for vendor order. imx681 v10 3/3 adds SONY0681
at the same place, so whichever lands second needs a trivial fixup.
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..c99c85b7e6f 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -124,6 +124,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
/* Sony IMX471 (found on Lenovo X1 Carbon G14) */
IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000),
+ /* STMicroelectronics VD55G0 */
+ IPU_SENSOR_CONFIG("SMO55F0", 1, 396000000),
/* STMicroelectronics VD55G1 (found on Lenovo X1 Carbon G14) */
IPU_SENSOR_CONFIG("TBE20A1", 1, 402000000),
/* Toshiba T4KA3 */
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 5/6] media: ipu-bridge: Index the sensor device properties as the endpoint ones
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
` (3 preceding siblings ...)
2026-11-10 19:55 ` [PATCH v4 4/6] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 6/6] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, devicetree, linux-kernel
Fill dev_properties through an enum and IPU_BRIDGE_NEXT_PROPERTY(), as
ep_properties already is, so that an entry can be added without
counting slots by hand. No functional change.
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
New in v4 (Sakari). The series that keeps what the software nodes point
to out of the module image is probably D. Manresa's, recalled on v3; it
changes the same lens-focus line, so whichever lands second needs a
trivial rebase:
https://lore.kernel.org/all/20261026082549.662885-1-dmanresa@gmail.com/
drivers/media/pci/intel/ipu-bridge.c | 19 ++++++++-----------
include/media/ipu-bridge.h | 11 ++++++++++-
2 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index c99c85b7e6f..2399a3d52b6 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -570,7 +570,7 @@ 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;
+ unsigned int i = 0, d = 0;
sensor->prop_names = prop_names;
@@ -612,19 +612,16 @@ static void ipu_bridge_create_fwnode_properties(
SOFTWARE_NODE_REFERENCE(&nodes[SWNODE_SENSOR_ENDPOINT]);
}
- sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
- sensor->prop_names.clock_frequency,
- sensor->mclkspeed);
- sensor->dev_properties[1] = PROPERTY_ENTRY_U32(
- sensor->prop_names.rotation,
- sensor->rotation);
- sensor->dev_properties[2] = PROPERTY_ENTRY_U32(
- sensor->prop_names.orientation,
- sensor->orientation);
+ sensor->dev_properties[IPU_BRIDGE_NEXT_PROPERTY(d, DEV_CLOCK_FREQUENCY)] =
+ PROPERTY_ENTRY_U32(names->clock_frequency, sensor->mclkspeed);
+ sensor->dev_properties[IPU_BRIDGE_NEXT_PROPERTY(d, DEV_ROTATION)] =
+ PROPERTY_ENTRY_U32(names->rotation, sensor->rotation);
+ sensor->dev_properties[IPU_BRIDGE_NEXT_PROPERTY(d, DEV_ORIENTATION)] =
+ PROPERTY_ENTRY_U32(names->orientation, sensor->orientation);
if (sensor->vcm_type) {
sensor->vcm_ref[0] =
SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]);
- sensor->dev_properties[3] =
+ sensor->dev_properties[IPU_BRIDGE_NEXT_PROPERTY(d, DEV_LENS_FOCUS)] =
PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
}
diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
index 3ef94c2b767..284e0d6cc93 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -89,6 +89,15 @@ enum ipu_bridge_ep_props {
IPU_BRIDGE_EP_NUM_ENTRIES
};
+enum ipu_bridge_dev_props {
+ IPU_BRIDGE_DEV_CLOCK_FREQUENCY,
+ IPU_BRIDGE_DEV_ROTATION,
+ IPU_BRIDGE_DEV_ORIENTATION,
+ IPU_BRIDGE_DEV_LENS_FOCUS,
+ IPU_BRIDGE_DEV_NUM_OF,
+ IPU_BRIDGE_DEV_NUM_ENTRIES
+};
+
/*
* Get the index of the next property in a property array, with a given maximum
* value.
@@ -179,7 +188,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[IPU_BRIDGE_DEV_NUM_ENTRIES];
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] 7+ messages in thread
* [PATCH v4 6/6] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
` (4 preceding siblings ...)
2026-11-10 19:55 ` [PATCH v4 5/6] media: ipu-bridge: Index the sensor device properties as the endpoint ones Sergey Lebedev
@ 2026-11-10 19:55 ` Sergey Lebedev
5 siblings, 0 replies; 7+ messages in thread
From: Sergey Lebedev @ 2026-11-10 19:55 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Ilpo Järvinen, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu,
Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
platform-driver-x86, devicetree, linux-kernel
On the Surface Pro 9 and 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, in a dev_properties entry after
lens-focus. The software nodes outlive the module, so the property name
goes into struct ipu_property_names and the GPIO numbers into struct
ipu_sensor, where nothing points into the module image.
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
v4: the st,leds entry takes its index through 5/6. Fernando's Tested-by
is off, as the code changed.
v3: no change.
v2: the st,leds name and GPIO numbers live in the bridge's own
structs, as the software nodes outlive the module (Fernando); the
Surface Pro 9 is added (Fernando).
drivers/media/pci/intel/ipu-bridge.c | 64 ++++++++++++++++++++++++++++
include/media/ipu-bridge.h | 5 +++
2 files changed, 69 insertions(+)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 2399a3d52b6..fe847fd39b4 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -283,6 +283,42 @@ 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;
+ u32 gpios[IPU_MAX_LED_GPIOS];
+ unsigned int nr_gpios;
+};
+
+/* The infrared illuminator, strobed by the VD55G0's GPIO 1 */
+static const struct ipu_sensor_leds surface_pro_ir_leds = {
+ .hid = "SMO55F0",
+ .gpios = { 1 },
+ .nr_gpios = 1,
+};
+
+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 9"),
+ },
+ .driver_data = (void *)&surface_pro_ir_leds,
+ },
+ {
+ .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_ir_leds,
+ },
+ {} /* Terminating entry */
+};
+
static const struct ipu_property_names prop_names = {
.clock_frequency = "clock-frequency",
.rotation = "rotation",
@@ -292,6 +328,7 @@ static const struct ipu_property_names prop_names = {
.remote_endpoint = "remote-endpoint",
.link_frequencies = "link-frequencies",
.clock_noncontinuous = "clock-noncontinuous",
+ .st_leds = "st,leds",
};
static const char * const ipu_vcm_types[] = {
@@ -563,6 +600,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,6 +623,7 @@ static void ipu_bridge_create_fwnode_properties(
{
struct ipu_property_names *names = &sensor->prop_names;
struct software_node *nodes = sensor->swnodes;
+ const struct ipu_sensor_leds *leds;
unsigned int i = 0, d = 0;
sensor->prop_names = prop_names;
@@ -625,6 +679,16 @@ static void ipu_bridge_create_fwnode_properties(
PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
}
+ leds = ipu_bridge_get_leds(cfg);
+ if (leds) {
+ memcpy(sensor->led_gpios, leds->gpios,
+ sizeof(sensor->led_gpios));
+ sensor->dev_properties[IPU_BRIDGE_NEXT_PROPERTY(d, DEV_ST_LEDS)] =
+ PROPERTY_ENTRY_U32_ARRAY_LEN(names->st_leds,
+ sensor->led_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 284e0d6cc93..46c7583ef4d 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -12,6 +12,7 @@
#define IPU_MAX_LANES 4
#define IPU_MAX_PORTS 4
#define MAX_NUM_LINK_FREQS 3
+#define IPU_MAX_LED_GPIOS 4
/* Values are educated guesses as we don't have a spec */
#define IPU_SENSOR_ROTATION_NORMAL 0
@@ -94,6 +95,7 @@ enum ipu_bridge_dev_props {
IPU_BRIDGE_DEV_ROTATION,
IPU_BRIDGE_DEV_ORIENTATION,
IPU_BRIDGE_DEV_LENS_FOCUS,
+ IPU_BRIDGE_DEV_ST_LEDS,
IPU_BRIDGE_DEV_NUM_OF,
IPU_BRIDGE_DEV_NUM_ENTRIES
};
@@ -145,6 +147,7 @@ struct ipu_property_names {
char remote_endpoint[16];
char link_frequencies[17];
char clock_noncontinuous[20];
+ char st_leds[8];
};
struct ipu_node_names {
@@ -187,6 +190,8 @@ struct ipu_sensor {
const char *vcm_type;
struct ipu_property_names prop_names;
+ /* The software nodes outlive the module, so these values live here */
+ u32 led_gpios[IPU_MAX_LED_GPIOS];
struct property_entry ep_properties[IPU_BRIDGE_EP_NUM_ENTRIES];
struct property_entry dev_properties[IPU_BRIDGE_DEV_NUM_ENTRIES];
struct property_entry ipu_properties[3];
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-10 19:55 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 19:54 [PATCH v4 0/6] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 1/6] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 2/6] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 3/6] platform/x86: int3472: Map the VD55G0 supplies to "vddio" and "vana" Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 4/6] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 5/6] media: ipu-bridge: Index the sensor device properties as the endpoint ones Sergey Lebedev
2026-11-10 19:55 ` [PATCH v4 6/6] media: ipu-bridge: Add the Surface Pro 9 and 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®