From: Sakari Ailus <sakari.ailus@linux.intel.com>
To: Sergey Lebedev <lsa.uz@pm.me>
Cc: Benjamin Mugnier <benjamin.mugnier@foss.st.com>,
Sylvain Petinot <sylvain.petinot@foss.st.com>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Dan Scally <dan.scally@ideasonboard.com>,
Hans de Goede <hansg@kernel.org>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Peter Marshall <pm@petermarshall.ca>,
George Neacsu <geocausa@gmail.com>, Hao Yao <hao.yao@intel.com>,
Fernando Rimoli <fernandorimoli11@gmail.com>,
"D. Manresa" <dmanresa@gmail.com>, Jizhou Tong <fqwqf@fqwqf.xyz>,
linux-media@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 2/4] media: i2c: Add ST VD55G0 global shutter sensor driver
Date: Mon, 9 Nov 2026 13:36:37 +0200 [thread overview]
Message-ID: <avGwRaKSqXdtXkII@kekkonen.localdomain> (raw)
In-Reply-To: <20261109093353.3757-3-lsa.uz@pm.me>
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
next prev parent reply other threads:[~2026-11-09 11:36 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=avGwRaKSqXdtXkII@kekkonen.localdomain \
--to=sakari.ailus@linux.intel.com \
--cc=benjamin.mugnier@foss.st.com \
--cc=conor+dt@kernel.org \
--cc=dan.scally@ideasonboard.com \
--cc=devicetree@vger.kernel.org \
--cc=dmanresa@gmail.com \
--cc=fernandorimoli11@gmail.com \
--cc=fqwqf@fqwqf.xyz \
--cc=geocausa@gmail.com \
--cc=hansg@kernel.org \
--cc=hao.yao@intel.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=lsa.uz@pm.me \
--cc=mchehab@kernel.org \
--cc=pm@petermarshall.ca \
--cc=robh@kernel.org \
--cc=sylvain.petinot@foss.st.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®