* [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor
@ 2026-11-03 10:32 Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 10:32 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, linux-media, devicetree,
linux-kernel
The ST VD55G0 is a 644x604 monochrome global shutter sensor. It is the
infrared camera of the Microsoft Surface Pro 11 for Business with Intel
(Lunar Lake, IPU7) and of the Surface Pro 9 (Alder Lake, IPU6), ACPI
device SMO55F0. Without a driver the camera does not appear at all.
1/4 dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
2/4 media: i2c: Add ST VD55G0 global shutter sensor driver
3/4 media: ipu-bridge: Add ST VD55G0
4/4 media: ipu-bridge: Add the Surface Pro 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
Benjamin reviewed it off-list before v1; 2/4 carries his
Co-developed-by, and its clock tree is based on his.
Changes since v1:
- 1/4: the VD55G0 joins st,vd55g1.yaml instead of a file of its own,
with its 375 MHz link-frequency minimum in an if/then, as Krzysztof
pointed to. Benjamin's Co-developed-by went with the separate file.
- 2/4: no code change; MAINTAINERS no longer lists a binding for it.
- 4/4: the st,leds name and GPIO numbers now live in the bridge's own
structs, as the software nodes outlive the module (Fernando). With
v1, unloading ipu-bridge and rebinding the sensor froze the machine.
Adds the Surface Pro 9, which strobes from the same GPIO (Fernando).
- Rebased on v7.4-rc1.
On both machines the sensor needs Hao Yao's int3472 fix: the SSDB asks
for 24 MHz, but INT3472 enables the clock at 19.2 MHz, so without it the
sensor runs at four fifths of every rate this series computes:
https://lore.kernel.org/all/20251208035122.2147678-1-hao.yao@intel.com/
Its v2 fixes the Surface Pro 11. On the Alder Lake Surface Pro 9 the
_DSM takes 0 for 24 MHz rather than 3, as Fernando measured, so its v3
has to choose the value per platform.
On IPU6, frames come out corrupted unless a CPU latency bound is held
while streaming; that is the ISYS, not this sensor (Fernando, on v1).
v1: https://lore.kernel.org/all/20261019162722.25587-1-lsa.uz@pm.me/
Tested on the Surface Pro 11 for Business, on v7.4-rc1 with Hao Yao's
int3472 v2, D. Manresa's ipu-bridge 1/2 for the unload test, KASAN with
KASAN_VMALLOC, and lockdep, IPU7 run by the ipu6 driver:
- probe defers until ipu-bridge has made the endpoint, then binds;
- every mode runs at 59.14 fps, as the controls give, in Y8 and Y10 at
all four sizes and in Y10 packed at full size;
- under auto exposure, exposure and the gains read back what the sensor
applies, and carry over to manual mode;
- the illuminator through led_mode;
- v4l2-compliance 46 of 46, and 54 of 54 through the media device,
with two warnings for the read-only crop;
- unloading the IPU stack and ipu-bridge, then unbinding and binding the
sensor, is clean; with v1 it froze the machine;
- no KASAN, lockdep or Oops report; the only WARNING is the ipu6
driver's lockdep_assert_held() at each stream start, as with v1;
- W=1 and sparse clean; dt_binding_check clean, and it refuses a VD55G0
example at 125 MHz.
The Surface Pro 9 entry is not tested here.
Sergey Lebedev (4):
dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
media: i2c: Add ST VD55G0 global shutter sensor driver
media: ipu-bridge: Add ST VD55G0
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 | 2434 +++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 70 +-
include/media/ipu-bridge.h | 6 +-
7 files changed, 2551 insertions(+), 7 deletions(-)
create mode 100644 drivers/media/i2c/vd55g0.c
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
2026-11-03 10:32 [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
@ 2026-11-03 10:32 ` Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 10:32 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, linux-media, devicetree,
linux-kernel
The VD55G0 is a monochrome global shutter sensor with a 644x604 active
array, sending over a single MIPI CSI-2 lane, 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.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
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 pointed to. It
is Peter Marshall's 01/11 and 02/11 of his vd55g1 v2 squashed, as
Krzysztof asked there, with the VD55G0's description added:
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] 6+ messages in thread
* [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-11-03 10:32 [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
@ 2026-11-03 10:32 ` Sergey Lebedev
2026-11-03 12:13 ` Sergey Lebedev
2026-11-03 10:33 ` [PATCH v2 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-11-03 10:33 ` [PATCH v2 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev
3 siblings, 1 reply; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 10:32 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, linux-media, devicetree,
linux-kernel
The VD55G0 is a 644x604 monochrome global shutter sensor from
STMicroelectronics. It streams Y8 or Y10 over one CSI-2 lane, runs its own
auto exposure, can strobe an illuminator from its GPIOs, and needs a
firmware patch at every power-on, carried in the driver as vd55g1 does.
The driver is ported from ST's own, GPL-2.0, to current interfaces: CCI
register access, .enable_streams with the subdev state, fwnode properties,
devm_v4l2_sensor_clk_get(), and an ACPI match for x86 machines where the
sensor sits behind ipu-bridge:
https://github.com/STMicroelectronics/vd55g0-linux-driver
ST's st,out-sync and st,in-sync and its two private controls are left out,
as vd55g1 has none of them. With the ACPI ID the supplies take the names
INT3472 registers, avdd and dovdd; a PRP0001 device keeps the DT names.
The clock tree is computed from the input clock and the one link frequency
the endpoint gives, as in vd55g1. The MIPI rate must be an integer multiple
of the input clock, or the sensor would run at another one, so any other
link frequency fails probe.
Exposure and the two gains form an auto cluster with auto exposure, as in
vd55g1: while auto exposure runs, they read back what the sensor applies.
Co-developed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
On ACPI the supplies take the names INT3472 registers, avdd and dovdd,
through acpi_match_device(); vd55g1 is served by an entry in int3472's
map instead. I did not add one, as it would have to say which rail each
pin feeds, and that is not known here: INT3472 calls pin type 0x10
DOVDD, while Windows' camera driver calls it Avdd. If a map entry is
preferred, I can move the names there.
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 | 2434 ++++++++++++++++++++++++++++++++++++
4 files changed, 2455 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..3b51a87e740
--- /dev/null
+++ b/drivers/media/i2c/vd55g0.c
@@ -0,0 +1,2434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the ST VD55G0 global shutter sensor
+ *
+ * Copyright (C) 2026 STMicroelectronics SA
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define VD55G0_REG_MODEL_ID CCI_REG16_LE(0x0000)
+#define VD55G0_MODEL_ID 0x4730
+#define VD55G0_REG_REVISION CCI_REG16_LE(0x0004)
+#define VD55G0_REVISION_CUT1 0x1111
+#define VD55G0_REVISION_CUT2 0x1120
+#define VD55G0_REG_FWPATCH_REVISION CCI_REG16_LE(0x0022)
+#define VD55G0_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
+#define VD55G0_REG_SYSTEM_FSM CCI_REG8(0x002c)
+#define VD55G0_SYSTEM_FSM_READY_TO_BOOT 0x01
+#define VD55G0_SYSTEM_FSM_SW_STBY 0x02
+#define VD55G0_SYSTEM_FSM_STREAMING 0x03
+#define VD55G0_REG_BOOT CCI_REG8(0x0200)
+#define VD55G0_BOOT_BOOT 1
+#define VD55G0_BOOT_PATCH_SETUP 2
+#define VD55G0_REG_SW_STBY CCI_REG8(0x0201)
+#define VD55G0_SW_STBY_START_STREAM 1
+#define VD55G0_REG_STREAMING CCI_REG8(0x0202)
+#define VD55G0_STREAMING_STOP_STREAM 1
+#define VD55G0_REG_EXT_CLOCK CCI_REG32_LE(0x0220)
+#define VD55G0_REG_LINE_LENGTH CCI_REG16_LE(0x0300)
+#define VD55G0_REG_ORIENTATION CCI_REG8(0x0302)
+#define VD55G0_REG_FORMAT_CTRL CCI_REG8(0x030a)
+#define VD55G0_REG_OIF_CTRL CCI_REG16_LE(0x030c)
+#define VD55G0_REG_OIF_IMG_CTRL CCI_REG8(0x030f)
+#define VD55G0_REG_ISL_ENABLE CCI_REG8(0x0329)
+#define VD55G0_REG_CLK_PLL_MIPI CCI_REG32_LE(0x0224)
+#define VD55G0_REG_PATGEN_CTRL CCI_REG16_LE(0x0400)
+#define VD55G0_PATGEN_TYPE_SHIFT 4
+#define VD55G0_PATGEN_ENABLE BIT(0)
+#define VD55G0_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x044d)
+#define VD55G0_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x044e)
+#define VD55G0_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0450)
+#define VD55G0_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x0064)
+#define VD55G0_REG_APPLIED_ANALOG_GAIN CCI_REG8(0x0066)
+#define VD55G0_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x0068)
+#define VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE CCI_REG16_LE(0x042e)
+#define VD55G0_REG_AE_COLDSTART_ANALOG_GAIN CCI_REG8(0x0430)
+#define VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN CCI_REG16_LE(0x0432)
+#define VD55G0_REG_EXP_MODE CCI_REG8(0x044c)
+#define VD55G0_REG_FRAME_LENGTH CCI_REG16_LE(0x0458)
+#define VD55G0_REG_ROI_X_START CCI_REG16_LE(0x045e)
+#define VD55G0_REG_ROI_X_END CCI_REG16_LE(0x0460)
+#define VD55G0_REG_ROI_Y_START CCI_REG16_LE(0x0462)
+#define VD55G0_REG_ROI_Y_END CCI_REG16_LE(0x0464)
+#define VD55G0_REG_AE_ROI_START_H CCI_REG16_LE(0x0436)
+#define VD55G0_REG_AE_ROI_START_V CCI_REG16_LE(0x0438)
+#define VD55G0_REG_AE_ROI_END_H CCI_REG16_LE(0x043a)
+#define VD55G0_REG_AE_ROI_END_V CCI_REG16_LE(0x043c)
+#define VD55G0_REG_GPIO_0_CTRL CCI_REG8(0x0467)
+#define VD55G0_REG_READOUT_CTRL CCI_REG8(0x047a)
+#define VD55G0_REG_DARKCAL_CTRL CCI_REG8(0x032c)
+#define VD55G0_DARKCAL_BYPASS 0
+#define VD55G0_DARKCAL_AUTO 1
+#define VD55G0_REG_DUSTER_CTRL CCI_REG8(0x0316)
+#define VD55G0_DUSTER_ENABLE BIT(0)
+#define VD55G0_DUSTER_DISABLE 0
+#define VD55G0_DUSTER_DYN_ENABLE BIT(1)
+#define VD55G0_DUSTER_RING_ENABLE BIT(4)
+#define VD55G0_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0440)
+
+#define VD55G0_WIDTH 644
+#define VD55G0_HEIGHT 604
+#define VD55G0_DEFAULT_MODE 1
+#define VD55G0_WRITE_MULTIPLE_CHUNK_MAX 16
+#define VD55G0_NB_GPIOS 4
+#define VD55G0_MIN_FRAME_LENGTH (605 + 76)
+#define VD55G0_FRAME_LENGTH_DEF 1860
+#define VD55G0_TIMEOUT_MS 500
+#define VD55G0_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
+#define VD55G0_EXPO_MAX_TERM 64
+#define VD55G0_EXPO_DEF 200
+#define VD55G0_EXPO_TARGET_DEF 27
+#define VD55G0_LINE_LENGTH_SLOW 1200
+#define VD55G0_LINE_LENGTH_FAST 1128
+#define VD55G0_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
+#define VD55G0_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
+#define VD55G0_MIPI_RATE_MIN (750 * MEGA)
+#define VD55G0_MIPI_RATE_MAX (1200 * MEGA)
+#define VD55G0_MIPI_RATE_FAST (900 * MEGA)
+
+static const u8 cut1_patch[] = {
+0x81, 0x06, 0x0b, 0x02, 0x00, 0x09, 0x42, 0x00, 0xd2, 0x08, 0x42, 0x00, 0xe6,
+0x08, 0x42, 0x00, 0x12, 0x09, 0x42, 0x00, 0xe0, 0x3f, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x40, 0x01, 0x60, 0x10, 0x40, 0x11,
+0x81, 0x16, 0x80, 0x93, 0xdd, 0x4c, 0x04, 0x04, 0xfa, 0x06, 0x4c, 0x94, 0x60,
+0xe6, 0x0e, 0xdc, 0xe0, 0x04, 0x18, 0x70, 0x02, 0x08, 0xe7, 0xce, 0x44, 0x01,
+0x60, 0x10, 0x40, 0x31, 0x85, 0x1e, 0x08, 0x4e, 0x43, 0x01, 0x60, 0x10, 0xc0,
+0x31, 0x40, 0x20, 0xe0, 0x04, 0x98, 0x29, 0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24,
+0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84, 0x2b, 0xa1, 0x15, 0xfe, 0xd8, 0x41,
+0x00, 0xe8, 0xf8, 0x47, 0x00, 0xe8, 0x78, 0x44, 0x40, 0xef, 0x0a, 0x9c, 0x1a,
+0x9e, 0x2a, 0xdc, 0x3a, 0xde, 0x03, 0x4e, 0x7c, 0xf9, 0x13, 0x4e, 0x78, 0xf9,
+0x23, 0x4e, 0x74, 0xf9, 0x33, 0x4e, 0x70, 0xf9, 0x18, 0x50, 0x40, 0xef, 0x38,
+0x49, 0x44, 0xef, 0x04, 0x18, 0x11, 0x83, 0x11, 0x40, 0x20, 0xe0, 0x4c, 0x0c,
+0x04, 0xf2, 0x93, 0xdd, 0x86, 0x44, 0x88, 0xe0, 0x05, 0x04, 0x40, 0xf8, 0x09,
+0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0x60, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80,
+0x65, 0x86, 0x31, 0x01, 0x06, 0x02, 0x0c, 0xe1, 0x33, 0x40, 0xc8, 0xe2, 0x33,
+0x40, 0x70, 0xe2, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0x55, 0x8a, 0x0a, 0x02,
+0x08, 0x70, 0x16, 0x43, 0x58, 0xe0, 0x26, 0x08, 0x00, 0x40, 0x16, 0x0a, 0x00,
+0xc0, 0x41, 0x44, 0x08, 0x40, 0x06, 0x46, 0x08, 0xe0, 0x51, 0x44, 0x04, 0xc0,
+0x45, 0x85, 0x66, 0x02, 0x04, 0xe0, 0x13, 0x40, 0x70, 0x62, 0x06, 0x02, 0x0c,
+0xe1, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0xb5, 0x86, 0x0a, 0x02, 0x08, 0x70,
+0xa6, 0x02, 0x68, 0xe0, 0x26, 0x0a, 0x00, 0x40, 0x16, 0x18, 0x00, 0xc0, 0x31,
+0x01, 0xc6, 0x09, 0x0c, 0xe0, 0x51, 0x44, 0x08, 0xc0, 0xc1, 0x44, 0x04, 0xc0,
+0x95, 0x83, 0x0a, 0x02, 0x04, 0xf0, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x44, 0x08,
+0xc0, 0x11, 0x44, 0x04, 0x40, 0x11, 0x81, 0x33, 0x40, 0x18, 0xe3, 0x15, 0x83,
+0x0a, 0x02, 0x04, 0x70, 0x11, 0x81, 0x16, 0x0a, 0x00, 0x40, 0x31, 0x9d, 0x51,
+0x44, 0x08, 0x40, 0x06, 0x09, 0x04, 0xe0, 0x51, 0x44, 0x04, 0xc0, 0x43, 0x40,
+0x18, 0xe3, 0x33, 0x40, 0xc8, 0xe2, 0x13, 0x40, 0xc0, 0x62, 0xa6, 0x05, 0x78,
+0xe0, 0x23, 0x40, 0xd0, 0xe3, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x7c, 0xfa,
+0x86, 0x42, 0x84, 0x60, 0x06, 0x00, 0x0c, 0xe1, 0x14, 0x04, 0xf6, 0x50, 0x78,
+0xe3, 0x04, 0x00, 0x26, 0x0f, 0x28, 0xe2, 0x16, 0x15, 0x2c, 0x64, 0xb1, 0xbb,
+0x6a, 0x84, 0x0b, 0xa3, 0x36, 0x10, 0x10, 0x41, 0x2e, 0x54, 0x08, 0xd8, 0x16,
+0x4f, 0x44, 0x43, 0x06, 0x16, 0x00, 0xc0, 0x0b, 0x10, 0x1b, 0x8c, 0xb8, 0x4f,
+0x00, 0x6f, 0xa4, 0x12, 0x80, 0xfb, 0x06, 0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc,
+0x73, 0x03, 0xc0, 0x83, 0x22, 0x0e, 0x02, 0xc1, 0x7f, 0x1f, 0xc0, 0x87, 0xd4,
+0x8f, 0x02, 0x1b, 0x8c, 0x8f, 0x20, 0x0b, 0x8e, 0x98, 0x4e, 0x00, 0xef, 0x06,
+0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc, 0xf0, 0x73, 0xa2, 0x77, 0xcc, 0x70, 0x42,
+0x01, 0x73, 0xff, 0xc7, 0x12, 0x4e, 0x80, 0x79, 0x0b, 0x92, 0xb8, 0x4d, 0x00,
+0x6f, 0x1b, 0x8c, 0x06, 0x42, 0x94, 0x60, 0x06, 0x46, 0x1a, 0xe0, 0x24, 0x04,
+0x00, 0x40, 0xfd, 0x73, 0x03, 0xc0, 0xa3, 0xa6, 0xa7, 0xd4, 0xb3, 0x44, 0x60,
+0xe3, 0x85, 0x44, 0x9c, 0xe0, 0x24, 0x84, 0x75, 0x44, 0xa0, 0x60, 0x0e, 0x04,
+0xc0, 0x7f, 0x1f, 0xc0, 0x14, 0x04, 0xaf, 0x84, 0xaf, 0xa0, 0xa5, 0x42, 0xa4,
+0xe0, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x0c, 0x04, 0x0c, 0xfa, 0x46, 0x0d,
+0xcc, 0xe0, 0x09, 0x0c, 0xbc, 0xee, 0x0a, 0x40, 0x80, 0xe0, 0x45, 0x82, 0xa6,
+0x40, 0x08, 0xe0, 0x06, 0x98, 0x08, 0x7f, 0x74, 0xef, 0x0b, 0xa1, 0xb5, 0xfe,
+0x01, 0x87, 0x06, 0x98, 0x06, 0x4c, 0x94, 0x60, 0x06, 0x42, 0x0c, 0xe0, 0x05,
+0x0c, 0x14, 0xe0, 0x11, 0x40, 0x30, 0x62, 0x26, 0x42, 0x00, 0xe0, 0x05, 0x0c,
+0x14, 0xe0, 0x11, 0x40, 0xa0, 0x62, 0x56, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14,
+0xe0, 0x11, 0x40, 0x40, 0x62, 0x76, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14, 0xe0,
+0x11, 0x40, 0x50, 0xe2, 0x04, 0x98, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x42, 0x88,
+0xe0, 0xc5, 0x80, 0x28, 0x67, 0x54, 0x6f, 0x66, 0x00, 0xa0, 0xe0, 0x04, 0x98,
+0x19, 0x00, 0x30, 0xe0, 0x19, 0xc4, 0x11, 0x40, 0x30, 0x60, 0x11, 0x83, 0x04,
+0x9a, 0x11, 0x40, 0x10, 0xe0, 0x04, 0x98, 0x11, 0x40, 0x20, 0x60, 0x11, 0x81,
+0x04, 0x98, 0x11, 0x40, 0xb0, 0xe7, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x02, 0x0c,
+0xfc, 0x85, 0xfe, 0xc6, 0x4c, 0xb4, 0xe0, 0x60, 0x10, 0xb1, 0x64, 0x01, 0xc0,
+0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8c, 0x04, 0xe0, 0x0b, 0xa1, 0x25, 0x8c, 0xd8,
+0x43, 0x5c, 0xef, 0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8a, 0x04, 0xe0, 0x0b, 0xa1,
+0x25, 0x8a, 0xc6, 0x4e, 0x84, 0xe0, 0x04, 0x9c, 0x09, 0x00, 0x20, 0xe1, 0x4a,
+0x40, 0x80, 0xe0, 0x25, 0x86, 0x08, 0x22, 0x46, 0x43, 0x00, 0xe0, 0x06, 0x40,
+0x06, 0xe0, 0x63, 0x80, 0x09, 0x0c, 0x40, 0xe2, 0x09, 0x86, 0x0b, 0xa5, 0xd5,
+0x82, 0x68, 0x4e, 0x48, 0x6f, 0x01, 0x85, 0x78, 0x44, 0x48, 0x6f, 0x06, 0x00,
+0x80, 0xe1, 0xf8, 0x75, 0x4c, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x78, 0x48, 0x48,
+0xef, 0x09, 0x0e, 0x48, 0xe0, 0x0b, 0xa1, 0xb5, 0x80, 0x58, 0x7b, 0x3c, 0xef,
+0x78, 0x74, 0x40, 0xef, 0xa6, 0x40, 0x08, 0xe0, 0x0c, 0x0c, 0x0c, 0xf2, 0x18,
+0x12, 0x50, 0xff, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x7b, 0x00, 0x06, 0x01,
+0x08, 0xe1, 0x08, 0x00, 0x6b, 0x82, 0x0b, 0xa1, 0x15, 0x82, 0xa6, 0x41, 0x84,
+0xe0, 0x18, 0x00, 0x01, 0x89, 0x18, 0x42, 0x00, 0xef, 0x63, 0x80, 0x04, 0x1c,
+0x64, 0x02, 0x84, 0xe0, 0x78, 0x40, 0x00, 0x68, 0x00, 0x00, 0x40, 0xe6, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x1e, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x3c, 0xfa,
+0xf0, 0x1e, 0x40, 0x6c, 0x86, 0x0f, 0xe0, 0xe0, 0x01, 0x05, 0x91, 0x81, 0x06,
+0x02, 0x00, 0x62, 0x81, 0xb1, 0x01, 0x4e, 0xd8, 0x69, 0x0e, 0x00, 0x00, 0x60,
+0x01, 0xc0, 0x0e, 0x7c, 0x6b, 0x9e, 0x93, 0x4e, 0x14, 0x65, 0x86, 0x00, 0x30,
+0xe1, 0x93, 0x4e, 0x18, 0x65, 0x2b, 0x8c, 0x91, 0x4e, 0x50, 0xe1, 0x81, 0x4e,
+0xc8, 0xe9, 0x1e, 0xbe, 0x1e, 0x7e, 0x11, 0x8f, 0x9e, 0xbc, 0xd8, 0x55, 0x30,
+0xef, 0x06, 0x00, 0x00, 0x64, 0x46, 0x02, 0x54, 0xe1, 0x0e, 0x3e, 0x06, 0x00,
+0x20, 0xe0, 0x0e, 0x7e, 0x86, 0x00, 0x30, 0xe1, 0x1e, 0x7c, 0x11, 0x91, 0x9e,
+0x3c, 0x2b, 0x8c, 0xd8, 0x54, 0x30, 0xef, 0x06, 0x00, 0x00, 0x66, 0xa1, 0x83,
+0x0e, 0x3e, 0xf6, 0x41, 0xfc, 0xe9, 0x0e, 0x7e, 0x06, 0x00, 0x01, 0x60, 0x20,
+0xc0, 0x0e, 0x7c, 0x86, 0x00, 0x30, 0xe1, 0xae, 0x3c, 0x11, 0x93, 0xc8, 0x53,
+0x30, 0x6f, 0x2b, 0x8c, 0x06, 0x40, 0x88, 0x60, 0xf6, 0x45, 0xfc, 0x63, 0xff,
+0xc3, 0x14, 0x00, 0xf1, 0xa0, 0x95, 0x4e, 0xa8, 0xe2, 0xa1, 0x4e, 0x44, 0xea,
+0x81, 0x4e, 0xf0, 0x69, 0x10, 0x42, 0x00, 0x7c, 0x00, 0xcc, 0x1f, 0x04, 0x21,
+0x8b, 0x1e, 0x00, 0x86, 0x40, 0xfc, 0xe7, 0x05, 0x4e, 0xa4, 0x62, 0xc6, 0x01,
+0x5c, 0xe0, 0x05, 0x4e, 0xac, 0x62, 0x9e, 0x00, 0x00, 0x60, 0x02, 0xc0, 0x05,
+0x4e, 0x94, 0x62, 0x86, 0x40, 0x3c, 0x60, 0x10, 0xe7, 0x0e, 0x9c, 0x09, 0x0e,
+0x0c, 0xe1, 0x15, 0x4e, 0x3c, 0x79, 0x06, 0x02, 0x80, 0xe0, 0x13, 0x4e, 0xf4,
+0x64, 0x86, 0x02, 0x00, 0x60, 0x02, 0xc0, 0x21, 0x4e, 0x5c, 0x69, 0x00, 0x40,
+0xc0, 0xf3, 0x1e, 0x9e, 0x01, 0x4e, 0x0c, 0xe1, 0x0c, 0x0c, 0x3c, 0xf2, 0x93,
+0xdd, 0xc6, 0x01, 0xcc, 0x60, 0x06, 0x42, 0x88, 0xe0, 0x29, 0x00, 0x98, 0xee,
+0x34, 0x84, 0x4a, 0x44, 0x80, 0x60, 0x86, 0x04, 0x30, 0xe1, 0x24, 0x08, 0x30,
+0x46, 0xfd, 0x73, 0xff, 0xc3, 0x3e, 0x04, 0xa6, 0x08, 0xa0, 0x80, 0x34, 0x06,
+0xc8, 0x9c, 0x45, 0x44, 0xa0, 0x80, 0x35, 0x42, 0x00, 0x00, 0x06, 0x06, 0x00,
+0xc0, 0x35, 0x44, 0xa0, 0xc0, 0x35, 0x02, 0x00, 0xc0, 0x3e, 0x80, 0x93, 0xdd,
+0x4c, 0x00, 0x00, 0xfa, 0x88, 0x40, 0x00, 0xe8, 0x86, 0x02, 0x0c, 0xe1, 0x0e,
+0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04,
+0x00, 0xf8, 0xc6, 0x40, 0xb4, 0xe0, 0x00, 0x02, 0xb1, 0x64, 0x01, 0xc0, 0x2a,
+0xc4, 0x2b, 0xa1, 0x35, 0x92, 0x24, 0xc4, 0x2b, 0xa1, 0xc5, 0x90, 0x38, 0x06,
+0x46, 0x45, 0x00, 0xe0, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0x49, 0x06, 0xf0,
+0xe2, 0x4d, 0xe6, 0xc6, 0x40, 0x48, 0x41, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xd5, 0x8c, 0xc9, 0x06, 0x44, 0x62, 0x06, 0x4b, 0x00, 0xe0, 0x46,
+0x48, 0x16, 0x60, 0x86, 0x4a, 0xc8, 0xe0, 0x53, 0x08, 0xc0, 0x58, 0x80, 0xf3,
+0x49, 0x0a, 0xa4, 0xe7, 0x6b, 0x0a, 0xd8, 0xe3, 0x49, 0xbe, 0xcf, 0xa8, 0xc1,
+0x46, 0x44, 0xe2, 0x48, 0x86, 0xc9, 0x0a, 0xa0, 0xe7, 0x46, 0x48, 0x0a, 0xe0,
+0xc1, 0x46, 0x40, 0xe2, 0xcb, 0x0a, 0xe0, 0x63, 0x43, 0x80, 0x63, 0x48, 0x28,
+0xe1, 0x38, 0x86, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0xc3, 0x46, 0x30, 0xe1,
+0x18, 0x86, 0xcb, 0x0a, 0xd4, 0xe3, 0x2b, 0x0a, 0xdc, 0x63, 0x16, 0x42, 0x0a,
+0xe0, 0x5b, 0x0a, 0xe4, 0xe3, 0xc3, 0x48, 0x24, 0x61, 0x03, 0x82, 0x23, 0x46,
+0x2c, 0xe1, 0x53, 0x40, 0x34, 0xe1, 0x4c, 0x0c, 0x00, 0xf0, 0x93, 0xdd, 0xc3,
+0xc1, 0x0c, 0x00, 0x80, 0xfa, 0x86, 0x43, 0xcc, 0x60, 0x0b, 0xa3, 0x29, 0x02,
+0xc4, 0xee, 0xb5, 0x84, 0x4a, 0x04, 0xfc, 0xfb, 0x45, 0x84, 0xc6, 0x40, 0x84,
+0xe0, 0x14, 0x82, 0x18, 0x84, 0x19, 0x86, 0x1b, 0xa5, 0x85, 0x8c, 0x04, 0x80,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0xc5, 0x8a, 0x0c, 0x08, 0x80,
+0xf2, 0x08, 0x34, 0x44, 0xff, 0x0b, 0xa5, 0x15, 0x8a, 0x0a, 0x04, 0x04, 0xf0,
+0xb5, 0x88, 0xc6, 0x40, 0x84, 0xe0, 0x24, 0x82, 0x28, 0x88, 0x29, 0x86, 0x2b,
+0xa5, 0xc5, 0x86, 0x04, 0x80, 0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0,
+0x05, 0x86, 0x09, 0x02, 0x24, 0xee, 0x0b, 0xa3, 0xd5, 0x80, 0x08, 0x84, 0x0a,
+0x40, 0x80, 0xe0, 0x75, 0xf8, 0xf5, 0x81, 0x38, 0x72, 0x44, 0xef, 0xf8, 0x51,
+0xfc, 0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0xa5,
+0x80, 0x0c, 0x08, 0x80, 0xf2, 0xb8, 0x11, 0xfc, 0xff, 0x0c, 0x08, 0x80, 0xf2,
+0x93, 0xdd, 0xcc, 0x04, 0x0c, 0xfa, 0x06, 0x4f, 0x84, 0x60, 0x86, 0x50, 0x4c,
+0xe1, 0x04, 0x1c, 0x80, 0x0c, 0x21, 0x6f, 0x01, 0xc0, 0xf0, 0x04, 0x50, 0x64,
+0x11, 0x81, 0x09, 0x00, 0x6c, 0xe0, 0x06, 0x29, 0x0b, 0x8c, 0x58, 0x4c, 0x58,
+0xef, 0x04, 0x9c, 0x19, 0x00, 0x6c, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3,
+0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f,
+0xa2, 0x21, 0x40, 0x6c, 0xe0, 0x86, 0x43, 0xcc, 0xe0, 0x29, 0x02, 0x24, 0xee,
+0x2b, 0xa3, 0xd5, 0x80, 0x18, 0x84, 0x4a, 0x42, 0x80, 0x60, 0x11, 0x8b, 0x65,
+0x80, 0x19, 0x00, 0x70, 0xe0, 0x16, 0x29, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x03,
+0x0b, 0x8c, 0x28, 0x4a, 0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x70, 0xe0, 0x10,
+0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3,
+0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40, 0x70, 0xe0, 0x09, 0x00, 0x74,
+0x60, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x85, 0x06, 0x29, 0x0b, 0x8c, 0xa8, 0x48,
+0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x74, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b,
+0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0,
+0x2f, 0xa2, 0x21, 0x40, 0x74, 0xe0, 0x09, 0x00, 0x78, 0x60, 0xf0, 0x04, 0x50,
+0xe4, 0x11, 0x87, 0x06, 0x29, 0x0b, 0x8c, 0x28, 0x47, 0x58, 0xef, 0x04, 0x9c,
+0x19, 0x00, 0x78, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28,
+0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40,
+0x78, 0xe0, 0x19, 0x0e, 0xd0, 0xe0, 0x1b, 0xa1, 0x85, 0x86, 0x05, 0x00, 0x24,
+0xe0, 0x1a, 0xa2, 0x04, 0x04, 0xc0, 0xe1, 0x2b, 0x83, 0xd5, 0x80, 0x19, 0x10,
+0x10, 0x60, 0x09, 0x9e, 0x0b, 0x83, 0x85, 0x84, 0x08, 0xa0, 0x0b, 0xa1, 0x38,
+0x61, 0x74, 0xcf, 0x58, 0x49, 0x34, 0xef, 0x14, 0x9c, 0x25, 0x02, 0x24, 0xe0,
+0x24, 0x02, 0xc0, 0x61, 0x29, 0x9e, 0x18, 0x58, 0x74, 0xef, 0x55, 0x81, 0x08,
+0xa0, 0x0b, 0xa1, 0x48, 0x60, 0x74, 0xcf, 0x28, 0x72, 0x4c, 0x6f, 0x46, 0x01,
+0x08, 0xe0, 0xcc, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x65, 0x5e, 0x04,
+0xfa, 0xf0, 0x0c, 0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc,
+0xfc, 0x07, 0x80, 0xfe, 0xf0, 0x1e, 0x20, 0x6c, 0xe6, 0x40, 0x00, 0xe0, 0xc8,
+0x70, 0x4c, 0xef, 0x0b, 0xa1, 0xb5, 0x88, 0xc6, 0x0d, 0xdc, 0xe0, 0x09, 0x0c,
+0x94, 0xea, 0x0b, 0xa3, 0xf5, 0x80, 0x09, 0x0c, 0x91, 0x79, 0x01, 0xc0, 0x0a,
+0x40, 0x80, 0xe0, 0xe5, 0x82, 0xc8, 0x71, 0x44, 0xef, 0x04, 0x18, 0x86, 0x02,
+0x84, 0xe0, 0x24, 0x04, 0x31, 0x83, 0x01, 0x82, 0x0e, 0x18, 0xe6, 0x00, 0x48,
+0xe1, 0x36, 0x00, 0x21, 0x82, 0x2e, 0x84, 0x46, 0x00, 0x2c, 0x61, 0x21, 0x83,
+0x14, 0x00, 0x46, 0x40, 0x34, 0xe1, 0x25, 0x42, 0x44, 0xe0, 0x28, 0x51, 0x28,
+0xef, 0x88, 0x40, 0x00, 0xe8, 0xf1, 0x90, 0xfc, 0x0f, 0x80, 0xf6, 0xf4, 0xbc,
+0x64, 0xfd, 0x53, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x01, 0x03, 0x11, 0x81, 0x48,
+0x79, 0x08, 0xef, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00,
+0x00, 0xfa, 0x0d, 0x62, 0x8e, 0x45, 0xf0, 0xff, 0x75, 0x82, 0x4e, 0x05, 0x01,
+0x60, 0x10, 0x40, 0x07, 0xc8, 0x24, 0x88, 0x23, 0x80, 0x1e, 0x8a, 0x78, 0x40,
+0x00, 0xe8, 0x21, 0x81, 0x0b, 0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x83,
+0xc1, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x41, 0xb9, 0x06, 0x48, 0x12, 0xe0,
+0x33, 0x88, 0x45, 0x06, 0x14, 0xe0, 0x4b, 0xa3, 0xc5, 0x80, 0x35, 0x06, 0x18,
+0xe0, 0x3b, 0x23, 0x31, 0x81, 0x55, 0x80, 0x31, 0x83, 0x0d, 0xe6, 0x04, 0x00,
+0x88, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x10, 0x80, 0x05, 0x00, 0x10, 0x9c, 0x08,
+0x40, 0x00, 0x98, 0x25, 0x0b, 0x8e, 0x40, 0xf0, 0xff, 0x3b, 0xa1, 0x45, 0x8a,
+0x2b, 0x23, 0x06, 0x4c, 0x00, 0xe0, 0x45, 0x90, 0x2b, 0x25, 0x6e, 0x40, 0xf0,
+0xff, 0x25, 0x87, 0x3b, 0xa1, 0x25, 0x8a, 0x2b, 0x23, 0x06, 0x0c, 0x08, 0xe0,
+0x05, 0x8e, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x0c, 0x06, 0x0c, 0x04,
+0xe0, 0x95, 0x87, 0x3b, 0xa1, 0xc5, 0x88, 0x2b, 0x23, 0x61, 0x91, 0x85, 0x8a,
+0x2b, 0x25, 0x7e, 0x40, 0xf0, 0xff, 0x25, 0x83, 0x2b, 0xa3, 0xc5, 0x08, 0x06,
+0x0c, 0x08, 0x80, 0x2b, 0x25, 0x4e, 0x40, 0xf0, 0xff, 0x15, 0x8a, 0xf5, 0x03,
+0x06, 0x0c, 0x04, 0xe0, 0x2b, 0x23, 0x61, 0x91, 0x45, 0x86, 0x2b, 0x25, 0x6e,
+0x40, 0xf0, 0xff, 0xc5, 0x04, 0x61, 0x89, 0xc5, 0x83, 0x2b, 0x23, 0x06, 0x4c,
+0x00, 0xe0, 0x05, 0x84, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x02, 0x61,
+0xa1, 0x25, 0x83, 0x2b, 0x23, 0x61, 0x85, 0xe5, 0x80, 0x2b, 0x25, 0x7e, 0x40,
+0xf0, 0xff, 0x65, 0x00, 0x61, 0x83, 0x95, 0x81, 0x0b, 0x02, 0x1b, 0x8c, 0x48,
+0x78, 0xfc, 0xef, 0x0b, 0xa1, 0x00, 0x4c, 0x00, 0xdc, 0x0b, 0x8c, 0x0c, 0x0c,
+0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x04, 0xfa, 0x6b, 0x00, 0xc6,
+0x40, 0xdc, 0xe0, 0x04, 0x00, 0xf0, 0x1e, 0x30, 0xec, 0x48, 0x55, 0xfc, 0x6e,
+0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0xb8, 0x68, 0xfc, 0xee, 0x58, 0x71, 0x08,
+0xef, 0x78, 0x68, 0xfc, 0x6e, 0x0b, 0x8c, 0x71, 0x01, 0x5b, 0x80, 0x7e, 0x3c,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x11, 0x03, 0x21, 0x81, 0x31, 0x05, 0x41,
+0x81, 0xa8, 0x77, 0x08, 0xef, 0x0e, 0x3e, 0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0,
+0x48, 0x4b, 0x0c, 0xef, 0x0e, 0x7c, 0x11, 0x81, 0x04, 0x3e, 0x21, 0x83, 0x68,
+0x76, 0xfc, 0x6f, 0x0e, 0x06, 0x04, 0x60, 0x10, 0xc0, 0xce, 0x0d, 0x01, 0x60,
+0x10, 0xc0, 0x76, 0x98, 0x04, 0xfc, 0xc8, 0x59, 0x0c, 0xef, 0x13, 0xc3, 0x08,
+0x98, 0x0b, 0xa1, 0xa5, 0xfe, 0x04, 0xfc, 0x18, 0x5c, 0x0c, 0xef, 0x14, 0x3e,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x38, 0x46, 0x0c, 0xef, 0x01, 0x01, 0xf1,
+0x98, 0x0c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x65, 0x5e, 0x04, 0xfa, 0xf0, 0x0c,
+0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc, 0xfc, 0x03, 0x80,
+0xfe, 0x26, 0x00, 0x4c, 0x61, 0x11, 0x83, 0x16, 0x00, 0xce, 0x01, 0x01, 0x60,
+0x10, 0xc0, 0x16, 0x00, 0x46, 0x00, 0x2c, 0xe1, 0x04, 0x00, 0xf0, 0x1e, 0x30,
+0xec, 0x14, 0xc0, 0x14, 0x02, 0x20, 0xfc, 0x1e, 0xc0, 0x98, 0x40, 0x00, 0xe8,
+0xf1, 0x98, 0xfc, 0x0b, 0x80, 0xf6, 0xf4, 0xbc, 0x64, 0xfd, 0x53, 0xc1, 0xc3,
+0xc1, 0x06, 0x00, 0x01, 0x60, 0x00, 0xe0, 0x06, 0x00, 0x62, 0xf4, 0x93, 0xdd,
+0x4c, 0x04, 0x7c, 0xfa, 0xc6, 0x54, 0x84, 0x60, 0x06, 0x0f, 0xdc, 0xe0, 0x04,
+0x2a, 0xae, 0x4d, 0x01, 0x60, 0x10, 0xc0, 0x29, 0x14, 0x70, 0x60, 0x8e, 0x51,
+0x00, 0x60, 0x10, 0xc0, 0x3b, 0x0e, 0x9c, 0x65, 0x06, 0x52, 0x94, 0xe0, 0x1b,
+0x00, 0x20, 0x60, 0x0e, 0x57, 0x08, 0x60, 0x10, 0xc0, 0x0b, 0x00, 0x1c, 0xe0,
+0x13, 0x20, 0x01, 0x81, 0x13, 0x84, 0x13, 0x86, 0x11, 0x9e, 0x1c, 0x98, 0x0c,
+0xa0, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0xac, 0x0a, 0x98, 0x1a, 0xac,
+0x0d, 0xc3, 0xf5, 0x82, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0x2c, 0x01,
+0x8b, 0x08, 0x56, 0x2c, 0xef, 0x0a, 0xa0, 0x01, 0x82, 0x0c, 0xa0, 0x0a, 0xa0,
+0x8a, 0x00, 0x24, 0xe7, 0xf5, 0xfa, 0x05, 0x0e, 0xbc, 0x61, 0x06, 0x04, 0x00,
+0x60, 0x00, 0xf0, 0x13, 0x0e, 0xb8, 0x79, 0x06, 0x06, 0x00, 0x60, 0x00, 0xe0,
+0x2f, 0x80, 0x2b, 0x07, 0x0e, 0x45, 0x01, 0x60, 0x10, 0xc0, 0x1c, 0x88, 0x95,
+0x86, 0x07, 0xe1, 0x09, 0xbe, 0x0b, 0xa1, 0x05, 0x86, 0x21, 0xff, 0x1d, 0x84,
+0x85, 0x02, 0x00, 0x00, 0x08, 0x8c, 0x10, 0x04, 0x18, 0xec, 0x8a, 0x04, 0x14,
+0xed, 0xb5, 0x80, 0x8a, 0x02, 0x40, 0xe1, 0x75, 0x82, 0x01, 0x84, 0x14, 0x24,
+0x09, 0xbe, 0x29, 0x02, 0x00, 0xe3, 0x20, 0x44, 0x80, 0xf3, 0x2f, 0xa0, 0x21,
+0x42, 0x00, 0xe3, 0x8e, 0x41, 0x09, 0x60, 0x10, 0x40, 0xc6, 0x0d, 0x30, 0xe1,
+0x04, 0x80, 0x0b, 0xa3, 0x75, 0xa0, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0xc5, 0x9e, 0xa8, 0x54, 0x00, 0x68, 0x01, 0x81, 0x04, 0x24, 0x11, 0x85,
+0x11, 0x40, 0x20, 0x60, 0x4e, 0x43, 0x00, 0x60, 0x10, 0xc0, 0x04, 0xa4, 0x29,
+0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24, 0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84,
+0x2b, 0xa1, 0x15, 0xfe, 0xa8, 0x52, 0x44, 0xef, 0x04, 0xaa, 0x08, 0x80, 0x0b,
+0xa5, 0xe5, 0x80, 0x04, 0xa8, 0x09, 0x00, 0x20, 0xe1, 0x0b, 0xa1, 0x48, 0x4a,
+0x44, 0x8f, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0x2c,
+0xfc, 0x1e, 0xc0, 0x14, 0xc0, 0x14, 0x02, 0x08, 0xfc, 0x1e, 0x40, 0x11, 0x83,
+0x04, 0xa4, 0x11, 0x40, 0xb0, 0x67, 0x11, 0x81, 0x04, 0xa6, 0x11, 0x40, 0x10,
+0xe0, 0xb8, 0x73, 0x74, 0xef, 0x84, 0x20, 0x70, 0x06, 0x00, 0xe7, 0x8e, 0x17,
+0x01, 0x60, 0x10, 0x40, 0xce, 0x54, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xe0, 0x04,
+0x00, 0x04, 0xfc, 0x0e, 0xe0, 0x04, 0xe0, 0x04, 0x00, 0x0c, 0xfc, 0x0e, 0xe0,
+0x04, 0xe0, 0x04, 0x00, 0x10, 0xfc, 0x0e, 0xe0, 0x45, 0x10, 0x2c, 0xe0, 0x44,
+0x00, 0x00, 0xfc, 0x05, 0x50, 0x2c, 0x60, 0x4e, 0x41, 0x08, 0x60, 0x10, 0xc0,
+0x1b, 0x0e, 0xa0, 0xe1, 0x04, 0x80, 0x2b, 0x0e, 0x9c, 0xe1, 0x1b, 0xc2, 0x03,
+0x02, 0xce, 0x42, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x04, 0x03, 0x84, 0x00, 0x00,
+0xf4, 0xec, 0x0e, 0xac, 0x09, 0x0e, 0x00, 0xe3, 0x4e, 0x8f, 0x0b, 0xa5, 0x0b,
+0x0e, 0x10, 0xd8, 0x95, 0x96, 0x0b, 0x0e, 0x84, 0xe1, 0x15, 0x0e, 0x10, 0xe0,
+0x03, 0x4e, 0x10, 0xf8, 0x1d, 0xc1, 0x13, 0x4e, 0x10, 0x58, 0x10, 0x40, 0x00,
+0xdc, 0x1a, 0x5e, 0x04, 0x04, 0x80, 0xfb, 0x2d, 0xc3, 0x13, 0x4e, 0x10, 0x58,
+0x10, 0x40, 0x00, 0xdc, 0x85, 0x89, 0x0b, 0xa1, 0x98, 0x0c, 0x00, 0xc0, 0x68,
+0x7b, 0x68, 0x6f, 0x01, 0x85, 0x60, 0x00, 0x10, 0x64, 0x11, 0x81, 0xc8, 0x71,
+0x54, 0xef, 0x28, 0x45, 0x70, 0xef, 0x09, 0x0e, 0xe8, 0xea, 0x4a, 0x40, 0x90,
+0xe0, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x4b, 0x00, 0xc8, 0x09, 0x0e, 0x5c, 0xeb,
+0x0a, 0x40, 0x80, 0xe0, 0x25, 0xac, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0x85, 0xaa, 0x0a, 0x40, 0x90, 0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1,
+0x04, 0x80, 0x14, 0xc0, 0x14, 0x02, 0x2c, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c,
+0xeb, 0x0a, 0x40, 0x84, 0xe0, 0x25, 0x82, 0xb8, 0x68, 0x30, 0x6f, 0x01, 0x81,
+0xc8, 0x58, 0x2c, 0x6f, 0x0b, 0x8c, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x94,
+0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1, 0x04, 0x80, 0x14, 0xc0, 0x14, 0x02,
+0x08, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x98, 0xe0, 0x18,
+0x49, 0x00, 0xc8, 0x45, 0x91, 0x14, 0x2c, 0x04, 0x00, 0x80, 0xfb, 0x13, 0x20,
+0x06, 0x41, 0x2c, 0xe0, 0x16, 0x40, 0x02, 0x60, 0x46, 0x42, 0x04, 0xe0, 0xc8,
+0x7a, 0xf8, 0xee, 0x0e, 0x2c, 0x0e, 0x4c, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xac,
+0x00, 0x00, 0xd1, 0x6f, 0x0f, 0xc0, 0x0e, 0xac, 0x05, 0x10, 0x40, 0xe0, 0x04,
+0x00, 0x38, 0xfc, 0x0e, 0x98, 0x05, 0x50, 0x40, 0xe0, 0x04, 0xac, 0xd8, 0x63,
+0xfc, 0xef, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x15, 0x00, 0x40, 0xe0, 0x14,
+0x02, 0xb8, 0xfc, 0x1e, 0x98, 0x15, 0x40, 0x40, 0xe0, 0x98, 0x52, 0x30, 0xef,
+0x0b, 0xa1, 0xa5, 0xfe, 0x34, 0x28, 0xc6, 0x0d, 0x30, 0xe1, 0x08, 0x8c, 0x0b,
+0xa5, 0x0b, 0x0e, 0x04, 0xd8, 0x1b, 0x0e, 0x00, 0xc0, 0x2b, 0x0e, 0x04, 0xc0,
+0x3b, 0x0e, 0x08, 0xc0, 0x75, 0x84, 0x08, 0x8e, 0x19, 0x0e, 0x10, 0xeb, 0x12,
+0x40, 0x80, 0xfb, 0x01, 0x4e, 0x10, 0xf8, 0x0a, 0xcc, 0x03, 0x4e, 0x04, 0xf8,
+0x1a, 0xce, 0x1c, 0x9c, 0x2b, 0x06, 0x10, 0xe0, 0x2c, 0x9e, 0x3b, 0x06, 0x14,
+0xe0, 0x3c, 0xdc, 0xa4, 0x8f, 0x03, 0x4e, 0x80, 0xf9, 0x13, 0x4e, 0x7c, 0xf9,
+0x23, 0x4e, 0x78, 0xf9, 0x33, 0x4e, 0x74, 0xf9, 0xc8, 0x6d, 0x38, 0xef, 0xe8,
+0x66, 0x3c, 0xef, 0x04, 0x20, 0xa9, 0xc2, 0xa5, 0x40, 0x2c, 0xe0, 0x19, 0x0c,
+0x40, 0xe6, 0x1b, 0xa1, 0x55, 0x88, 0x14, 0xc0, 0x19, 0xd0, 0x1e, 0xc0, 0x14,
+0xc0, 0x19, 0xe0, 0x1e, 0xc0, 0x14, 0xc0, 0x19, 0xc4, 0x1e, 0x40, 0x01, 0x83,
+0xa8, 0x41, 0x00, 0xe8, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0xac, 0xfc, 0x1e,
+0xc0, 0x14, 0xc0, 0x19, 0xc8, 0x1e, 0xc0, 0x48, 0x51, 0x74, 0xef, 0x04, 0x26,
+0x11, 0x83, 0x11, 0x40, 0x10, 0xe0, 0x04, 0xa4, 0x11, 0x40, 0x20, 0x60, 0x11,
+0x81, 0x04, 0xa4, 0x11, 0x40, 0xb0, 0xe7, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd,
+0x1b, 0x00, 0x06, 0x01, 0x38, 0xe1, 0x04, 0x80, 0xc8, 0x20, 0x04, 0x60, 0x01,
+0xb8, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04,
+0x00, 0xfa, 0xe6, 0x41, 0x84, 0x60, 0x8e, 0x42, 0x00, 0x60, 0x10, 0xc0, 0x08,
+0x80, 0x14, 0x84, 0x0b, 0x21, 0xa6, 0x41, 0x04, 0xe1, 0x1c, 0x80, 0x05, 0x82,
+0x28, 0x47, 0x64, 0xef, 0x88, 0x7b, 0x28, 0x6f, 0x46, 0x40, 0x04, 0xe0, 0x35,
+0x81, 0x38, 0x56, 0x40, 0xef, 0xd8, 0x67, 0x64, 0xef, 0x38, 0x63, 0x64, 0xef,
+0xd8, 0x44, 0x44, 0xef, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80, 0x09, 0x00, 0x50,
+0xeb, 0x0a, 0x40, 0x88, 0xe0, 0x76, 0x00, 0xa0, 0xc0, 0x68, 0x4e, 0x50, 0xcf,
+0xc6, 0x0d, 0x30, 0xe1, 0x09, 0x0c, 0x30, 0xe7, 0x0b, 0xa1, 0x78, 0x6c, 0x70,
+0xcf, 0x98, 0x7f, 0x6c, 0xef, 0x38, 0x4b, 0x2c, 0x6f, 0x0b, 0x8c, 0xc8, 0x5a,
+0x30, 0x6f, 0x01, 0x81, 0x58, 0x49, 0x30, 0xef, 0xb8, 0x5f, 0x70, 0xef, 0x01,
+0x83, 0x01, 0x4c, 0xc0, 0xe5, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1,
+0x0c, 0x04, 0x84, 0xfa, 0x06, 0x4e, 0x88, 0x60, 0x06, 0x0c, 0x0c, 0xe1, 0x05,
+0x0e, 0x38, 0x78, 0x06, 0x04, 0x00, 0x70, 0x40, 0xc0, 0x19, 0x0c, 0x94, 0x78,
+0x06, 0x06, 0x10, 0x60, 0x40, 0xc0, 0x08, 0xc0, 0x4a, 0x02, 0x00, 0xe0, 0x09,
+0x86, 0x07, 0xc6, 0x2f, 0x61, 0x3f, 0xe1, 0x21, 0x4e, 0x10, 0xf8, 0x0c, 0x00,
+0x00, 0xe2, 0x31, 0x4e, 0x0c, 0xf8, 0x18, 0x62, 0x30, 0xef, 0x05, 0x0e, 0x38,
+0xf8, 0x14, 0x98, 0x09, 0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0xe0, 0x65, 0x82,
+0x0b, 0x0c, 0x28, 0x78, 0x1b, 0xa1, 0x0c, 0xde, 0x05, 0x84, 0x0a, 0x01, 0x20,
+0xec, 0xf5, 0x84, 0x55, 0x03, 0x01, 0x93, 0x0b, 0x0c, 0x24, 0x78, 0x1b, 0xa1,
+0x0c, 0xde, 0x65, 0x82, 0x0a, 0x01, 0x24, 0xec, 0xb5, 0x82, 0xb5, 0x01, 0x01,
+0x95, 0x0a, 0x01, 0x48, 0xec, 0x15, 0x82, 0x65, 0x01, 0x01, 0xa7, 0x0a, 0x01,
+0x50, 0xec, 0x75, 0x80, 0x01, 0xab, 0x0c, 0xde, 0x15, 0x0c, 0x20, 0x79, 0x06,
+0x04, 0x00, 0x60, 0x00, 0xf0, 0x49, 0x0c, 0x64, 0x78, 0x06, 0x06, 0x00, 0x60,
+0x00, 0xe0, 0x2f, 0x82, 0x3b, 0x0c, 0x8c, 0x78, 0x2b, 0x87, 0x30, 0x42, 0x00,
+0x5c, 0x4a, 0x08, 0x14, 0xfc, 0x14, 0x04, 0x80, 0x7a, 0xce, 0x43, 0x00, 0x60,
+0x10, 0xc0, 0x2c, 0x04, 0x8e, 0x48, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x90, 0x85,
+0x88, 0x45, 0x0e, 0x30, 0xf8, 0x3b, 0x08, 0x18, 0xe0, 0x5b, 0x08, 0x20, 0xe0,
+0x4b, 0x08, 0x1c, 0xe0, 0x32, 0x4a, 0x94, 0xf1, 0x53, 0x88, 0x8a, 0x0a, 0x89,
+0x63, 0x05, 0xc0, 0xb5, 0x84, 0x54, 0x1a, 0x8e, 0x59, 0x09, 0x60, 0x10, 0xc0,
+0x71, 0x83, 0x7e, 0x30, 0x2e, 0x59, 0x09, 0x60, 0x10, 0xc0, 0x5c, 0x30, 0x54,
+0x08, 0x80, 0xfb, 0x4d, 0x44, 0x4e, 0x45, 0x09, 0x60, 0x10, 0xc0, 0x3e, 0x88,
+0x43, 0x42, 0x00, 0xc0, 0x65, 0x81, 0x8e, 0x43, 0x09, 0x60, 0x10, 0x40, 0x21,
+0x81, 0x2e, 0x84, 0x03, 0x4c, 0xd5, 0x79, 0x02, 0x40, 0xd6, 0x41, 0x00, 0xe0,
+0x0c, 0x0c, 0x84, 0xf2, 0x18, 0x34, 0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0xfc,
+0xfa, 0x6b, 0x00, 0x06, 0x41, 0x08, 0xe0, 0x98, 0x73, 0x48, 0xef, 0x0b, 0xa1,
+0x78, 0x14, 0x00, 0xc0, 0x86, 0x52, 0x84, 0xe0, 0x74, 0xe4, 0x08, 0x9c, 0x09,
+0x86, 0x0b, 0xa3, 0x36, 0x40, 0x98, 0x40, 0x06, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xf5, 0x80, 0x21, 0x01, 0x36, 0x40, 0x98, 0xe0, 0x2e, 0x18, 0x11,
+0x83, 0x16, 0x80, 0x84, 0x26, 0x16, 0x44, 0x98, 0xe0, 0x18, 0x9c, 0x09, 0x10,
+0xb4, 0xe0, 0x0a, 0x02, 0x0c, 0xfc, 0x0c, 0x02, 0x00, 0xe2, 0x01, 0x4c, 0x10,
+0xe1, 0x0b, 0x10, 0x80, 0xe0, 0x16, 0x88, 0xe8, 0x75, 0xf8, 0xee, 0x05, 0x4c,
+0x48, 0xe0, 0x08, 0x9c, 0x00, 0x42, 0x08, 0xfc, 0x19, 0x86, 0x1b, 0xa5, 0xd5,
+0x80, 0x4a, 0x00, 0x0c, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x0e, 0x98, 0x09, 0x10,
+0xb0, 0xe0, 0x98, 0x51, 0x00, 0x68, 0x09, 0x82, 0x01, 0x4c, 0x18, 0x60, 0x11,
+0x9f, 0x0b, 0x10, 0x88, 0xe0, 0x78, 0x51, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x4c, 0xe0, 0x0b, 0x02, 0x84, 0x60, 0x11, 0x91, 0xf8, 0x50, 0x00, 0xe8, 0xb4,
+0xa6, 0x05, 0x4c, 0x50, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x0a, 0x40, 0x84, 0x60,
+0x06, 0x00, 0x00, 0x60, 0x48, 0xd0, 0x56, 0x01, 0x55, 0x15, 0x41, 0xd0, 0x05,
+0x4c, 0x34, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x68, 0x4f, 0x00, 0x68, 0x09, 0x82,
+0x14, 0x64, 0x06, 0x54, 0xdc, 0xe0, 0x01, 0x4c, 0x10, 0x60, 0xce, 0x41, 0x00,
+0x60, 0x10, 0xc0, 0x0a, 0x80, 0x2b, 0x02, 0x18, 0xe0, 0x23, 0xa0, 0x23, 0x4c,
+0x60, 0xe0, 0x09, 0x14, 0x70, 0xea, 0x22, 0x40, 0x80, 0xf1, 0x03, 0x4c, 0x60,
+0xe0, 0x25, 0x14, 0xe0, 0xe2, 0x2b, 0xa3, 0x35, 0x88, 0x24, 0xa4, 0x3b, 0x02,
+0x18, 0xe0, 0x4b, 0x02, 0x20, 0xe0, 0x1b, 0x02, 0x1c, 0xe0, 0x59, 0x04, 0x24,
+0xe0, 0x33, 0xa8, 0x32, 0x44, 0x84, 0x70, 0x2e, 0x47, 0x08, 0x60, 0x10, 0xc0,
+0x20, 0x02, 0xfc, 0x6c, 0x0a, 0x4a, 0x88, 0xe0, 0x1c, 0x0c, 0x20, 0x02, 0x14,
+0xcc, 0x10, 0x02, 0xf0, 0xcc, 0x13, 0x46, 0x00, 0x40, 0x0a, 0x41, 0x04, 0xec,
+0x13, 0x4c, 0x60, 0x40, 0x10, 0x40, 0x00, 0xdc, 0x15, 0x14, 0xdc, 0x62, 0x04,
+0x00, 0x80, 0xfb, 0x86, 0x05, 0x74, 0xe1, 0x1d, 0xc1, 0x13, 0x4c, 0x60, 0x40,
+0x14, 0x00, 0x80, 0xdb, 0x14, 0xa4, 0x0e, 0xa8, 0x04, 0x88, 0x7a, 0x84, 0x73,
+0x4c, 0x64, 0xe0, 0xe8, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x8e, 0x40, 0xb1, 0x66,
+0x9b, 0xd3, 0x08, 0x6d, 0xf8, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x85, 0x4c, 0x3c,
+0xe0, 0x28, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0x28, 0x6d, 0xf8, 0xee,
+0x48, 0x6c, 0xf8, 0x6e, 0x0e, 0x02, 0x00, 0x60, 0x1e, 0xd1, 0x05, 0x4c, 0x40,
+0x60, 0x11, 0x91, 0x0b, 0x16, 0x64, 0xe0, 0x7b, 0x16, 0x5c, 0xe0, 0x89, 0x16,
+0xc0, 0xe0, 0x08, 0x49, 0x00, 0xe8, 0x3b, 0x00, 0x80, 0x44, 0x7c, 0xf0, 0x81,
+0x01, 0x11, 0x81, 0xb8, 0x40, 0x40, 0x6f, 0x0b, 0x8e, 0x4b, 0x0c, 0x60, 0x60,
+0x06, 0x0e, 0x00, 0x60, 0xe0, 0xcf, 0x0e, 0x5a, 0x11, 0x81, 0x21, 0x01, 0x06,
+0x06, 0x01, 0x60, 0xe0, 0xcf, 0xd8, 0x7f, 0x3c, 0x6f, 0x0b, 0x88, 0x14, 0x64,
+0x86, 0x57, 0x94, 0xe1, 0x05, 0x4c, 0x2c, 0xe0, 0x0a, 0x86, 0x83, 0x4c, 0xe4,
+0xe0, 0x03, 0x4c, 0xe0, 0xe0, 0x08, 0x86, 0x09, 0xbe, 0x03, 0x4c, 0xe8, 0xe0,
+0x08, 0x86, 0x09, 0xbe, 0x07, 0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x54,
+0xe0, 0x0a, 0xc4, 0x03, 0x4c, 0xec, 0xe0, 0x0a, 0x44, 0x11, 0x91, 0xe8, 0x45,
+0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x58, 0xe0, 0x0a, 0x46, 0x11, 0x91, 0x78,
+0x45, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x5c, 0xe0, 0x0b, 0x02, 0x10, 0x60,
+0x11, 0x91, 0xf8, 0x44, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x60, 0xe0, 0x0b,
+0x02, 0x14, 0x60, 0x11, 0x91, 0x78, 0x44, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x64, 0xe0, 0x85, 0x4c, 0x14, 0xe0, 0x85, 0x4c, 0x18, 0xe0, 0x0b, 0x02, 0x7c,
+0x60, 0x11, 0x91, 0xb8, 0x43, 0x00, 0xe8, 0x05, 0x4c, 0x10, 0x60, 0x11, 0x91,
+0x81, 0x4c, 0x14, 0xe0, 0x75, 0x4c, 0x1c, 0xe0, 0x09, 0x14, 0xd0, 0xea, 0x07,
+0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x20, 0xe0, 0x0b, 0x14, 0x94, 0xe5,
+0x98, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x38, 0x60, 0x11, 0x91, 0x0b, 0x14, 0x64,
+0xe5, 0x28, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x28, 0x60, 0x11, 0x81, 0x09, 0x14,
+0x8c, 0x6a, 0x21, 0x81, 0x0b, 0x12, 0x7c, 0x60, 0x0a, 0x40, 0x88, 0xe0, 0xd6,
+0x0e, 0xcd, 0x0c, 0x73, 0xcf, 0x75, 0x4c, 0x24, 0x60, 0x3b, 0x8e, 0x28, 0x79,
+0x3c, 0xef, 0x0e, 0xd8, 0x16, 0x41, 0x08, 0xe0, 0x0c, 0x0c, 0xfc, 0xf2, 0xb8,
+0x1e, 0x48, 0xff, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2,
+0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x88, 0x61, 0x28, 0xef, 0x4c,
+0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x88, 0x5d,
+0x48, 0x6f, 0xf6, 0x40, 0x04, 0xe0, 0x0b, 0xa1, 0x85, 0x80, 0x0c, 0x0c, 0x00,
+0xf2, 0x93, 0xdd, 0xb8, 0x6b, 0x54, 0xef, 0xc6, 0x4c, 0x84, 0xe0, 0x04, 0x98,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0x85, 0x88, 0xc6, 0x40, 0xb4,
+0x60, 0x46, 0x45, 0x00, 0xe0, 0x19, 0x00, 0xb4, 0xe4, 0x16, 0x42, 0x0a, 0xe0,
+0x03, 0x82, 0x09, 0x00, 0x40, 0xe2, 0x0b, 0xa5, 0xf5, 0x84, 0x48, 0x56, 0x40,
+0x6f, 0x01, 0x85, 0x58, 0x4c, 0x40, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0xd8, 0x7d,
+0x44, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x58, 0x50, 0x40, 0xef, 0x18, 0x70, 0xf4,
+0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x70,
+0xf4, 0xcf, 0x09, 0x0c, 0x48, 0xe0, 0x0b, 0xa1, 0x95, 0x84, 0xe8, 0x70, 0x38,
+0xef, 0x06, 0x40, 0x94, 0x60, 0x16, 0x07, 0x04, 0xe1, 0x04, 0x80, 0x1b, 0x00,
+0x18, 0xe1, 0x2b, 0x00, 0x50, 0xe1, 0x48, 0x8c, 0x39, 0x06, 0xac, 0xe0, 0x13,
+0xa4, 0x13, 0xa8, 0x13, 0xa6, 0x15, 0x40, 0x2c, 0xe0, 0xc6, 0x00, 0x2c, 0x61,
+0x11, 0xa1, 0x04, 0x80, 0x13, 0x40, 0xd0, 0x65, 0xe6, 0x40, 0xe0, 0xe0, 0x0a,
+0x00, 0x06, 0x42, 0x94, 0xe0, 0x14, 0x84, 0x03, 0x42, 0x10, 0x62, 0x06, 0x41,
+0x04, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x98, 0x17, 0x48, 0xff, 0xc3, 0xc1, 0x66,
+0x01, 0xcc, 0xe0, 0x18, 0x80, 0x0a, 0x42, 0x84, 0xe0, 0x10, 0x42, 0xf8, 0xd3,
+0x11, 0x40, 0x00, 0xc0, 0x93, 0xdd, 0x4a, 0x40, 0x80, 0xe0, 0x55, 0x84, 0x14,
+0x04, 0xa0, 0x60, 0x0e, 0x06, 0x00, 0x70, 0x3f, 0xc0, 0x2f, 0x06, 0x14, 0x06,
+0xa0, 0xe1, 0x14, 0x00, 0xe0, 0x61, 0x30, 0x46, 0x00, 0x70, 0xff, 0xc0, 0x17,
+0x70, 0x3f, 0xa0, 0x1f, 0xa4, 0x1f, 0xa6, 0x93, 0x5d, 0x0b, 0x82, 0x4c, 0x04,
+0x04, 0xfa, 0x86, 0x0d, 0xcc, 0x60, 0x71, 0x81, 0x7c, 0x98, 0x88, 0x55, 0x68,
+0xef, 0x0a, 0x98, 0x00, 0x42, 0xf1, 0x73, 0xff, 0xc0, 0x19, 0xc2, 0x1c, 0x18,
+0x60, 0x02, 0xe1, 0x65, 0x02, 0xc0, 0x65, 0x81, 0x24, 0x84, 0x2b, 0xa1, 0xf5,
+0x80, 0x71, 0x02, 0x11, 0x88, 0x0a, 0x0e, 0x00, 0xe1, 0x35, 0xfe, 0x75, 0x81,
+0x77, 0x50, 0x00, 0x40, 0xf0, 0xf3, 0x0f, 0xae, 0x09, 0xc4, 0x0c, 0x98, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x4c, 0x04, 0x7c, 0xfa, 0xbb, 0x06, 0x6b, 0x82,
+0x14, 0x2c, 0x9b, 0x80, 0x74, 0x24, 0xab, 0x84, 0xa4, 0x00, 0xa0, 0x61, 0x8b,
+0x88, 0x1b, 0x81, 0x65, 0x82, 0x0e, 0x2c, 0x0b, 0x8e, 0x78, 0x56, 0x30, 0x6f,
+0x1b, 0x8c, 0x24, 0x2c, 0x0b, 0x8e, 0x28, 0x59, 0x30, 0x6f, 0x1b, 0x8c, 0x04,
+0x18, 0xb1, 0x83, 0x11, 0x01, 0x21, 0x83, 0x38, 0x44, 0x00, 0x68, 0x31, 0x85,
+0xa4, 0x02, 0x80, 0x7a, 0x21, 0x8b, 0xd8, 0x43, 0x00, 0x68, 0x06, 0x46, 0xfc,
+0xe1, 0xab, 0x00, 0x0b, 0x8e, 0x78, 0x40, 0xf8, 0x6f, 0x1b, 0x94, 0x09, 0x12,
+0x10, 0x60, 0x1b, 0x90, 0x98, 0x79, 0xfc, 0x6f, 0x70, 0x12, 0x10, 0xe4, 0x1b,
+0x00, 0x0b, 0x92, 0xb8, 0x7f, 0xf4, 0xef, 0x11, 0x03, 0x21, 0x85, 0x31, 0x09,
+0x0b, 0x94, 0x58, 0x42, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x18, 0x7f, 0xf4,
+0x6f, 0x1b, 0x90, 0x70, 0x12, 0x50, 0x64, 0x36, 0x54, 0x48, 0xe1, 0x08, 0x42,
+0x00, 0x68, 0x0b, 0x92, 0x0a, 0x00, 0x00, 0xfb, 0xb1, 0x54, 0x00, 0x40, 0x0a,
+0x40, 0xa0, 0xe0, 0xe5, 0xfc, 0x11, 0x01, 0x21, 0x85, 0x31, 0x09, 0x0b, 0x90,
+0xc8, 0x40, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x88, 0x7d, 0xf4, 0x6f, 0x1b,
+0x90, 0x8e, 0x98, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1f, 0x25,
+0x32, 0x40, 0x80, 0xf8, 0x1f, 0x86, 0x93, 0x5d, 0x0f, 0xa2, 0x04, 0x80, 0x93,
+0xdd, 0x0c, 0x00, 0x80, 0xfa, 0xa8, 0x4c, 0x48, 0x6f, 0xe6, 0x40, 0x08, 0xe0,
+0x0b, 0xa1, 0x95, 0x84, 0xc8, 0x46, 0x40, 0xef, 0x86, 0x02, 0x84, 0x60, 0xc6,
+0x04, 0x84, 0xe0, 0x14, 0x04, 0x5a, 0x00, 0x08, 0xe0, 0x24, 0x88, 0x4a, 0x42,
+0x08, 0x60, 0x46, 0x02, 0x84, 0xe0, 0x0a, 0x42, 0x80, 0xe1, 0x0c, 0x00, 0x00,
+0xe2, 0x06, 0x84, 0xf6, 0x40, 0x08, 0xe0, 0x0c, 0x08, 0x80, 0xf2, 0xd8, 0x0a,
+0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0xf0, 0x1e, 0x60, 0x6c, 0xa6,
+0x01, 0x04, 0xe0, 0x48, 0x4a, 0x48, 0xef, 0x0b, 0xa1, 0x15, 0x90, 0xc8, 0x5d,
+0x2c, 0xef, 0xe8, 0x7a, 0xf4, 0x6e, 0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0x86,
+0x50, 0x84, 0xe0, 0x69, 0x10, 0x44, 0xe0, 0x58, 0x7a, 0xf4, 0x6e, 0x1b, 0x8c,
+0x04, 0x60, 0x7b, 0x80, 0x1b, 0x00, 0x28, 0xe0, 0x0b, 0x00, 0x24, 0xe0, 0x13,
+0xa0, 0x10, 0x00, 0x04, 0x64, 0x1b, 0x8c, 0x88, 0x79, 0xf4, 0xee, 0x14, 0xa0,
+0x19, 0x02, 0x28, 0xe0, 0x19, 0xbe, 0x06, 0x44, 0x06, 0x60, 0x06, 0x41, 0xdc,
+0xe0, 0x04, 0x00, 0x16, 0x42, 0x1e, 0xe0, 0x21, 0xbe, 0x1e, 0x7c, 0x27, 0xcb,
+0x2c, 0x7c, 0x0e, 0x02, 0x25, 0x74, 0x03, 0xc0, 0x48, 0x78, 0xf4, 0xee, 0x0e,
+0x7e, 0x11, 0x81, 0x1e, 0x09, 0xf0, 0x00, 0x10, 0xe4, 0x1c, 0x95, 0x88, 0x7e,
+0x1c, 0xef, 0x0a, 0x95, 0x18, 0x41, 0x00, 0x68, 0x01, 0xa0, 0x06, 0x00, 0x38,
+0x61, 0x11, 0x83, 0x78, 0x7c, 0x1c, 0xef, 0x18, 0x46, 0x48, 0x6f, 0xb6, 0x01,
+0x04, 0xe0, 0xf1, 0xb0, 0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1b,
+0x00, 0x06, 0x00, 0x38, 0xe1, 0x04, 0x80, 0x08, 0x35, 0xf4, 0x7f, 0x01, 0xc8,
+0xc3, 0xc1, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x4e, 0x84, 0x60, 0xf0, 0x1e, 0x20,
+0xee, 0x84, 0x9c, 0x19, 0x0e, 0x48, 0xe0, 0x0a, 0xe2, 0x2a, 0xe0, 0x03, 0xa4,
+0x01, 0x82, 0x38, 0x75, 0xf4, 0x6e, 0x04, 0x00, 0x80, 0xfb, 0x0b, 0x10, 0x64,
+0x60, 0x6b, 0x80, 0x0b, 0xa1, 0x05, 0x0e, 0x04, 0xc0, 0x99, 0x00, 0xa0, 0xc0,
+0xd5, 0x84, 0x09, 0x10, 0x24, 0x60, 0x06, 0x03, 0xd0, 0xe0, 0x1a, 0x84, 0x07,
+0xc5, 0x09, 0x82, 0x1f, 0x21, 0x06, 0x00, 0x04, 0xe0, 0x10, 0x42, 0x01, 0x70,
+0xff, 0xc0, 0x17, 0xd1, 0x1b, 0xa1, 0x78, 0x73, 0xf4, 0x6e, 0x16, 0x02, 0x00,
+0x80, 0x00, 0x12, 0x04, 0xec, 0x18, 0x42, 0x48, 0x6f, 0x36, 0x01, 0x04, 0xe0,
+0x0b, 0xa1, 0xb5, 0xb0, 0x04, 0x1c, 0x06, 0x11, 0xd0, 0xe0, 0x19, 0x10, 0x24,
+0x6e, 0x46, 0x05, 0xb8, 0xe0, 0xa4, 0x9e, 0x09, 0x00, 0xb0, 0xe0, 0x18, 0x08,
+0x5a, 0x02, 0x00, 0xe0, 0x1c, 0x06, 0x00, 0xe2, 0x36, 0x3e, 0x09, 0x86, 0x0b,
+0x21, 0x19, 0x82, 0x0c, 0x04, 0x00, 0x62, 0x0b, 0xa5, 0x16, 0x7c, 0x64, 0x02,
+0x80, 0xfb, 0x26, 0x3c, 0x6b, 0x9e, 0x1e, 0xbe, 0x19, 0x0e, 0x60, 0xe0, 0x2b,
+0x10, 0x30, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x16, 0x1b, 0x11, 0x81, 0x06, 0x7e,
+0x2b, 0xa3, 0x19, 0x14, 0x5c, 0xc0, 0x09, 0x0e, 0x80, 0xe0, 0x25, 0x10, 0xa4,
+0xe3, 0x35, 0x10, 0xa8, 0x63, 0x10, 0x42, 0x3c, 0xd0, 0x0c, 0x0b, 0x01, 0x81,
+0x16, 0x1d, 0x11, 0x81, 0x26, 0x1f, 0x46, 0x05, 0x04, 0xe0, 0x0c, 0x89, 0x06,
+0x99, 0x06, 0xa5, 0x0c, 0x91, 0x3e, 0x8b, 0x0e, 0x91, 0x0e, 0x8f, 0x0e, 0x0d,
+0x60, 0x00, 0xa0, 0xe4, 0x91, 0x5e, 0x90, 0xe0, 0xc8, 0x7a, 0xf4, 0xee, 0x09,
+0x14, 0x58, 0xe0, 0x19, 0x10, 0xf0, 0xf8, 0x0e, 0x3f, 0xfe, 0x41, 0x3d, 0x70,
+0xff, 0xc0, 0x1b, 0x25, 0xf6, 0x43, 0x3c, 0xe0, 0x05, 0x5e, 0x80, 0x60, 0x16,
+0x00, 0x00, 0x80, 0x15, 0x5e, 0x84, 0xe0, 0x01, 0x5e, 0x34, 0x80, 0x05, 0x14,
+0x18, 0xe0, 0x38, 0x47, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a,
+0x20, 0x11, 0x91, 0x0e, 0x97, 0x78, 0x5d, 0xfc, 0x6f, 0x0b, 0x84, 0x19, 0x10,
+0xc0, 0xe9, 0x0e, 0xb7, 0x4a, 0x42, 0x80, 0xe0, 0xc5, 0x82, 0x60, 0x00, 0xc0,
+0x65, 0x11, 0xfb, 0x75, 0x81, 0x24, 0x37, 0x11, 0x82, 0x2e, 0xc1, 0x24, 0x97,
+0x25, 0x40, 0x44, 0xf8, 0x1b, 0xa1, 0x35, 0xfe, 0xe5, 0x85, 0x04, 0x9e, 0x05,
+0x00, 0x1c, 0xe0, 0x28, 0x45, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7,
+0x14, 0x9e, 0x0e, 0x99, 0x05, 0x02, 0x20, 0xe0, 0x98, 0x44, 0x00, 0x68, 0x00,
+0x40, 0xfc, 0x73, 0xff, 0xc7, 0x14, 0x9e, 0x0e, 0x9b, 0x05, 0x02, 0x24, 0xe0,
+0x08, 0x44, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a, 0x22, 0x11,
+0x91, 0x0e, 0x9d, 0x48, 0x5a, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x60, 0x11, 0x91,
+0x0e, 0xb9, 0xe8, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x62, 0x11, 0x91, 0x0e,
+0xbb, 0x88, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x0e, 0xbd, 0x04, 0x9c, 0x09, 0x00,
+0x24, 0xe0, 0x0a, 0x40, 0x88, 0xe0, 0xa5, 0x84, 0x04, 0xb7, 0xf8, 0x7a, 0xf4,
+0x6e, 0x1b, 0x80, 0x14, 0xb9, 0x0e, 0xb7, 0xa8, 0x7a, 0xf4, 0x6e, 0x0b, 0x82,
+0x14, 0xbb, 0x0e, 0xb9, 0x58, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x14, 0xbd, 0x0e,
+0xbb, 0x08, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x0e, 0xbd, 0x86, 0x41, 0x30, 0x61,
+0x1b, 0x9e, 0xd8, 0x4c, 0x08, 0xef, 0x78, 0x75, 0x44, 0x6f, 0x46, 0x01, 0x04,
+0xe0, 0xf0, 0x1e, 0x20, 0xe6, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0x4c, 0x00,
+0x00, 0xfa, 0x08, 0x74, 0x24, 0x6f, 0x11, 0x8f, 0x4c, 0x08, 0x00, 0xf2, 0x93,
+0xdd, 0x0c, 0x04, 0x7c, 0xfe, 0x6b, 0x04, 0xf0, 0x1e, 0x60, 0xec, 0xb5, 0x0c,
+0x10, 0xe0, 0x85, 0x0c, 0x18, 0xe0, 0x79, 0x0c, 0x24, 0xe0, 0xa4, 0xda, 0x1e,
+0x0b, 0x11, 0x81, 0x0e, 0x7e, 0x0b, 0x96, 0x68, 0x79, 0xf4, 0xee, 0x0b, 0xa1,
+0x15, 0x82, 0x14, 0x18, 0x0b, 0x96, 0x88, 0x79, 0xf4, 0xee, 0x0b, 0xa1, 0xb5,
+0x4c, 0x00, 0xc0, 0x11, 0x01, 0x0b, 0x94, 0x98, 0x78, 0xf4, 0xee, 0x94, 0x18,
+0x0b, 0xa1, 0x05, 0x1e, 0x14, 0xc0, 0xbe, 0xbe, 0x75, 0x82, 0x0b, 0x14, 0x1b,
+0x92, 0xf8, 0x78, 0xf4, 0xee, 0x04, 0x0b, 0x0b, 0xa1, 0xa5, 0x4c, 0x00, 0x40,
+0xa0, 0x52, 0x00, 0xdc, 0xae, 0x7c, 0xab, 0x80, 0x77, 0x42, 0x0b, 0x92, 0x15,
+0x14, 0x1c, 0xfe, 0x58, 0x75, 0xf4, 0xee, 0x0b, 0x14, 0x44, 0x60, 0x9b, 0x80,
+0x88, 0x75, 0xf4, 0xee, 0xbb, 0x00, 0x0b, 0x92, 0x08, 0x77, 0xf4, 0x6e, 0x1b,
+0x96, 0x0b, 0xa1, 0x90, 0x56, 0x00, 0x1c, 0xa0, 0x12, 0x88, 0xe4, 0xc8, 0x75,
+0xf4, 0x6e, 0x0b, 0x96, 0xab, 0x00, 0x01, 0x81, 0x01, 0x4c, 0x20, 0x60, 0x8b,
+0xa1, 0x04, 0x7e, 0x76, 0x03, 0x71, 0x7b, 0x34, 0xce, 0xac, 0xd8, 0x03, 0x8e,
+0x00, 0x00, 0x70, 0xe7, 0x0e, 0x89, 0x65, 0x88, 0x8b, 0xa3, 0x35, 0x08, 0x06,
+0x02, 0x01, 0x00, 0xc0, 0xcf, 0x58, 0x74, 0xf4, 0x6e, 0x0b, 0x96, 0x28, 0x73,
+0xf4, 0x6e, 0x8b, 0x80, 0x1b, 0x00, 0x0b, 0x96, 0x58, 0x73, 0xf4, 0xee, 0xd8,
+0x49, 0x00, 0xe8, 0x18, 0x74, 0xf4, 0x6e, 0x76, 0x03, 0x70, 0x7b, 0x34, 0xce,
+0x0b, 0xa1, 0x25, 0x84, 0x11, 0x01, 0x0b, 0x96, 0x48, 0x74, 0xf4, 0xee, 0x0b,
+0xa1, 0x80, 0x14, 0x04, 0x04, 0x80, 0x54, 0x00, 0xdc, 0x75, 0x81, 0xc8, 0x70,
+0xf4, 0x6e, 0x0b, 0x96, 0x58, 0x72, 0xf4, 0xee, 0xab, 0x80, 0xac, 0xd8, 0x0a,
+0xa4, 0x08, 0x71, 0xf4, 0xee, 0x04, 0x0b, 0x9b, 0x80, 0x73, 0x80, 0x0b, 0x0e,
+0x40, 0xe0, 0x98, 0x70, 0xf4, 0xee, 0xbb, 0x00, 0xa4, 0x00, 0x80, 0xfb, 0x48,
+0x70, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x96, 0x68, 0x49, 0x00, 0xe8, 0x1b, 0x00,
+0x0b, 0x92, 0xc8, 0x47, 0x00, 0xe8, 0xa8, 0x70, 0xf4, 0xee, 0x04, 0x09, 0xab,
+0x80, 0xac, 0xd8, 0x9a, 0x80, 0x9c, 0xda, 0x0b, 0x0e, 0x4c, 0xe0, 0x18, 0x6f,
+0xf4, 0xee, 0x0b, 0x0e, 0x48, 0x60, 0xbb, 0x80, 0xc8, 0x6e, 0xf4, 0xee, 0x8b,
+0x00, 0x0b, 0x92, 0x88, 0x6e, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0xa8, 0x47,
+0x00, 0xe8, 0x1b, 0x00, 0x0b, 0x96, 0x08, 0x46, 0x00, 0xe8, 0xe8, 0x6e, 0xf4,
+0xee, 0xb4, 0x3e, 0x9b, 0x80, 0x84, 0xfc, 0x9c, 0xda, 0x1b, 0x96, 0xf8, 0x6d,
+0xf4, 0x6e, 0x0b, 0x90, 0x68, 0x44, 0x00, 0xe8, 0xe8, 0x6e, 0xf4, 0x6e, 0x11,
+0x81, 0x0b, 0xa1, 0x85, 0x8e, 0x11, 0x01, 0x0b, 0x96, 0xf8, 0x6d, 0xf4, 0xee,
+0x2a, 0x00, 0x00, 0x60, 0x0b, 0x94, 0x24, 0x7e, 0x1b, 0x92, 0x34, 0x8b, 0xf8,
+0x74, 0x18, 0xef, 0x58, 0x01, 0x01, 0x40, 0x00, 0x40, 0x9b, 0x80, 0x0b, 0x16,
+0x1b, 0x92, 0x78, 0x6d, 0xf4, 0xee, 0x0b, 0xa1, 0x85, 0x82, 0x1a, 0xd8, 0x09,
+0x0c, 0x20, 0xe0, 0x11, 0x82, 0x04, 0x09, 0x0b, 0xa1, 0x1c, 0x58, 0x70, 0x00,
+0x90, 0xc4, 0x0a, 0x80, 0x0c, 0xda, 0x11, 0x01, 0x0b, 0x90, 0xe8, 0x6b, 0xf4,
+0xee, 0x0b, 0xa1, 0xb5, 0x84, 0x0b, 0x10, 0x1b, 0x92, 0x88, 0x6c, 0xf4, 0xee,
+0x0b, 0xa1, 0xe5, 0x82, 0x09, 0x0c, 0x20, 0xe0, 0x1a, 0xd8, 0x0b, 0xa1, 0x70,
+0x0e, 0x98, 0x44, 0x10, 0x00, 0x04, 0xec, 0x75, 0x5e, 0x10, 0xc0, 0x0c, 0xd8,
+0x04, 0x89, 0x0a, 0x80, 0x0c, 0xda, 0xf1, 0xb0, 0x0c, 0x0c, 0x7c, 0xf6, 0x93,
+0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x00, 0x11, 0x81, 0xe8, 0x6a, 0xf4, 0xee,
+0x0b, 0xa1, 0x64, 0x0c, 0x7c, 0xdd, 0x0b, 0x8c, 0x0c, 0x0c, 0x00, 0xf2, 0x93,
+0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x02, 0x7b, 0x80, 0x08, 0x6a,
+0xf4, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x48, 0x69, 0xf4, 0x6e, 0x1b, 0x8c, 0x0b,
+0xa1, 0x70, 0x4c, 0x00, 0x1c, 0x8b, 0xa1, 0x70, 0x4c, 0x00, 0xdc, 0x0b, 0x8c,
+0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x6b,
+0x02, 0x7b, 0x80, 0xa8, 0x68, 0xf4, 0xee, 0x0b, 0xa1, 0x70, 0x4c, 0x00, 0x9c,
+0x0b, 0x8c, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00,
+0xfa, 0x6b, 0x00, 0x21, 0x81, 0x04, 0x18, 0x31, 0x83, 0x68, 0x53, 0xfc, 0xef,
+0x0e, 0x98, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x18, 0xa9, 0x00, 0x00, 0x38,
+0x90, 0x40, 0x00, 0x08, 0x14, 0xb8, 0xe0, 0x80, 0x2c, 0x40, 0x00, 0xa8, 0x3a,
+0xe8, 0xe0, 0x08, 0x9f, 0x40, 0x00, 0x28, 0x28, 0xb0, 0xe0, 0x18, 0x72, 0x40,
+0x00, 0x88, 0x19, 0xc8, 0xe0, 0xe0, 0xbb, 0x40, 0x00, 0x08, 0x0c, 0xa4, 0xe0,
+0xa8, 0xe3, 0x40, 0x00, 0x28, 0x13, 0x90, 0xe0, 0xf0, 0xec, 0x40, 0x00, 0x68,
+0x0d, 0x8c, 0xe0, 0x48, 0xa8, 0x40, 0x00, 0xa8, 0x3c, 0xac, 0xe0, 0x28, 0xe4,
+0x40, 0x00, 0x08, 0x0e, 0x94, 0xe0, 0xb8, 0xe2, 0x40, 0x00, 0x28, 0x3d, 0x90,
+0xe0, 0x90, 0xa7, 0x40, 0x00, 0xc8, 0x13, 0xb0, 0xe0, 0x18, 0xc1, 0x40, 0x00,
+0xa8, 0x2a, 0xa4, 0xe0, 0xa8, 0x91, 0x40, 0x00, 0x68, 0x2f, 0xbc, 0xe0, 0x88,
+0xf1, 0x40, 0x00, 0x08, 0x1c, 0x88, 0xe0, 0xd0, 0x8e, 0x40, 0x00, 0xa8, 0x19,
+0xb8, 0xe0, 0x38, 0x82, 0x40, 0x00, 0x28, 0x38, 0xbc, 0xe0, 0x88, 0xe0, 0x40,
+0x00, 0xa8, 0x0e, 0x98, 0xe0, 0xd8, 0xe4, 0x40, 0x00, 0x68, 0x32, 0x94, 0xe0,
+0x18, 0xd7, 0x40, 0x00, 0x28, 0x2d, 0x9c, 0xe0, 0x40, 0xe8, 0x40, 0x00, 0x28,
+0x19, 0x94, 0xe0, 0xa8, 0x78, 0x40, 0x00, 0x28, 0x18, 0xcc, 0xe0, 0xf0, 0x95,
+0x40, 0x00, 0x48, 0x34, 0xbc, 0xe0, 0xa8, 0xd2, 0x40, 0x00, 0x48, 0x16, 0xa0,
+0xe0, 0x18, 0x52, 0x40, 0x00, 0x68, 0x2b, 0xe0, 0xe0, 0x00, 0xb9, 0x00, 0x00,
+0x23, 0xdb, 0x00, 0x00
+};
+
+static const u8 cut2_patch[] = {
+0x37, 0x00, 0x05, 0x00, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x0e, 0x84, 0x60, 0x06,
+0x11, 0x84, 0xe0, 0x04, 0x1c, 0x06, 0x13, 0x88, 0xe1, 0x14, 0xa0, 0x0d, 0xc2,
+0x95, 0x80, 0x04, 0xa4, 0x0b, 0xa3, 0xb5, 0x88, 0x78, 0x6c, 0x40, 0xef, 0x6b,
+0x00, 0x46, 0x01, 0x84, 0xe0, 0x88, 0x53, 0x48, 0x6f, 0x46, 0x15, 0x84, 0xe0,
+0x6b, 0x25, 0xc6, 0x0d, 0x84, 0xe0, 0x95, 0x84, 0x08, 0xa8, 0x4a, 0x00, 0xfc,
+0xfb, 0x05, 0x84, 0x09, 0x12, 0x1c, 0xe0, 0xa8, 0x46, 0x48, 0xef, 0x04, 0x1c,
+0x11, 0x83, 0x16, 0x18, 0xc6, 0x04, 0x84, 0xe0, 0x0e, 0x08, 0x01, 0x87, 0xf8,
+0x6a, 0x40, 0xef, 0x46, 0x0c, 0x84, 0xe0, 0x04, 0x1c, 0x11, 0x81, 0x16, 0x98,
+0x0e, 0xa0, 0x06, 0x41, 0x84, 0x60, 0xc6, 0x02, 0xd8, 0xe0, 0x04, 0x80, 0x08,
+0x80, 0x06, 0x84, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x00,
+0x80, 0xfa, 0xc6, 0x40, 0x84, 0x60, 0x86, 0x43, 0xd8, 0xe0, 0x04, 0x80, 0x09,
+0x00, 0x18, 0xe1, 0x06, 0x84, 0x88, 0x41, 0x68, 0xef, 0x0c, 0x08, 0x80, 0xf2,
+0x48, 0x20, 0x68, 0xff, 0x1e, 0xc0, 0xb5, 0x00, 0x2c, 0xe0, 0xb4, 0x02, 0x00,
+0xfc, 0x15, 0x40, 0x2c, 0xe0, 0x19, 0x10, 0x31, 0x78, 0x01, 0x40, 0x86, 0x04,
+0x0c, 0xe0, 0x4a, 0x04, 0x00, 0x64, 0x31, 0x81, 0x30, 0x0a, 0x04, 0x64, 0x4b,
+0x82, 0x1b, 0x06, 0x3b, 0x8a, 0xc8, 0x44, 0xfc, 0x87, 0x41, 0x50, 0x31, 0x78,
+0x01, 0x40, 0xa1, 0xe1, 0xe6, 0x43, 0x99, 0x6c, 0x10, 0xc0, 0x08, 0x42, 0x00,
+0xf8, 0x00, 0x00, 0x03, 0xa9, 0x00, 0x00, 0x30, 0x85, 0x40, 0x00, 0xa8, 0x16,
+0xbc, 0xe0, 0xcc, 0xc1, 0x40, 0x00, 0xa8, 0x35, 0x9c, 0xe0, 0xb0, 0xc9, 0x40,
+0x00, 0x88, 0x37, 0x98, 0xe0, 0x00, 0xb9, 0x00, 0x00, 0x49, 0xa7, 0x00, 0x00,
+};
+
+struct vd55g0_patch {
+ const u8 *bin;
+ const uint size;
+ const u16 major;
+ const u16 minor;
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut1 = {
+ .bin = cut1_patch,
+ .size = ARRAY_SIZE(cut1_patch),
+ .major = 2,
+ .minor = 11,
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut2 = {
+ .bin = cut2_patch,
+ .size = ARRAY_SIZE(cut2_patch),
+ .major = 0,
+ .minor = 5,
+};
+
+static const char * const vd55g0_test_pattern_menu[] = {
+ "Disabled",
+ "Solid",
+ "Colorbar",
+ "Gradbar",
+ "Hgrey",
+ "Vgrey",
+ "Dgrey",
+ "PN28",
+};
+
+static const s64 vd55g0_ev_bias_menu[] = {
+ -3000, -2500, -2000, -1500, -1000, -500,
+ 0,
+ 500, 1000, 1500, 2000, 2500, 3000,
+};
+
+/*
+ * On ACPI, the INT3472 registers the rails the platform wired under its own
+ * names; the DT names would get dummy regulators there.
+ */
+static const char * const vd55g0_dt_supply_name[] = {
+ "vcore",
+ "vddio",
+ "vana",
+};
+
+static const char * const vd55g0_acpi_supply_name[] = {
+ "avdd",
+ "dovdd",
+};
+
+static const struct acpi_device_id vd55g0_acpi_ids[] = {
+ { "SMO55F0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, vd55g0_acpi_ids);
+
+#define VD55G0_NUM_SUPPLIES ARRAY_SIZE(vd55g0_dt_supply_name)
+
+enum vd55g0_bin_mode {
+ VD55G0_BIN_MODE_NORMAL,
+ VD55G0_BIN_MODE_DIGITAL_X2,
+};
+
+enum vd55g0_gpio_modes {
+ VD55G0_GPIO_MODE_DISABLED,
+ VD55G0_GPIO_MODE_STROBE,
+};
+
+struct vd55g0_mode_info {
+ u32 width;
+ u32 height;
+ enum vd55g0_bin_mode bin_mode;
+ struct v4l2_rect crop;
+};
+
+struct vd55g0_fmt_desc {
+ u32 code;
+ u8 bpp;
+ u8 data_type;
+};
+
+static const struct vd55g0_fmt_desc vd55g0_supported_codes[] = {
+ {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .bpp = 8,
+ .data_type = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .bpp = 10,
+ .data_type = MIPI_CSI2_DT_RAW10,
+ },
+};
+
+static const struct vd55g0_mode_info vd55g0_mode_data[] = {
+ {
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 0,
+ .top = 0,
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ },
+ },
+ {
+ .width = 640,
+ .height = 600,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 2,
+ .width = 640,
+ .height = 600,
+ },
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ {
+ .width = 320,
+ .height = 240,
+ .bin_mode = VD55G0_BIN_MODE_DIGITAL_X2,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+};
+
+enum vd55g0_expo_state {
+ VD55G0_EXP_AUTO,
+ VD55G0_EXP_FREEZE,
+ VD55G0_EXP_MANUAL
+};
+
+struct vd55g0_dev {
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[VD55G0_NUM_SUPPLIES];
+ unsigned int nb_supplies;
+ struct gpio_desc *reset_gpio;
+ struct clk *xclk;
+ u32 clk_freq;
+ u16 oif_ctrl;
+ u32 mipi_rate;
+ u32 pixel_rate;
+ u64 link_freq;
+ u16 line_length;
+ u16 revision;
+ /* Lock to protect all members below */
+ struct mutex lock;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vflip_ctrl;
+ struct v4l2_ctrl *hflip_ctrl;
+ struct v4l2_ctrl *pattern_ctrl;
+ /* The auto exposure cluster, the auto control first */
+ struct {
+ struct v4l2_ctrl *ae_ctrl;
+ struct v4l2_ctrl *expo_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ struct v4l2_ctrl *dgain_ctrl;
+ };
+ bool streaming;
+ u32 code;
+ const struct vd55g0_mode_info *current_mode;
+ bool hflip;
+ bool vflip;
+ u16 manual_expo;
+ enum vd55g0_expo_state expo_state;
+ u16 digital_gain;
+ u8 analog_gain;
+ u16 vblank;
+ u16 vblank_min;
+ u16 frame_length;
+ bool ae_frozen;
+ u32 pattern;
+ u16 exposure_target;
+ u32 leds[VD55G0_NB_GPIOS];
+ bool flash_en;
+ unsigned int nb_leds;
+ struct {
+ u16 expo;
+ u16 digital_gain;
+ u8 analog_gain;
+ } cold_start;
+};
+
+static inline struct vd55g0_dev *to_vd55g0_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vd55g0_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vd55g0_dev,
+ ctrl_handler)->sd;
+}
+
+static u8 get_bpp_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].bpp;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to 8 bpp\n", code);
+ return 8;
+}
+
+static u8 get_data_type_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].data_type;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type\n",
+ code);
+ return MIPI_CSI2_DT_RAW8;
+}
+
+#define vd55g0_read(sensor, reg, val, err) \
+ cci_read((sensor)->regmap, reg, val, err)
+
+#define vd55g0_write(sensor, reg, val, err) \
+ cci_write((sensor)->regmap, reg, val, err)
+
+static int get_chunk_size(struct vd55g0_dev *sensor)
+{
+ int max_write_len = VD55G0_WRITE_MULTIPLE_CHUNK_MAX;
+ struct i2c_adapter *adapter = sensor->i2c_client->adapter;
+
+ if (adapter->quirks && adapter->quirks->max_write_len)
+ max_write_len = adapter->quirks->max_write_len - 2;
+
+ max_write_len = min(max_write_len, VD55G0_WRITE_MULTIPLE_CHUNK_MAX);
+
+ return max(max_write_len, 1);
+}
+
+static int vd55g0_write_array(struct vd55g0_dev *sensor, u32 reg,
+ unsigned int nb, const u8 *array)
+{
+ const unsigned int chunk_size = get_chunk_size(sensor);
+ unsigned int addr = CCI_REG_ADDR(reg);
+ unsigned int sz;
+ int ret;
+
+ while (nb) {
+ sz = min(nb, chunk_size);
+ ret = regmap_bulk_write(sensor->regmap, addr, array, sz);
+ if (ret < 0)
+ return ret;
+ nb -= sz;
+ addr += sz;
+ array += sz;
+ }
+
+ return 0;
+}
+
+static int vd55g0_poll_reg(struct vd55g0_dev *sensor, u32 reg, u8 poll_val,
+ unsigned int timeout_ms)
+{
+ unsigned int val = 0;
+
+ return regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
+ (val == poll_val), 10 * USEC_PER_MSEC,
+ timeout_ms * USEC_PER_MSEC);
+}
+
+static int vd55g0_wait_state(struct vd55g0_dev *sensor, int state,
+ unsigned int timeout_ms)
+{
+ return vd55g0_poll_reg(sensor, VD55G0_REG_SYSTEM_FSM, state,
+ timeout_ms);
+}
+
+static int vd55g0_prepare_clock_tree(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ u32 pixel_div;
+
+ if (sensor->clk_freq < VD55G0_XCLK_FREQ_MIN ||
+ sensor->clk_freq > VD55G0_XCLK_FREQ_MAX) {
+ dev_err(dev,
+ "Only %luMHz-%luMHz clock range supported. Provided %lu MHz\n",
+ VD55G0_XCLK_FREQ_MIN / HZ_PER_MHZ,
+ VD55G0_XCLK_FREQ_MAX / HZ_PER_MHZ,
+ sensor->clk_freq / HZ_PER_MHZ);
+ return -EINVAL;
+ }
+
+ /*
+ * The MIPI bus is double data rate, and its rate is an integer multiple
+ * of the input clock. The sensor would round any other rate down
+ * (UM2829 13.6), so it is refused rather than used.
+ */
+ if (sensor->link_freq < VD55G0_MIPI_RATE_MIN / 2 ||
+ sensor->link_freq > VD55G0_MIPI_RATE_MAX / 2) {
+ dev_err(dev,
+ "Only %luMbps-%luMbps data rate range supported. Provided link frequency %llu Hz\n",
+ VD55G0_MIPI_RATE_MIN / MEGA, VD55G0_MIPI_RATE_MAX / MEGA,
+ sensor->link_freq);
+ return -EINVAL;
+ }
+ sensor->mipi_rate = sensor->link_freq * 2;
+ if (sensor->mipi_rate % sensor->clk_freq) {
+ dev_err(dev,
+ "Link frequency %llu Hz is not a multiple of half the %u Hz input clock\n",
+ sensor->link_freq, sensor->clk_freq);
+ return -EINVAL;
+ }
+
+ pixel_div = sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST ? 6 : 8;
+ sensor->pixel_rate = sensor->mipi_rate / pixel_div;
+
+ return 0;
+}
+
+static int vd55g0_update_gpio_mode(struct vd55g0_dev *sensor, u32 mode,
+ int gpio)
+{
+ static const u8 index2val[] = {0x01, 0x02};
+
+ return vd55g0_write(sensor, VD55G0_REG_GPIO_0_CTRL + gpio,
+ index2val[mode], NULL);
+}
+
+static int vd55g0_set_gpios_array(struct vd55g0_dev *sensor, u32 *array,
+ int size, enum vd55g0_gpio_modes mode)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < size; i++) {
+ if (array[i] == ~0)
+ break;
+ ret = vd55g0_update_gpio_mode(sensor, mode, array[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vd55g0_apply_exposure_auto(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_expo_state exp = sensor->expo_state;
+
+ if (sensor->ae_frozen && sensor->expo_state == VD55G0_EXP_AUTO)
+ exp = VD55G0_EXP_FREEZE;
+
+ return vd55g0_write(sensor, VD55G0_REG_EXP_MODE, exp, NULL);
+}
+
+static int vd55g0_get_regulators(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ const char * const *names;
+ unsigned int i;
+
+ /* A PRP0001 device has DT names; only the ACPI ID has INT3472's */
+ if (acpi_match_device(vd55g0_acpi_ids, dev)) {
+ names = vd55g0_acpi_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_acpi_supply_name);
+ } else {
+ names = vd55g0_dt_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_dt_supply_name);
+ }
+
+ for (i = 0; i < sensor->nb_supplies; i++)
+ sensor->supplies[i].supply = names[i];
+
+ return devm_regulator_bulk_get(dev, sensor->nb_supplies,
+ sensor->supplies);
+}
+
+static int vd55g0_apply_patgen(struct vd55g0_dev *sensor)
+{
+ static const u8 index2val[] = {
+ 0x0, 0x0, 0x8, 0x10, 0x20, 0x21, 0x22, 0x28
+ };
+ u32 pattern = index2val[sensor->pattern];
+ u32 reg = pattern << VD55G0_PATGEN_TYPE_SHIFT;
+ u8 darkcal = VD55G0_DARKCAL_AUTO;
+ u8 duster = VD55G0_DUSTER_RING_ENABLE | VD55G0_DUSTER_DYN_ENABLE |
+ VD55G0_DUSTER_ENABLE;
+ int ret = 0;
+
+ if (sensor->pattern != 0) {
+ reg |= VD55G0_PATGEN_ENABLE;
+ /*
+ * Take care of dark calibaration and duster to not mess up the
+ * test pattern output.
+ */
+ darkcal = VD55G0_DARKCAL_BYPASS;
+ duster = VD55G0_DUSTER_DISABLE;
+ }
+
+ vd55g0_write(sensor, VD55G0_REG_DARKCAL_CTRL, darkcal, &ret);
+ vd55g0_write(sensor, VD55G0_REG_DUSTER_CTRL, duster, &ret);
+ if (ret)
+ return ret;
+
+ return vd55g0_write(sensor, VD55G0_REG_PATGEN_CTRL, reg, NULL);
+}
+
+static int vd55g0_apply_flash(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_gpio_modes mode = sensor->flash_en ?
+ VD55G0_GPIO_MODE_STROBE :
+ VD55G0_GPIO_MODE_DISABLED;
+
+ return vd55g0_set_gpios_array(sensor, sensor->leds,
+ ARRAY_SIZE(sensor->leds), mode);
+}
+
+/* The helpers below cache a value only once the sensor has taken it */
+static int vd55g0_update_exposure_auto(struct vd55g0_dev *sensor, u32 index)
+{
+ enum vd55g0_expo_state old = sensor->expo_state;
+ int ret;
+
+ switch (index) {
+ case V4L2_EXPOSURE_AUTO:
+ sensor->expo_state = VD55G0_EXP_AUTO;
+ break;
+ case V4L2_EXPOSURE_MANUAL:
+ sensor->expo_state = VD55G0_EXP_MANUAL;
+ break;
+ default:
+ /* Should never happen */
+ return -EINVAL;
+ }
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->expo_state = old;
+
+ return ret;
+}
+
+static int vd55g0_lock_exposure(struct vd55g0_dev *sensor,
+ struct v4l2_ctrl *ctrl)
+{
+ /* Only exposure lock is supported */
+ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
+ bool old = sensor->ae_frozen;
+ int ret;
+
+ sensor->ae_frozen = ae_lock;
+
+ /* Cap control value to reflect the hardware state */
+ ctrl->val = ae_lock;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->ae_frozen = old;
+
+ return ret;
+}
+
+static int vd55g0_apply_framelength(struct vd55g0_dev *sensor)
+{
+ return vd55g0_write(sensor, VD55G0_REG_FRAME_LENGTH,
+ sensor->frame_length, NULL);
+}
+
+static int vd55g0_update_vblank(struct vd55g0_dev *sensor, u16 vblank)
+{
+ u16 old_vblank = sensor->vblank, old_frame_length = sensor->frame_length;
+ int ret;
+
+ sensor->vblank_min = VD55G0_MIN_FRAME_LENGTH -
+ sensor->current_mode->crop.height;
+ sensor->vblank = vblank;
+ sensor->frame_length = sensor->current_mode->crop.height +
+ sensor->vblank;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret) {
+ sensor->vblank = old_vblank;
+ sensor->frame_length = old_frame_length;
+ }
+
+ return ret;
+}
+
+static int vd55g0_update_analog_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->analog_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_digital_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->digital_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure(struct vd55g0_dev *sensor, int expo_ms)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ expo_ms, NULL);
+ if (!ret)
+ sensor->manual_expo = expo_ms;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure_target(struct vd55g0_dev *sensor, int index)
+{
+ /*
+ * Find auto exposure target with: default target exposure * 2^EV,
+ * the default being VD55G0_EXPO_TARGET_DEF at index 6.
+ */
+ static const unsigned int index2exposure_target[] = {
+ 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
+ };
+ unsigned int target = index2exposure_target[index];
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ target, NULL);
+ if (!ret)
+ sensor->exposure_target = target;
+
+ return ret;
+}
+
+static int vd55g0_update_flash(struct vd55g0_dev *sensor, int flash_en)
+{
+ bool old = sensor->flash_en;
+ int ret;
+
+ sensor->flash_en = flash_en;
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ sensor->flash_en = old;
+
+ return ret;
+}
+
+static void vd55g0_apply_reset(struct vd55g0_dev *sensor)
+{
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+}
+
+static void vd55g0_fill_framefmt(struct vd55g0_dev *sensor,
+ const struct vd55g0_mode_info *mode,
+ struct v4l2_mbus_framefmt *fmt, u32 code)
+{
+ fmt->code = code;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int vd55g0_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ const struct vd55g0_mode_info **new_mode)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *mode = vd55g0_mode_data;
+ unsigned int index;
+
+ for (index = 0; index < ARRAY_SIZE(vd55g0_supported_codes); index++) {
+ if (vd55g0_supported_codes[index].code == fmt->code)
+ break;
+ }
+ if (index == ARRAY_SIZE(vd55g0_supported_codes))
+ index = 0;
+
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data), width,
+ height, fmt->width, fmt->height);
+ if (new_mode)
+ *new_mode = mode;
+
+ vd55g0_fill_framefmt(sensor, mode, fmt,
+ vd55g0_supported_codes[index].code);
+
+ return 0;
+}
+
+static int vd55g0_apply_cold_start(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE,
+ sensor->cold_start.expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_ANALOG_GAIN,
+ sensor->cold_start.analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN,
+ sensor->cold_start.digital_gain, &ret);
+
+ return ret;
+}
+
+static int vd55g0_apply_settings(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ sensor->manual_expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ sensor->analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ sensor->digital_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ sensor->exposure_target, &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_cold_start(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_ORIENTATION,
+ sensor->hflip | (sensor->vflip << 1), &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_patgen(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_apply_frame_format(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_rect *crop = &sensor->current_mode->crop;
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_READOUT_CTRL,
+ sensor->current_mode->bin_mode, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_START, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_END,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_START, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_END,
+ crop->top + crop->height - 1, &ret);
+
+ /*
+ * Use the same auto exposure crop as the image crop, performing auto
+ * exposure computation only on image boundaries.
+ */
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_H, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_H,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_V, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_V,
+ crop->top + crop->height - 1, &ret);
+
+ return ret;
+}
+
+static int vd55g0_set_gpios(struct vd55g0_dev *sensor)
+{
+ int ret;
+ unsigned int i;
+
+ /* GPIOs in input (disabled) by default */
+ for (i = 0; i < VD55G0_NB_GPIOS; i++) {
+ ret = vd55g0_update_gpio_mode(sensor,
+ VD55G0_GPIO_MODE_DISABLED, i);
+ if (ret)
+ return ret;
+ }
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_stream_enable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = vd55g0_set_gpios(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ vd55g0_write(sensor, VD55G0_REG_FORMAT_CTRL,
+ get_bpp_by_code(sensor->code), &ret);
+ vd55g0_write(sensor, VD55G0_REG_OIF_IMG_CTRL,
+ get_data_type_by_code(sensor->code), &ret);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_frame_format(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_settings(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_SW_STBY,
+ VD55G0_SW_STBY_START_STREAM, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_SW_STBY, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_STREAMING,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+ return ret;
+}
+
+static void vd55g0_save_exposure(struct vd55g0_dev *sensor)
+{
+ u64 val;
+
+ /* On a read error, keep what the previous stream left */
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &val, NULL))
+ sensor->cold_start.expo = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &val, NULL))
+ sensor->cold_start.digital_gain = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &val, NULL))
+ sensor->cold_start.analog_gain = val;
+}
+
+static void vd55g0_stream_disable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ /* Keep exposure values for next cold start boot */
+ vd55g0_save_exposure(sensor);
+
+ ret = vd55g0_write(sensor, VD55G0_REG_STREAMING,
+ VD55G0_STREAMING_STOP_STREAM, NULL);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_STREAMING, 0, 2000);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+
+err_str_dis:
+ if (ret)
+ dev_err(&client->dev, "failed to stop streaming: %d\n", ret);
+ pm_runtime_put_autosuspend(&client->dev);
+}
+
+static int vd55g0_tx_from_ep(struct vd55g0_dev *sensor,
+ struct fwnode_handle *handle)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct v4l2_fwnode_endpoint *ep;
+ struct v4l2_fwnode_endpoint ep_node = { .bus_type =
+ V4L2_MBUS_CSI2_DPHY
+ };
+ int ret;
+
+ ep = &ep_node;
+ ret = v4l2_fwnode_endpoint_alloc_parse(handle, ep);
+ if (ret)
+ return ret;
+
+ /* The sensor has a single data lane */
+ if (ep->bus.mipi_csi2.num_data_lanes != 1) {
+ dev_err(&client->dev, "only one data lane is supported, found %u\n",
+ ep->bus.mipi_csi2.num_data_lanes);
+ goto error_ep;
+ }
+
+ if (ep->bus.mipi_csi2.clock_lane != 0) {
+ dev_err(&client->dev, "clock lane must be lane 0\n");
+ goto error_ep;
+ }
+
+ /* Only the two polarity bits: the rest of OIF_CTRL is reserved */
+ sensor->oif_ctrl = (ep->bus.mipi_csi2.lane_polarities[0] << 3) |
+ (ep->bus.mipi_csi2.lane_polarities[1] << 6);
+
+ if (ep->nr_of_link_frequencies != 1) {
+ dev_err(&client->dev, "one link frequency is needed, found %u\n",
+ ep->nr_of_link_frequencies);
+ goto error_ep;
+ }
+ sensor->link_freq = ep->link_frequencies[0];
+
+ v4l2_fwnode_endpoint_free(ep);
+
+ return 0;
+
+error_ep:
+ v4l2_fwnode_endpoint_free(ep);
+
+ return -EINVAL;
+}
+
+static int vd55g0_patch(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ const struct vd55g0_patch *patch;
+ u64 patch_version;
+ int ret;
+
+ /* Choose appropriate patch for sensor revision */
+ if (sensor->revision == VD55G0_REVISION_CUT1)
+ patch = &vd55g0_patch_cut1;
+ else if (sensor->revision == VD55G0_REVISION_CUT2)
+ patch = &vd55g0_patch_cut2;
+ else
+ return -EINVAL;
+
+ /* Write patch */
+ ret = vd55g0_write_array(sensor, VD55G0_REG_FWPATCH_START_ADDR,
+ patch->size, patch->bin);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT,
+ VD55G0_BOOT_PATCH_SETUP, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ /* Read back patch version */
+ ret = vd55g0_read(sensor, VD55G0_REG_FWPATCH_REVISION, &patch_version,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (patch_version != (patch->major << 8) + patch->minor) {
+ dev_err(&client->dev,
+ "bad patch version expected %d.%d got %llu.%llu\n",
+ patch->major, patch->minor,
+ patch_version >> 8, patch_version & 0xff);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "patch %llu.%llu applied\n",
+ patch_version >> 8, patch_version & 0xff);
+
+ return 0;
+}
+
+static int vd55g0_boot(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT, VD55G0_BOOT_BOOT, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_configure(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ sensor->line_length = VD55G0_LINE_LENGTH_FAST;
+ if (sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST)
+ sensor->line_length = VD55G0_LINE_LENGTH_SLOW;
+ vd55g0_write(sensor, VD55G0_REG_LINE_LENGTH, sensor->line_length,
+ &ret);
+
+ /*
+ * PLL_PREDIV and PLL_MULT are not accessible. We rely on the firmware
+ * to set the correct multiplier for the clock. Hence we don't do
+ * anything more here.
+ */
+ vd55g0_write(sensor, VD55G0_REG_EXT_CLOCK, sensor->clk_freq, &ret);
+
+ vd55g0_write(sensor, VD55G0_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
+ /* The status lines are not exposed, so do not send them, as vd55g1 */
+ vd55g0_write(sensor, VD55G0_REG_ISL_ENABLE, 0, &ret);
+ vd55g0_write(sensor, VD55G0_REG_CLK_PLL_MIPI, sensor->mipi_rate, &ret);
+
+ return ret;
+}
+
+/* Called with sensor->lock held, which is also the control handler's lock */
+static void vd55g0_grab_stream_ctrls(struct vd55g0_dev *sensor, bool grab)
+{
+ __v4l2_ctrl_grab(sensor->vflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->hflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->pattern_ctrl, grab);
+}
+
+static int vd55g0_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = vd55g0_stream_enable(sensor);
+ if (ret)
+ return ret;
+
+ sensor->streaming = true;
+ vd55g0_grab_stream_ctrls(sensor, true);
+
+ return 0;
+}
+
+static int vd55g0_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Powered down either way, so report success as call_s_stream() does */
+ vd55g0_stream_disable(sensor);
+
+ sensor->streaming = false;
+ vd55g0_grab_stream_ctrls(sensor, false);
+
+ return 0;
+}
+
+static int vd55g0_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ const struct vd55g0_mode_info *mode;
+ const struct v4l2_mbus_framefmt *fmt;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ /* From the state asked about, so that TRY gets its own crop */
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data),
+ width, height,
+ fmt->width, fmt->height);
+ sel->r = mode->crop;
+ return 0;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = VD55G0_WIDTH;
+ sel->r.height = VD55G0_HEIGHT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vd55g0_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ code->code = vd55g0_supported_codes[code->index].code;
+
+ return 0;
+}
+
+static int vd55g0_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *new_mode;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int expo_max, hblank;
+ u16 vblank_def;
+ int ret;
+
+ /* The mode cannot change under a running stream */
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && sensor->streaming)
+ return -EBUSY;
+
+ ret = vd55g0_try_fmt_internal(sd, &format->format, &new_mode);
+ if (ret)
+ return ret;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ *fmt = format->format;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return 0;
+
+ if (sensor->current_mode == new_mode && sensor->code == fmt->code)
+ return 0;
+
+ sensor->code = fmt->code;
+ sensor->current_mode = new_mode;
+
+ /* Reset vblank and framelength to default */
+ vblank_def = VD55G0_FRAME_LENGTH_DEF - new_mode->crop.height;
+ ret = vd55g0_update_vblank(sensor, vblank_def);
+ if (ret)
+ return ret;
+
+ /* The range change may clamp and apply the old vblank; set the default */
+ ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, sensor->vblank_min,
+ 0xffff - new_mode->crop.height, 1,
+ vblank_def);
+ if (ret)
+ return ret;
+ ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank_def);
+ if (ret)
+ return ret;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1,
+ VD55G0_EXPO_DEF);
+ if (ret)
+ return ret;
+ /* Blanking counts from the analogue crop, as in vd55g1 */
+ hblank = sensor->line_length - sensor->current_mode->crop.width;
+ ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
+ hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_s_ctrl(sensor->hblank_ctrl, hblank);
+}
+
+static int vd55g0_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ vd55g0_fill_framefmt(sensor, &vd55g0_mode_data[VD55G0_DEFAULT_MODE],
+ &fmt.format, VD55G0_MEDIA_BUS_FMT_DEF);
+
+ return vd55g0_set_fmt(sd, NULL, sd_state, &fmt);
+}
+
+static int vd55g0_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ unsigned int i;
+
+ if (fse->index >= ARRAY_SIZE(vd55g0_mode_data))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++)
+ if (vd55g0_supported_codes[i].code == fse->code)
+ break;
+ if (i == ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ fse->min_width = vd55g0_mode_data[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = vd55g0_mode_data[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops vd55g0_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops vd55g0_pad_ops = {
+ .enum_mbus_code = vd55g0_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = vd55g0_set_fmt,
+ .enable_streams = vd55g0_enable_streams,
+ .disable_streams = vd55g0_disable_streams,
+ .get_selection = vd55g0_get_selection,
+ .enum_frame_size = vd55g0_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops vd55g0_subdev_ops = {
+ .video = &vd55g0_video_ops,
+ .pad = &vd55g0_pad_ops,
+};
+
+static const struct media_entity_operations vd55g0_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops vd55g0_internal_ops = {
+ .init_state = vd55g0_init_state,
+};
+
+/*
+ * The manual values first, so that a switch to manual starts from them; the
+ * sensor ignores them under auto exposure.
+ */
+static int vd55g0_update_expo_cluster(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ if (sensor->ae_ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ /* A range change under auto exposure did not clamp it */
+ sensor->expo_ctrl->val = min(sensor->expo_ctrl->val,
+ sensor->expo_ctrl->maximum);
+ if (sensor->expo_ctrl->is_new)
+ ret = vd55g0_update_exposure(sensor,
+ sensor->expo_ctrl->val);
+ if (!ret && sensor->again_ctrl->is_new)
+ ret = vd55g0_update_analog_gain(sensor,
+ sensor->again_ctrl->val);
+ if (!ret && sensor->dgain_ctrl->is_new)
+ ret = vd55g0_update_digital_gain(sensor,
+ sensor->dgain_ctrl->val);
+ if (ret)
+ return ret;
+ }
+
+ if (sensor->ae_ctrl->is_new)
+ ret = vd55g0_update_exposure_auto(sensor, sensor->ae_ctrl->val);
+
+ return ret;
+}
+
+/* Under auto exposure, the manual controls read what the sensor applies */
+static int vd55g0_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(ctrl_to_sd(ctrl));
+ struct device *dev = &sensor->i2c_client->dev;
+ u64 expo, again, dgain;
+ int ret = 0;
+
+ if (ctrl->id != V4L2_CID_EXPOSURE_AUTO)
+ return -EINVAL;
+
+ /* Only a powered sensor has applied values; keep the last ones */
+ if (!pm_runtime_get_if_in_use(dev))
+ return 0;
+
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &expo, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &again, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret);
+ if (!ret) {
+ sensor->expo_ctrl->val = expo;
+ sensor->again_ctrl->val = again;
+ sensor->dgain_ctrl->val = dgain;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int vd55g0_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ unsigned int expo_max;
+ u16 old_vblank;
+ int ret;
+
+ /* Read-only controls are set by the driver, there is nothing to apply */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ break;
+ }
+ if (ctrl->id == V4L2_CID_VFLIP)
+ sensor->vflip = ctrl->val;
+ if (ctrl->id == V4L2_CID_HFLIP)
+ sensor->hflip = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ /* Can't be done while streaming because of duster disabling */
+ sensor->pattern = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = vd55g0_update_expo_cluster(sensor);
+ break;
+ case V4L2_CID_3A_LOCK:
+ ret = vd55g0_lock_exposure(sensor, ctrl);
+ break;
+ case V4L2_CID_VBLANK:
+ old_vblank = sensor->vblank;
+ ret = vd55g0_update_vblank(sensor, ctrl->val);
+ if (ret)
+ break;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max,
+ 1, VD55G0_EXPO_DEF);
+ /* A clamped exposure failed to apply: put the frame back */
+ if (ret)
+ vd55g0_update_vblank(sensor, old_vblank);
+ break;
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ /*
+ * We use auto exposure target percentage register to control
+ * exposure bias for more precision.
+ */
+ ret = vd55g0_update_exposure_target(sensor, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_LED_MODE:
+ ret = vd55g0_update_flash(sensor, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vd55g0_ctrl_ops = {
+ .g_volatile_ctrl = vd55g0_g_volatile_ctrl,
+ .s_ctrl = vd55g0_s_ctrl,
+};
+
+static int vd55g0_init_controls(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &vd55g0_ctrl_ops;
+ struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
+ const struct vd55g0_mode_info *cur_mode = sensor->current_mode;
+ struct v4l2_fwnode_device_properties fwnode_props;
+ struct v4l2_ctrl *ctrl;
+ unsigned int patgen_size = ARRAY_SIZE(vd55g0_test_pattern_menu) - 1;
+ unsigned int hblank = sensor->line_length - cur_mode->crop.width;
+ unsigned int expo_mode = sensor->expo_state == VD55G0_EXP_AUTO ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 16);
+ /* we can use our own mutex for the ctrl lock */
+ hdl->lock = &sensor->lock;
+ v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) - 1,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) / 2,
+ vd55g0_ev_bias_menu);
+ ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
+ 0, 0, &sensor->link_freq);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, sensor->pixel_rate,
+ sensor->pixel_rate, 1, sensor->pixel_rate);
+ sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO, 1,
+ ~0x3, expo_mode);
+ sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ 0, 24, 1, sensor->analog_gain);
+ sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+ 256, 2048, 1,
+ sensor->digital_gain);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, 1, 0, 0);
+ /* Only when the firmware describes the LED GPIOs */
+ if (sensor->nb_leds)
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_FLASH, ~0x7,
+ sensor->flash_en);
+
+ /*
+ * Keep a pointer to these controls as we need to update them when
+ * setting the format
+ */
+ sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ sensor->vblank_min,
+ 0xffff - cur_mode->crop.height,
+ 1, sensor->vblank);
+ sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ hblank, hblank, 1, hblank);
+ if (sensor->hblank_ctrl)
+ sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
+ 0, 1, 1, sensor->vflip);
+ sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
+ 0, 1, 1, sensor->hflip);
+ sensor->pattern_ctrl =
+ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+ patgen_size, 0, 0,
+ vd55g0_test_pattern_menu);
+ sensor->expo_ctrl =
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
+ sensor->frame_length - VD55G0_EXPO_MAX_TERM,
+ 1, sensor->manual_expo);
+ if (!hdl->error)
+ v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_ctrls;
+ }
+
+ sensor->sd.ctrl_handler = hdl;
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+}
+
+static int vd55g0_detect(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ u64 revision, id = 0;
+ int ret;
+
+ ret = vd55g0_read(sensor, VD55G0_REG_MODEL_ID, &id, NULL);
+ if (ret)
+ return ret;
+
+ if (id != VD55G0_MODEL_ID) {
+ dev_warn(&client->dev, "Unsupported sensor id %llx\n", id);
+ return -ENODEV;
+ }
+
+ ret = vd55g0_read(sensor, VD55G0_REG_REVISION, &revision, NULL);
+ if (ret)
+ return ret;
+ dev_dbg(&client->dev, "Sensor revision 0x%llx\n", revision);
+
+ if (revision != VD55G0_REVISION_CUT1 &&
+ revision != VD55G0_REVISION_CUT2) {
+ dev_err(&client->dev, "Unsupported device revision 0x%llx\n",
+ revision);
+ return -ENODEV;
+ }
+
+ sensor->revision = revision;
+
+ return 0;
+}
+
+/* Power/clock management functions */
+static int vd55g0_power_on(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(sensor->nb_supplies, sensor->supplies);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable regulators %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sensor->xclk);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable clock %d\n", ret);
+ goto disable_bulk;
+ }
+
+ if (sensor->reset_gpio)
+ vd55g0_apply_reset(sensor);
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_READY_TO_BOOT,
+ VD55G0_TIMEOUT_MS);
+ if (ret) {
+ dev_err(&client->dev, "sensor reset failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_detect(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor detect failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_patch(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor patch failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_boot(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor boot failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_configure(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor configuration failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ return 0;
+
+disable_clock:
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+disable_bulk:
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+
+ return ret;
+}
+
+static int vd55g0_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Hold the sensor in reset while its rails are off, as vd55g1 does */
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios_array(struct vd55g0_dev *sensor,
+ char *prop_name, u32 *array, int *nb)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ unsigned int i;
+ int ret;
+
+ *nb = device_property_count_u32(&client->dev, prop_name);
+ if (*nb == -EINVAL) {
+ /* Property not found */
+ *nb = 0;
+ return 0;
+ }
+ if (*nb < 0)
+ return *nb;
+ if (*nb > VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "%s lists %d GPIOs, the sensor has %d\n",
+ prop_name, *nb, VD55G0_NB_GPIOS);
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32_array(&client->dev, prop_name, array,
+ *nb);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read %s prop\n", prop_name);
+ return ret;
+ }
+
+ for (i = 0; i < *nb; i++) {
+ if (array[i] >= VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "invalid GPIO %d for leds\n",
+ array[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios(struct vd55g0_dev *sensor)
+{
+ int nb_gpios_leds;
+ int ret;
+
+ memset(sensor->leds, ~0, sizeof(sensor->leds));
+
+ ret = vd55g0_parse_dt_gpios_array(sensor, "st,leds", sensor->leds,
+ &nb_gpios_leds);
+ if (ret)
+ return ret;
+
+ sensor->nb_leds = nb_gpios_leds;
+
+ return 0;
+}
+
+static int vd55g0_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct fwnode_handle *handle;
+ struct vd55g0_dev *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->analog_gain = 0;
+ sensor->digital_gain = 256;
+
+ sensor->i2c_client = client;
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->regmap),
+ "failed to init CCI\n");
+ sensor->streaming = false;
+ /* Must match what vd55g0_init_state() puts in the subdev state */
+ sensor->code = VD55G0_MEDIA_BUS_FMT_DEF;
+ sensor->manual_expo = VD55G0_EXPO_DEF;
+ sensor->vflip = false;
+ sensor->hflip = false;
+ sensor->flash_en = false;
+ /* Written at every stream start, so it must match the control default */
+ sensor->exposure_target = VD55G0_EXPO_TARGET_DEF;
+ sensor->cold_start.expo = 0x32;
+ sensor->cold_start.digital_gain = 256;
+ sensor->cold_start.analog_gain = 0;
+
+ sensor->current_mode = &vd55g0_mode_data[VD55G0_DEFAULT_MODE];
+
+ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+
+ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+ "Failed to get reset gpio\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_parse_dt_gpios(sensor);
+ if (ret) {
+ dev_err(dev, "Failed to get gpios\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_tx_from_ep(sensor, handle);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to parse endpoint\n");
+ goto error_put_handle;
+ }
+ fwnode_handle_put(handle);
+
+ sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
+ if (IS_ERR(sensor->xclk))
+ return dev_err_probe(dev, PTR_ERR(sensor->xclk),
+ "failed to get xclk\n");
+
+ sensor->clk_freq = clk_get_rate(sensor->xclk);
+ ret = vd55g0_prepare_clock_tree(sensor);
+ if (ret)
+ return ret;
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g0_subdev_ops);
+ sensor->sd.internal_ops = &vd55g0_internal_ops;
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.ops = &vd55g0_subdev_entity_ops;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = vd55g0_get_regulators(sensor);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
+
+ /* Power on once to check that the sensor answers */
+ ret = vd55g0_power_on(dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&sensor->lock);
+
+ ret = vd55g0_update_vblank(sensor, VD55G0_FRAME_LENGTH_DEF -
+ sensor->current_mode->crop.height);
+ if (ret)
+ goto error_power_off;
+
+ ret = vd55g0_init_controls(sensor);
+ if (ret) {
+ dev_err(&client->dev, "controls initialization failed %d\n",
+ ret);
+ goto error_power_off;
+ }
+
+ sensor->sd.state_lock = &sensor->lock;
+
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret) {
+ dev_err(&client->dev, "pads init failed %d\n", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "subdev init error %d\n", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "async subdev register failed %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ /* Runtime PM may power the sensor down only from here on */
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_idle(dev);
+
+ dev_dbg(&client->dev, "vd55g0 probe successfully\n");
+
+ return 0;
+
+error_put_handle:
+ fwnode_handle_put(handle);
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ v4l2_subdev_cleanup(&sensor->sd);
+error_media_entity:
+ media_entity_cleanup(&sensor->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+error_power_off:
+ mutex_destroy(&sensor->lock);
+ vd55g0_power_off(dev);
+
+ return ret;
+}
+
+static void vd55g0_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_subdev_cleanup(&sensor->sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+ mutex_destroy(&sensor->lock);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ vd55g0_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
+}
+
+static const struct of_device_id vd55g0_dt_ids[] = {
+ { .compatible = "st,vd55g0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vd55g0_dt_ids);
+
+static const struct dev_pm_ops vd55g0_pm_ops = {
+ SET_RUNTIME_PM_OPS(vd55g0_power_off, vd55g0_power_on, NULL)
+};
+
+static struct i2c_driver vd55g0_i2c_driver = {
+ .driver = {
+ .name = "vd55g0",
+ .of_match_table = vd55g0_dt_ids,
+ .acpi_match_table = vd55g0_acpi_ids,
+ .pm = &vd55g0_pm_ops,
+ },
+ .probe = vd55g0_probe,
+ .remove = vd55g0_remove,
+};
+
+module_i2c_driver(vd55g0_i2c_driver);
+
+MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>");
+MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>");
+MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>");
+MODULE_AUTHOR("Sergey Lebedev <lsa.uz@pm.me>");
+MODULE_DESCRIPTION("VD55G0 camera subdev driver");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 3/4] media: ipu-bridge: Add ST VD55G0
2026-11-03 10:32 [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
@ 2026-11-03 10:33 ` Sergey Lebedev
2026-11-03 10:33 ` [PATCH v2 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev
3 siblings, 0 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 10:33 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, linux-media, devicetree,
linux-kernel
The infrared camera of the Surface Pro 11 with Intel is an ST VD55G0,
enumerated by ACPI as SMO55F0 and wired to the IPU7 over one CSI-2 data
lane.
The vd55g0 driver takes only a link frequency whose MIPI rate, twice it,
is an integer multiple of the input clock. 396 MHz gives 792 Mbps, 33
times the 24 MHz the sensor's SSDB lists on the Surface Pro 8, 9, 10 and
11, and within the sensor's 750 to 1200 Mbps. Peter Marshall's earlier
patch for the Surface Pro 9 used 380 MHz, 760 Mbps, which is not a
multiple of 24 MHz, so the driver would refuse it:
https://lore.kernel.org/all/20260918221705.323510-4-pm@petermarshall.ca/
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
drivers/media/pci/intel/ipu-bridge.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 3739c4a2a8c..cb1d1582fa1 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -120,6 +120,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+ /* STMicroelectronics VD55G0 */
+ IPU_SENSOR_CONFIG("SMO55F0", 1, 396000000),
/* Sony IMX471 */
IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
/* Sony IMX471 (found on Lenovo X1 Carbon G14) */
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator
2026-11-03 10:32 [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
` (2 preceding siblings ...)
2026-11-03 10:33 ` [PATCH v2 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
@ 2026-11-03 10:33 ` Sergey Lebedev
3 siblings, 0 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 10:33 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, linux-media, 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. dev_properties gains an entry for it,
as a sensor can also have a lens-focus reference. 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.
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
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).
D. Manresa's ipu-bridge series changes the same lens-focus line;
whichever of the two lands second needs a trivial rebase:
https://lore.kernel.org/all/20261026082549.662885-1-dmanresa@gmail.com/
drivers/media/pci/intel/ipu-bridge.c | 68 +++++++++++++++++++++++++++-
include/media/ipu-bridge.h | 6 ++-
2 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index cb1d1582fa1..b5ce9a64dde 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,7 +623,8 @@ static void ipu_bridge_create_fwnode_properties(
{
struct ipu_property_names *names = &sensor->prop_names;
struct software_node *nodes = sensor->swnodes;
- unsigned int i = 0;
+ const struct ipu_sensor_leds *leds;
+ unsigned int i = 0, d = 3;
sensor->prop_names = prop_names;
@@ -624,10 +678,20 @@ static void ipu_bridge_create_fwnode_properties(
if (sensor->vcm_type) {
sensor->vcm_ref[0] =
SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]);
- sensor->dev_properties[3] =
+ sensor->dev_properties[d++] =
PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
}
+ leds = ipu_bridge_get_leds(cfg);
+ if (leds) {
+ memcpy(sensor->led_gpios, leds->gpios,
+ sizeof(sensor->led_gpios));
+ sensor->dev_properties[d++] =
+ 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 3ef94c2b767..7383bdf5a45 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
@@ -136,6 +137,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 {
@@ -178,8 +180,10 @@ 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[5];
+ struct property_entry dev_properties[6];
struct property_entry ipu_properties[3];
struct property_entry ivsc_properties[1];
struct property_entry ivsc_sensor_ep_properties[4];
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-11-03 10:32 ` [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
@ 2026-11-03 12:13 ` Sergey Lebedev
0 siblings, 0 replies; 6+ messages in thread
From: Sergey Lebedev @ 2026-11-03 12:13 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, sashiko-bot, sashiko-reviews,
linux-media, devicetree, linux-kernel
Sashiko's review of this patch [1] went to devicetree, not linux-media.
All three points are right; v3 will fix them. Tested on the Surface Pro
11 with v2 and with the fix:
1. Auto exposure. Once a stream stops, the exposure and gains read back,
and carry into manual mode, the values set before switching to auto,
not the last applied ones. No runtime suspend is needed for this:
pm_runtime_get_if_in_use() fails as soon as the stream's reference
is dropped. Here the stream applied 1796/24/2048; after the stop the
controls read 123/7/333, the manual values set before, and a manual
stream then applied 123/7/333. v3 returns what the last stream
applied, which the driver already keeps for the next cold start; the
controls then read 1796/24/2048 and the manual stream applies that.
So the v2 cover letter's "carry over to manual mode" held only while
streaming.
2. Probe. The sensor was runtime suspended 22 ms after bind returned,
not after the 1 s autosuspend delay. v3 takes a reference before
pm_runtime_enable() and sets up autosuspend before registering, as
vd55g1 does, and drops the reference once registered; the sensor
then suspends 1.2 s after bind. The error path was not exercised.
3. RUNTIME_PM_OPS() and pm_ptr(): in v3. With CONFIG_PM=n,
vd55g0_pm_ops is then no longer in the object.
With the fix, v4l2-compliance passes 46 of 46, with no KASAN or lockdep
report.
[1] https://lore.kernel.org/all/sashiko-outbox-195119@kernel.org/
Sergey
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-03 12:13 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 10:32 [PATCH v2 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
2026-11-03 10:32 ` [PATCH v2 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev
2026-11-03 12:13 ` Sergey Lebedev
2026-11-03 10:33 ` [PATCH v2 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-11-03 10:33 ` [PATCH v2 4/4] 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®