* [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor
@ 2026-11-09 9:34 Sergey Lebedev
2026-11-09 9:34 ` [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Sergey Lebedev @ 2026-11-09 9:34 UTC (permalink / raw)
To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab
Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Peter Marshall, George Neacsu, Hao Yao,
Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media,
devicetree, linux-kernel
The ST VD55G0 is a 644x604 monochrome global shutter sensor. It is the
infrared camera of the Microsoft Surface Pro 11 for Business with Intel
(Lunar Lake, IPU7) and of the Surface Pro 9 (Alder Lake, IPU6), ACPI
device SMO55F0. Without a driver the camera does not appear at all.
1/4 dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
2/4 media: i2c: Add ST VD55G0 global shutter sensor driver
3/4 media: ipu-bridge: Add ST VD55G0
4/4 media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator
The driver is a port of ST's own downstream driver, firmware patch
included, as vd55g1 carries its own:
https://github.com/STMicroelectronics/vd55g0-linux-driver
2/4 carries Benjamin's Co-developed-by, and its clock tree is based on
his.
Changes in v3
=============
- 2/4, the three points of Sashiko's review of v2 2/4:
- under auto exposure, a stopped sensor's exposure and gains read back
what the last stream applied, not the manual values set before;
- probe holds a runtime PM reference until the subdev is registered,
so the sensor no longer suspends 22 ms after probe;
- RUNTIME_PM_OPS() and pm_ptr(), so the ops go with CONFIG_PM=n.
- 2/4: register with v4l2_async_register_subdev_sensor(), so that the
privacy LED the firmware describes is lit while streaming (Jizhou
Tong); a failed registration now goes through dev_err_probe(), as the
LED can defer probe.
- 3/4: SMO55F0 moves before VD55G1, so the table stays in vendor order.
imx681 v10 3/3 adds SONY0681 at the same place, so whichever of the two
goes second needs a trivial fixup.
- Fernando's Tested-by on 3/4 and 4/4. Not on 2/4: he offered it for
the Sashiko fixes alone, and 2/4 now also registers differently.
Fernando, if you want it on 2/4 as it is now, say so and I will add
it back.
- The Assisted-by tags are dropped; see the end of this letter.
v2: https://lore.kernel.org/all/20261103103235.94397-1-lsa.uz@pm.me/
On both machines the sensor needs Hao Yao's int3472 fix, which sets the
24 MHz clock the SSDB asks for; without it the sensor runs at four
fifths of every rate this series computes. Its v2 fixes the Surface Pro
11; on the Surface Pro 9 the _DSM wants 0 rather than 3 (Fernando):
https://lore.kernel.org/all/20251208035122.2147678-1-hao.yao@intel.com/
Tested with v3 on the Surface Pro 11 for Business, on v7.4-rc1 with Hao
Yao's int3472 v2 and D. Manresa's ipu-bridge v5 1/2, KASAN and lockdep
on, IPU7 run by the ipu6 driver, in three boots:
- probe defers until ipu-bridge has made the endpoint, then binds;
- after probe the sensor stays active and suspends about 1 s later,
with no power-off inside probe;
- probe forced to fail at registration with a test-only parameter: no
clock or regulator warning, and a deferral prints no error. As a
control, the same failure after a runtime suspend inside probe gives
three warnings per probe (clock already disabled and unprepared,
unbalanced regulator disables);
- every mode runs at 59.14 fps, in Y8 and Y10 at all four sizes and in
Y10 packed at full size;
- under auto exposure, exposure and the gains read back what the sensor
applies, and after the stream stops, what it last applied;
- the illuminator through led_mode, and the privacy LED that INT3472
describes, lit while streaming and off otherwise;
- three unload and reload cycles, each with a capture;
- s2idle for 20 s with the camera idle, and in the middle of a stream,
which the ISYS refuses: the sensor gets no callback, the stream goes
on, and a stream and compliance pass after both;
- v4l2-compliance 46 of 46, and 54 of 54 through the media device, with
two warnings for the read-only crop;
- no KASAN, lockdep or Oops report; the only other WARNING is the ipu6
driver's lockdep_assert_held() at each stream start, as with v2;
- checkpatch --strict, W=1 and sparse clean on x86_64, i386,
allmodconfig and without CONFIG_PM; smatch and coccicheck clean; each
patch builds on its own; dt_binding_check clean, and it refuses a
VD55G0 example at 125 MHz.
3/4 only moves its line and 4/4 does not change, so their unload test
is v2's. The Surface Pro 9 entry is Fernando's test, not mine.
Tool use, per Documentation/process/generated-content.rst (Assisted-by
tags in commits were dropped in 7.4-rc1):
* My contributions to this series were prepared with Claude Code
(claude-opus-5-5); work by others in it is their own.
* This replaces any Assisted-by tags in earlier versions.
Sergey Lebedev (4):
dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0
media: i2c: Add ST VD55G0 global shutter sensor driver
media: ipu-bridge: Add ST VD55G0
media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator
.../bindings/media/i2c/st,vd55g1.yaml | 27 +-
MAINTAINERS | 9 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/vd55g0.c | 2442 +++++++++++++++++
drivers/media/pci/intel/ipu-bridge.c | 70 +-
include/media/ipu-bridge.h | 6 +-
7 files changed, 2559 insertions(+), 7 deletions(-)
create mode 100644 drivers/media/i2c/vd55g0.c
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 2026-11-09 9:34 [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev @ 2026-11-09 9:34 ` Sergey Lebedev 2026-11-09 10:28 ` Benjamin Mugnier 2026-11-09 9:34 ` [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev ` (2 subsequent siblings) 3 siblings, 1 reply; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 9:34 UTC (permalink / raw) To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel The VD55G0 is a monochrome global shutter sensor with a 644x604 active array, sending over a single MIPI CSI-2 lane, with the same supplies, reset and strobe GPIOs as the VD55G1. Its MIPI rate runs from 750 to 1200 Mbps (UM2829, 13.1), so its link frequency starts at 375 MHz, and its I2C address is set in its NVM rather than fixed. allOf moves below required to carry the if/then for that minimum. Signed-off-by: Sergey Lebedev <lsa.uz@pm.me> --- v3: no change. v2: the VD55G0 joins this file instead of a st,vd55g0.yaml of its own, with allOf below required for the if/then, as Krzysztof pointed to. It is Peter Marshall's 01/11 and 02/11 of his vd55g1 v2 squashed, as Krzysztof asked there, with the VD55G0's description added: https://lore.kernel.org/all/20260918221705.323510-3-pm@petermarshall.ca/ .../bindings/media/i2c/st,vd55g1.yaml | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml index dfd87beba06..0245673d105 100644 --- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml @@ -16,21 +16,23 @@ description: |- array size of 804H x 704V. It is programmable through I2C interface. The I2C address is fixed to 0x10. + The VD55G0 is a monochrome global shutter image sensor with an active array + size of 644H x 604V, programmable through I2C at an address set in its NVM. + Image data is sent through MIPI CSI-2, which is configured as only 1 data lane. The sensor provides 4 GPIOS that can be used for external LED signal (synchronized with sensor integration periods). -allOf: - - $ref: /schemas/media/video-interface-devices.yaml# - properties: compatible: enum: + - st,vd55g0 - st,vd55g1 - st,vd55g4 - st,vd65g4 description: - VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one. + VD55G0, VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a + color one. reg: maxItems: 1 @@ -101,6 +103,23 @@ required: - reset-gpios - port +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + - if: + properties: + compatible: + contains: + const: st,vd55g0 + then: + properties: + port: + properties: + endpoint: + properties: + link-frequencies: + items: + minimum: 375000000 + unevaluatedProperties: false examples: -- 2.54.0 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 2026-11-09 9:34 ` [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev @ 2026-11-09 10:28 ` Benjamin Mugnier 2026-11-09 10:36 ` Sergey Lebedev 0 siblings, 1 reply; 11+ messages in thread From: Benjamin Mugnier @ 2026-11-09 10:28 UTC (permalink / raw) To: Sergey Lebedev, Sakari Ailus, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Sergey, Le 09/11/2026 à 10:34, Sergey Lebedev a écrit : > The VD55G0 is a monochrome global shutter sensor with a 644x604 active > array, sending over a single MIPI CSI-2 lane, with the same supplies, > reset and strobe GPIOs as the VD55G1. Its MIPI rate runs from 750 to > 1200 Mbps (UM2829, 13.1), so its link frequency starts at 375 MHz, and > its I2C address is set in its NVM rather than fixed. > > allOf moves below required to carry the if/then for that minimum. > > Signed-off-by: Sergey Lebedev <lsa.uz@pm.me> > --- > v3: no change. > > v2: the VD55G0 joins this file instead of a st,vd55g0.yaml of its own, > with allOf below required for the if/then, as Krzysztof pointed to. It > is Peter Marshall's 01/11 and 02/11 of his vd55g1 v2 squashed, as > Krzysztof asked there, with the VD55G0's description added: > https://lore.kernel.org/all/20260918221705.323510-3-pm@petermarshall.ca/ As vd55g1 and vd55g0 are two different drivers I think they should be kept separated. Not sure what Krzysztof meant, maybe he just wanted to point to a similar patchset. Krzysztof coud you clarify ? Could this patch be reverted to v1 ? I see that similarly the MAINTAINERS file as also been altered in the second patch. > > .../bindings/media/i2c/st,vd55g1.yaml | 27 ++++++++++++++++--- > 1 file changed, 23 insertions(+), 4 deletions(-) > > diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml > index dfd87beba06..0245673d105 100644 > --- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml > +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml > @@ -16,21 +16,23 @@ description: |- > array size of 804H x 704V. It is programmable through I2C interface. The I2C > address is fixed to 0x10. > > + The VD55G0 is a monochrome global shutter image sensor with an active array > + size of 644H x 604V, programmable through I2C at an address set in its NVM. > + > Image data is sent through MIPI CSI-2, which is configured as only 1 data > lane. The sensor provides 4 GPIOS that can be used for external LED signal > (synchronized with sensor integration periods). > > -allOf: > - - $ref: /schemas/media/video-interface-devices.yaml# > - > properties: > compatible: > enum: > + - st,vd55g0 > - st,vd55g1 > - st,vd55g4 > - st,vd65g4 > description: > - VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one. > + VD55G0, VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a > + color one. > > reg: > maxItems: 1 > @@ -101,6 +103,23 @@ required: > - reset-gpios > - port > > +allOf: > + - $ref: /schemas/media/video-interface-devices.yaml# > + - if: > + properties: > + compatible: > + contains: > + const: st,vd55g0 > + then: > + properties: > + port: > + properties: > + endpoint: > + properties: > + link-frequencies: > + items: > + minimum: 375000000 > + > unevaluatedProperties: false > > examples: -- Regards, Benjamin ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 2026-11-09 10:28 ` Benjamin Mugnier @ 2026-11-09 10:36 ` Sergey Lebedev 0 siblings, 0 replies; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 10:36 UTC (permalink / raw) To: Benjamin Mugnier, Sakari Ailus, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Benjamin, Yes. The single file came from Krzysztof's answer on v1. I offered to fold the compatible into st,vd55g1.yaml with an if/then for the link-frequency minimum, and he replied with a link to Peter's 2/7, which adds the VD55G0 to that binding. His review of that patch asked only to keep the file name and to move allOf to the bottom, so I read it as one file for both: https://lore.kernel.org/all/20261020-cute-peccary-of-attack-2376af@quoll/ https://lore.kernel.org/all/20260903-utopian-vagabond-mamba-44caea@quoll/ I am happy to do either. Once you and the DT maintainers agree, I will send v4 promptly. For a separate file, it brings back v1's st,vd55g0.yaml with your Co-developed-by, and its MAINTAINERS line in 2/4. Sergey ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver 2026-11-09 9:34 [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev @ 2026-11-09 9:34 ` Sergey Lebedev 2026-11-09 11:36 ` Sakari Ailus 2026-11-09 9:34 ` [PATCH v3 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev 3 siblings, 1 reply; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 9:34 UTC (permalink / raw) To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, 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> 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. v3: Sashiko's three points on v2 (auto exposure readback after a stream, the runtime PM reference across probe, RUNTIME_PM_OPS() and pm_ptr()); registration through v4l2_async_register_subdev_sensor() (Jizhou Tong), with dev_err_probe() on failure. v2: no code change; MAINTAINERS no longer lists a binding for it. MAINTAINERS | 9 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/vd55g0.c | 2442 ++++++++++++++++++++++++++++++++++++ 4 files changed, 2463 insertions(+) create mode 100644 drivers/media/i2c/vd55g0.c diff --git a/MAINTAINERS b/MAINTAINERS index 93abbb31506..dbbc5b05954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26592,6 +26592,14 @@ F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml F: Documentation/hwmon/tsc1641.rst F: drivers/hwmon/tsc1641.c +ST VD55G0 DRIVER +M: Benjamin Mugnier <benjamin.mugnier@foss.st.com> +M: Sergey Lebedev <lsa.uz@pm.me> +M: Sylvain Petinot <sylvain.petinot@foss.st.com> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/vd55g0.c + ST VD55G1 DRIVER M: Benjamin Mugnier <benjamin.mugnier@foss.st.com> M: Sylvain Petinot <sylvain.petinot@foss.st.com> @@ -29317,6 +29325,7 @@ F: drivers/media/i2c/mt* F: drivers/media/i2c/og* F: drivers/media/i2c/ov* F: drivers/media/i2c/s5* +F: drivers/media/i2c/vd55g0.c F: drivers/media/i2c/vd55g1.c F: drivers/media/i2c/vd56g3.c F: drivers/media/i2c/vgxy61.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 11c30252540..f8a653154de 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -891,6 +891,17 @@ config VIDEO_T4KA3 To compile this driver as a module, choose M here: the module will be called t4ka3. +config VIDEO_VD55G0 + tristate "ST VD55G0 sensor support" + select V4L2_CCI_I2C + depends on GPIOLIB + help + This is a Video4Linux2 sensor driver for the ST VD55G0 + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called vd55g0. + config VIDEO_VD55G1 tristate "ST VD55G1 sensor support" select V4L2_CCI_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 59e31af457c..a7e990ec650 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -171,6 +171,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VD55G0) += vd55g0.o obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o diff --git a/drivers/media/i2c/vd55g0.c b/drivers/media/i2c/vd55g0.c new file mode 100644 index 00000000000..6e9f9d44625 --- /dev/null +++ b/drivers/media/i2c/vd55g0.c @@ -0,0 +1,2442 @@ +// 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; + + /* Unpowered, the last stream's values, which the next one starts from */ + if (!pm_runtime_get_if_in_use(dev)) { + sensor->expo_ctrl->val = min_t(u32, sensor->cold_start.expo, + sensor->expo_ctrl->maximum); + sensor->again_ctrl->val = sensor->cold_start.analog_gain; + sensor->dgain_ctrl->val = sensor->cold_start.digital_gain; + return 0; + } + + vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &expo, &ret); + vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &again, &ret); + vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + if (!ret) { + sensor->expo_ctrl->val = 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; + } + + /* Held until registered, so that runtime PM cannot power it down */ + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret) { + dev_err_probe(dev, ret, "async subdev register failed\n"); + goto error_pm_runtime; + } + + pm_runtime_put_autosuspend(dev); + + dev_dbg(&client->dev, "vd55g0 probe successfully\n"); + + return 0; + +error_put_handle: + fwnode_handle_put(handle); + + return ret; + +error_pm_runtime: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + pm_runtime_dont_use_autosuspend(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 = { + RUNTIME_PM_OPS(vd55g0_power_off, vd55g0_power_on, NULL) +}; + +static struct i2c_driver vd55g0_i2c_driver = { + .driver = { + .name = "vd55g0", + .of_match_table = vd55g0_dt_ids, + .acpi_match_table = vd55g0_acpi_ids, + .pm = pm_ptr(&vd55g0_pm_ops), + }, + .probe = vd55g0_probe, + .remove = vd55g0_remove, +}; + +module_i2c_driver(vd55g0_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>"); +MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>"); +MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>"); +MODULE_AUTHOR("Sergey Lebedev <lsa.uz@pm.me>"); +MODULE_DESCRIPTION("VD55G0 camera subdev driver"); +MODULE_LICENSE("GPL"); -- 2.54.0 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver 2026-11-09 9:34 ` [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev @ 2026-11-09 11:36 ` Sakari Ailus 2026-11-09 12:46 ` Sergey Lebedev 0 siblings, 1 reply; 11+ messages in thread From: Sakari Ailus @ 2026-11-09 11:36 UTC (permalink / raw) To: Sergey Lebedev Cc: Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Sergey, Benjamin, Thanks for the set. On Mon, Nov 09, 2026 at 09:34:28AM +0000, Sergey Lebedev wrote: > 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> > 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. > > v3: Sashiko's three points on v2 (auto exposure readback after a > stream, the runtime PM reference across probe, RUNTIME_PM_OPS() and > pm_ptr()); registration through v4l2_async_register_subdev_sensor() > (Jizhou Tong), with dev_err_probe() on failure. > v2: no code change; MAINTAINERS no longer lists a binding for it. > > MAINTAINERS | 9 + > drivers/media/i2c/Kconfig | 11 + > drivers/media/i2c/Makefile | 1 + > drivers/media/i2c/vd55g0.c | 2442 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 2463 insertions(+) > create mode 100644 drivers/media/i2c/vd55g0.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 93abbb31506..dbbc5b05954 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -26592,6 +26592,14 @@ F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml > F: Documentation/hwmon/tsc1641.rst > F: drivers/hwmon/tsc1641.c > > +ST VD55G0 DRIVER > +M: Benjamin Mugnier <benjamin.mugnier@foss.st.com> > +M: Sergey Lebedev <lsa.uz@pm.me> > +M: Sylvain Petinot <sylvain.petinot@foss.st.com> > +L: linux-media@vger.kernel.org > +S: Maintained > +F: drivers/media/i2c/vd55g0.c > + > ST VD55G1 DRIVER > M: Benjamin Mugnier <benjamin.mugnier@foss.st.com> > M: Sylvain Petinot <sylvain.petinot@foss.st.com> > @@ -29317,6 +29325,7 @@ F: drivers/media/i2c/mt* > F: drivers/media/i2c/og* > F: drivers/media/i2c/ov* > F: drivers/media/i2c/s5* > +F: drivers/media/i2c/vd55g0.c > F: drivers/media/i2c/vd55g1.c > F: drivers/media/i2c/vd56g3.c > F: drivers/media/i2c/vgxy61.c > diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig > index 11c30252540..f8a653154de 100644 > --- a/drivers/media/i2c/Kconfig > +++ b/drivers/media/i2c/Kconfig > @@ -891,6 +891,17 @@ config VIDEO_T4KA3 > To compile this driver as a module, choose M here: the > module will be called t4ka3. > > +config VIDEO_VD55G0 > + tristate "ST VD55G0 sensor support" > + select V4L2_CCI_I2C > + depends on GPIOLIB > + help > + This is a Video4Linux2 sensor driver for the ST VD55G0 > + camera sensor. > + > + To compile this driver as a module, choose M here: the > + module will be called vd55g0. > + > config VIDEO_VD55G1 > tristate "ST VD55G1 sensor support" > select V4L2_CCI_I2C > diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile > index 59e31af457c..a7e990ec650 100644 > --- a/drivers/media/i2c/Makefile > +++ b/drivers/media/i2c/Makefile > @@ -171,6 +171,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o > obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o > obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o > obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o > +obj-$(CONFIG_VIDEO_VD55G0) += vd55g0.o > obj-$(CONFIG_VIDEO_VD55G1) += vd55g1.o > obj-$(CONFIG_VIDEO_VD56G3) += vd56g3.o > obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o > diff --git a/drivers/media/i2c/vd55g0.c b/drivers/media/i2c/vd55g0.c > new file mode 100644 > index 00000000000..6e9f9d44625 > --- /dev/null > +++ b/drivers/media/i2c/vd55g0.c > @@ -0,0 +1,2442 @@ > +// 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, Some indentation would be nice here. > +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, Is this > +}; > + > +/* > + * 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); How did this find its place here? > + > +#define VD55G0_NUM_SUPPLIES ARRAY_SIZE(vd55g0_dt_supply_name) Just use plain ARRAY_SIZE() where you need it. > + > +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; Can you access the value in the pixel rate control instead? > + u64 link_freq; > + u16 line_length; hblank + crop width? > + 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++) { Declare i here. > + 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; unsigned int? And the same for the macro value? > + 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", I'd just use Hz here, the division loses possibly useful information. > + 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; Is it useful to keep both in the context struct? > + 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}; Spaces inside braces. > + > + 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) Could size be unsigned, too? > +{ > + unsigned int i; > + int ret; > + > + for (i = 0; i < size; i++) { i can be declared here. > + 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); Shouldn't this be the same for DT and ACPI...? > + } 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; Do you need to maintain this separately from the control value? The same for those below. > + > + 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); Please do rely on the control framework instead of doing this manually. > + 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++) { You can declare i here. > + 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; Why? > + 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) { Why to keep minor and major in separate fields? > + 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 */ Redundant comment. > + 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) Instead of maintaining streaming state locally, could you use media_entity_is_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; This information would be better maintained as part of sub-device state. > + > + /* 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; Can this happen, > + > + /* Unpowered, the last stream's values, which the next one starts from */ > + if (!pm_runtime_get_if_in_use(dev)) { > + sensor->expo_ctrl->val = min_t(u32, sensor->cold_start.expo, > + sensor->expo_ctrl->maximum); > + sensor->again_ctrl->val = sensor->cold_start.analog_gain; > + sensor->dgain_ctrl->val = sensor->cold_start.digital_gain; > + return 0; > + } > + > + vd55g0_read(sensor, VD55G0_REG_APPLIED_COARSE_EXPOSURE, &expo, &ret); > + vd55g0_read(sensor, VD55G0_REG_APPLIED_ANALOG_GAIN, &again, &ret); > + vd55g0_read(sensor, VD55G0_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); > + if (!ret) { > + sensor->expo_ctrl->val = expo; > + sensor->again_ctrl->val = again; > + sensor->dgain_ctrl->val = dgain; Can you use __v4l2_ctrl_s_ctrl()? That way events get handled correctly. > + } > + > + 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); You can move this past the handler's error check and omit the above check. > + > + ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &fwnode_props); This goes before initialising the handler, then error handling is simplified. > + if (ret) > + goto free_ctrls; > + > + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); > + if (ret) > + goto free_ctrls; > + > + if (hdl->error) { This check is redundant. > + ret = hdl->error; > + goto free_ctrls; > + } > + > + sensor->sd.ctrl_handler = hdl; One more newline here. > + return 0; > + > +free_ctrls: > + v4l2_ctrl_handler_free(hdl); return 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; > + } > + > + /* Held until registered, so that runtime PM cannot power it down */ > + pm_runtime_set_active(dev); > + pm_runtime_get_noresume(dev); > + pm_runtime_enable(dev); > + pm_runtime_set_autosuspend_delay(dev, 1000); > + pm_runtime_use_autosuspend(dev); > + > + ret = v4l2_async_register_subdev_sensor(&sensor->sd); > + if (ret) { > + dev_err_probe(dev, ret, "async subdev register failed\n"); > + goto error_pm_runtime; > + } > + > + pm_runtime_put_autosuspend(dev); > + > + dev_dbg(&client->dev, "vd55g0 probe successfully\n"); > + > + return 0; > + > +error_put_handle: > + fwnode_handle_put(handle); > + > + return ret; > + > +error_pm_runtime: > + pm_runtime_disable(dev); > + pm_runtime_set_suspended(dev); > + pm_runtime_put_noidle(dev); > + pm_runtime_dont_use_autosuspend(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 = { > + RUNTIME_PM_OPS(vd55g0_power_off, vd55g0_power_on, NULL) > +}; > + > +static struct i2c_driver vd55g0_i2c_driver = { > + .driver = { > + .name = "vd55g0", > + .of_match_table = vd55g0_dt_ids, > + .acpi_match_table = vd55g0_acpi_ids, > + .pm = pm_ptr(&vd55g0_pm_ops), > + }, > + .probe = vd55g0_probe, > + .remove = vd55g0_remove, > +}; > + > +module_i2c_driver(vd55g0_i2c_driver); > + > +MODULE_AUTHOR("Benjamin Mugnier <benjamin.mugnier@foss.st.com>"); > +MODULE_AUTHOR("Mickael Guene <mickael.guene@st.com>"); > +MODULE_AUTHOR("Sylvain Petinot <sylvain.petinot@foss.st.com>"); > +MODULE_AUTHOR("Sergey Lebedev <lsa.uz@pm.me>"); > +MODULE_DESCRIPTION("VD55G0 camera subdev driver"); > +MODULE_LICENSE("GPL"); -- Regards, Sakari Ailus ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver 2026-11-09 11:36 ` Sakari Ailus @ 2026-11-09 12:46 ` Sergey Lebedev 2026-11-09 12:59 ` Sakari Ailus 0 siblings, 1 reply; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 12:46 UTC (permalink / raw) To: Sakari Ailus Cc: Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Sakari, Thank you for the quick and thorough review. I am working on v4 with the rest of your comments, which will take a few days. Two points first, as your answers may change the code. On Mon, Nov 09, 2026 at 01:36:37PM +0200, Sakari Ailus wrote: >> +static const s64 vd55g0_ev_bias_menu[] = { >> + -3000, -2500, -2000, -1500, -1000, -500, >> + 0, >> + 500, 1000, 1500, 2000, 2500, 3000, > > Is this The sentence seems to have been cut off here. What did you mean? The table is vd55g1's, in the control's 0.001 EV units. >> + if (!ret) { >> + sensor->expo_ctrl->val = expo; >> + sensor->again_ctrl->val = again; >> + sensor->dgain_ctrl->val = dgain; > > Can you use __v4l2_ctrl_s_ctrl()? That way events get handled correctly. These three are volatile while auto exposure is on, which is the only time g_volatile_ctrl() is called. v4l2-controls.rst says to set ctrl->val there, and that a volatile control does not generate V4L2_EVENT_CTRL_CH_VALUE; cluster_changed() skips volatile controls, so __v4l2_ctrl_s_ctrl() would not send one either. Did I miss a case? Sergey ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver 2026-11-09 12:46 ` Sergey Lebedev @ 2026-11-09 12:59 ` Sakari Ailus 0 siblings, 0 replies; 11+ messages in thread From: Sakari Ailus @ 2026-11-09 12:59 UTC (permalink / raw) To: Sergey Lebedev Cc: Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Sergey, On Mon, Nov 09, 2026 at 12:46:16PM +0000, Sergey Lebedev wrote: > Hi Sakari, > > Thank you for the quick and thorough review. I am working on v4 with > the rest of your comments, which will take a few days. Two points > first, as your answers may change the code. > > On Mon, Nov 09, 2026 at 01:36:37PM +0200, Sakari Ailus wrote: > >> +static const s64 vd55g0_ev_bias_menu[] = { > >> + -3000, -2500, -2000, -1500, -1000, -500, > >> + 0, > >> + 500, 1000, 1500, 2000, 2500, 3000, > > > > Is this > > The sentence seems to have been cut off here. What did you mean? The > table is vd55g1's, in the control's 0.001 EV units. Uh, yes. I meant to ask if this is a firmware limitation. I guess so? > > >> + if (!ret) { > >> + sensor->expo_ctrl->val = expo; > >> + sensor->again_ctrl->val = again; > >> + sensor->dgain_ctrl->val = dgain; > > > > Can you use __v4l2_ctrl_s_ctrl()? That way events get handled correctly. > > These three are volatile while auto exposure is on, which is the only > time g_volatile_ctrl() is called. v4l2-controls.rst says to set > ctrl->val there, and that a volatile control does not generate > V4L2_EVENT_CTRL_CH_VALUE; cluster_changed() skips volatile controls, so > __v4l2_ctrl_s_ctrl() would not send one either. Did I miss a case? Ack. -- Regards, Sakari Ailus ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 3/4] media: ipu-bridge: Add ST VD55G0 2026-11-09 9:34 [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev @ 2026-11-09 9:34 ` Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev 3 siblings, 0 replies; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 9:34 UTC (permalink / raw) To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel The infrared camera of the Surface Pro 11 with Intel is an ST VD55G0, enumerated by ACPI as SMO55F0 and wired to the IPU7 over one CSI-2 data lane. The vd55g0 driver takes only a link frequency whose MIPI rate, twice it, is an integer multiple of the input clock. 396 MHz gives 792 Mbps, 33 times the 24 MHz the sensor's SSDB lists on the Surface Pro 8, 9, 10 and 11, and within the sensor's 750 to 1200 Mbps. Peter Marshall's earlier patch for the Surface Pro 9 used 380 MHz, 760 Mbps, which is not a multiple of 24 MHz, so the driver would refuse it: https://lore.kernel.org/all/20260918221705.323510-4-pm@petermarshall.ca/ Tested-by: Fernando Rimoli <fernandorimoli11@gmail.com> # Surface Pro 9, IPU6 Signed-off-by: Sergey Lebedev <lsa.uz@pm.me> --- v3: moved before VD55G1, for vendor order. imx681 v10 3/3 adds SONY0681 at the same place, so whichever lands second needs a trivial fixup. drivers/media/pci/intel/ipu-bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 3739c4a2a8c..c99c85b7e6f 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -124,6 +124,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), + /* STMicroelectronics VD55G0 */ + IPU_SENSOR_CONFIG("SMO55F0", 1, 396000000), /* STMicroelectronics VD55G1 (found on Lenovo X1 Carbon G14) */ IPU_SENSOR_CONFIG("TBE20A1", 1, 402000000), /* Toshiba T4KA3 */ -- 2.54.0 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator 2026-11-09 9:34 [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev ` (2 preceding siblings ...) 2026-11-09 9:34 ` [PATCH v3 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev @ 2026-11-09 9:34 ` Sergey Lebedev 2026-11-09 11:39 ` Sakari Ailus 3 siblings, 1 reply; 11+ messages in thread From: Sergey Lebedev @ 2026-11-09 9:34 UTC (permalink / raw) To: Sakari Ailus, Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab Cc: Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel On the Surface Pro 9 and on the Surface Pro 11 with Intel, the infrared illuminator is driven by the VD55G0's GPIO 1, which strobes it during the exposure. The ACPI tables do not describe it, so the sensor driver finds no st,leds and offers no V4L2_CID_FLASH_LED_MODE. Give the sensor's software node st,leds from a DMI match, as the upside-down quirk gives a rotation. dev_properties gains an entry for it, as a sensor can also have a lens-focus reference. The software nodes outlive the module, so the property name goes into struct ipu_property_names and the GPIO numbers into struct ipu_sensor, where nothing points into the module image. Tested-by: Fernando Rimoli <fernandorimoli11@gmail.com> # Surface Pro 9, IPU6 Signed-off-by: Sergey Lebedev <lsa.uz@pm.me> --- v3: no change. v2: the st,leds name and GPIO numbers live in the bridge's own structs, as the software nodes outlive the module (Fernando); the Surface Pro 9 is added (Fernando). D. Manresa's ipu-bridge series changes the same lens-focus line; whichever of the two lands second needs a trivial rebase: https://lore.kernel.org/all/20261026082549.662885-1-dmanresa@gmail.com/ drivers/media/pci/intel/ipu-bridge.c | 68 +++++++++++++++++++++++++++- include/media/ipu-bridge.h | 6 ++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index c99c85b7e6f..8d2025acf7a 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -283,6 +283,42 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { {} /* Terminating entry */ }; +/* + * Sensor GPIOs that drive an illuminator, for machines whose ACPI tables do + * not say which. They reach the sensor driver as its own leds property. + */ +struct ipu_sensor_leds { + const char *hid; + u32 gpios[IPU_MAX_LED_GPIOS]; + unsigned int nr_gpios; +}; + +/* The infrared illuminator, strobed by the VD55G0's GPIO 1 */ +static const struct ipu_sensor_leds surface_pro_ir_leds = { + .hid = "SMO55F0", + .gpios = { 1 }, + .nr_gpios = 1, +}; + +static const struct dmi_system_id sensor_leds_dmi_ids[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), + }, + .driver_data = (void *)&surface_pro_ir_leds, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, + "Surface Pro for Business 11th Edition with Intel"), + }, + .driver_data = (void *)&surface_pro_ir_leds, + }, + {} /* Terminating entry */ +}; + static const struct ipu_property_names prop_names = { .clock_frequency = "clock-frequency", .rotation = "rotation", @@ -292,6 +328,7 @@ static const struct ipu_property_names prop_names = { .remote_endpoint = "remote-endpoint", .link_frequencies = "link-frequencies", .clock_noncontinuous = "clock-noncontinuous", + .st_leds = "st,leds", }; static const char * const ipu_vcm_types[] = { @@ -563,6 +600,22 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) } EXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, "INTEL_IPU_BRIDGE"); +static const struct ipu_sensor_leds * +ipu_bridge_get_leds(const struct ipu_sensor_config *cfg) +{ + const struct dmi_system_id *dmi_id; + const struct ipu_sensor_leds *leds; + + for (dmi_id = dmi_first_match(sensor_leds_dmi_ids); dmi_id; + dmi_id = dmi_first_match(dmi_id + 1)) { + leds = dmi_id->driver_data; + if (!strcmp(cfg->hid, leds->hid)) + return leds; + } + + return NULL; +} + static void ipu_bridge_create_fwnode_properties( struct ipu_sensor *sensor, struct ipu_bridge *bridge, @@ -570,7 +623,8 @@ static void ipu_bridge_create_fwnode_properties( { struct ipu_property_names *names = &sensor->prop_names; struct software_node *nodes = sensor->swnodes; - unsigned int i = 0; + const struct ipu_sensor_leds *leds; + unsigned int i = 0, d = 3; sensor->prop_names = prop_names; @@ -624,10 +678,20 @@ static void ipu_bridge_create_fwnode_properties( if (sensor->vcm_type) { sensor->vcm_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); - sensor->dev_properties[3] = + sensor->dev_properties[d++] = PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); } + leds = ipu_bridge_get_leds(cfg); + if (leds) { + memcpy(sensor->led_gpios, leds->gpios, + sizeof(sensor->led_gpios)); + sensor->dev_properties[d++] = + PROPERTY_ENTRY_U32_ARRAY_LEN(names->st_leds, + sensor->led_gpios, + leds->nr_gpios); + } + sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_BUS_TYPE)] = PROPERTY_ENTRY_U32(names->bus_type, V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h index 3ef94c2b767..7383bdf5a45 100644 --- a/include/media/ipu-bridge.h +++ b/include/media/ipu-bridge.h @@ -12,6 +12,7 @@ #define IPU_MAX_LANES 4 #define IPU_MAX_PORTS 4 #define MAX_NUM_LINK_FREQS 3 +#define IPU_MAX_LED_GPIOS 4 /* Values are educated guesses as we don't have a spec */ #define IPU_SENSOR_ROTATION_NORMAL 0 @@ -136,6 +137,7 @@ struct ipu_property_names { char remote_endpoint[16]; char link_frequencies[17]; char clock_noncontinuous[20]; + char st_leds[8]; }; struct ipu_node_names { @@ -178,8 +180,10 @@ struct ipu_sensor { const char *vcm_type; struct ipu_property_names prop_names; + /* The software nodes outlive the module, so these values live here */ + u32 led_gpios[IPU_MAX_LED_GPIOS]; struct property_entry ep_properties[IPU_BRIDGE_EP_NUM_ENTRIES]; - struct property_entry dev_properties[5]; + struct property_entry dev_properties[6]; struct property_entry ipu_properties[3]; struct property_entry ivsc_properties[1]; struct property_entry ivsc_sensor_ep_properties[4]; -- 2.54.0 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator 2026-11-09 9:34 ` [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev @ 2026-11-09 11:39 ` Sakari Ailus 0 siblings, 0 replies; 11+ messages in thread From: Sakari Ailus @ 2026-11-09 11:39 UTC (permalink / raw) To: Sergey Lebedev Cc: Benjamin Mugnier, Sylvain Petinot, Mauro Carvalho Chehab, Dan Scally, Hans de Goede, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Peter Marshall, George Neacsu, Hao Yao, Fernando Rimoli, D. Manresa, Jizhou Tong, linux-media, devicetree, linux-kernel Hi Sergey, On Mon, Nov 09, 2026 at 09:34:50AM +0000, Sergey Lebedev wrote: > On the Surface Pro 9 and on the Surface Pro 11 with Intel, the infrared > illuminator is driven by the VD55G0's GPIO 1, which strobes it during > the exposure. The ACPI tables do not describe it, so the sensor driver > finds no st,leds and offers no V4L2_CID_FLASH_LED_MODE. > > Give the sensor's software node st,leds from a DMI match, as the > upside-down quirk gives a rotation. dev_properties gains an entry for it, > as a sensor can also have a lens-focus reference. The software nodes > outlive the module, so the property name goes into struct > ipu_property_names and the GPIO numbers into struct ipu_sensor, where > nothing points into the module image. > > Tested-by: Fernando Rimoli <fernandorimoli11@gmail.com> # Surface Pro 9, IPU6 > Signed-off-by: Sergey Lebedev <lsa.uz@pm.me> > --- > v3: no change. > v2: the st,leds name and GPIO numbers live in the bridge's own > structs, as the software nodes outlive the module (Fernando); the > Surface Pro 9 is added (Fernando). > > D. Manresa's ipu-bridge series changes the same lens-focus line; > whichever of the two lands second needs a trivial rebase: > https://lore.kernel.org/all/20261026082549.662885-1-dmanresa@gmail.com/ > > drivers/media/pci/intel/ipu-bridge.c | 68 +++++++++++++++++++++++++++- > include/media/ipu-bridge.h | 6 ++- > 2 files changed, 71 insertions(+), 3 deletions(-) > > diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c > index c99c85b7e6f..8d2025acf7a 100644 > --- a/drivers/media/pci/intel/ipu-bridge.c > +++ b/drivers/media/pci/intel/ipu-bridge.c > @@ -283,6 +283,42 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { > {} /* Terminating entry */ > }; > > +/* > + * Sensor GPIOs that drive an illuminator, for machines whose ACPI tables do > + * not say which. They reach the sensor driver as its own leds property. > + */ > +struct ipu_sensor_leds { > + const char *hid; > + u32 gpios[IPU_MAX_LED_GPIOS]; > + unsigned int nr_gpios; > +}; > + > +/* The infrared illuminator, strobed by the VD55G0's GPIO 1 */ > +static const struct ipu_sensor_leds surface_pro_ir_leds = { > + .hid = "SMO55F0", > + .gpios = { 1 }, > + .nr_gpios = 1, > +}; > + > +static const struct dmi_system_id sensor_leds_dmi_ids[] = { > + { > + .matches = { > + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), > + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), > + }, > + .driver_data = (void *)&surface_pro_ir_leds, > + }, > + { > + .matches = { > + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), > + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, > + "Surface Pro for Business 11th Edition with Intel"), > + }, > + .driver_data = (void *)&surface_pro_ir_leds, > + }, > + {} /* Terminating entry */ > +}; > + > static const struct ipu_property_names prop_names = { > .clock_frequency = "clock-frequency", > .rotation = "rotation", > @@ -292,6 +328,7 @@ static const struct ipu_property_names prop_names = { > .remote_endpoint = "remote-endpoint", > .link_frequencies = "link-frequencies", > .clock_noncontinuous = "clock-noncontinuous", > + .st_leds = "st,leds", > }; > > static const char * const ipu_vcm_types[] = { > @@ -563,6 +600,22 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) > } > EXPORT_SYMBOL_NS_GPL(ipu_bridge_parse_ssdb, "INTEL_IPU_BRIDGE"); > > +static const struct ipu_sensor_leds * > +ipu_bridge_get_leds(const struct ipu_sensor_config *cfg) > +{ > + const struct dmi_system_id *dmi_id; > + const struct ipu_sensor_leds *leds; > + > + for (dmi_id = dmi_first_match(sensor_leds_dmi_ids); dmi_id; > + dmi_id = dmi_first_match(dmi_id + 1)) { > + leds = dmi_id->driver_data; > + if (!strcmp(cfg->hid, leds->hid)) > + return leds; > + } > + > + return NULL; > +} > + > static void ipu_bridge_create_fwnode_properties( > struct ipu_sensor *sensor, > struct ipu_bridge *bridge, > @@ -570,7 +623,8 @@ static void ipu_bridge_create_fwnode_properties( > { > struct ipu_property_names *names = &sensor->prop_names; > struct software_node *nodes = sensor->swnodes; > - unsigned int i = 0; > + const struct ipu_sensor_leds *leds; > + unsigned int i = 0, d = 3; > > sensor->prop_names = prop_names; > > @@ -624,10 +678,20 @@ static void ipu_bridge_create_fwnode_properties( > if (sensor->vcm_type) { > sensor->vcm_ref[0] = > SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_VCM]); > - sensor->dev_properties[3] = > + sensor->dev_properties[d++] = > PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); > } > > + leds = ipu_bridge_get_leds(cfg); > + if (leds) { > + memcpy(sensor->led_gpios, leds->gpios, > + sizeof(sensor->led_gpios)); > + sensor->dev_properties[d++] = > + PROPERTY_ENTRY_U32_ARRAY_LEN(names->st_leds, > + sensor->led_gpios, > + leds->nr_gpios); > + } > + > sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_BUS_TYPE)] = > PROPERTY_ENTRY_U32(names->bus_type, > V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); > diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h > index 3ef94c2b767..7383bdf5a45 100644 > --- a/include/media/ipu-bridge.h > +++ b/include/media/ipu-bridge.h > @@ -12,6 +12,7 @@ > #define IPU_MAX_LANES 4 > #define IPU_MAX_PORTS 4 > #define MAX_NUM_LINK_FREQS 3 > +#define IPU_MAX_LED_GPIOS 4 > > /* Values are educated guesses as we don't have a spec */ > #define IPU_SENSOR_ROTATION_NORMAL 0 > @@ -136,6 +137,7 @@ struct ipu_property_names { > char remote_endpoint[16]; > char link_frequencies[17]; > char clock_noncontinuous[20]; > + char st_leds[8]; > }; > > struct ipu_node_names { > @@ -178,8 +180,10 @@ struct ipu_sensor { > const char *vcm_type; > > struct ipu_property_names prop_names; > + /* The software nodes outlive the module, so these values live here */ > + u32 led_gpios[IPU_MAX_LED_GPIOS]; > struct property_entry ep_properties[IPU_BRIDGE_EP_NUM_ENTRIES]; > - struct property_entry dev_properties[5]; > + struct property_entry dev_properties[6]; Can you handle dev_properties as ep_properties is handled now? I recall there were patches that also did something in the area, related to keeping all memory allocated when ipu-bridge is unloaded. > struct property_entry ipu_properties[3]; > struct property_entry ivsc_properties[1]; > struct property_entry ivsc_sensor_ep_properties[4]; > -- Regards, Sakari Ailus ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-11-09 12:59 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-09 9:34 [PATCH v3 0/4] media: Add the ST VD55G0 global shutter sensor Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 1/4] dt-bindings: media: i2c: st,vd55g1: Add ST VD55G0 Sergey Lebedev 2026-11-09 10:28 ` Benjamin Mugnier 2026-11-09 10:36 ` Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver Sergey Lebedev 2026-11-09 11:36 ` Sakari Ailus 2026-11-09 12:46 ` Sergey Lebedev 2026-11-09 12:59 ` Sakari Ailus 2026-11-09 9:34 ` [PATCH v3 3/4] media: ipu-bridge: Add ST VD55G0 Sergey Lebedev 2026-11-09 9:34 ` [PATCH v3 4/4] media: ipu-bridge: Add the Surface Pro 9 and 11 infrared illuminator Sergey Lebedev 2026-11-09 11:39 ` Sakari Ailus
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®