* [PATCH 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
2026-10-19 16:27 [PATCH 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev
2026-10-19 16:27 ` [PATCH 1/4] dt-bindings: media: i2c: Add ST VD55G0 camera sensor Sergey Lebedev
@ 2026-10-19 16:27 ` Sergey Lebedev
2026-10-19 18:24 ` George Neacsu
2026-10-19 16:28 ` [PATCH 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev
2026-10-19 16:28 ` [PATCH 4/4] media: ipu-bridge: Add the Surface Pro 11 infrared illuminator Sergey Lebedev
3 siblings, 1 reply; 12+ messages in thread
From: Sergey Lebedev @ 2026-10-19 16:27 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
linux-media, devicetree, linux-kernel
The VD55G0 is a 644x604 monochrome global shutter sensor from
STMicroelectronics. It streams Y8 or Y10 over one CSI-2 lane, runs its own
auto exposure, can strobe an illuminator from its GPIOs, and needs a
firmware patch at every power-on, carried in the driver as vd55g1 does.
The driver is ported from ST's own, GPL-2.0, to current interfaces: CCI
register access, .enable_streams with the subdev state, fwnode properties,
devm_v4l2_sensor_clk_get(), and an ACPI match for x86 machines where the
sensor sits behind ipu-bridge:
https://github.com/STMicroelectronics/vd55g0-linux-driver
ST's st,out-sync and st,in-sync and its two private controls are left out,
as vd55g1 has none of them. With the ACPI ID the supplies take the names
INT3472 registers, avdd and dovdd; a PRP0001 device keeps the DT names.
The clock tree is computed from the input clock and the one link frequency
the endpoint gives, as in vd55g1. The MIPI rate must be an integer multiple
of the input clock, or the sensor would run at another one, so any other
link frequency fails probe.
Exposure and the two gains form an auto cluster with auto exposure, as in
vd55g1: while auto exposure runs, they read back what the sensor applies.
Co-developed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Signed-off-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
Assisted-by: Claude:claude-opus-5-5 sparse
Signed-off-by: Sergey Lebedev <lsa.uz@pm.me>
---
On ACPI the supplies take the names INT3472 registers, avdd and dovdd,
through acpi_match_device(); vd55g1 is served by an entry in int3472's
map instead. I did not add one, as it would have to say which rail each
pin feeds, and that is not known here: INT3472 calls pin type 0x10
DOVDD, while Windows' camera driver calls it Avdd. If a map entry is
preferred, I can move the names there.
MAINTAINERS | 10 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2434 ++++++++++++++++++++++++++++++++++++
4 files changed, 2456 insertions(+)
create mode 100644 drivers/media/i2c/vd55g0.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 760791e3635..7745ad9ef81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26036,6 +26036,15 @@ F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
F: Documentation/hwmon/tsc1641.rst
F: drivers/hwmon/tsc1641.c
+ST VD55G0 DRIVER
+M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
+M: Sergey Lebedev <lsa.uz@pm.me>
+M: Sylvain Petinot <sylvain.petinot@foss.st.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/st,vd55g0.yaml
+F: drivers/media/i2c/vd55g0.c
+
ST VD55G1 DRIVER
M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
M: Sylvain Petinot <sylvain.petinot@foss.st.com>
@@ -28683,6 +28692,7 @@ F: drivers/media/i2c/mt*
F: drivers/media/i2c/og*
F: drivers/media/i2c/ov*
F: drivers/media/i2c/s5*
+F: drivers/media/i2c/vd55g0.c
F: drivers/media/i2c/vd55g1.c
F: drivers/media/i2c/vd56g3.c
F: drivers/media/i2c/vgxy61.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 7de2d095c96..05343f82ec6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -891,6 +891,17 @@ config VIDEO_T4KA3
To compile this driver as a module, choose M here: the
module will be called t4ka3.
+config VIDEO_VD55G0
+ tristate "ST VD55G0 sensor support"
+ select V4L2_CCI_I2C
+ depends on GPIOLIB
+ help
+ This is a Video4Linux2 sensor driver for the ST VD55G0
+ camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vd55g0.
+
config VIDEO_VD55G1
tristate "ST VD55G1 sensor support"
select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 59e31af457c..a7e990ec650 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -171,6 +171,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o
obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_VD55G0) += vd55g0.o
obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o
obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o
obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o
diff --git a/drivers/media/i2c/vd55g0.c b/drivers/media/i2c/vd55g0.c
new file mode 100644
index 00000000000..3b51a87e740
--- /dev/null
+++ b/drivers/media/i2c/vd55g0.c
@@ -0,0 +1,2434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the ST VD55G0 global shutter sensor
+ *
+ * Copyright (C) 2026 STMicroelectronics SA
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define VD55G0_REG_MODEL_ID CCI_REG16_LE(0x0000)
+#define VD55G0_MODEL_ID 0x4730
+#define VD55G0_REG_REVISION CCI_REG16_LE(0x0004)
+#define VD55G0_REVISION_CUT1 0x1111
+#define VD55G0_REVISION_CUT2 0x1120
+#define VD55G0_REG_FWPATCH_REVISION CCI_REG16_LE(0x0022)
+#define VD55G0_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
+#define VD55G0_REG_SYSTEM_FSM CCI_REG8(0x002c)
+#define VD55G0_SYSTEM_FSM_READY_TO_BOOT 0x01
+#define VD55G0_SYSTEM_FSM_SW_STBY 0x02
+#define VD55G0_SYSTEM_FSM_STREAMING 0x03
+#define VD55G0_REG_BOOT CCI_REG8(0x0200)
+#define VD55G0_BOOT_BOOT 1
+#define VD55G0_BOOT_PATCH_SETUP 2
+#define VD55G0_REG_SW_STBY CCI_REG8(0x0201)
+#define VD55G0_SW_STBY_START_STREAM 1
+#define VD55G0_REG_STREAMING CCI_REG8(0x0202)
+#define VD55G0_STREAMING_STOP_STREAM 1
+#define VD55G0_REG_EXT_CLOCK CCI_REG32_LE(0x0220)
+#define VD55G0_REG_LINE_LENGTH CCI_REG16_LE(0x0300)
+#define VD55G0_REG_ORIENTATION CCI_REG8(0x0302)
+#define VD55G0_REG_FORMAT_CTRL CCI_REG8(0x030a)
+#define VD55G0_REG_OIF_CTRL CCI_REG16_LE(0x030c)
+#define VD55G0_REG_OIF_IMG_CTRL CCI_REG8(0x030f)
+#define VD55G0_REG_ISL_ENABLE CCI_REG8(0x0329)
+#define VD55G0_REG_CLK_PLL_MIPI CCI_REG32_LE(0x0224)
+#define VD55G0_REG_PATGEN_CTRL CCI_REG16_LE(0x0400)
+#define VD55G0_PATGEN_TYPE_SHIFT 4
+#define VD55G0_PATGEN_ENABLE BIT(0)
+#define VD55G0_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x044d)
+#define VD55G0_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x044e)
+#define VD55G0_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0450)
+#define VD55G0_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x0064)
+#define VD55G0_REG_APPLIED_ANALOG_GAIN CCI_REG8(0x0066)
+#define VD55G0_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x0068)
+#define VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE CCI_REG16_LE(0x042e)
+#define VD55G0_REG_AE_COLDSTART_ANALOG_GAIN CCI_REG8(0x0430)
+#define VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN CCI_REG16_LE(0x0432)
+#define VD55G0_REG_EXP_MODE CCI_REG8(0x044c)
+#define VD55G0_REG_FRAME_LENGTH CCI_REG16_LE(0x0458)
+#define VD55G0_REG_ROI_X_START CCI_REG16_LE(0x045e)
+#define VD55G0_REG_ROI_X_END CCI_REG16_LE(0x0460)
+#define VD55G0_REG_ROI_Y_START CCI_REG16_LE(0x0462)
+#define VD55G0_REG_ROI_Y_END CCI_REG16_LE(0x0464)
+#define VD55G0_REG_AE_ROI_START_H CCI_REG16_LE(0x0436)
+#define VD55G0_REG_AE_ROI_START_V CCI_REG16_LE(0x0438)
+#define VD55G0_REG_AE_ROI_END_H CCI_REG16_LE(0x043a)
+#define VD55G0_REG_AE_ROI_END_V CCI_REG16_LE(0x043c)
+#define VD55G0_REG_GPIO_0_CTRL CCI_REG8(0x0467)
+#define VD55G0_REG_READOUT_CTRL CCI_REG8(0x047a)
+#define VD55G0_REG_DARKCAL_CTRL CCI_REG8(0x032c)
+#define VD55G0_DARKCAL_BYPASS 0
+#define VD55G0_DARKCAL_AUTO 1
+#define VD55G0_REG_DUSTER_CTRL CCI_REG8(0x0316)
+#define VD55G0_DUSTER_ENABLE BIT(0)
+#define VD55G0_DUSTER_DISABLE 0
+#define VD55G0_DUSTER_DYN_ENABLE BIT(1)
+#define VD55G0_DUSTER_RING_ENABLE BIT(4)
+#define VD55G0_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0440)
+
+#define VD55G0_WIDTH 644
+#define VD55G0_HEIGHT 604
+#define VD55G0_DEFAULT_MODE 1
+#define VD55G0_WRITE_MULTIPLE_CHUNK_MAX 16
+#define VD55G0_NB_GPIOS 4
+#define VD55G0_MIN_FRAME_LENGTH (605 + 76)
+#define VD55G0_FRAME_LENGTH_DEF 1860
+#define VD55G0_TIMEOUT_MS 500
+#define VD55G0_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
+#define VD55G0_EXPO_MAX_TERM 64
+#define VD55G0_EXPO_DEF 200
+#define VD55G0_EXPO_TARGET_DEF 27
+#define VD55G0_LINE_LENGTH_SLOW 1200
+#define VD55G0_LINE_LENGTH_FAST 1128
+#define VD55G0_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
+#define VD55G0_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
+#define VD55G0_MIPI_RATE_MIN (750 * MEGA)
+#define VD55G0_MIPI_RATE_MAX (1200 * MEGA)
+#define VD55G0_MIPI_RATE_FAST (900 * MEGA)
+
+static const u8 cut1_patch[] = {
+0x81, 0x06, 0x0b, 0x02, 0x00, 0x09, 0x42, 0x00, 0xd2, 0x08, 0x42, 0x00, 0xe6,
+0x08, 0x42, 0x00, 0x12, 0x09, 0x42, 0x00, 0xe0, 0x3f, 0x80, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x40, 0x01, 0x60, 0x10, 0x40, 0x11,
+0x81, 0x16, 0x80, 0x93, 0xdd, 0x4c, 0x04, 0x04, 0xfa, 0x06, 0x4c, 0x94, 0x60,
+0xe6, 0x0e, 0xdc, 0xe0, 0x04, 0x18, 0x70, 0x02, 0x08, 0xe7, 0xce, 0x44, 0x01,
+0x60, 0x10, 0x40, 0x31, 0x85, 0x1e, 0x08, 0x4e, 0x43, 0x01, 0x60, 0x10, 0xc0,
+0x31, 0x40, 0x20, 0xe0, 0x04, 0x98, 0x29, 0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24,
+0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84, 0x2b, 0xa1, 0x15, 0xfe, 0xd8, 0x41,
+0x00, 0xe8, 0xf8, 0x47, 0x00, 0xe8, 0x78, 0x44, 0x40, 0xef, 0x0a, 0x9c, 0x1a,
+0x9e, 0x2a, 0xdc, 0x3a, 0xde, 0x03, 0x4e, 0x7c, 0xf9, 0x13, 0x4e, 0x78, 0xf9,
+0x23, 0x4e, 0x74, 0xf9, 0x33, 0x4e, 0x70, 0xf9, 0x18, 0x50, 0x40, 0xef, 0x38,
+0x49, 0x44, 0xef, 0x04, 0x18, 0x11, 0x83, 0x11, 0x40, 0x20, 0xe0, 0x4c, 0x0c,
+0x04, 0xf2, 0x93, 0xdd, 0x86, 0x44, 0x88, 0xe0, 0x05, 0x04, 0x40, 0xf8, 0x09,
+0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0x60, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80,
+0x65, 0x86, 0x31, 0x01, 0x06, 0x02, 0x0c, 0xe1, 0x33, 0x40, 0xc8, 0xe2, 0x33,
+0x40, 0x70, 0xe2, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0x55, 0x8a, 0x0a, 0x02,
+0x08, 0x70, 0x16, 0x43, 0x58, 0xe0, 0x26, 0x08, 0x00, 0x40, 0x16, 0x0a, 0x00,
+0xc0, 0x41, 0x44, 0x08, 0x40, 0x06, 0x46, 0x08, 0xe0, 0x51, 0x44, 0x04, 0xc0,
+0x45, 0x85, 0x66, 0x02, 0x04, 0xe0, 0x13, 0x40, 0x70, 0x62, 0x06, 0x02, 0x0c,
+0xe1, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0xb5, 0x86, 0x0a, 0x02, 0x08, 0x70,
+0xa6, 0x02, 0x68, 0xe0, 0x26, 0x0a, 0x00, 0x40, 0x16, 0x18, 0x00, 0xc0, 0x31,
+0x01, 0xc6, 0x09, 0x0c, 0xe0, 0x51, 0x44, 0x08, 0xc0, 0xc1, 0x44, 0x04, 0xc0,
+0x95, 0x83, 0x0a, 0x02, 0x04, 0xf0, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x44, 0x08,
+0xc0, 0x11, 0x44, 0x04, 0x40, 0x11, 0x81, 0x33, 0x40, 0x18, 0xe3, 0x15, 0x83,
+0x0a, 0x02, 0x04, 0x70, 0x11, 0x81, 0x16, 0x0a, 0x00, 0x40, 0x31, 0x9d, 0x51,
+0x44, 0x08, 0x40, 0x06, 0x09, 0x04, 0xe0, 0x51, 0x44, 0x04, 0xc0, 0x43, 0x40,
+0x18, 0xe3, 0x33, 0x40, 0xc8, 0xe2, 0x13, 0x40, 0xc0, 0x62, 0xa6, 0x05, 0x78,
+0xe0, 0x23, 0x40, 0xd0, 0xe3, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x7c, 0xfa,
+0x86, 0x42, 0x84, 0x60, 0x06, 0x00, 0x0c, 0xe1, 0x14, 0x04, 0xf6, 0x50, 0x78,
+0xe3, 0x04, 0x00, 0x26, 0x0f, 0x28, 0xe2, 0x16, 0x15, 0x2c, 0x64, 0xb1, 0xbb,
+0x6a, 0x84, 0x0b, 0xa3, 0x36, 0x10, 0x10, 0x41, 0x2e, 0x54, 0x08, 0xd8, 0x16,
+0x4f, 0x44, 0x43, 0x06, 0x16, 0x00, 0xc0, 0x0b, 0x10, 0x1b, 0x8c, 0xb8, 0x4f,
+0x00, 0x6f, 0xa4, 0x12, 0x80, 0xfb, 0x06, 0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc,
+0x73, 0x03, 0xc0, 0x83, 0x22, 0x0e, 0x02, 0xc1, 0x7f, 0x1f, 0xc0, 0x87, 0xd4,
+0x8f, 0x02, 0x1b, 0x8c, 0x8f, 0x20, 0x0b, 0x8e, 0x98, 0x4e, 0x00, 0xef, 0x06,
+0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc, 0xf0, 0x73, 0xa2, 0x77, 0xcc, 0x70, 0x42,
+0x01, 0x73, 0xff, 0xc7, 0x12, 0x4e, 0x80, 0x79, 0x0b, 0x92, 0xb8, 0x4d, 0x00,
+0x6f, 0x1b, 0x8c, 0x06, 0x42, 0x94, 0x60, 0x06, 0x46, 0x1a, 0xe0, 0x24, 0x04,
+0x00, 0x40, 0xfd, 0x73, 0x03, 0xc0, 0xa3, 0xa6, 0xa7, 0xd4, 0xb3, 0x44, 0x60,
+0xe3, 0x85, 0x44, 0x9c, 0xe0, 0x24, 0x84, 0x75, 0x44, 0xa0, 0x60, 0x0e, 0x04,
+0xc0, 0x7f, 0x1f, 0xc0, 0x14, 0x04, 0xaf, 0x84, 0xaf, 0xa0, 0xa5, 0x42, 0xa4,
+0xe0, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x0c, 0x04, 0x0c, 0xfa, 0x46, 0x0d,
+0xcc, 0xe0, 0x09, 0x0c, 0xbc, 0xee, 0x0a, 0x40, 0x80, 0xe0, 0x45, 0x82, 0xa6,
+0x40, 0x08, 0xe0, 0x06, 0x98, 0x08, 0x7f, 0x74, 0xef, 0x0b, 0xa1, 0xb5, 0xfe,
+0x01, 0x87, 0x06, 0x98, 0x06, 0x4c, 0x94, 0x60, 0x06, 0x42, 0x0c, 0xe0, 0x05,
+0x0c, 0x14, 0xe0, 0x11, 0x40, 0x30, 0x62, 0x26, 0x42, 0x00, 0xe0, 0x05, 0x0c,
+0x14, 0xe0, 0x11, 0x40, 0xa0, 0x62, 0x56, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14,
+0xe0, 0x11, 0x40, 0x40, 0x62, 0x76, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14, 0xe0,
+0x11, 0x40, 0x50, 0xe2, 0x04, 0x98, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x42, 0x88,
+0xe0, 0xc5, 0x80, 0x28, 0x67, 0x54, 0x6f, 0x66, 0x00, 0xa0, 0xe0, 0x04, 0x98,
+0x19, 0x00, 0x30, 0xe0, 0x19, 0xc4, 0x11, 0x40, 0x30, 0x60, 0x11, 0x83, 0x04,
+0x9a, 0x11, 0x40, 0x10, 0xe0, 0x04, 0x98, 0x11, 0x40, 0x20, 0x60, 0x11, 0x81,
+0x04, 0x98, 0x11, 0x40, 0xb0, 0xe7, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x02, 0x0c,
+0xfc, 0x85, 0xfe, 0xc6, 0x4c, 0xb4, 0xe0, 0x60, 0x10, 0xb1, 0x64, 0x01, 0xc0,
+0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8c, 0x04, 0xe0, 0x0b, 0xa1, 0x25, 0x8c, 0xd8,
+0x43, 0x5c, 0xef, 0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8a, 0x04, 0xe0, 0x0b, 0xa1,
+0x25, 0x8a, 0xc6, 0x4e, 0x84, 0xe0, 0x04, 0x9c, 0x09, 0x00, 0x20, 0xe1, 0x4a,
+0x40, 0x80, 0xe0, 0x25, 0x86, 0x08, 0x22, 0x46, 0x43, 0x00, 0xe0, 0x06, 0x40,
+0x06, 0xe0, 0x63, 0x80, 0x09, 0x0c, 0x40, 0xe2, 0x09, 0x86, 0x0b, 0xa5, 0xd5,
+0x82, 0x68, 0x4e, 0x48, 0x6f, 0x01, 0x85, 0x78, 0x44, 0x48, 0x6f, 0x06, 0x00,
+0x80, 0xe1, 0xf8, 0x75, 0x4c, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x78, 0x48, 0x48,
+0xef, 0x09, 0x0e, 0x48, 0xe0, 0x0b, 0xa1, 0xb5, 0x80, 0x58, 0x7b, 0x3c, 0xef,
+0x78, 0x74, 0x40, 0xef, 0xa6, 0x40, 0x08, 0xe0, 0x0c, 0x0c, 0x0c, 0xf2, 0x18,
+0x12, 0x50, 0xff, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x7b, 0x00, 0x06, 0x01,
+0x08, 0xe1, 0x08, 0x00, 0x6b, 0x82, 0x0b, 0xa1, 0x15, 0x82, 0xa6, 0x41, 0x84,
+0xe0, 0x18, 0x00, 0x01, 0x89, 0x18, 0x42, 0x00, 0xef, 0x63, 0x80, 0x04, 0x1c,
+0x64, 0x02, 0x84, 0xe0, 0x78, 0x40, 0x00, 0x68, 0x00, 0x00, 0x40, 0xe6, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x1e, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x3c, 0xfa,
+0xf0, 0x1e, 0x40, 0x6c, 0x86, 0x0f, 0xe0, 0xe0, 0x01, 0x05, 0x91, 0x81, 0x06,
+0x02, 0x00, 0x62, 0x81, 0xb1, 0x01, 0x4e, 0xd8, 0x69, 0x0e, 0x00, 0x00, 0x60,
+0x01, 0xc0, 0x0e, 0x7c, 0x6b, 0x9e, 0x93, 0x4e, 0x14, 0x65, 0x86, 0x00, 0x30,
+0xe1, 0x93, 0x4e, 0x18, 0x65, 0x2b, 0x8c, 0x91, 0x4e, 0x50, 0xe1, 0x81, 0x4e,
+0xc8, 0xe9, 0x1e, 0xbe, 0x1e, 0x7e, 0x11, 0x8f, 0x9e, 0xbc, 0xd8, 0x55, 0x30,
+0xef, 0x06, 0x00, 0x00, 0x64, 0x46, 0x02, 0x54, 0xe1, 0x0e, 0x3e, 0x06, 0x00,
+0x20, 0xe0, 0x0e, 0x7e, 0x86, 0x00, 0x30, 0xe1, 0x1e, 0x7c, 0x11, 0x91, 0x9e,
+0x3c, 0x2b, 0x8c, 0xd8, 0x54, 0x30, 0xef, 0x06, 0x00, 0x00, 0x66, 0xa1, 0x83,
+0x0e, 0x3e, 0xf6, 0x41, 0xfc, 0xe9, 0x0e, 0x7e, 0x06, 0x00, 0x01, 0x60, 0x20,
+0xc0, 0x0e, 0x7c, 0x86, 0x00, 0x30, 0xe1, 0xae, 0x3c, 0x11, 0x93, 0xc8, 0x53,
+0x30, 0x6f, 0x2b, 0x8c, 0x06, 0x40, 0x88, 0x60, 0xf6, 0x45, 0xfc, 0x63, 0xff,
+0xc3, 0x14, 0x00, 0xf1, 0xa0, 0x95, 0x4e, 0xa8, 0xe2, 0xa1, 0x4e, 0x44, 0xea,
+0x81, 0x4e, 0xf0, 0x69, 0x10, 0x42, 0x00, 0x7c, 0x00, 0xcc, 0x1f, 0x04, 0x21,
+0x8b, 0x1e, 0x00, 0x86, 0x40, 0xfc, 0xe7, 0x05, 0x4e, 0xa4, 0x62, 0xc6, 0x01,
+0x5c, 0xe0, 0x05, 0x4e, 0xac, 0x62, 0x9e, 0x00, 0x00, 0x60, 0x02, 0xc0, 0x05,
+0x4e, 0x94, 0x62, 0x86, 0x40, 0x3c, 0x60, 0x10, 0xe7, 0x0e, 0x9c, 0x09, 0x0e,
+0x0c, 0xe1, 0x15, 0x4e, 0x3c, 0x79, 0x06, 0x02, 0x80, 0xe0, 0x13, 0x4e, 0xf4,
+0x64, 0x86, 0x02, 0x00, 0x60, 0x02, 0xc0, 0x21, 0x4e, 0x5c, 0x69, 0x00, 0x40,
+0xc0, 0xf3, 0x1e, 0x9e, 0x01, 0x4e, 0x0c, 0xe1, 0x0c, 0x0c, 0x3c, 0xf2, 0x93,
+0xdd, 0xc6, 0x01, 0xcc, 0x60, 0x06, 0x42, 0x88, 0xe0, 0x29, 0x00, 0x98, 0xee,
+0x34, 0x84, 0x4a, 0x44, 0x80, 0x60, 0x86, 0x04, 0x30, 0xe1, 0x24, 0x08, 0x30,
+0x46, 0xfd, 0x73, 0xff, 0xc3, 0x3e, 0x04, 0xa6, 0x08, 0xa0, 0x80, 0x34, 0x06,
+0xc8, 0x9c, 0x45, 0x44, 0xa0, 0x80, 0x35, 0x42, 0x00, 0x00, 0x06, 0x06, 0x00,
+0xc0, 0x35, 0x44, 0xa0, 0xc0, 0x35, 0x02, 0x00, 0xc0, 0x3e, 0x80, 0x93, 0xdd,
+0x4c, 0x00, 0x00, 0xfa, 0x88, 0x40, 0x00, 0xe8, 0x86, 0x02, 0x0c, 0xe1, 0x0e,
+0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04,
+0x00, 0xf8, 0xc6, 0x40, 0xb4, 0xe0, 0x00, 0x02, 0xb1, 0x64, 0x01, 0xc0, 0x2a,
+0xc4, 0x2b, 0xa1, 0x35, 0x92, 0x24, 0xc4, 0x2b, 0xa1, 0xc5, 0x90, 0x38, 0x06,
+0x46, 0x45, 0x00, 0xe0, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0x49, 0x06, 0xf0,
+0xe2, 0x4d, 0xe6, 0xc6, 0x40, 0x48, 0x41, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xd5, 0x8c, 0xc9, 0x06, 0x44, 0x62, 0x06, 0x4b, 0x00, 0xe0, 0x46,
+0x48, 0x16, 0x60, 0x86, 0x4a, 0xc8, 0xe0, 0x53, 0x08, 0xc0, 0x58, 0x80, 0xf3,
+0x49, 0x0a, 0xa4, 0xe7, 0x6b, 0x0a, 0xd8, 0xe3, 0x49, 0xbe, 0xcf, 0xa8, 0xc1,
+0x46, 0x44, 0xe2, 0x48, 0x86, 0xc9, 0x0a, 0xa0, 0xe7, 0x46, 0x48, 0x0a, 0xe0,
+0xc1, 0x46, 0x40, 0xe2, 0xcb, 0x0a, 0xe0, 0x63, 0x43, 0x80, 0x63, 0x48, 0x28,
+0xe1, 0x38, 0x86, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0xc3, 0x46, 0x30, 0xe1,
+0x18, 0x86, 0xcb, 0x0a, 0xd4, 0xe3, 0x2b, 0x0a, 0xdc, 0x63, 0x16, 0x42, 0x0a,
+0xe0, 0x5b, 0x0a, 0xe4, 0xe3, 0xc3, 0x48, 0x24, 0x61, 0x03, 0x82, 0x23, 0x46,
+0x2c, 0xe1, 0x53, 0x40, 0x34, 0xe1, 0x4c, 0x0c, 0x00, 0xf0, 0x93, 0xdd, 0xc3,
+0xc1, 0x0c, 0x00, 0x80, 0xfa, 0x86, 0x43, 0xcc, 0x60, 0x0b, 0xa3, 0x29, 0x02,
+0xc4, 0xee, 0xb5, 0x84, 0x4a, 0x04, 0xfc, 0xfb, 0x45, 0x84, 0xc6, 0x40, 0x84,
+0xe0, 0x14, 0x82, 0x18, 0x84, 0x19, 0x86, 0x1b, 0xa5, 0x85, 0x8c, 0x04, 0x80,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0xc5, 0x8a, 0x0c, 0x08, 0x80,
+0xf2, 0x08, 0x34, 0x44, 0xff, 0x0b, 0xa5, 0x15, 0x8a, 0x0a, 0x04, 0x04, 0xf0,
+0xb5, 0x88, 0xc6, 0x40, 0x84, 0xe0, 0x24, 0x82, 0x28, 0x88, 0x29, 0x86, 0x2b,
+0xa5, 0xc5, 0x86, 0x04, 0x80, 0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0,
+0x05, 0x86, 0x09, 0x02, 0x24, 0xee, 0x0b, 0xa3, 0xd5, 0x80, 0x08, 0x84, 0x0a,
+0x40, 0x80, 0xe0, 0x75, 0xf8, 0xf5, 0x81, 0x38, 0x72, 0x44, 0xef, 0xf8, 0x51,
+0xfc, 0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0xa5,
+0x80, 0x0c, 0x08, 0x80, 0xf2, 0xb8, 0x11, 0xfc, 0xff, 0x0c, 0x08, 0x80, 0xf2,
+0x93, 0xdd, 0xcc, 0x04, 0x0c, 0xfa, 0x06, 0x4f, 0x84, 0x60, 0x86, 0x50, 0x4c,
+0xe1, 0x04, 0x1c, 0x80, 0x0c, 0x21, 0x6f, 0x01, 0xc0, 0xf0, 0x04, 0x50, 0x64,
+0x11, 0x81, 0x09, 0x00, 0x6c, 0xe0, 0x06, 0x29, 0x0b, 0x8c, 0x58, 0x4c, 0x58,
+0xef, 0x04, 0x9c, 0x19, 0x00, 0x6c, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3,
+0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f,
+0xa2, 0x21, 0x40, 0x6c, 0xe0, 0x86, 0x43, 0xcc, 0xe0, 0x29, 0x02, 0x24, 0xee,
+0x2b, 0xa3, 0xd5, 0x80, 0x18, 0x84, 0x4a, 0x42, 0x80, 0x60, 0x11, 0x8b, 0x65,
+0x80, 0x19, 0x00, 0x70, 0xe0, 0x16, 0x29, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x03,
+0x0b, 0x8c, 0x28, 0x4a, 0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x70, 0xe0, 0x10,
+0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3,
+0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40, 0x70, 0xe0, 0x09, 0x00, 0x74,
+0x60, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x85, 0x06, 0x29, 0x0b, 0x8c, 0xa8, 0x48,
+0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x74, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b,
+0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0,
+0x2f, 0xa2, 0x21, 0x40, 0x74, 0xe0, 0x09, 0x00, 0x78, 0x60, 0xf0, 0x04, 0x50,
+0xe4, 0x11, 0x87, 0x06, 0x29, 0x0b, 0x8c, 0x28, 0x47, 0x58, 0xef, 0x04, 0x9c,
+0x19, 0x00, 0x78, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28,
+0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40,
+0x78, 0xe0, 0x19, 0x0e, 0xd0, 0xe0, 0x1b, 0xa1, 0x85, 0x86, 0x05, 0x00, 0x24,
+0xe0, 0x1a, 0xa2, 0x04, 0x04, 0xc0, 0xe1, 0x2b, 0x83, 0xd5, 0x80, 0x19, 0x10,
+0x10, 0x60, 0x09, 0x9e, 0x0b, 0x83, 0x85, 0x84, 0x08, 0xa0, 0x0b, 0xa1, 0x38,
+0x61, 0x74, 0xcf, 0x58, 0x49, 0x34, 0xef, 0x14, 0x9c, 0x25, 0x02, 0x24, 0xe0,
+0x24, 0x02, 0xc0, 0x61, 0x29, 0x9e, 0x18, 0x58, 0x74, 0xef, 0x55, 0x81, 0x08,
+0xa0, 0x0b, 0xa1, 0x48, 0x60, 0x74, 0xcf, 0x28, 0x72, 0x4c, 0x6f, 0x46, 0x01,
+0x08, 0xe0, 0xcc, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x65, 0x5e, 0x04,
+0xfa, 0xf0, 0x0c, 0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc,
+0xfc, 0x07, 0x80, 0xfe, 0xf0, 0x1e, 0x20, 0x6c, 0xe6, 0x40, 0x00, 0xe0, 0xc8,
+0x70, 0x4c, 0xef, 0x0b, 0xa1, 0xb5, 0x88, 0xc6, 0x0d, 0xdc, 0xe0, 0x09, 0x0c,
+0x94, 0xea, 0x0b, 0xa3, 0xf5, 0x80, 0x09, 0x0c, 0x91, 0x79, 0x01, 0xc0, 0x0a,
+0x40, 0x80, 0xe0, 0xe5, 0x82, 0xc8, 0x71, 0x44, 0xef, 0x04, 0x18, 0x86, 0x02,
+0x84, 0xe0, 0x24, 0x04, 0x31, 0x83, 0x01, 0x82, 0x0e, 0x18, 0xe6, 0x00, 0x48,
+0xe1, 0x36, 0x00, 0x21, 0x82, 0x2e, 0x84, 0x46, 0x00, 0x2c, 0x61, 0x21, 0x83,
+0x14, 0x00, 0x46, 0x40, 0x34, 0xe1, 0x25, 0x42, 0x44, 0xe0, 0x28, 0x51, 0x28,
+0xef, 0x88, 0x40, 0x00, 0xe8, 0xf1, 0x90, 0xfc, 0x0f, 0x80, 0xf6, 0xf4, 0xbc,
+0x64, 0xfd, 0x53, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x01, 0x03, 0x11, 0x81, 0x48,
+0x79, 0x08, 0xef, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00,
+0x00, 0xfa, 0x0d, 0x62, 0x8e, 0x45, 0xf0, 0xff, 0x75, 0x82, 0x4e, 0x05, 0x01,
+0x60, 0x10, 0x40, 0x07, 0xc8, 0x24, 0x88, 0x23, 0x80, 0x1e, 0x8a, 0x78, 0x40,
+0x00, 0xe8, 0x21, 0x81, 0x0b, 0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x83,
+0xc1, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x41, 0xb9, 0x06, 0x48, 0x12, 0xe0,
+0x33, 0x88, 0x45, 0x06, 0x14, 0xe0, 0x4b, 0xa3, 0xc5, 0x80, 0x35, 0x06, 0x18,
+0xe0, 0x3b, 0x23, 0x31, 0x81, 0x55, 0x80, 0x31, 0x83, 0x0d, 0xe6, 0x04, 0x00,
+0x88, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x10, 0x80, 0x05, 0x00, 0x10, 0x9c, 0x08,
+0x40, 0x00, 0x98, 0x25, 0x0b, 0x8e, 0x40, 0xf0, 0xff, 0x3b, 0xa1, 0x45, 0x8a,
+0x2b, 0x23, 0x06, 0x4c, 0x00, 0xe0, 0x45, 0x90, 0x2b, 0x25, 0x6e, 0x40, 0xf0,
+0xff, 0x25, 0x87, 0x3b, 0xa1, 0x25, 0x8a, 0x2b, 0x23, 0x06, 0x0c, 0x08, 0xe0,
+0x05, 0x8e, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x0c, 0x06, 0x0c, 0x04,
+0xe0, 0x95, 0x87, 0x3b, 0xa1, 0xc5, 0x88, 0x2b, 0x23, 0x61, 0x91, 0x85, 0x8a,
+0x2b, 0x25, 0x7e, 0x40, 0xf0, 0xff, 0x25, 0x83, 0x2b, 0xa3, 0xc5, 0x08, 0x06,
+0x0c, 0x08, 0x80, 0x2b, 0x25, 0x4e, 0x40, 0xf0, 0xff, 0x15, 0x8a, 0xf5, 0x03,
+0x06, 0x0c, 0x04, 0xe0, 0x2b, 0x23, 0x61, 0x91, 0x45, 0x86, 0x2b, 0x25, 0x6e,
+0x40, 0xf0, 0xff, 0xc5, 0x04, 0x61, 0x89, 0xc5, 0x83, 0x2b, 0x23, 0x06, 0x4c,
+0x00, 0xe0, 0x05, 0x84, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x02, 0x61,
+0xa1, 0x25, 0x83, 0x2b, 0x23, 0x61, 0x85, 0xe5, 0x80, 0x2b, 0x25, 0x7e, 0x40,
+0xf0, 0xff, 0x65, 0x00, 0x61, 0x83, 0x95, 0x81, 0x0b, 0x02, 0x1b, 0x8c, 0x48,
+0x78, 0xfc, 0xef, 0x0b, 0xa1, 0x00, 0x4c, 0x00, 0xdc, 0x0b, 0x8c, 0x0c, 0x0c,
+0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x04, 0xfa, 0x6b, 0x00, 0xc6,
+0x40, 0xdc, 0xe0, 0x04, 0x00, 0xf0, 0x1e, 0x30, 0xec, 0x48, 0x55, 0xfc, 0x6e,
+0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0xb8, 0x68, 0xfc, 0xee, 0x58, 0x71, 0x08,
+0xef, 0x78, 0x68, 0xfc, 0x6e, 0x0b, 0x8c, 0x71, 0x01, 0x5b, 0x80, 0x7e, 0x3c,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x11, 0x03, 0x21, 0x81, 0x31, 0x05, 0x41,
+0x81, 0xa8, 0x77, 0x08, 0xef, 0x0e, 0x3e, 0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0,
+0x48, 0x4b, 0x0c, 0xef, 0x0e, 0x7c, 0x11, 0x81, 0x04, 0x3e, 0x21, 0x83, 0x68,
+0x76, 0xfc, 0x6f, 0x0e, 0x06, 0x04, 0x60, 0x10, 0xc0, 0xce, 0x0d, 0x01, 0x60,
+0x10, 0xc0, 0x76, 0x98, 0x04, 0xfc, 0xc8, 0x59, 0x0c, 0xef, 0x13, 0xc3, 0x08,
+0x98, 0x0b, 0xa1, 0xa5, 0xfe, 0x04, 0xfc, 0x18, 0x5c, 0x0c, 0xef, 0x14, 0x3e,
+0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x38, 0x46, 0x0c, 0xef, 0x01, 0x01, 0xf1,
+0x98, 0x0c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x65, 0x5e, 0x04, 0xfa, 0xf0, 0x0c,
+0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc, 0xfc, 0x03, 0x80,
+0xfe, 0x26, 0x00, 0x4c, 0x61, 0x11, 0x83, 0x16, 0x00, 0xce, 0x01, 0x01, 0x60,
+0x10, 0xc0, 0x16, 0x00, 0x46, 0x00, 0x2c, 0xe1, 0x04, 0x00, 0xf0, 0x1e, 0x30,
+0xec, 0x14, 0xc0, 0x14, 0x02, 0x20, 0xfc, 0x1e, 0xc0, 0x98, 0x40, 0x00, 0xe8,
+0xf1, 0x98, 0xfc, 0x0b, 0x80, 0xf6, 0xf4, 0xbc, 0x64, 0xfd, 0x53, 0xc1, 0xc3,
+0xc1, 0x06, 0x00, 0x01, 0x60, 0x00, 0xe0, 0x06, 0x00, 0x62, 0xf4, 0x93, 0xdd,
+0x4c, 0x04, 0x7c, 0xfa, 0xc6, 0x54, 0x84, 0x60, 0x06, 0x0f, 0xdc, 0xe0, 0x04,
+0x2a, 0xae, 0x4d, 0x01, 0x60, 0x10, 0xc0, 0x29, 0x14, 0x70, 0x60, 0x8e, 0x51,
+0x00, 0x60, 0x10, 0xc0, 0x3b, 0x0e, 0x9c, 0x65, 0x06, 0x52, 0x94, 0xe0, 0x1b,
+0x00, 0x20, 0x60, 0x0e, 0x57, 0x08, 0x60, 0x10, 0xc0, 0x0b, 0x00, 0x1c, 0xe0,
+0x13, 0x20, 0x01, 0x81, 0x13, 0x84, 0x13, 0x86, 0x11, 0x9e, 0x1c, 0x98, 0x0c,
+0xa0, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0xac, 0x0a, 0x98, 0x1a, 0xac,
+0x0d, 0xc3, 0xf5, 0x82, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0x2c, 0x01,
+0x8b, 0x08, 0x56, 0x2c, 0xef, 0x0a, 0xa0, 0x01, 0x82, 0x0c, 0xa0, 0x0a, 0xa0,
+0x8a, 0x00, 0x24, 0xe7, 0xf5, 0xfa, 0x05, 0x0e, 0xbc, 0x61, 0x06, 0x04, 0x00,
+0x60, 0x00, 0xf0, 0x13, 0x0e, 0xb8, 0x79, 0x06, 0x06, 0x00, 0x60, 0x00, 0xe0,
+0x2f, 0x80, 0x2b, 0x07, 0x0e, 0x45, 0x01, 0x60, 0x10, 0xc0, 0x1c, 0x88, 0x95,
+0x86, 0x07, 0xe1, 0x09, 0xbe, 0x0b, 0xa1, 0x05, 0x86, 0x21, 0xff, 0x1d, 0x84,
+0x85, 0x02, 0x00, 0x00, 0x08, 0x8c, 0x10, 0x04, 0x18, 0xec, 0x8a, 0x04, 0x14,
+0xed, 0xb5, 0x80, 0x8a, 0x02, 0x40, 0xe1, 0x75, 0x82, 0x01, 0x84, 0x14, 0x24,
+0x09, 0xbe, 0x29, 0x02, 0x00, 0xe3, 0x20, 0x44, 0x80, 0xf3, 0x2f, 0xa0, 0x21,
+0x42, 0x00, 0xe3, 0x8e, 0x41, 0x09, 0x60, 0x10, 0x40, 0xc6, 0x0d, 0x30, 0xe1,
+0x04, 0x80, 0x0b, 0xa3, 0x75, 0xa0, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0xc5, 0x9e, 0xa8, 0x54, 0x00, 0x68, 0x01, 0x81, 0x04, 0x24, 0x11, 0x85,
+0x11, 0x40, 0x20, 0x60, 0x4e, 0x43, 0x00, 0x60, 0x10, 0xc0, 0x04, 0xa4, 0x29,
+0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24, 0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84,
+0x2b, 0xa1, 0x15, 0xfe, 0xa8, 0x52, 0x44, 0xef, 0x04, 0xaa, 0x08, 0x80, 0x0b,
+0xa5, 0xe5, 0x80, 0x04, 0xa8, 0x09, 0x00, 0x20, 0xe1, 0x0b, 0xa1, 0x48, 0x4a,
+0x44, 0x8f, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0x2c,
+0xfc, 0x1e, 0xc0, 0x14, 0xc0, 0x14, 0x02, 0x08, 0xfc, 0x1e, 0x40, 0x11, 0x83,
+0x04, 0xa4, 0x11, 0x40, 0xb0, 0x67, 0x11, 0x81, 0x04, 0xa6, 0x11, 0x40, 0x10,
+0xe0, 0xb8, 0x73, 0x74, 0xef, 0x84, 0x20, 0x70, 0x06, 0x00, 0xe7, 0x8e, 0x17,
+0x01, 0x60, 0x10, 0x40, 0xce, 0x54, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xe0, 0x04,
+0x00, 0x04, 0xfc, 0x0e, 0xe0, 0x04, 0xe0, 0x04, 0x00, 0x0c, 0xfc, 0x0e, 0xe0,
+0x04, 0xe0, 0x04, 0x00, 0x10, 0xfc, 0x0e, 0xe0, 0x45, 0x10, 0x2c, 0xe0, 0x44,
+0x00, 0x00, 0xfc, 0x05, 0x50, 0x2c, 0x60, 0x4e, 0x41, 0x08, 0x60, 0x10, 0xc0,
+0x1b, 0x0e, 0xa0, 0xe1, 0x04, 0x80, 0x2b, 0x0e, 0x9c, 0xe1, 0x1b, 0xc2, 0x03,
+0x02, 0xce, 0x42, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x04, 0x03, 0x84, 0x00, 0x00,
+0xf4, 0xec, 0x0e, 0xac, 0x09, 0x0e, 0x00, 0xe3, 0x4e, 0x8f, 0x0b, 0xa5, 0x0b,
+0x0e, 0x10, 0xd8, 0x95, 0x96, 0x0b, 0x0e, 0x84, 0xe1, 0x15, 0x0e, 0x10, 0xe0,
+0x03, 0x4e, 0x10, 0xf8, 0x1d, 0xc1, 0x13, 0x4e, 0x10, 0x58, 0x10, 0x40, 0x00,
+0xdc, 0x1a, 0x5e, 0x04, 0x04, 0x80, 0xfb, 0x2d, 0xc3, 0x13, 0x4e, 0x10, 0x58,
+0x10, 0x40, 0x00, 0xdc, 0x85, 0x89, 0x0b, 0xa1, 0x98, 0x0c, 0x00, 0xc0, 0x68,
+0x7b, 0x68, 0x6f, 0x01, 0x85, 0x60, 0x00, 0x10, 0x64, 0x11, 0x81, 0xc8, 0x71,
+0x54, 0xef, 0x28, 0x45, 0x70, 0xef, 0x09, 0x0e, 0xe8, 0xea, 0x4a, 0x40, 0x90,
+0xe0, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x4b, 0x00, 0xc8, 0x09, 0x0e, 0x5c, 0xeb,
+0x0a, 0x40, 0x80, 0xe0, 0x25, 0xac, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc,
+0xfb, 0x85, 0xaa, 0x0a, 0x40, 0x90, 0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1,
+0x04, 0x80, 0x14, 0xc0, 0x14, 0x02, 0x2c, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c,
+0xeb, 0x0a, 0x40, 0x84, 0xe0, 0x25, 0x82, 0xb8, 0x68, 0x30, 0x6f, 0x01, 0x81,
+0xc8, 0x58, 0x2c, 0x6f, 0x0b, 0x8c, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x94,
+0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1, 0x04, 0x80, 0x14, 0xc0, 0x14, 0x02,
+0x08, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x98, 0xe0, 0x18,
+0x49, 0x00, 0xc8, 0x45, 0x91, 0x14, 0x2c, 0x04, 0x00, 0x80, 0xfb, 0x13, 0x20,
+0x06, 0x41, 0x2c, 0xe0, 0x16, 0x40, 0x02, 0x60, 0x46, 0x42, 0x04, 0xe0, 0xc8,
+0x7a, 0xf8, 0xee, 0x0e, 0x2c, 0x0e, 0x4c, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xac,
+0x00, 0x00, 0xd1, 0x6f, 0x0f, 0xc0, 0x0e, 0xac, 0x05, 0x10, 0x40, 0xe0, 0x04,
+0x00, 0x38, 0xfc, 0x0e, 0x98, 0x05, 0x50, 0x40, 0xe0, 0x04, 0xac, 0xd8, 0x63,
+0xfc, 0xef, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x15, 0x00, 0x40, 0xe0, 0x14,
+0x02, 0xb8, 0xfc, 0x1e, 0x98, 0x15, 0x40, 0x40, 0xe0, 0x98, 0x52, 0x30, 0xef,
+0x0b, 0xa1, 0xa5, 0xfe, 0x34, 0x28, 0xc6, 0x0d, 0x30, 0xe1, 0x08, 0x8c, 0x0b,
+0xa5, 0x0b, 0x0e, 0x04, 0xd8, 0x1b, 0x0e, 0x00, 0xc0, 0x2b, 0x0e, 0x04, 0xc0,
+0x3b, 0x0e, 0x08, 0xc0, 0x75, 0x84, 0x08, 0x8e, 0x19, 0x0e, 0x10, 0xeb, 0x12,
+0x40, 0x80, 0xfb, 0x01, 0x4e, 0x10, 0xf8, 0x0a, 0xcc, 0x03, 0x4e, 0x04, 0xf8,
+0x1a, 0xce, 0x1c, 0x9c, 0x2b, 0x06, 0x10, 0xe0, 0x2c, 0x9e, 0x3b, 0x06, 0x14,
+0xe0, 0x3c, 0xdc, 0xa4, 0x8f, 0x03, 0x4e, 0x80, 0xf9, 0x13, 0x4e, 0x7c, 0xf9,
+0x23, 0x4e, 0x78, 0xf9, 0x33, 0x4e, 0x74, 0xf9, 0xc8, 0x6d, 0x38, 0xef, 0xe8,
+0x66, 0x3c, 0xef, 0x04, 0x20, 0xa9, 0xc2, 0xa5, 0x40, 0x2c, 0xe0, 0x19, 0x0c,
+0x40, 0xe6, 0x1b, 0xa1, 0x55, 0x88, 0x14, 0xc0, 0x19, 0xd0, 0x1e, 0xc0, 0x14,
+0xc0, 0x19, 0xe0, 0x1e, 0xc0, 0x14, 0xc0, 0x19, 0xc4, 0x1e, 0x40, 0x01, 0x83,
+0xa8, 0x41, 0x00, 0xe8, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0xac, 0xfc, 0x1e,
+0xc0, 0x14, 0xc0, 0x19, 0xc8, 0x1e, 0xc0, 0x48, 0x51, 0x74, 0xef, 0x04, 0x26,
+0x11, 0x83, 0x11, 0x40, 0x10, 0xe0, 0x04, 0xa4, 0x11, 0x40, 0x20, 0x60, 0x11,
+0x81, 0x04, 0xa4, 0x11, 0x40, 0xb0, 0xe7, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd,
+0x1b, 0x00, 0x06, 0x01, 0x38, 0xe1, 0x04, 0x80, 0xc8, 0x20, 0x04, 0x60, 0x01,
+0xb8, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04,
+0x00, 0xfa, 0xe6, 0x41, 0x84, 0x60, 0x8e, 0x42, 0x00, 0x60, 0x10, 0xc0, 0x08,
+0x80, 0x14, 0x84, 0x0b, 0x21, 0xa6, 0x41, 0x04, 0xe1, 0x1c, 0x80, 0x05, 0x82,
+0x28, 0x47, 0x64, 0xef, 0x88, 0x7b, 0x28, 0x6f, 0x46, 0x40, 0x04, 0xe0, 0x35,
+0x81, 0x38, 0x56, 0x40, 0xef, 0xd8, 0x67, 0x64, 0xef, 0x38, 0x63, 0x64, 0xef,
+0xd8, 0x44, 0x44, 0xef, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80, 0x09, 0x00, 0x50,
+0xeb, 0x0a, 0x40, 0x88, 0xe0, 0x76, 0x00, 0xa0, 0xc0, 0x68, 0x4e, 0x50, 0xcf,
+0xc6, 0x0d, 0x30, 0xe1, 0x09, 0x0c, 0x30, 0xe7, 0x0b, 0xa1, 0x78, 0x6c, 0x70,
+0xcf, 0x98, 0x7f, 0x6c, 0xef, 0x38, 0x4b, 0x2c, 0x6f, 0x0b, 0x8c, 0xc8, 0x5a,
+0x30, 0x6f, 0x01, 0x81, 0x58, 0x49, 0x30, 0xef, 0xb8, 0x5f, 0x70, 0xef, 0x01,
+0x83, 0x01, 0x4c, 0xc0, 0xe5, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1,
+0x0c, 0x04, 0x84, 0xfa, 0x06, 0x4e, 0x88, 0x60, 0x06, 0x0c, 0x0c, 0xe1, 0x05,
+0x0e, 0x38, 0x78, 0x06, 0x04, 0x00, 0x70, 0x40, 0xc0, 0x19, 0x0c, 0x94, 0x78,
+0x06, 0x06, 0x10, 0x60, 0x40, 0xc0, 0x08, 0xc0, 0x4a, 0x02, 0x00, 0xe0, 0x09,
+0x86, 0x07, 0xc6, 0x2f, 0x61, 0x3f, 0xe1, 0x21, 0x4e, 0x10, 0xf8, 0x0c, 0x00,
+0x00, 0xe2, 0x31, 0x4e, 0x0c, 0xf8, 0x18, 0x62, 0x30, 0xef, 0x05, 0x0e, 0x38,
+0xf8, 0x14, 0x98, 0x09, 0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0xe0, 0x65, 0x82,
+0x0b, 0x0c, 0x28, 0x78, 0x1b, 0xa1, 0x0c, 0xde, 0x05, 0x84, 0x0a, 0x01, 0x20,
+0xec, 0xf5, 0x84, 0x55, 0x03, 0x01, 0x93, 0x0b, 0x0c, 0x24, 0x78, 0x1b, 0xa1,
+0x0c, 0xde, 0x65, 0x82, 0x0a, 0x01, 0x24, 0xec, 0xb5, 0x82, 0xb5, 0x01, 0x01,
+0x95, 0x0a, 0x01, 0x48, 0xec, 0x15, 0x82, 0x65, 0x01, 0x01, 0xa7, 0x0a, 0x01,
+0x50, 0xec, 0x75, 0x80, 0x01, 0xab, 0x0c, 0xde, 0x15, 0x0c, 0x20, 0x79, 0x06,
+0x04, 0x00, 0x60, 0x00, 0xf0, 0x49, 0x0c, 0x64, 0x78, 0x06, 0x06, 0x00, 0x60,
+0x00, 0xe0, 0x2f, 0x82, 0x3b, 0x0c, 0x8c, 0x78, 0x2b, 0x87, 0x30, 0x42, 0x00,
+0x5c, 0x4a, 0x08, 0x14, 0xfc, 0x14, 0x04, 0x80, 0x7a, 0xce, 0x43, 0x00, 0x60,
+0x10, 0xc0, 0x2c, 0x04, 0x8e, 0x48, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x90, 0x85,
+0x88, 0x45, 0x0e, 0x30, 0xf8, 0x3b, 0x08, 0x18, 0xe0, 0x5b, 0x08, 0x20, 0xe0,
+0x4b, 0x08, 0x1c, 0xe0, 0x32, 0x4a, 0x94, 0xf1, 0x53, 0x88, 0x8a, 0x0a, 0x89,
+0x63, 0x05, 0xc0, 0xb5, 0x84, 0x54, 0x1a, 0x8e, 0x59, 0x09, 0x60, 0x10, 0xc0,
+0x71, 0x83, 0x7e, 0x30, 0x2e, 0x59, 0x09, 0x60, 0x10, 0xc0, 0x5c, 0x30, 0x54,
+0x08, 0x80, 0xfb, 0x4d, 0x44, 0x4e, 0x45, 0x09, 0x60, 0x10, 0xc0, 0x3e, 0x88,
+0x43, 0x42, 0x00, 0xc0, 0x65, 0x81, 0x8e, 0x43, 0x09, 0x60, 0x10, 0x40, 0x21,
+0x81, 0x2e, 0x84, 0x03, 0x4c, 0xd5, 0x79, 0x02, 0x40, 0xd6, 0x41, 0x00, 0xe0,
+0x0c, 0x0c, 0x84, 0xf2, 0x18, 0x34, 0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0xfc,
+0xfa, 0x6b, 0x00, 0x06, 0x41, 0x08, 0xe0, 0x98, 0x73, 0x48, 0xef, 0x0b, 0xa1,
+0x78, 0x14, 0x00, 0xc0, 0x86, 0x52, 0x84, 0xe0, 0x74, 0xe4, 0x08, 0x9c, 0x09,
+0x86, 0x0b, 0xa3, 0x36, 0x40, 0x98, 0x40, 0x06, 0x02, 0x00, 0xc0, 0x11, 0x40,
+0x00, 0xc0, 0xf5, 0x80, 0x21, 0x01, 0x36, 0x40, 0x98, 0xe0, 0x2e, 0x18, 0x11,
+0x83, 0x16, 0x80, 0x84, 0x26, 0x16, 0x44, 0x98, 0xe0, 0x18, 0x9c, 0x09, 0x10,
+0xb4, 0xe0, 0x0a, 0x02, 0x0c, 0xfc, 0x0c, 0x02, 0x00, 0xe2, 0x01, 0x4c, 0x10,
+0xe1, 0x0b, 0x10, 0x80, 0xe0, 0x16, 0x88, 0xe8, 0x75, 0xf8, 0xee, 0x05, 0x4c,
+0x48, 0xe0, 0x08, 0x9c, 0x00, 0x42, 0x08, 0xfc, 0x19, 0x86, 0x1b, 0xa5, 0xd5,
+0x80, 0x4a, 0x00, 0x0c, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x0e, 0x98, 0x09, 0x10,
+0xb0, 0xe0, 0x98, 0x51, 0x00, 0x68, 0x09, 0x82, 0x01, 0x4c, 0x18, 0x60, 0x11,
+0x9f, 0x0b, 0x10, 0x88, 0xe0, 0x78, 0x51, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x4c, 0xe0, 0x0b, 0x02, 0x84, 0x60, 0x11, 0x91, 0xf8, 0x50, 0x00, 0xe8, 0xb4,
+0xa6, 0x05, 0x4c, 0x50, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x0a, 0x40, 0x84, 0x60,
+0x06, 0x00, 0x00, 0x60, 0x48, 0xd0, 0x56, 0x01, 0x55, 0x15, 0x41, 0xd0, 0x05,
+0x4c, 0x34, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x68, 0x4f, 0x00, 0x68, 0x09, 0x82,
+0x14, 0x64, 0x06, 0x54, 0xdc, 0xe0, 0x01, 0x4c, 0x10, 0x60, 0xce, 0x41, 0x00,
+0x60, 0x10, 0xc0, 0x0a, 0x80, 0x2b, 0x02, 0x18, 0xe0, 0x23, 0xa0, 0x23, 0x4c,
+0x60, 0xe0, 0x09, 0x14, 0x70, 0xea, 0x22, 0x40, 0x80, 0xf1, 0x03, 0x4c, 0x60,
+0xe0, 0x25, 0x14, 0xe0, 0xe2, 0x2b, 0xa3, 0x35, 0x88, 0x24, 0xa4, 0x3b, 0x02,
+0x18, 0xe0, 0x4b, 0x02, 0x20, 0xe0, 0x1b, 0x02, 0x1c, 0xe0, 0x59, 0x04, 0x24,
+0xe0, 0x33, 0xa8, 0x32, 0x44, 0x84, 0x70, 0x2e, 0x47, 0x08, 0x60, 0x10, 0xc0,
+0x20, 0x02, 0xfc, 0x6c, 0x0a, 0x4a, 0x88, 0xe0, 0x1c, 0x0c, 0x20, 0x02, 0x14,
+0xcc, 0x10, 0x02, 0xf0, 0xcc, 0x13, 0x46, 0x00, 0x40, 0x0a, 0x41, 0x04, 0xec,
+0x13, 0x4c, 0x60, 0x40, 0x10, 0x40, 0x00, 0xdc, 0x15, 0x14, 0xdc, 0x62, 0x04,
+0x00, 0x80, 0xfb, 0x86, 0x05, 0x74, 0xe1, 0x1d, 0xc1, 0x13, 0x4c, 0x60, 0x40,
+0x14, 0x00, 0x80, 0xdb, 0x14, 0xa4, 0x0e, 0xa8, 0x04, 0x88, 0x7a, 0x84, 0x73,
+0x4c, 0x64, 0xe0, 0xe8, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x8e, 0x40, 0xb1, 0x66,
+0x9b, 0xd3, 0x08, 0x6d, 0xf8, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x85, 0x4c, 0x3c,
+0xe0, 0x28, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0x28, 0x6d, 0xf8, 0xee,
+0x48, 0x6c, 0xf8, 0x6e, 0x0e, 0x02, 0x00, 0x60, 0x1e, 0xd1, 0x05, 0x4c, 0x40,
+0x60, 0x11, 0x91, 0x0b, 0x16, 0x64, 0xe0, 0x7b, 0x16, 0x5c, 0xe0, 0x89, 0x16,
+0xc0, 0xe0, 0x08, 0x49, 0x00, 0xe8, 0x3b, 0x00, 0x80, 0x44, 0x7c, 0xf0, 0x81,
+0x01, 0x11, 0x81, 0xb8, 0x40, 0x40, 0x6f, 0x0b, 0x8e, 0x4b, 0x0c, 0x60, 0x60,
+0x06, 0x0e, 0x00, 0x60, 0xe0, 0xcf, 0x0e, 0x5a, 0x11, 0x81, 0x21, 0x01, 0x06,
+0x06, 0x01, 0x60, 0xe0, 0xcf, 0xd8, 0x7f, 0x3c, 0x6f, 0x0b, 0x88, 0x14, 0x64,
+0x86, 0x57, 0x94, 0xe1, 0x05, 0x4c, 0x2c, 0xe0, 0x0a, 0x86, 0x83, 0x4c, 0xe4,
+0xe0, 0x03, 0x4c, 0xe0, 0xe0, 0x08, 0x86, 0x09, 0xbe, 0x03, 0x4c, 0xe8, 0xe0,
+0x08, 0x86, 0x09, 0xbe, 0x07, 0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x54,
+0xe0, 0x0a, 0xc4, 0x03, 0x4c, 0xec, 0xe0, 0x0a, 0x44, 0x11, 0x91, 0xe8, 0x45,
+0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x58, 0xe0, 0x0a, 0x46, 0x11, 0x91, 0x78,
+0x45, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x5c, 0xe0, 0x0b, 0x02, 0x10, 0x60,
+0x11, 0x91, 0xf8, 0x44, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x60, 0xe0, 0x0b,
+0x02, 0x14, 0x60, 0x11, 0x91, 0x78, 0x44, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c,
+0x64, 0xe0, 0x85, 0x4c, 0x14, 0xe0, 0x85, 0x4c, 0x18, 0xe0, 0x0b, 0x02, 0x7c,
+0x60, 0x11, 0x91, 0xb8, 0x43, 0x00, 0xe8, 0x05, 0x4c, 0x10, 0x60, 0x11, 0x91,
+0x81, 0x4c, 0x14, 0xe0, 0x75, 0x4c, 0x1c, 0xe0, 0x09, 0x14, 0xd0, 0xea, 0x07,
+0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x20, 0xe0, 0x0b, 0x14, 0x94, 0xe5,
+0x98, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x38, 0x60, 0x11, 0x91, 0x0b, 0x14, 0x64,
+0xe5, 0x28, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x28, 0x60, 0x11, 0x81, 0x09, 0x14,
+0x8c, 0x6a, 0x21, 0x81, 0x0b, 0x12, 0x7c, 0x60, 0x0a, 0x40, 0x88, 0xe0, 0xd6,
+0x0e, 0xcd, 0x0c, 0x73, 0xcf, 0x75, 0x4c, 0x24, 0x60, 0x3b, 0x8e, 0x28, 0x79,
+0x3c, 0xef, 0x0e, 0xd8, 0x16, 0x41, 0x08, 0xe0, 0x0c, 0x0c, 0xfc, 0xf2, 0xb8,
+0x1e, 0x48, 0xff, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2,
+0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x88, 0x61, 0x28, 0xef, 0x4c,
+0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x88, 0x5d,
+0x48, 0x6f, 0xf6, 0x40, 0x04, 0xe0, 0x0b, 0xa1, 0x85, 0x80, 0x0c, 0x0c, 0x00,
+0xf2, 0x93, 0xdd, 0xb8, 0x6b, 0x54, 0xef, 0xc6, 0x4c, 0x84, 0xe0, 0x04, 0x98,
+0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0x85, 0x88, 0xc6, 0x40, 0xb4,
+0x60, 0x46, 0x45, 0x00, 0xe0, 0x19, 0x00, 0xb4, 0xe4, 0x16, 0x42, 0x0a, 0xe0,
+0x03, 0x82, 0x09, 0x00, 0x40, 0xe2, 0x0b, 0xa5, 0xf5, 0x84, 0x48, 0x56, 0x40,
+0x6f, 0x01, 0x85, 0x58, 0x4c, 0x40, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0xd8, 0x7d,
+0x44, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x58, 0x50, 0x40, 0xef, 0x18, 0x70, 0xf4,
+0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x70,
+0xf4, 0xcf, 0x09, 0x0c, 0x48, 0xe0, 0x0b, 0xa1, 0x95, 0x84, 0xe8, 0x70, 0x38,
+0xef, 0x06, 0x40, 0x94, 0x60, 0x16, 0x07, 0x04, 0xe1, 0x04, 0x80, 0x1b, 0x00,
+0x18, 0xe1, 0x2b, 0x00, 0x50, 0xe1, 0x48, 0x8c, 0x39, 0x06, 0xac, 0xe0, 0x13,
+0xa4, 0x13, 0xa8, 0x13, 0xa6, 0x15, 0x40, 0x2c, 0xe0, 0xc6, 0x00, 0x2c, 0x61,
+0x11, 0xa1, 0x04, 0x80, 0x13, 0x40, 0xd0, 0x65, 0xe6, 0x40, 0xe0, 0xe0, 0x0a,
+0x00, 0x06, 0x42, 0x94, 0xe0, 0x14, 0x84, 0x03, 0x42, 0x10, 0x62, 0x06, 0x41,
+0x04, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x98, 0x17, 0x48, 0xff, 0xc3, 0xc1, 0x66,
+0x01, 0xcc, 0xe0, 0x18, 0x80, 0x0a, 0x42, 0x84, 0xe0, 0x10, 0x42, 0xf8, 0xd3,
+0x11, 0x40, 0x00, 0xc0, 0x93, 0xdd, 0x4a, 0x40, 0x80, 0xe0, 0x55, 0x84, 0x14,
+0x04, 0xa0, 0x60, 0x0e, 0x06, 0x00, 0x70, 0x3f, 0xc0, 0x2f, 0x06, 0x14, 0x06,
+0xa0, 0xe1, 0x14, 0x00, 0xe0, 0x61, 0x30, 0x46, 0x00, 0x70, 0xff, 0xc0, 0x17,
+0x70, 0x3f, 0xa0, 0x1f, 0xa4, 0x1f, 0xa6, 0x93, 0x5d, 0x0b, 0x82, 0x4c, 0x04,
+0x04, 0xfa, 0x86, 0x0d, 0xcc, 0x60, 0x71, 0x81, 0x7c, 0x98, 0x88, 0x55, 0x68,
+0xef, 0x0a, 0x98, 0x00, 0x42, 0xf1, 0x73, 0xff, 0xc0, 0x19, 0xc2, 0x1c, 0x18,
+0x60, 0x02, 0xe1, 0x65, 0x02, 0xc0, 0x65, 0x81, 0x24, 0x84, 0x2b, 0xa1, 0xf5,
+0x80, 0x71, 0x02, 0x11, 0x88, 0x0a, 0x0e, 0x00, 0xe1, 0x35, 0xfe, 0x75, 0x81,
+0x77, 0x50, 0x00, 0x40, 0xf0, 0xf3, 0x0f, 0xae, 0x09, 0xc4, 0x0c, 0x98, 0x4c,
+0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x4c, 0x04, 0x7c, 0xfa, 0xbb, 0x06, 0x6b, 0x82,
+0x14, 0x2c, 0x9b, 0x80, 0x74, 0x24, 0xab, 0x84, 0xa4, 0x00, 0xa0, 0x61, 0x8b,
+0x88, 0x1b, 0x81, 0x65, 0x82, 0x0e, 0x2c, 0x0b, 0x8e, 0x78, 0x56, 0x30, 0x6f,
+0x1b, 0x8c, 0x24, 0x2c, 0x0b, 0x8e, 0x28, 0x59, 0x30, 0x6f, 0x1b, 0x8c, 0x04,
+0x18, 0xb1, 0x83, 0x11, 0x01, 0x21, 0x83, 0x38, 0x44, 0x00, 0x68, 0x31, 0x85,
+0xa4, 0x02, 0x80, 0x7a, 0x21, 0x8b, 0xd8, 0x43, 0x00, 0x68, 0x06, 0x46, 0xfc,
+0xe1, 0xab, 0x00, 0x0b, 0x8e, 0x78, 0x40, 0xf8, 0x6f, 0x1b, 0x94, 0x09, 0x12,
+0x10, 0x60, 0x1b, 0x90, 0x98, 0x79, 0xfc, 0x6f, 0x70, 0x12, 0x10, 0xe4, 0x1b,
+0x00, 0x0b, 0x92, 0xb8, 0x7f, 0xf4, 0xef, 0x11, 0x03, 0x21, 0x85, 0x31, 0x09,
+0x0b, 0x94, 0x58, 0x42, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x18, 0x7f, 0xf4,
+0x6f, 0x1b, 0x90, 0x70, 0x12, 0x50, 0x64, 0x36, 0x54, 0x48, 0xe1, 0x08, 0x42,
+0x00, 0x68, 0x0b, 0x92, 0x0a, 0x00, 0x00, 0xfb, 0xb1, 0x54, 0x00, 0x40, 0x0a,
+0x40, 0xa0, 0xe0, 0xe5, 0xfc, 0x11, 0x01, 0x21, 0x85, 0x31, 0x09, 0x0b, 0x90,
+0xc8, 0x40, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x88, 0x7d, 0xf4, 0x6f, 0x1b,
+0x90, 0x8e, 0x98, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1f, 0x25,
+0x32, 0x40, 0x80, 0xf8, 0x1f, 0x86, 0x93, 0x5d, 0x0f, 0xa2, 0x04, 0x80, 0x93,
+0xdd, 0x0c, 0x00, 0x80, 0xfa, 0xa8, 0x4c, 0x48, 0x6f, 0xe6, 0x40, 0x08, 0xe0,
+0x0b, 0xa1, 0x95, 0x84, 0xc8, 0x46, 0x40, 0xef, 0x86, 0x02, 0x84, 0x60, 0xc6,
+0x04, 0x84, 0xe0, 0x14, 0x04, 0x5a, 0x00, 0x08, 0xe0, 0x24, 0x88, 0x4a, 0x42,
+0x08, 0x60, 0x46, 0x02, 0x84, 0xe0, 0x0a, 0x42, 0x80, 0xe1, 0x0c, 0x00, 0x00,
+0xe2, 0x06, 0x84, 0xf6, 0x40, 0x08, 0xe0, 0x0c, 0x08, 0x80, 0xf2, 0xd8, 0x0a,
+0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0xf0, 0x1e, 0x60, 0x6c, 0xa6,
+0x01, 0x04, 0xe0, 0x48, 0x4a, 0x48, 0xef, 0x0b, 0xa1, 0x15, 0x90, 0xc8, 0x5d,
+0x2c, 0xef, 0xe8, 0x7a, 0xf4, 0x6e, 0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0x86,
+0x50, 0x84, 0xe0, 0x69, 0x10, 0x44, 0xe0, 0x58, 0x7a, 0xf4, 0x6e, 0x1b, 0x8c,
+0x04, 0x60, 0x7b, 0x80, 0x1b, 0x00, 0x28, 0xe0, 0x0b, 0x00, 0x24, 0xe0, 0x13,
+0xa0, 0x10, 0x00, 0x04, 0x64, 0x1b, 0x8c, 0x88, 0x79, 0xf4, 0xee, 0x14, 0xa0,
+0x19, 0x02, 0x28, 0xe0, 0x19, 0xbe, 0x06, 0x44, 0x06, 0x60, 0x06, 0x41, 0xdc,
+0xe0, 0x04, 0x00, 0x16, 0x42, 0x1e, 0xe0, 0x21, 0xbe, 0x1e, 0x7c, 0x27, 0xcb,
+0x2c, 0x7c, 0x0e, 0x02, 0x25, 0x74, 0x03, 0xc0, 0x48, 0x78, 0xf4, 0xee, 0x0e,
+0x7e, 0x11, 0x81, 0x1e, 0x09, 0xf0, 0x00, 0x10, 0xe4, 0x1c, 0x95, 0x88, 0x7e,
+0x1c, 0xef, 0x0a, 0x95, 0x18, 0x41, 0x00, 0x68, 0x01, 0xa0, 0x06, 0x00, 0x38,
+0x61, 0x11, 0x83, 0x78, 0x7c, 0x1c, 0xef, 0x18, 0x46, 0x48, 0x6f, 0xb6, 0x01,
+0x04, 0xe0, 0xf1, 0xb0, 0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1b,
+0x00, 0x06, 0x00, 0x38, 0xe1, 0x04, 0x80, 0x08, 0x35, 0xf4, 0x7f, 0x01, 0xc8,
+0xc3, 0xc1, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x4e, 0x84, 0x60, 0xf0, 0x1e, 0x20,
+0xee, 0x84, 0x9c, 0x19, 0x0e, 0x48, 0xe0, 0x0a, 0xe2, 0x2a, 0xe0, 0x03, 0xa4,
+0x01, 0x82, 0x38, 0x75, 0xf4, 0x6e, 0x04, 0x00, 0x80, 0xfb, 0x0b, 0x10, 0x64,
+0x60, 0x6b, 0x80, 0x0b, 0xa1, 0x05, 0x0e, 0x04, 0xc0, 0x99, 0x00, 0xa0, 0xc0,
+0xd5, 0x84, 0x09, 0x10, 0x24, 0x60, 0x06, 0x03, 0xd0, 0xe0, 0x1a, 0x84, 0x07,
+0xc5, 0x09, 0x82, 0x1f, 0x21, 0x06, 0x00, 0x04, 0xe0, 0x10, 0x42, 0x01, 0x70,
+0xff, 0xc0, 0x17, 0xd1, 0x1b, 0xa1, 0x78, 0x73, 0xf4, 0x6e, 0x16, 0x02, 0x00,
+0x80, 0x00, 0x12, 0x04, 0xec, 0x18, 0x42, 0x48, 0x6f, 0x36, 0x01, 0x04, 0xe0,
+0x0b, 0xa1, 0xb5, 0xb0, 0x04, 0x1c, 0x06, 0x11, 0xd0, 0xe0, 0x19, 0x10, 0x24,
+0x6e, 0x46, 0x05, 0xb8, 0xe0, 0xa4, 0x9e, 0x09, 0x00, 0xb0, 0xe0, 0x18, 0x08,
+0x5a, 0x02, 0x00, 0xe0, 0x1c, 0x06, 0x00, 0xe2, 0x36, 0x3e, 0x09, 0x86, 0x0b,
+0x21, 0x19, 0x82, 0x0c, 0x04, 0x00, 0x62, 0x0b, 0xa5, 0x16, 0x7c, 0x64, 0x02,
+0x80, 0xfb, 0x26, 0x3c, 0x6b, 0x9e, 0x1e, 0xbe, 0x19, 0x0e, 0x60, 0xe0, 0x2b,
+0x10, 0x30, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x16, 0x1b, 0x11, 0x81, 0x06, 0x7e,
+0x2b, 0xa3, 0x19, 0x14, 0x5c, 0xc0, 0x09, 0x0e, 0x80, 0xe0, 0x25, 0x10, 0xa4,
+0xe3, 0x35, 0x10, 0xa8, 0x63, 0x10, 0x42, 0x3c, 0xd0, 0x0c, 0x0b, 0x01, 0x81,
+0x16, 0x1d, 0x11, 0x81, 0x26, 0x1f, 0x46, 0x05, 0x04, 0xe0, 0x0c, 0x89, 0x06,
+0x99, 0x06, 0xa5, 0x0c, 0x91, 0x3e, 0x8b, 0x0e, 0x91, 0x0e, 0x8f, 0x0e, 0x0d,
+0x60, 0x00, 0xa0, 0xe4, 0x91, 0x5e, 0x90, 0xe0, 0xc8, 0x7a, 0xf4, 0xee, 0x09,
+0x14, 0x58, 0xe0, 0x19, 0x10, 0xf0, 0xf8, 0x0e, 0x3f, 0xfe, 0x41, 0x3d, 0x70,
+0xff, 0xc0, 0x1b, 0x25, 0xf6, 0x43, 0x3c, 0xe0, 0x05, 0x5e, 0x80, 0x60, 0x16,
+0x00, 0x00, 0x80, 0x15, 0x5e, 0x84, 0xe0, 0x01, 0x5e, 0x34, 0x80, 0x05, 0x14,
+0x18, 0xe0, 0x38, 0x47, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a,
+0x20, 0x11, 0x91, 0x0e, 0x97, 0x78, 0x5d, 0xfc, 0x6f, 0x0b, 0x84, 0x19, 0x10,
+0xc0, 0xe9, 0x0e, 0xb7, 0x4a, 0x42, 0x80, 0xe0, 0xc5, 0x82, 0x60, 0x00, 0xc0,
+0x65, 0x11, 0xfb, 0x75, 0x81, 0x24, 0x37, 0x11, 0x82, 0x2e, 0xc1, 0x24, 0x97,
+0x25, 0x40, 0x44, 0xf8, 0x1b, 0xa1, 0x35, 0xfe, 0xe5, 0x85, 0x04, 0x9e, 0x05,
+0x00, 0x1c, 0xe0, 0x28, 0x45, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7,
+0x14, 0x9e, 0x0e, 0x99, 0x05, 0x02, 0x20, 0xe0, 0x98, 0x44, 0x00, 0x68, 0x00,
+0x40, 0xfc, 0x73, 0xff, 0xc7, 0x14, 0x9e, 0x0e, 0x9b, 0x05, 0x02, 0x24, 0xe0,
+0x08, 0x44, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a, 0x22, 0x11,
+0x91, 0x0e, 0x9d, 0x48, 0x5a, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x60, 0x11, 0x91,
+0x0e, 0xb9, 0xe8, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x62, 0x11, 0x91, 0x0e,
+0xbb, 0x88, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x0e, 0xbd, 0x04, 0x9c, 0x09, 0x00,
+0x24, 0xe0, 0x0a, 0x40, 0x88, 0xe0, 0xa5, 0x84, 0x04, 0xb7, 0xf8, 0x7a, 0xf4,
+0x6e, 0x1b, 0x80, 0x14, 0xb9, 0x0e, 0xb7, 0xa8, 0x7a, 0xf4, 0x6e, 0x0b, 0x82,
+0x14, 0xbb, 0x0e, 0xb9, 0x58, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x14, 0xbd, 0x0e,
+0xbb, 0x08, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x0e, 0xbd, 0x86, 0x41, 0x30, 0x61,
+0x1b, 0x9e, 0xd8, 0x4c, 0x08, 0xef, 0x78, 0x75, 0x44, 0x6f, 0x46, 0x01, 0x04,
+0xe0, 0xf0, 0x1e, 0x20, 0xe6, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0x4c, 0x00,
+0x00, 0xfa, 0x08, 0x74, 0x24, 0x6f, 0x11, 0x8f, 0x4c, 0x08, 0x00, 0xf2, 0x93,
+0xdd, 0x0c, 0x04, 0x7c, 0xfe, 0x6b, 0x04, 0xf0, 0x1e, 0x60, 0xec, 0xb5, 0x0c,
+0x10, 0xe0, 0x85, 0x0c, 0x18, 0xe0, 0x79, 0x0c, 0x24, 0xe0, 0xa4, 0xda, 0x1e,
+0x0b, 0x11, 0x81, 0x0e, 0x7e, 0x0b, 0x96, 0x68, 0x79, 0xf4, 0xee, 0x0b, 0xa1,
+0x15, 0x82, 0x14, 0x18, 0x0b, 0x96, 0x88, 0x79, 0xf4, 0xee, 0x0b, 0xa1, 0xb5,
+0x4c, 0x00, 0xc0, 0x11, 0x01, 0x0b, 0x94, 0x98, 0x78, 0xf4, 0xee, 0x94, 0x18,
+0x0b, 0xa1, 0x05, 0x1e, 0x14, 0xc0, 0xbe, 0xbe, 0x75, 0x82, 0x0b, 0x14, 0x1b,
+0x92, 0xf8, 0x78, 0xf4, 0xee, 0x04, 0x0b, 0x0b, 0xa1, 0xa5, 0x4c, 0x00, 0x40,
+0xa0, 0x52, 0x00, 0xdc, 0xae, 0x7c, 0xab, 0x80, 0x77, 0x42, 0x0b, 0x92, 0x15,
+0x14, 0x1c, 0xfe, 0x58, 0x75, 0xf4, 0xee, 0x0b, 0x14, 0x44, 0x60, 0x9b, 0x80,
+0x88, 0x75, 0xf4, 0xee, 0xbb, 0x00, 0x0b, 0x92, 0x08, 0x77, 0xf4, 0x6e, 0x1b,
+0x96, 0x0b, 0xa1, 0x90, 0x56, 0x00, 0x1c, 0xa0, 0x12, 0x88, 0xe4, 0xc8, 0x75,
+0xf4, 0x6e, 0x0b, 0x96, 0xab, 0x00, 0x01, 0x81, 0x01, 0x4c, 0x20, 0x60, 0x8b,
+0xa1, 0x04, 0x7e, 0x76, 0x03, 0x71, 0x7b, 0x34, 0xce, 0xac, 0xd8, 0x03, 0x8e,
+0x00, 0x00, 0x70, 0xe7, 0x0e, 0x89, 0x65, 0x88, 0x8b, 0xa3, 0x35, 0x08, 0x06,
+0x02, 0x01, 0x00, 0xc0, 0xcf, 0x58, 0x74, 0xf4, 0x6e, 0x0b, 0x96, 0x28, 0x73,
+0xf4, 0x6e, 0x8b, 0x80, 0x1b, 0x00, 0x0b, 0x96, 0x58, 0x73, 0xf4, 0xee, 0xd8,
+0x49, 0x00, 0xe8, 0x18, 0x74, 0xf4, 0x6e, 0x76, 0x03, 0x70, 0x7b, 0x34, 0xce,
+0x0b, 0xa1, 0x25, 0x84, 0x11, 0x01, 0x0b, 0x96, 0x48, 0x74, 0xf4, 0xee, 0x0b,
+0xa1, 0x80, 0x14, 0x04, 0x04, 0x80, 0x54, 0x00, 0xdc, 0x75, 0x81, 0xc8, 0x70,
+0xf4, 0x6e, 0x0b, 0x96, 0x58, 0x72, 0xf4, 0xee, 0xab, 0x80, 0xac, 0xd8, 0x0a,
+0xa4, 0x08, 0x71, 0xf4, 0xee, 0x04, 0x0b, 0x9b, 0x80, 0x73, 0x80, 0x0b, 0x0e,
+0x40, 0xe0, 0x98, 0x70, 0xf4, 0xee, 0xbb, 0x00, 0xa4, 0x00, 0x80, 0xfb, 0x48,
+0x70, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x96, 0x68, 0x49, 0x00, 0xe8, 0x1b, 0x00,
+0x0b, 0x92, 0xc8, 0x47, 0x00, 0xe8, 0xa8, 0x70, 0xf4, 0xee, 0x04, 0x09, 0xab,
+0x80, 0xac, 0xd8, 0x9a, 0x80, 0x9c, 0xda, 0x0b, 0x0e, 0x4c, 0xe0, 0x18, 0x6f,
+0xf4, 0xee, 0x0b, 0x0e, 0x48, 0x60, 0xbb, 0x80, 0xc8, 0x6e, 0xf4, 0xee, 0x8b,
+0x00, 0x0b, 0x92, 0x88, 0x6e, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0xa8, 0x47,
+0x00, 0xe8, 0x1b, 0x00, 0x0b, 0x96, 0x08, 0x46, 0x00, 0xe8, 0xe8, 0x6e, 0xf4,
+0xee, 0xb4, 0x3e, 0x9b, 0x80, 0x84, 0xfc, 0x9c, 0xda, 0x1b, 0x96, 0xf8, 0x6d,
+0xf4, 0x6e, 0x0b, 0x90, 0x68, 0x44, 0x00, 0xe8, 0xe8, 0x6e, 0xf4, 0x6e, 0x11,
+0x81, 0x0b, 0xa1, 0x85, 0x8e, 0x11, 0x01, 0x0b, 0x96, 0xf8, 0x6d, 0xf4, 0xee,
+0x2a, 0x00, 0x00, 0x60, 0x0b, 0x94, 0x24, 0x7e, 0x1b, 0x92, 0x34, 0x8b, 0xf8,
+0x74, 0x18, 0xef, 0x58, 0x01, 0x01, 0x40, 0x00, 0x40, 0x9b, 0x80, 0x0b, 0x16,
+0x1b, 0x92, 0x78, 0x6d, 0xf4, 0xee, 0x0b, 0xa1, 0x85, 0x82, 0x1a, 0xd8, 0x09,
+0x0c, 0x20, 0xe0, 0x11, 0x82, 0x04, 0x09, 0x0b, 0xa1, 0x1c, 0x58, 0x70, 0x00,
+0x90, 0xc4, 0x0a, 0x80, 0x0c, 0xda, 0x11, 0x01, 0x0b, 0x90, 0xe8, 0x6b, 0xf4,
+0xee, 0x0b, 0xa1, 0xb5, 0x84, 0x0b, 0x10, 0x1b, 0x92, 0x88, 0x6c, 0xf4, 0xee,
+0x0b, 0xa1, 0xe5, 0x82, 0x09, 0x0c, 0x20, 0xe0, 0x1a, 0xd8, 0x0b, 0xa1, 0x70,
+0x0e, 0x98, 0x44, 0x10, 0x00, 0x04, 0xec, 0x75, 0x5e, 0x10, 0xc0, 0x0c, 0xd8,
+0x04, 0x89, 0x0a, 0x80, 0x0c, 0xda, 0xf1, 0xb0, 0x0c, 0x0c, 0x7c, 0xf6, 0x93,
+0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x00, 0x11, 0x81, 0xe8, 0x6a, 0xf4, 0xee,
+0x0b, 0xa1, 0x64, 0x0c, 0x7c, 0xdd, 0x0b, 0x8c, 0x0c, 0x0c, 0x00, 0xf2, 0x93,
+0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x02, 0x7b, 0x80, 0x08, 0x6a,
+0xf4, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x48, 0x69, 0xf4, 0x6e, 0x1b, 0x8c, 0x0b,
+0xa1, 0x70, 0x4c, 0x00, 0x1c, 0x8b, 0xa1, 0x70, 0x4c, 0x00, 0xdc, 0x0b, 0x8c,
+0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x6b,
+0x02, 0x7b, 0x80, 0xa8, 0x68, 0xf4, 0xee, 0x0b, 0xa1, 0x70, 0x4c, 0x00, 0x9c,
+0x0b, 0x8c, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00,
+0xfa, 0x6b, 0x00, 0x21, 0x81, 0x04, 0x18, 0x31, 0x83, 0x68, 0x53, 0xfc, 0xef,
+0x0e, 0x98, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x18, 0xa9, 0x00, 0x00, 0x38,
+0x90, 0x40, 0x00, 0x08, 0x14, 0xb8, 0xe0, 0x80, 0x2c, 0x40, 0x00, 0xa8, 0x3a,
+0xe8, 0xe0, 0x08, 0x9f, 0x40, 0x00, 0x28, 0x28, 0xb0, 0xe0, 0x18, 0x72, 0x40,
+0x00, 0x88, 0x19, 0xc8, 0xe0, 0xe0, 0xbb, 0x40, 0x00, 0x08, 0x0c, 0xa4, 0xe0,
+0xa8, 0xe3, 0x40, 0x00, 0x28, 0x13, 0x90, 0xe0, 0xf0, 0xec, 0x40, 0x00, 0x68,
+0x0d, 0x8c, 0xe0, 0x48, 0xa8, 0x40, 0x00, 0xa8, 0x3c, 0xac, 0xe0, 0x28, 0xe4,
+0x40, 0x00, 0x08, 0x0e, 0x94, 0xe0, 0xb8, 0xe2, 0x40, 0x00, 0x28, 0x3d, 0x90,
+0xe0, 0x90, 0xa7, 0x40, 0x00, 0xc8, 0x13, 0xb0, 0xe0, 0x18, 0xc1, 0x40, 0x00,
+0xa8, 0x2a, 0xa4, 0xe0, 0xa8, 0x91, 0x40, 0x00, 0x68, 0x2f, 0xbc, 0xe0, 0x88,
+0xf1, 0x40, 0x00, 0x08, 0x1c, 0x88, 0xe0, 0xd0, 0x8e, 0x40, 0x00, 0xa8, 0x19,
+0xb8, 0xe0, 0x38, 0x82, 0x40, 0x00, 0x28, 0x38, 0xbc, 0xe0, 0x88, 0xe0, 0x40,
+0x00, 0xa8, 0x0e, 0x98, 0xe0, 0xd8, 0xe4, 0x40, 0x00, 0x68, 0x32, 0x94, 0xe0,
+0x18, 0xd7, 0x40, 0x00, 0x28, 0x2d, 0x9c, 0xe0, 0x40, 0xe8, 0x40, 0x00, 0x28,
+0x19, 0x94, 0xe0, 0xa8, 0x78, 0x40, 0x00, 0x28, 0x18, 0xcc, 0xe0, 0xf0, 0x95,
+0x40, 0x00, 0x48, 0x34, 0xbc, 0xe0, 0xa8, 0xd2, 0x40, 0x00, 0x48, 0x16, 0xa0,
+0xe0, 0x18, 0x52, 0x40, 0x00, 0x68, 0x2b, 0xe0, 0xe0, 0x00, 0xb9, 0x00, 0x00,
+0x23, 0xdb, 0x00, 0x00
+};
+
+static const u8 cut2_patch[] = {
+0x37, 0x00, 0x05, 0x00, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x0e, 0x84, 0x60, 0x06,
+0x11, 0x84, 0xe0, 0x04, 0x1c, 0x06, 0x13, 0x88, 0xe1, 0x14, 0xa0, 0x0d, 0xc2,
+0x95, 0x80, 0x04, 0xa4, 0x0b, 0xa3, 0xb5, 0x88, 0x78, 0x6c, 0x40, 0xef, 0x6b,
+0x00, 0x46, 0x01, 0x84, 0xe0, 0x88, 0x53, 0x48, 0x6f, 0x46, 0x15, 0x84, 0xe0,
+0x6b, 0x25, 0xc6, 0x0d, 0x84, 0xe0, 0x95, 0x84, 0x08, 0xa8, 0x4a, 0x00, 0xfc,
+0xfb, 0x05, 0x84, 0x09, 0x12, 0x1c, 0xe0, 0xa8, 0x46, 0x48, 0xef, 0x04, 0x1c,
+0x11, 0x83, 0x16, 0x18, 0xc6, 0x04, 0x84, 0xe0, 0x0e, 0x08, 0x01, 0x87, 0xf8,
+0x6a, 0x40, 0xef, 0x46, 0x0c, 0x84, 0xe0, 0x04, 0x1c, 0x11, 0x81, 0x16, 0x98,
+0x0e, 0xa0, 0x06, 0x41, 0x84, 0x60, 0xc6, 0x02, 0xd8, 0xe0, 0x04, 0x80, 0x08,
+0x80, 0x06, 0x84, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x00,
+0x80, 0xfa, 0xc6, 0x40, 0x84, 0x60, 0x86, 0x43, 0xd8, 0xe0, 0x04, 0x80, 0x09,
+0x00, 0x18, 0xe1, 0x06, 0x84, 0x88, 0x41, 0x68, 0xef, 0x0c, 0x08, 0x80, 0xf2,
+0x48, 0x20, 0x68, 0xff, 0x1e, 0xc0, 0xb5, 0x00, 0x2c, 0xe0, 0xb4, 0x02, 0x00,
+0xfc, 0x15, 0x40, 0x2c, 0xe0, 0x19, 0x10, 0x31, 0x78, 0x01, 0x40, 0x86, 0x04,
+0x0c, 0xe0, 0x4a, 0x04, 0x00, 0x64, 0x31, 0x81, 0x30, 0x0a, 0x04, 0x64, 0x4b,
+0x82, 0x1b, 0x06, 0x3b, 0x8a, 0xc8, 0x44, 0xfc, 0x87, 0x41, 0x50, 0x31, 0x78,
+0x01, 0x40, 0xa1, 0xe1, 0xe6, 0x43, 0x99, 0x6c, 0x10, 0xc0, 0x08, 0x42, 0x00,
+0xf8, 0x00, 0x00, 0x03, 0xa9, 0x00, 0x00, 0x30, 0x85, 0x40, 0x00, 0xa8, 0x16,
+0xbc, 0xe0, 0xcc, 0xc1, 0x40, 0x00, 0xa8, 0x35, 0x9c, 0xe0, 0xb0, 0xc9, 0x40,
+0x00, 0x88, 0x37, 0x98, 0xe0, 0x00, 0xb9, 0x00, 0x00, 0x49, 0xa7, 0x00, 0x00,
+};
+
+struct vd55g0_patch {
+ const u8 *bin;
+ const uint size;
+ const u16 major;
+ const u16 minor;
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut1 = {
+ .bin = cut1_patch,
+ .size = ARRAY_SIZE(cut1_patch),
+ .major = 2,
+ .minor = 11,
+};
+
+static const struct vd55g0_patch vd55g0_patch_cut2 = {
+ .bin = cut2_patch,
+ .size = ARRAY_SIZE(cut2_patch),
+ .major = 0,
+ .minor = 5,
+};
+
+static const char * const vd55g0_test_pattern_menu[] = {
+ "Disabled",
+ "Solid",
+ "Colorbar",
+ "Gradbar",
+ "Hgrey",
+ "Vgrey",
+ "Dgrey",
+ "PN28",
+};
+
+static const s64 vd55g0_ev_bias_menu[] = {
+ -3000, -2500, -2000, -1500, -1000, -500,
+ 0,
+ 500, 1000, 1500, 2000, 2500, 3000,
+};
+
+/*
+ * On ACPI, the INT3472 registers the rails the platform wired under its own
+ * names; the DT names would get dummy regulators there.
+ */
+static const char * const vd55g0_dt_supply_name[] = {
+ "vcore",
+ "vddio",
+ "vana",
+};
+
+static const char * const vd55g0_acpi_supply_name[] = {
+ "avdd",
+ "dovdd",
+};
+
+static const struct acpi_device_id vd55g0_acpi_ids[] = {
+ { "SMO55F0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, vd55g0_acpi_ids);
+
+#define VD55G0_NUM_SUPPLIES ARRAY_SIZE(vd55g0_dt_supply_name)
+
+enum vd55g0_bin_mode {
+ VD55G0_BIN_MODE_NORMAL,
+ VD55G0_BIN_MODE_DIGITAL_X2,
+};
+
+enum vd55g0_gpio_modes {
+ VD55G0_GPIO_MODE_DISABLED,
+ VD55G0_GPIO_MODE_STROBE,
+};
+
+struct vd55g0_mode_info {
+ u32 width;
+ u32 height;
+ enum vd55g0_bin_mode bin_mode;
+ struct v4l2_rect crop;
+};
+
+struct vd55g0_fmt_desc {
+ u32 code;
+ u8 bpp;
+ u8 data_type;
+};
+
+static const struct vd55g0_fmt_desc vd55g0_supported_codes[] = {
+ {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .bpp = 8,
+ .data_type = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .bpp = 10,
+ .data_type = MIPI_CSI2_DT_RAW10,
+ },
+};
+
+static const struct vd55g0_mode_info vd55g0_mode_data[] = {
+ {
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 0,
+ .top = 0,
+ .width = VD55G0_WIDTH,
+ .height = VD55G0_HEIGHT,
+ },
+ },
+ {
+ .width = 640,
+ .height = 600,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 2,
+ .width = 640,
+ .height = 600,
+ },
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .bin_mode = VD55G0_BIN_MODE_NORMAL,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ {
+ .width = 320,
+ .height = 240,
+ .bin_mode = VD55G0_BIN_MODE_DIGITAL_X2,
+ .crop = {
+ .left = 2,
+ .top = 62,
+ .width = 640,
+ .height = 480,
+ },
+ },
+};
+
+enum vd55g0_expo_state {
+ VD55G0_EXP_AUTO,
+ VD55G0_EXP_FREEZE,
+ VD55G0_EXP_MANUAL
+};
+
+struct vd55g0_dev {
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct regulator_bulk_data supplies[VD55G0_NUM_SUPPLIES];
+ unsigned int nb_supplies;
+ struct gpio_desc *reset_gpio;
+ struct clk *xclk;
+ u32 clk_freq;
+ u16 oif_ctrl;
+ u32 mipi_rate;
+ u32 pixel_rate;
+ u64 link_freq;
+ u16 line_length;
+ u16 revision;
+ /* Lock to protect all members below */
+ struct mutex lock;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *hblank_ctrl;
+ struct v4l2_ctrl *vflip_ctrl;
+ struct v4l2_ctrl *hflip_ctrl;
+ struct v4l2_ctrl *pattern_ctrl;
+ /* The auto exposure cluster, the auto control first */
+ struct {
+ struct v4l2_ctrl *ae_ctrl;
+ struct v4l2_ctrl *expo_ctrl;
+ struct v4l2_ctrl *again_ctrl;
+ struct v4l2_ctrl *dgain_ctrl;
+ };
+ bool streaming;
+ u32 code;
+ const struct vd55g0_mode_info *current_mode;
+ bool hflip;
+ bool vflip;
+ u16 manual_expo;
+ enum vd55g0_expo_state expo_state;
+ u16 digital_gain;
+ u8 analog_gain;
+ u16 vblank;
+ u16 vblank_min;
+ u16 frame_length;
+ bool ae_frozen;
+ u32 pattern;
+ u16 exposure_target;
+ u32 leds[VD55G0_NB_GPIOS];
+ bool flash_en;
+ unsigned int nb_leds;
+ struct {
+ u16 expo;
+ u16 digital_gain;
+ u8 analog_gain;
+ } cold_start;
+};
+
+static inline struct vd55g0_dev *to_vd55g0_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vd55g0_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vd55g0_dev,
+ ctrl_handler)->sd;
+}
+
+static u8 get_bpp_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].bpp;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to 8 bpp\n", code);
+ return 8;
+}
+
+static u8 get_data_type_by_code(__u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++) {
+ if (vd55g0_supported_codes[i].code == code)
+ return vd55g0_supported_codes[i].data_type;
+ }
+ /* Should never happen */
+ WARN(1, "Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type\n",
+ code);
+ return MIPI_CSI2_DT_RAW8;
+}
+
+#define vd55g0_read(sensor, reg, val, err) \
+ cci_read((sensor)->regmap, reg, val, err)
+
+#define vd55g0_write(sensor, reg, val, err) \
+ cci_write((sensor)->regmap, reg, val, err)
+
+static int get_chunk_size(struct vd55g0_dev *sensor)
+{
+ int max_write_len = VD55G0_WRITE_MULTIPLE_CHUNK_MAX;
+ struct i2c_adapter *adapter = sensor->i2c_client->adapter;
+
+ if (adapter->quirks && adapter->quirks->max_write_len)
+ max_write_len = adapter->quirks->max_write_len - 2;
+
+ max_write_len = min(max_write_len, VD55G0_WRITE_MULTIPLE_CHUNK_MAX);
+
+ return max(max_write_len, 1);
+}
+
+static int vd55g0_write_array(struct vd55g0_dev *sensor, u32 reg,
+ unsigned int nb, const u8 *array)
+{
+ const unsigned int chunk_size = get_chunk_size(sensor);
+ unsigned int addr = CCI_REG_ADDR(reg);
+ unsigned int sz;
+ int ret;
+
+ while (nb) {
+ sz = min(nb, chunk_size);
+ ret = regmap_bulk_write(sensor->regmap, addr, array, sz);
+ if (ret < 0)
+ return ret;
+ nb -= sz;
+ addr += sz;
+ array += sz;
+ }
+
+ return 0;
+}
+
+static int vd55g0_poll_reg(struct vd55g0_dev *sensor, u32 reg, u8 poll_val,
+ unsigned int timeout_ms)
+{
+ unsigned int val = 0;
+
+ return regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val,
+ (val == poll_val), 10 * USEC_PER_MSEC,
+ timeout_ms * USEC_PER_MSEC);
+}
+
+static int vd55g0_wait_state(struct vd55g0_dev *sensor, int state,
+ unsigned int timeout_ms)
+{
+ return vd55g0_poll_reg(sensor, VD55G0_REG_SYSTEM_FSM, state,
+ timeout_ms);
+}
+
+static int vd55g0_prepare_clock_tree(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ u32 pixel_div;
+
+ if (sensor->clk_freq < VD55G0_XCLK_FREQ_MIN ||
+ sensor->clk_freq > VD55G0_XCLK_FREQ_MAX) {
+ dev_err(dev,
+ "Only %luMHz-%luMHz clock range supported. Provided %lu MHz\n",
+ VD55G0_XCLK_FREQ_MIN / HZ_PER_MHZ,
+ VD55G0_XCLK_FREQ_MAX / HZ_PER_MHZ,
+ sensor->clk_freq / HZ_PER_MHZ);
+ return -EINVAL;
+ }
+
+ /*
+ * The MIPI bus is double data rate, and its rate is an integer multiple
+ * of the input clock. The sensor would round any other rate down
+ * (UM2829 13.6), so it is refused rather than used.
+ */
+ if (sensor->link_freq < VD55G0_MIPI_RATE_MIN / 2 ||
+ sensor->link_freq > VD55G0_MIPI_RATE_MAX / 2) {
+ dev_err(dev,
+ "Only %luMbps-%luMbps data rate range supported. Provided link frequency %llu Hz\n",
+ VD55G0_MIPI_RATE_MIN / MEGA, VD55G0_MIPI_RATE_MAX / MEGA,
+ sensor->link_freq);
+ return -EINVAL;
+ }
+ sensor->mipi_rate = sensor->link_freq * 2;
+ if (sensor->mipi_rate % sensor->clk_freq) {
+ dev_err(dev,
+ "Link frequency %llu Hz is not a multiple of half the %u Hz input clock\n",
+ sensor->link_freq, sensor->clk_freq);
+ return -EINVAL;
+ }
+
+ pixel_div = sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST ? 6 : 8;
+ sensor->pixel_rate = sensor->mipi_rate / pixel_div;
+
+ return 0;
+}
+
+static int vd55g0_update_gpio_mode(struct vd55g0_dev *sensor, u32 mode,
+ int gpio)
+{
+ static const u8 index2val[] = {0x01, 0x02};
+
+ return vd55g0_write(sensor, VD55G0_REG_GPIO_0_CTRL + gpio,
+ index2val[mode], NULL);
+}
+
+static int vd55g0_set_gpios_array(struct vd55g0_dev *sensor, u32 *array,
+ int size, enum vd55g0_gpio_modes mode)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < size; i++) {
+ if (array[i] == ~0)
+ break;
+ ret = vd55g0_update_gpio_mode(sensor, mode, array[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vd55g0_apply_exposure_auto(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_expo_state exp = sensor->expo_state;
+
+ if (sensor->ae_frozen && sensor->expo_state == VD55G0_EXP_AUTO)
+ exp = VD55G0_EXP_FREEZE;
+
+ return vd55g0_write(sensor, VD55G0_REG_EXP_MODE, exp, NULL);
+}
+
+static int vd55g0_get_regulators(struct vd55g0_dev *sensor)
+{
+ struct device *dev = &sensor->i2c_client->dev;
+ const char * const *names;
+ unsigned int i;
+
+ /* A PRP0001 device has DT names; only the ACPI ID has INT3472's */
+ if (acpi_match_device(vd55g0_acpi_ids, dev)) {
+ names = vd55g0_acpi_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_acpi_supply_name);
+ } else {
+ names = vd55g0_dt_supply_name;
+ sensor->nb_supplies = ARRAY_SIZE(vd55g0_dt_supply_name);
+ }
+
+ for (i = 0; i < sensor->nb_supplies; i++)
+ sensor->supplies[i].supply = names[i];
+
+ return devm_regulator_bulk_get(dev, sensor->nb_supplies,
+ sensor->supplies);
+}
+
+static int vd55g0_apply_patgen(struct vd55g0_dev *sensor)
+{
+ static const u8 index2val[] = {
+ 0x0, 0x0, 0x8, 0x10, 0x20, 0x21, 0x22, 0x28
+ };
+ u32 pattern = index2val[sensor->pattern];
+ u32 reg = pattern << VD55G0_PATGEN_TYPE_SHIFT;
+ u8 darkcal = VD55G0_DARKCAL_AUTO;
+ u8 duster = VD55G0_DUSTER_RING_ENABLE | VD55G0_DUSTER_DYN_ENABLE |
+ VD55G0_DUSTER_ENABLE;
+ int ret = 0;
+
+ if (sensor->pattern != 0) {
+ reg |= VD55G0_PATGEN_ENABLE;
+ /*
+ * Take care of dark calibaration and duster to not mess up the
+ * test pattern output.
+ */
+ darkcal = VD55G0_DARKCAL_BYPASS;
+ duster = VD55G0_DUSTER_DISABLE;
+ }
+
+ vd55g0_write(sensor, VD55G0_REG_DARKCAL_CTRL, darkcal, &ret);
+ vd55g0_write(sensor, VD55G0_REG_DUSTER_CTRL, duster, &ret);
+ if (ret)
+ return ret;
+
+ return vd55g0_write(sensor, VD55G0_REG_PATGEN_CTRL, reg, NULL);
+}
+
+static int vd55g0_apply_flash(struct vd55g0_dev *sensor)
+{
+ enum vd55g0_gpio_modes mode = sensor->flash_en ?
+ VD55G0_GPIO_MODE_STROBE :
+ VD55G0_GPIO_MODE_DISABLED;
+
+ return vd55g0_set_gpios_array(sensor, sensor->leds,
+ ARRAY_SIZE(sensor->leds), mode);
+}
+
+/* The helpers below cache a value only once the sensor has taken it */
+static int vd55g0_update_exposure_auto(struct vd55g0_dev *sensor, u32 index)
+{
+ enum vd55g0_expo_state old = sensor->expo_state;
+ int ret;
+
+ switch (index) {
+ case V4L2_EXPOSURE_AUTO:
+ sensor->expo_state = VD55G0_EXP_AUTO;
+ break;
+ case V4L2_EXPOSURE_MANUAL:
+ sensor->expo_state = VD55G0_EXP_MANUAL;
+ break;
+ default:
+ /* Should never happen */
+ return -EINVAL;
+ }
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->expo_state = old;
+
+ return ret;
+}
+
+static int vd55g0_lock_exposure(struct vd55g0_dev *sensor,
+ struct v4l2_ctrl *ctrl)
+{
+ /* Only exposure lock is supported */
+ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
+ bool old = sensor->ae_frozen;
+ int ret;
+
+ sensor->ae_frozen = ae_lock;
+
+ /* Cap control value to reflect the hardware state */
+ ctrl->val = ae_lock;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ sensor->ae_frozen = old;
+
+ return ret;
+}
+
+static int vd55g0_apply_framelength(struct vd55g0_dev *sensor)
+{
+ return vd55g0_write(sensor, VD55G0_REG_FRAME_LENGTH,
+ sensor->frame_length, NULL);
+}
+
+static int vd55g0_update_vblank(struct vd55g0_dev *sensor, u16 vblank)
+{
+ u16 old_vblank = sensor->vblank, old_frame_length = sensor->frame_length;
+ int ret;
+
+ sensor->vblank_min = VD55G0_MIN_FRAME_LENGTH -
+ sensor->current_mode->crop.height;
+ sensor->vblank = vblank;
+ sensor->frame_length = sensor->current_mode->crop.height +
+ sensor->vblank;
+
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret) {
+ sensor->vblank = old_vblank;
+ sensor->frame_length = old_frame_length;
+ }
+
+ return ret;
+}
+
+static int vd55g0_update_analog_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->analog_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_digital_gain(struct vd55g0_dev *sensor, u32 target)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ target, NULL);
+ if (!ret)
+ sensor->digital_gain = target;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure(struct vd55g0_dev *sensor, int expo_ms)
+{
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ expo_ms, NULL);
+ if (!ret)
+ sensor->manual_expo = expo_ms;
+
+ return ret;
+}
+
+static int vd55g0_update_exposure_target(struct vd55g0_dev *sensor, int index)
+{
+ /*
+ * Find auto exposure target with: default target exposure * 2^EV,
+ * the default being VD55G0_EXPO_TARGET_DEF at index 6.
+ */
+ static const unsigned int index2exposure_target[] = {
+ 3, 5, 7, 10, 14, 19, 27, 38, 54, 76, 108, 153, 216,
+ };
+ unsigned int target = index2exposure_target[index];
+ int ret = 0;
+
+ if (sensor->streaming)
+ ret = vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ target, NULL);
+ if (!ret)
+ sensor->exposure_target = target;
+
+ return ret;
+}
+
+static int vd55g0_update_flash(struct vd55g0_dev *sensor, int flash_en)
+{
+ bool old = sensor->flash_en;
+ int ret;
+
+ sensor->flash_en = flash_en;
+ if (!sensor->streaming)
+ return 0;
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ sensor->flash_en = old;
+
+ return ret;
+}
+
+static void vd55g0_apply_reset(struct vd55g0_dev *sensor)
+{
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
+ usleep_range(5000, 10000);
+}
+
+static void vd55g0_fill_framefmt(struct vd55g0_dev *sensor,
+ const struct vd55g0_mode_info *mode,
+ struct v4l2_mbus_framefmt *fmt, u32 code)
+{
+ fmt->code = code;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int vd55g0_try_fmt_internal(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt,
+ const struct vd55g0_mode_info **new_mode)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *mode = vd55g0_mode_data;
+ unsigned int index;
+
+ for (index = 0; index < ARRAY_SIZE(vd55g0_supported_codes); index++) {
+ if (vd55g0_supported_codes[index].code == fmt->code)
+ break;
+ }
+ if (index == ARRAY_SIZE(vd55g0_supported_codes))
+ index = 0;
+
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data), width,
+ height, fmt->width, fmt->height);
+ if (new_mode)
+ *new_mode = mode;
+
+ vd55g0_fill_framefmt(sensor, mode, fmt,
+ vd55g0_supported_codes[index].code);
+
+ return 0;
+}
+
+static int vd55g0_apply_cold_start(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_COARSE_EXPOSURE,
+ sensor->cold_start.expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_ANALOG_GAIN,
+ sensor->cold_start.analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_COLDSTART_DIGITAL_GAIN,
+ sensor->cold_start.digital_gain, &ret);
+
+ return ret;
+}
+
+static int vd55g0_apply_settings(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_apply_framelength(sensor);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_exposure_auto(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_COARSE_EXPOSURE,
+ sensor->manual_expo, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_ANALOG_GAIN,
+ sensor->analog_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_MANUAL_DIGITAL_GAIN,
+ sensor->digital_gain, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_TARGET_PERCENTAGE,
+ sensor->exposure_target, &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_cold_start(sensor);
+ if (ret)
+ return ret;
+
+ vd55g0_write(sensor, VD55G0_REG_ORIENTATION,
+ sensor->hflip | (sensor->vflip << 1), &ret);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_apply_patgen(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_apply_frame_format(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_rect *crop = &sensor->current_mode->crop;
+ int ret = 0;
+
+ vd55g0_write(sensor, VD55G0_REG_READOUT_CTRL,
+ sensor->current_mode->bin_mode, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_START, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_X_END,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_START, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_ROI_Y_END,
+ crop->top + crop->height - 1, &ret);
+
+ /*
+ * Use the same auto exposure crop as the image crop, performing auto
+ * exposure computation only on image boundaries.
+ */
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_H, crop->left, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_H,
+ crop->left + crop->width - 1, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_START_V, crop->top, &ret);
+ vd55g0_write(sensor, VD55G0_REG_AE_ROI_END_V,
+ crop->top + crop->height - 1, &ret);
+
+ return ret;
+}
+
+static int vd55g0_set_gpios(struct vd55g0_dev *sensor)
+{
+ int ret;
+ unsigned int i;
+
+ /* GPIOs in input (disabled) by default */
+ for (i = 0; i < VD55G0_NB_GPIOS; i++) {
+ ret = vd55g0_update_gpio_mode(sensor,
+ VD55G0_GPIO_MODE_DISABLED, i);
+ if (ret)
+ return ret;
+ }
+
+ ret = vd55g0_apply_flash(sensor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_stream_enable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&client->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = vd55g0_set_gpios(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ vd55g0_write(sensor, VD55G0_REG_FORMAT_CTRL,
+ get_bpp_by_code(sensor->code), &ret);
+ vd55g0_write(sensor, VD55G0_REG_OIF_IMG_CTRL,
+ get_data_type_by_code(sensor->code), &ret);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_frame_format(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_apply_settings(sensor);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_SW_STBY,
+ VD55G0_SW_STBY_START_STREAM, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_SW_STBY, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_STREAMING,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ goto err_rpm_put;
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+ return ret;
+}
+
+static void vd55g0_save_exposure(struct vd55g0_dev *sensor)
+{
+ u64 val;
+
+ /* On a read error, keep what the previous stream left */
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &val, NULL))
+ sensor->cold_start.expo = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &val, NULL))
+ sensor->cold_start.digital_gain = val;
+ if (!vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &val, NULL))
+ sensor->cold_start.analog_gain = val;
+}
+
+static void vd55g0_stream_disable(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ int ret;
+
+ /* Keep exposure values for next cold start boot */
+ vd55g0_save_exposure(sensor);
+
+ ret = vd55g0_write(sensor, VD55G0_REG_STREAMING,
+ VD55G0_STREAMING_STOP_STREAM, NULL);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_STREAMING, 0, 2000);
+ if (ret)
+ goto err_str_dis;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+
+err_str_dis:
+ if (ret)
+ dev_err(&client->dev, "failed to stop streaming: %d\n", ret);
+ pm_runtime_put_autosuspend(&client->dev);
+}
+
+static int vd55g0_tx_from_ep(struct vd55g0_dev *sensor,
+ struct fwnode_handle *handle)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ struct v4l2_fwnode_endpoint *ep;
+ struct v4l2_fwnode_endpoint ep_node = { .bus_type =
+ V4L2_MBUS_CSI2_DPHY
+ };
+ int ret;
+
+ ep = &ep_node;
+ ret = v4l2_fwnode_endpoint_alloc_parse(handle, ep);
+ if (ret)
+ return ret;
+
+ /* The sensor has a single data lane */
+ if (ep->bus.mipi_csi2.num_data_lanes != 1) {
+ dev_err(&client->dev, "only one data lane is supported, found %u\n",
+ ep->bus.mipi_csi2.num_data_lanes);
+ goto error_ep;
+ }
+
+ if (ep->bus.mipi_csi2.clock_lane != 0) {
+ dev_err(&client->dev, "clock lane must be lane 0\n");
+ goto error_ep;
+ }
+
+ /* Only the two polarity bits: the rest of OIF_CTRL is reserved */
+ sensor->oif_ctrl = (ep->bus.mipi_csi2.lane_polarities[0] << 3) |
+ (ep->bus.mipi_csi2.lane_polarities[1] << 6);
+
+ if (ep->nr_of_link_frequencies != 1) {
+ dev_err(&client->dev, "one link frequency is needed, found %u\n",
+ ep->nr_of_link_frequencies);
+ goto error_ep;
+ }
+ sensor->link_freq = ep->link_frequencies[0];
+
+ v4l2_fwnode_endpoint_free(ep);
+
+ return 0;
+
+error_ep:
+ v4l2_fwnode_endpoint_free(ep);
+
+ return -EINVAL;
+}
+
+static int vd55g0_patch(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ const struct vd55g0_patch *patch;
+ u64 patch_version;
+ int ret;
+
+ /* Choose appropriate patch for sensor revision */
+ if (sensor->revision == VD55G0_REVISION_CUT1)
+ patch = &vd55g0_patch_cut1;
+ else if (sensor->revision == VD55G0_REVISION_CUT2)
+ patch = &vd55g0_patch_cut2;
+ else
+ return -EINVAL;
+
+ /* Write patch */
+ ret = vd55g0_write_array(sensor, VD55G0_REG_FWPATCH_START_ADDR,
+ patch->size, patch->bin);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT,
+ VD55G0_BOOT_PATCH_SETUP, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ /* Read back patch version */
+ ret = vd55g0_read(sensor, VD55G0_REG_FWPATCH_REVISION, &patch_version,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (patch_version != (patch->major << 8) + patch->minor) {
+ dev_err(&client->dev,
+ "bad patch version expected %d.%d got %llu.%llu\n",
+ patch->major, patch->minor,
+ patch_version >> 8, patch_version & 0xff);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "patch %llu.%llu applied\n",
+ patch_version >> 8, patch_version & 0xff);
+
+ return 0;
+}
+
+static int vd55g0_boot(struct vd55g0_dev *sensor)
+{
+ int ret;
+
+ ret = vd55g0_write(sensor, VD55G0_REG_BOOT, VD55G0_BOOT_BOOT, NULL);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_poll_reg(sensor, VD55G0_REG_BOOT, 0, VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_SW_STBY,
+ VD55G0_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vd55g0_configure(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ sensor->line_length = VD55G0_LINE_LENGTH_FAST;
+ if (sensor->mipi_rate <= VD55G0_MIPI_RATE_FAST)
+ sensor->line_length = VD55G0_LINE_LENGTH_SLOW;
+ vd55g0_write(sensor, VD55G0_REG_LINE_LENGTH, sensor->line_length,
+ &ret);
+
+ /*
+ * PLL_PREDIV and PLL_MULT are not accessible. We rely on the firmware
+ * to set the correct multiplier for the clock. Hence we don't do
+ * anything more here.
+ */
+ vd55g0_write(sensor, VD55G0_REG_EXT_CLOCK, sensor->clk_freq, &ret);
+
+ vd55g0_write(sensor, VD55G0_REG_OIF_CTRL, sensor->oif_ctrl, &ret);
+ /* The status lines are not exposed, so do not send them, as vd55g1 */
+ vd55g0_write(sensor, VD55G0_REG_ISL_ENABLE, 0, &ret);
+ vd55g0_write(sensor, VD55G0_REG_CLK_PLL_MIPI, sensor->mipi_rate, &ret);
+
+ return ret;
+}
+
+/* Called with sensor->lock held, which is also the control handler's lock */
+static void vd55g0_grab_stream_ctrls(struct vd55g0_dev *sensor, bool grab)
+{
+ __v4l2_ctrl_grab(sensor->vflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->hflip_ctrl, grab);
+ __v4l2_ctrl_grab(sensor->pattern_ctrl, grab);
+}
+
+static int vd55g0_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = vd55g0_stream_enable(sensor);
+ if (ret)
+ return ret;
+
+ sensor->streaming = true;
+ vd55g0_grab_stream_ctrls(sensor, true);
+
+ return 0;
+}
+
+static int vd55g0_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Powered down either way, so report success as call_s_stream() does */
+ vd55g0_stream_disable(sensor);
+
+ sensor->streaming = false;
+ vd55g0_grab_stream_ctrls(sensor, false);
+
+ return 0;
+}
+
+static int vd55g0_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ const struct vd55g0_mode_info *mode;
+ const struct v4l2_mbus_framefmt *fmt;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ /* From the state asked about, so that TRY gets its own crop */
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ mode = v4l2_find_nearest_size(vd55g0_mode_data,
+ ARRAY_SIZE(vd55g0_mode_data),
+ width, height,
+ fmt->width, fmt->height);
+ sel->r = mode->crop;
+ return 0;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = VD55G0_WIDTH;
+ sel->r.height = VD55G0_HEIGHT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vd55g0_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ code->code = vd55g0_supported_codes[code->index].code;
+
+ return 0;
+}
+
+static int vd55g0_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ const struct vd55g0_mode_info *new_mode;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int expo_max, hblank;
+ u16 vblank_def;
+ int ret;
+
+ /* The mode cannot change under a running stream */
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && sensor->streaming)
+ return -EBUSY;
+
+ ret = vd55g0_try_fmt_internal(sd, &format->format, &new_mode);
+ if (ret)
+ return ret;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+ *fmt = format->format;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return 0;
+
+ if (sensor->current_mode == new_mode && sensor->code == fmt->code)
+ return 0;
+
+ sensor->code = fmt->code;
+ sensor->current_mode = new_mode;
+
+ /* Reset vblank and framelength to default */
+ vblank_def = VD55G0_FRAME_LENGTH_DEF - new_mode->crop.height;
+ ret = vd55g0_update_vblank(sensor, vblank_def);
+ if (ret)
+ return ret;
+
+ /* The range change may clamp and apply the old vblank; set the default */
+ ret = __v4l2_ctrl_modify_range(sensor->vblank_ctrl, sensor->vblank_min,
+ 0xffff - new_mode->crop.height, 1,
+ vblank_def);
+ if (ret)
+ return ret;
+ ret = __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, vblank_def);
+ if (ret)
+ return ret;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1,
+ VD55G0_EXPO_DEF);
+ if (ret)
+ return ret;
+ /* Blanking counts from the analogue crop, as in vd55g1 */
+ hblank = sensor->line_length - sensor->current_mode->crop.width;
+ ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1,
+ hblank);
+ if (ret)
+ return ret;
+
+ return __v4l2_ctrl_s_ctrl(sensor->hblank_ctrl, hblank);
+}
+
+static int vd55g0_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ struct v4l2_subdev_format fmt = { 0 };
+
+ vd55g0_fill_framefmt(sensor, &vd55g0_mode_data[VD55G0_DEFAULT_MODE],
+ &fmt.format, VD55G0_MEDIA_BUS_FMT_DEF);
+
+ return vd55g0_set_fmt(sd, NULL, sd_state, &fmt);
+}
+
+static int vd55g0_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ unsigned int i;
+
+ if (fse->index >= ARRAY_SIZE(vd55g0_mode_data))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g0_supported_codes); i++)
+ if (vd55g0_supported_codes[i].code == fse->code)
+ break;
+ if (i == ARRAY_SIZE(vd55g0_supported_codes))
+ return -EINVAL;
+
+ fse->min_width = vd55g0_mode_data[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = vd55g0_mode_data[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops vd55g0_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops vd55g0_pad_ops = {
+ .enum_mbus_code = vd55g0_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = vd55g0_set_fmt,
+ .enable_streams = vd55g0_enable_streams,
+ .disable_streams = vd55g0_disable_streams,
+ .get_selection = vd55g0_get_selection,
+ .enum_frame_size = vd55g0_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops vd55g0_subdev_ops = {
+ .video = &vd55g0_video_ops,
+ .pad = &vd55g0_pad_ops,
+};
+
+static const struct media_entity_operations vd55g0_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops vd55g0_internal_ops = {
+ .init_state = vd55g0_init_state,
+};
+
+/*
+ * The manual values first, so that a switch to manual starts from them; the
+ * sensor ignores them under auto exposure.
+ */
+static int vd55g0_update_expo_cluster(struct vd55g0_dev *sensor)
+{
+ int ret = 0;
+
+ if (sensor->ae_ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ /* A range change under auto exposure did not clamp it */
+ sensor->expo_ctrl->val = min(sensor->expo_ctrl->val,
+ sensor->expo_ctrl->maximum);
+ if (sensor->expo_ctrl->is_new)
+ ret = vd55g0_update_exposure(sensor,
+ sensor->expo_ctrl->val);
+ if (!ret && sensor->again_ctrl->is_new)
+ ret = vd55g0_update_analog_gain(sensor,
+ sensor->again_ctrl->val);
+ if (!ret && sensor->dgain_ctrl->is_new)
+ ret = vd55g0_update_digital_gain(sensor,
+ sensor->dgain_ctrl->val);
+ if (ret)
+ return ret;
+ }
+
+ if (sensor->ae_ctrl->is_new)
+ ret = vd55g0_update_exposure_auto(sensor, sensor->ae_ctrl->val);
+
+ return ret;
+}
+
+/* Under auto exposure, the manual controls read what the sensor applies */
+static int vd55g0_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vd55g0_dev *sensor = to_vd55g0_dev(ctrl_to_sd(ctrl));
+ struct device *dev = &sensor->i2c_client->dev;
+ u64 expo, again, dgain;
+ int ret = 0;
+
+ if (ctrl->id != V4L2_CID_EXPOSURE_AUTO)
+ return -EINVAL;
+
+ /* Only a powered sensor has applied values; keep the last ones */
+ if (!pm_runtime_get_if_in_use(dev))
+ return 0;
+
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &expo, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &again, &ret);
+ vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret);
+ if (!ret) {
+ sensor->expo_ctrl->val = expo;
+ sensor->again_ctrl->val = again;
+ sensor->dgain_ctrl->val = dgain;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int vd55g0_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ unsigned int expo_max;
+ u16 old_vblank;
+ int ret;
+
+ /* Read-only controls are set by the driver, there is nothing to apply */
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ break;
+ }
+ if (ctrl->id == V4L2_CID_VFLIP)
+ sensor->vflip = ctrl->val;
+ if (ctrl->id == V4L2_CID_HFLIP)
+ sensor->hflip = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ /* Can't be done while streaming because of duster disabling */
+ sensor->pattern = ctrl->val;
+ ret = 0;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = vd55g0_update_expo_cluster(sensor);
+ break;
+ case V4L2_CID_3A_LOCK:
+ ret = vd55g0_lock_exposure(sensor, ctrl);
+ break;
+ case V4L2_CID_VBLANK:
+ old_vblank = sensor->vblank;
+ ret = vd55g0_update_vblank(sensor, ctrl->val);
+ if (ret)
+ break;
+ /* Max exposure changes with vblank */
+ expo_max = sensor->frame_length - VD55G0_EXPO_MAX_TERM;
+ ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max,
+ 1, VD55G0_EXPO_DEF);
+ /* A clamped exposure failed to apply: put the frame back */
+ if (ret)
+ vd55g0_update_vblank(sensor, old_vblank);
+ break;
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ /*
+ * We use auto exposure target percentage register to control
+ * exposure bias for more precision.
+ */
+ ret = vd55g0_update_exposure_target(sensor, ctrl->val);
+ break;
+ case V4L2_CID_FLASH_LED_MODE:
+ ret = vd55g0_update_flash(sensor, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops vd55g0_ctrl_ops = {
+ .g_volatile_ctrl = vd55g0_g_volatile_ctrl,
+ .s_ctrl = vd55g0_s_ctrl,
+};
+
+static int vd55g0_init_controls(struct vd55g0_dev *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &vd55g0_ctrl_ops;
+ struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler;
+ const struct vd55g0_mode_info *cur_mode = sensor->current_mode;
+ struct v4l2_fwnode_device_properties fwnode_props;
+ struct v4l2_ctrl *ctrl;
+ unsigned int patgen_size = ARRAY_SIZE(vd55g0_test_pattern_menu) - 1;
+ unsigned int hblank = sensor->line_length - cur_mode->crop.width;
+ unsigned int expo_mode = sensor->expo_state == VD55G0_EXP_AUTO ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL;
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 16);
+ /* we can use our own mutex for the ctrl lock */
+ hdl->lock = &sensor->lock;
+ v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) - 1,
+ ARRAY_SIZE(vd55g0_ev_bias_menu) / 2,
+ vd55g0_ev_bias_menu);
+ ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ,
+ 0, 0, &sensor->link_freq);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, sensor->pixel_rate,
+ sensor->pixel_rate, 1, sensor->pixel_rate);
+ sensor->ae_ctrl = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO, 1,
+ ~0x3, expo_mode);
+ sensor->again_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ 0, 24, 1, sensor->analog_gain);
+ sensor->dgain_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+ 256, 2048, 1,
+ sensor->digital_gain);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, 0, 1, 0, 0);
+ /* Only when the firmware describes the LED GPIOs */
+ if (sensor->nb_leds)
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_FLASH, ~0x7,
+ sensor->flash_en);
+
+ /*
+ * Keep a pointer to these controls as we need to update them when
+ * setting the format
+ */
+ sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ sensor->vblank_min,
+ 0xffff - cur_mode->crop.height,
+ 1, sensor->vblank);
+ sensor->hblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ hblank, hblank, 1, hblank);
+ if (sensor->hblank_ctrl)
+ sensor->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
+ 0, 1, 1, sensor->vflip);
+ sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
+ 0, 1, 1, sensor->hflip);
+ sensor->pattern_ctrl =
+ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+ patgen_size, 0, 0,
+ vd55g0_test_pattern_menu);
+ sensor->expo_ctrl =
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 0,
+ sensor->frame_length - VD55G0_EXPO_MAX_TERM,
+ 1, sensor->manual_expo);
+ if (!hdl->error)
+ v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props);
+ if (ret)
+ goto free_ctrls;
+
+ if (hdl->error) {
+ ret = hdl->error;
+ goto free_ctrls;
+ }
+
+ sensor->sd.ctrl_handler = hdl;
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+}
+
+static int vd55g0_detect(struct vd55g0_dev *sensor)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ u64 revision, id = 0;
+ int ret;
+
+ ret = vd55g0_read(sensor, VD55G0_REG_MODEL_ID, &id, NULL);
+ if (ret)
+ return ret;
+
+ if (id != VD55G0_MODEL_ID) {
+ dev_warn(&client->dev, "Unsupported sensor id %llx\n", id);
+ return -ENODEV;
+ }
+
+ ret = vd55g0_read(sensor, VD55G0_REG_REVISION, &revision, NULL);
+ if (ret)
+ return ret;
+ dev_dbg(&client->dev, "Sensor revision 0x%llx\n", revision);
+
+ if (revision != VD55G0_REVISION_CUT1 &&
+ revision != VD55G0_REVISION_CUT2) {
+ dev_err(&client->dev, "Unsupported device revision 0x%llx\n",
+ revision);
+ return -ENODEV;
+ }
+
+ sensor->revision = revision;
+
+ return 0;
+}
+
+/* Power/clock management functions */
+static int vd55g0_power_on(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(sensor->nb_supplies, sensor->supplies);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable regulators %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(sensor->xclk);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable clock %d\n", ret);
+ goto disable_bulk;
+ }
+
+ if (sensor->reset_gpio)
+ vd55g0_apply_reset(sensor);
+
+ ret = vd55g0_wait_state(sensor, VD55G0_SYSTEM_FSM_READY_TO_BOOT,
+ VD55G0_TIMEOUT_MS);
+ if (ret) {
+ dev_err(&client->dev, "sensor reset failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_detect(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor detect failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_patch(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor patch failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_boot(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor boot failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ ret = vd55g0_configure(sensor);
+ if (ret) {
+ dev_err(&client->dev, "sensor configuration failed %d\n", ret);
+ goto disable_clock;
+ }
+
+ return 0;
+
+disable_clock:
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+disable_bulk:
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+
+ return ret;
+}
+
+static int vd55g0_power_off(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ /* Hold the sensor in reset while its rails are off, as vd55g1 does */
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
+ clk_disable_unprepare(sensor->xclk);
+ regulator_bulk_disable(sensor->nb_supplies, sensor->supplies);
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios_array(struct vd55g0_dev *sensor,
+ char *prop_name, u32 *array, int *nb)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ unsigned int i;
+ int ret;
+
+ *nb = device_property_count_u32(&client->dev, prop_name);
+ if (*nb == -EINVAL) {
+ /* Property not found */
+ *nb = 0;
+ return 0;
+ }
+ if (*nb < 0)
+ return *nb;
+ if (*nb > VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "%s lists %d GPIOs, the sensor has %d\n",
+ prop_name, *nb, VD55G0_NB_GPIOS);
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32_array(&client->dev, prop_name, array,
+ *nb);
+ if (ret) {
+ dev_err(&client->dev, "Failed to read %s prop\n", prop_name);
+ return ret;
+ }
+
+ for (i = 0; i < *nb; i++) {
+ if (array[i] >= VD55G0_NB_GPIOS) {
+ dev_err(&client->dev, "invalid GPIO %d for leds\n",
+ array[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vd55g0_parse_dt_gpios(struct vd55g0_dev *sensor)
+{
+ int nb_gpios_leds;
+ int ret;
+
+ memset(sensor->leds, ~0, sizeof(sensor->leds));
+
+ ret = vd55g0_parse_dt_gpios_array(sensor, "st,leds", sensor->leds,
+ &nb_gpios_leds);
+ if (ret)
+ return ret;
+
+ sensor->nb_leds = nb_gpios_leds;
+
+ return 0;
+}
+
+static int vd55g0_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct fwnode_handle *handle;
+ struct vd55g0_dev *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->analog_gain = 0;
+ sensor->digital_gain = 256;
+
+ sensor->i2c_client = client;
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->regmap),
+ "failed to init CCI\n");
+ sensor->streaming = false;
+ /* Must match what vd55g0_init_state() puts in the subdev state */
+ sensor->code = VD55G0_MEDIA_BUS_FMT_DEF;
+ sensor->manual_expo = VD55G0_EXPO_DEF;
+ sensor->vflip = false;
+ sensor->hflip = false;
+ sensor->flash_en = false;
+ /* Written at every stream start, so it must match the control default */
+ sensor->exposure_target = VD55G0_EXPO_TARGET_DEF;
+ sensor->cold_start.expo = 0x32;
+ sensor->cold_start.digital_gain = 256;
+ sensor->cold_start.analog_gain = 0;
+
+ sensor->current_mode = &vd55g0_mode_data[VD55G0_DEFAULT_MODE];
+
+ handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+
+ sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
+ "Failed to get reset gpio\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_parse_dt_gpios(sensor);
+ if (ret) {
+ dev_err(dev, "Failed to get gpios\n");
+ goto error_put_handle;
+ }
+
+ ret = vd55g0_tx_from_ep(sensor, handle);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to parse endpoint\n");
+ goto error_put_handle;
+ }
+ fwnode_handle_put(handle);
+
+ sensor->xclk = devm_v4l2_sensor_clk_get(dev, NULL);
+ if (IS_ERR(sensor->xclk))
+ return dev_err_probe(dev, PTR_ERR(sensor->xclk),
+ "failed to get xclk\n");
+
+ sensor->clk_freq = clk_get_rate(sensor->xclk);
+ ret = vd55g0_prepare_clock_tree(sensor);
+ if (ret)
+ return ret;
+
+ v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g0_subdev_ops);
+ sensor->sd.internal_ops = &vd55g0_internal_ops;
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.ops = &vd55g0_subdev_entity_ops;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = vd55g0_get_regulators(sensor);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
+
+ /* Power on once to check that the sensor answers */
+ ret = vd55g0_power_on(dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&sensor->lock);
+
+ ret = vd55g0_update_vblank(sensor, VD55G0_FRAME_LENGTH_DEF -
+ sensor->current_mode->crop.height);
+ if (ret)
+ goto error_power_off;
+
+ ret = vd55g0_init_controls(sensor);
+ if (ret) {
+ dev_err(&client->dev, "controls initialization failed %d\n",
+ ret);
+ goto error_power_off;
+ }
+
+ sensor->sd.state_lock = &sensor->lock;
+
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret) {
+ dev_err(&client->dev, "pads init failed %d\n", ret);
+ goto error_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "subdev init error %d\n", ret);
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev(&sensor->sd);
+ if (ret) {
+ dev_err(&client->dev, "async subdev register failed %d\n", ret);
+ goto error_pm_runtime;
+ }
+
+ /* Runtime PM may power the sensor down only from here on */
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_idle(dev);
+
+ dev_dbg(&client->dev, "vd55g0 probe successfully\n");
+
+ return 0;
+
+error_put_handle:
+ fwnode_handle_put(handle);
+
+ return ret;
+
+error_pm_runtime:
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ v4l2_subdev_cleanup(&sensor->sd);
+error_media_entity:
+ media_entity_cleanup(&sensor->sd.entity);
+error_handler_free:
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+error_power_off:
+ mutex_destroy(&sensor->lock);
+ vd55g0_power_off(dev);
+
+ return ret;
+}
+
+static void vd55g0_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vd55g0_dev *sensor = to_vd55g0_dev(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_subdev_cleanup(&sensor->sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
+ mutex_destroy(&sensor->lock);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ vd55g0_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_dont_use_autosuspend(&client->dev);
+}
+
+static const struct of_device_id vd55g0_dt_ids[] = {
+ { .compatible = "st,vd55g0" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vd55g0_dt_ids);
+
+static const struct dev_pm_ops vd55g0_pm_ops = {
+ SET_RUNTIME_PM_OPS(vd55g0_power_off, vd55g0_power_on, NULL)
+};
+
+static struct i2c_driver vd55g0_i2c_driver = {
+ .driver = {
+ .name = "vd55g0",
+ .of_match_table = vd55g0_dt_ids,
+ .acpi_match_table = vd55g0_acpi_ids,
+ .pm = &vd55g0_pm_ops,
+ },
+ .probe = vd55g0_probe,
+ .remove = vd55g0_remove,
+};
+
+module_i2c_driver(vd55g0_i2c_driver);
+
+MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>");
+MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>");
+MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>");
+MODULE_AUTHOR("Sergey Lebedev <lsa.uz@pm.me>");
+MODULE_DESCRIPTION("VD55G0 camera subdev driver");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread