From 7d134a4b79c68cb5ce2bd713931584f88284eb3a Mon Sep 17 00:00:00 2001 From: Leon Silcott Date: Thu, 12 Nov 2026 20:06:30 +0000 Subject: [PATCH] media: port OV13858 and VD55G0 research with explicit stop retries (#73) Port the SP11 camera research from the v25 native camera branch onto the current 7.3 lifetime. Add the OmniVision OV13858 device-tree binding and native OF power management. Extend the ST VD55G1 driver for the VD55G0 global-shutter sensor: indirect hardware register addressing, model/revision feature abstraction, unified frame timing, corrected register access widths, bounded optional LED configuration with illumination gated pending qualification, GPIO inputs established after firmware boot, native exposure and HDR limit retention, stream ownership preserved until stop succeeds, legacy retries after retained stop errors, and safe line lengths with sensor-style asynchronous registration. Narrow the st,vd55g1 binding to avoid a family-wide fixed-address claim and track both bindings in MAINTAINERS. Validated with strict checkpatch, hermetic AArch64 W=1 builds, and behavioral fixtures for the stop/retry ownership (see sp11-driver-parity-artifacts/20261015/private/camera-v3*). The v4l2-subdev retained-stop opt-in this series depends on merged earlier as PR #80. Assisted-by: Codex:gpt-5.6 Assisted-by: OpenCode:omni-route/zai/glm-5.3 Signed-off-by: Leon Silcott --- .../bindings/media/i2c/ovti,ov13858.yaml | 128 ++ .../bindings/media/i2c/st,vd55g1.yaml | 37 +- MAINTAINERS | 2 + drivers/media/i2c/ov13858.c | 292 ++- drivers/media/i2c/vd55g1.c | 1801 +++++++++++++---- 5 files changed, 1882 insertions(+), 378 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov13858.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov13858.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov13858.yaml new file mode 100644 index 00000000000000..3544e865ee6b3a --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov13858.yaml @@ -0,0 +1,128 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov13858.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OV13858 CMOS image sensor + +maintainers: + - Leon Silcott + +description: | + The OV13858 is a CMOS image sensor controlled over I2C/SCCB. The supported + modes output 10-bit Bayer data over four CSI-2 D-PHY data lanes and use a + 19.2 MHz external clock. + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: ovti,ov13858 + + reg: + maxItems: 1 + + clocks: + description: External sensor clock (XVCLK), running at 19.2 MHz. + maxItems: 1 + + dovdd-supply: + description: Digital I/O supply. + + avdd-supply: + description: Analog supply. + + dvdd-supply: + description: Digital core supply. + + reset-gpios: + description: Active-low sensor reset signal. + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + const: 4 + + clock-lanes: + const: 0 + + clock-noncontinuous: false + + data-lanes: + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + lane-polarities: + items: + - const: 0 + - const: 0 + - const: 0 + - const: 0 + - const: 0 + + link-frequencies: + minItems: 1 + maxItems: 2 + uniqueItems: true + items: + enum: [270000000, 540000000] + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - dovdd-supply + - avdd-supply + - dvdd-supply + - port + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@10 { + compatible = "ovti,ov13858"; + reg = <0x10>; + clocks = <&camera_clk>; + avdd-supply = <&camera_avdd>; + dovdd-supply = <&camera_dovdd>; + dvdd-supply = <&camera_dvdd>; + reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>; + + port { + endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <270000000 540000000>; + remote-endpoint = <&csi_in>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml index 58b1f9e85a9d1c..819e29fcf7aa6d 100644 --- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml @@ -5,32 +5,32 @@ $id: http://devicetree.org/schemas/media/i2c/st,vd55g1.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: STMicroelectronics VD55G1 Global Shutter Image Sensor +title: STMicroelectronics VD55G1 Family Global Shutter Image Sensors maintainers: - Benjamin Mugnier - Sylvain Petinot + - Peter Marshall description: |- - The STMicroelectronics VD55G1 is a global shutter image sensor with an active - array size of 804H x 704V. It is programmable through I2C interface. The I2C - address is fixed to 0x10. + The STMicroelectronics VD55G1 family (VD55G0, VD55G1, and VD65G4) consists of + global shutter image sensors. The VD55G0 has an array size of 644H x 604V. + The VD55G1 and VD65G4 have an array size of 804H x 704V. They are programmable + through I2C interface. Image data is sent through MIPI CSI-2, which is configured as only 1 data lane. The sensor provides 4 GPIOS that can be used for external LED signal (synchronized with sensor integration periods). -allOf: - - $ref: /schemas/media/video-interface-devices.yaml# - properties: compatible: enum: + - st,vd55g0 - st,vd55g1 - st,vd55g4 - st,vd65g4 description: - VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one. + VD55G0 and VD55G1 are monochrome variants, while VD65G4 is a color variant. reg: maxItems: 1 @@ -101,6 +101,27 @@ required: - reset-gpios - port +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + + - if: + properties: + compatible: + contains: + const: st,vd55g0 + then: + properties: + # Illumination is not qualified by the sensor-only VD55G0 port. + st,leds: false + + port: + properties: + endpoint: + properties: + link-frequencies: + items: + minimum: 375000000 + unevaluatedProperties: false examples: diff --git a/MAINTAINERS b/MAINTAINERS index 05500bccf76b7a..e25b662358cdf5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20225,6 +20225,7 @@ M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git +F: Documentation/devicetree/bindings/media/i2c/ovti,ov13858.yaml F: drivers/media/i2c/ov13858.c OMNIVISION OV13B10 SENSOR DRIVER @@ -26034,6 +26035,7 @@ F: drivers/hwmon/tsc1641.c ST VD55G1 DRIVER M: Benjamin Mugnier M: Sylvain Petinot +M: Peter Marshall L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 09d58e8b1c7fbd..bb81aca8d79338 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -3,9 +3,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -1028,9 +1031,22 @@ static const struct ov13858_mode supported_modes[] = { } }; +static const char * const ov13858_supply_names[] = { + "dovdd", + "avdd", + "dvdd", +}; + struct ov13858 { struct device *dev; struct clk *clk; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov13858_supply_names)]; + struct gpio_desc *reset_gpio; + bool managed_power; + bool power_fault; + unsigned long link_freq_bitmap; + struct ov13858_mode modes[ARRAY_SIZE(supported_modes)]; + unsigned int num_modes; struct v4l2_subdev sd; struct media_pad pad; @@ -1063,6 +1079,9 @@ static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, __be32 data_be = 0; __be16 reg_addr_be = cpu_to_be16(reg); + if (READ_ONCE(ov13858->power_fault)) + return -EIO; + if (len > 4) return -EINVAL; @@ -1097,6 +1116,9 @@ static int ov13858_write_reg(struct ov13858 *ov13858, u16 reg, u32 len, u8 buf[6], *val_p; __be32 val; + if (READ_ONCE(ov13858->power_fault)) + return -EIO; + if (len > 4) return -EINVAL; @@ -1290,15 +1312,17 @@ static int ov13858_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes)) + struct ov13858 *ov13858 = to_ov13858(sd); + + if (fse->index >= ov13858->num_modes) return -EINVAL; if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) return -EINVAL; - fse->min_width = supported_modes[fse->index].width; + fse->min_width = ov13858->modes[fse->index].width; fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; + fse->min_height = ov13858->modes[fse->index].height; fse->max_height = fse->min_height; return 0; @@ -1363,8 +1387,7 @@ ov13858_set_pad_format(struct v4l2_subdev *sd, if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), + mode = v4l2_find_nearest_size(ov13858->modes, ov13858->num_modes, width, height, fmt->format.width, fmt->format.height); ov13858_update_pad_format(mode, fmt); @@ -1463,6 +1486,11 @@ static int ov13858_set_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&ov13858->mutex); + if (enable && READ_ONCE(ov13858->power_fault)) { + ret = -EIO; + goto err_unlock; + } + if (enable) { ret = pm_runtime_resume_and_get(ov13858->dev); if (ret < 0) @@ -1559,6 +1587,7 @@ static int ov13858_init_controls(struct ov13858 *ov13858) s64 hblank; s64 pixel_rate_min; s64 pixel_rate_max; + s64 link_freq; const struct ov13858_mode *mode; int ret; @@ -1572,14 +1601,20 @@ static int ov13858_init_controls(struct ov13858 *ov13858) ov13858->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_LINK_FREQ, - OV13858_NUM_OF_LINK_FREQS - 1, - 0, + __fls(ov13858->link_freq_bitmap), + ov13858->cur_mode->link_freq_index, link_freq_menu_items); - if (ov13858->link_freq) + if (ov13858->link_freq) { ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ov13858->link_freq->menu_skip_mask = + ~ov13858->link_freq_bitmap & + GENMASK(OV13858_NUM_OF_LINK_FREQS - 1, 0); + } - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); - pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); + link_freq = link_freq_menu_items[__ffs(ov13858->link_freq_bitmap)]; + pixel_rate_max = link_freq_to_pixel_rate(link_freq); + link_freq = link_freq_menu_items[__fls(ov13858->link_freq_bitmap)]; + pixel_rate_min = link_freq_to_pixel_rate(link_freq); /* By default, PIXEL_RATE is read only */ ov13858->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1655,6 +1690,173 @@ static void ov13858_free_controls(struct ov13858 *ov13858) mutex_destroy(&ov13858->mutex); } +static int ov13858_get_dt_resources(struct ov13858 *ov13858) +{ + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct device *dev = ov13858->dev; + struct fwnode_handle *endpoint; + u32 lanes[4]; + u32 clock_lane = 0; + int ret; + + /* Existing ACPI systems leave power sequencing to their PM domain. */ + if (!dev->of_node) + return 0; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "missing CSI-2 endpoint\n"); + + if (fwnode_property_count_u32(endpoint, "data-lanes") != ARRAY_SIZE(lanes)) { + ret = -EINVAL; + goto put_endpoint; + } + ret = fwnode_property_read_u32_array(endpoint, "data-lanes", lanes, + ARRAY_SIZE(lanes)); + if (ret) + goto put_endpoint; + if (fwnode_property_present(endpoint, "clock-lanes")) { + ret = fwnode_property_read_u32(endpoint, "clock-lanes", &clock_lane); + if (ret) + goto put_endpoint; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + +put_endpoint: + fwnode_handle_put(endpoint); + if (ret) { + dev_err_probe(dev, ret, "invalid CSI-2 endpoint\n"); + goto free_endpoint; + } + + if (ep.bus_type != V4L2_MBUS_CSI2_DPHY || + ep.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(dev, -EINVAL, + "four CSI-2 D-PHY data lanes are required\n"); + goto free_endpoint; + } + + if (clock_lane || ep.bus.mipi_csi2.lane_polarities[0] || + ep.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) { + ret = dev_err_probe(dev, -EINVAL, + "unsupported CSI-2 clock configuration\n"); + goto free_endpoint; + } + for (unsigned int i = 0; i < ARRAY_SIZE(lanes); i++) { + if (lanes[i] != i + 1 || ep.bus.mipi_csi2.lane_polarities[i + 1]) { + ret = dev_err_probe(dev, -EINVAL, + "unsupported CSI-2 data lane mapping\n"); + goto free_endpoint; + } + } + + ret = v4l2_link_freq_to_bitmap(dev, ep.link_frequencies, + ep.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &ov13858->link_freq_bitmap); + if (ret) + goto free_endpoint; + + for (unsigned int i = 0; i < ARRAY_SIZE(ov13858_supply_names); i++) + ov13858->supplies[i].supply = ov13858_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov13858_supply_names), + ov13858->supplies); + if (ret) { + dev_err_probe(dev, ret, "failed to get supplies\n"); + goto free_endpoint; + } + + ov13858->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov13858->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(ov13858->reset_gpio), + "failed to get reset GPIO\n"); + goto free_endpoint; + } + + ov13858->managed_power = true; + +free_endpoint: + v4l2_fwnode_endpoint_free(&ep); + return ret; +} + +static int ov13858_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13858 *ov13858 = to_ov13858(sd); + int cleanup_ret; + int ret; + + if (!ov13858->managed_power) + return 0; + if (READ_ONCE(ov13858->power_fault)) + return -EIO; + + gpiod_set_value_cansleep(ov13858->reset_gpio, 1); + ret = regulator_bulk_enable(ARRAY_SIZE(ov13858_supply_names), + ov13858->supplies); + if (ret) { + WRITE_ONCE(ov13858->power_fault, true); + dev_err(dev, "supply enable failed; power state unknown: %d\n", ret); + return -EIO; + } + + ret = clk_prepare_enable(ov13858->clk); + if (ret) { + dev_err(dev, "failed to enable clock: %d\n", ret); + cleanup_ret = regulator_bulk_disable(ARRAY_SIZE(ov13858_supply_names), + ov13858->supplies); + if (cleanup_ret) { + WRITE_ONCE(ov13858->power_fault, true); + dev_err(dev, "supply unwind failed; power state unknown: %d\n", + cleanup_ret); + return -EIO; + } + return ret; + } + + fsleep(1000); + gpiod_set_value_cansleep(ov13858->reset_gpio, 0); + /* Allow reset recovery before the first SCCB transaction. */ + fsleep(5000); + + return 0; +} + +static int ov13858_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov13858 *ov13858 = to_ov13858(sd); + int ret; + + if (!ov13858->managed_power) + return 0; + if (READ_ONCE(ov13858->power_fault)) + return -EIO; + + gpiod_set_value_cansleep(ov13858->reset_gpio, 1); + ret = regulator_bulk_disable(ARRAY_SIZE(ov13858_supply_names), + ov13858->supplies); + clk_disable_unprepare(ov13858->clk); + if (ret) { + /* Rollback can fail too; do not issue I/O or retry unknown rails. */ + WRITE_ONCE(ov13858->power_fault, true); + dev_err(dev, "supply disable failed; power state unknown: %d\n", ret); + return -EIO; + } + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ov13858_pm_ops, ov13858_power_off, + ov13858_power_on, NULL); + static int ov13858_probe(struct i2c_client *client) { struct ov13858 *ov13858; @@ -1666,6 +1868,8 @@ static int ov13858_probe(struct i2c_client *client) return -ENOMEM; ov13858->dev = &client->dev; + ov13858->link_freq_bitmap = + GENMASK(OV13858_NUM_OF_LINK_FREQS - 1, 0); ov13858->clk = devm_v4l2_sensor_clk_get(ov13858->dev, NULL); if (IS_ERR(ov13858->clk)) @@ -1678,22 +1882,36 @@ static int ov13858_probe(struct i2c_client *client) "external clock %lu is not supported\n", freq); + ret = ov13858_get_dt_resources(ov13858); + if (ret) + return ret; + + for (unsigned int i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (ov13858->link_freq_bitmap & BIT(supported_modes[i].link_freq_index)) + ov13858->modes[ov13858->num_modes++] = supported_modes[i]; + } + /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops); + ret = ov13858_power_on(ov13858->dev); + if (ret) + return dev_err_probe(ov13858->dev, ret, + "failed to power on sensor\n"); + /* Check module identity */ ret = ov13858_identify_module(ov13858); if (ret) { dev_err(ov13858->dev, "failed to find sensor: %d\n", ret); - return ret; + goto error_power_off; } - /* Set default mode to max resolution */ - ov13858->cur_mode = &supported_modes[0]; + /* The first enabled mode has the highest available resolution. */ + ov13858->cur_mode = &ov13858->modes[0]; ret = ov13858_init_controls(ov13858); if (ret) - return ret; + goto error_power_off; /* Initialize subdev */ ov13858->sd.internal_ops = &ov13858_internal_ops; @@ -1710,20 +1928,29 @@ static int ov13858_probe(struct i2c_client *client) goto error_handler_free; } - ret = v4l2_async_register_subdev_sensor(&ov13858->sd); - if (ret < 0) - goto error_media_entity; - /* - * Device is already turned on by i2c-core with ACPI domain PM. - * Enable runtime PM and turn off the device. + * The driver powers DT devices; i2c-core powers ACPI devices. Hold the + * initial reference until async registration has finished, since binding + * a bridge can expose sub-device operations immediately. */ - pm_runtime_set_active(ov13858->dev); + ret = pm_runtime_set_active(ov13858->dev); + if (ret) + goto error_media_entity; + pm_runtime_get_noresume(ov13858->dev); pm_runtime_enable(ov13858->dev); - pm_runtime_idle(ov13858->dev); + + ret = v4l2_async_register_subdev_sensor(&ov13858->sd); + if (ret < 0) + goto error_runtime_pm; + + pm_runtime_put(ov13858->dev); return 0; +error_runtime_pm: + pm_runtime_disable(ov13858->dev); + pm_runtime_put_noidle(ov13858->dev); + error_media_entity: media_entity_cleanup(&ov13858->sd.entity); @@ -1731,6 +1958,12 @@ static int ov13858_probe(struct i2c_client *client) ov13858_free_controls(ov13858); dev_err(ov13858->dev, "%s failed:%d\n", __func__, ret); +error_power_off: + if (ov13858_power_off(ov13858->dev)) + dev_warn(ov13858->dev, "failed to power off sensor\n"); + else + pm_runtime_set_suspended(ov13858->dev); + return ret; } @@ -1738,12 +1971,19 @@ static void ov13858_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov13858 *ov13858 = to_ov13858(sd); + int ret = 0; v4l2_async_unregister_subdev(sd); media_entity_cleanup(&sd->entity); ov13858_free_controls(ov13858); pm_runtime_disable(ov13858->dev); + if (!pm_runtime_status_suspended(ov13858->dev)) + ret = ov13858_power_off(ov13858->dev); + if (ret) + dev_warn(ov13858->dev, "failed to power off sensor: %d\n", ret); + else + pm_runtime_set_suspended(ov13858->dev); } static const struct i2c_device_id ov13858_id_table[] = { @@ -1762,10 +2002,18 @@ static const struct acpi_device_id ov13858_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids); #endif +static const struct of_device_id ov13858_of_match[] = { + { .compatible = "ovti,ov13858" }, + { } +}; +MODULE_DEVICE_TABLE(of, ov13858_of_match); + static struct i2c_driver ov13858_i2c_driver = { .driver = { .name = "ov13858", .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), + .of_match_table = ov13858_of_match, + .pm = pm_ptr(&ov13858_pm_ops), }, .probe = ov13858_probe, .remove = ov13858_remove, diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 6f458f611f6345..340da6d64811d7 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -2,6 +2,7 @@ /* * Driver for VD55G1 global shutter sensor family driver * + * Copyright (C) 2024 STMicroelectronics SA (VD55G0 firmware patches) * Copyright (C) 2025 STMicroelectronics SA */ @@ -28,89 +29,35 @@ #include /* Register Map */ -#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000) -#define VD55G1_REG_COLOR_VERSION CCI_REG32_LE(0x0670) -#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012) -#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000) -#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c) #define VD55G1_SYSTEM_FSM_READY_TO_BOOT 0x01 #define VD55G1_SYSTEM_FSM_SW_STBY 0x02 #define VD55G1_SYSTEM_FSM_STREAMING 0x03 -#define VD55G1_REG_BOOT CCI_REG8(0x0200) #define VD55G1_BOOT_BOOT 1 #define VD55G1_BOOT_PATCH_AND_BOOT 2 -#define VD55G1_REG_STBY CCI_REG8(0x0201) #define VD55G1_STBY_START_STREAM 1 -#define VD55G1_REG_STREAMING CCI_REG8(0x0202) #define VD55G1_STREAMING_STOP_STREAM 1 -#define VD55G1_REG_EXT_CLOCK CCI_REG32_LE(0x0220) -#define VD55G1_REG_LINE_LENGTH CCI_REG16_LE(0x0300) -#define VD55G1_REG_ORIENTATION CCI_REG8(0x0302) -#define VD55G1_REG_FORMAT_CTRL CCI_REG8(0x030a) -#define VD55G1_REG_OIF_CTRL CCI_REG16_LE(0x030c) -#define VD55G1_REG_ISL_ENABLE CCI_REG16_LE(0x326) -#define VD55G1_REG_OIF_IMG_CTRL CCI_REG8(0x030f) -#define VD55G1_REG_MIPI_DATA_RATE CCI_REG32_LE(0x0224) -#define VD55G1_REG_PATGEN_CTRL CCI_REG16_LE(0x0304) #define VD55G1_PATGEN_TYPE_SHIFT 4 #define VD55G1_PATGEN_ENABLE BIT(0) -#define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501) -#define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0504) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0506) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0508) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x050a) -#define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8) -#define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea) -#define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec) -#define VD55G1_REG_AE_FORCE_COLDSTART CCI_REG8(0x0308) -#define VD55G1_REG_AE_COLDSTART_EXP_TIME CCI_REG32_LE(0x0374) -#define VD55G1_REG_READOUT_CTRL CCI_REG8(0x052e) #define VD55G1_READOUT_CTRL_BIN_MODE_NORMAL 0 #define VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2 1 -#define VD55G1_REG_DUSTER_CTRL CCI_REG8(0x03ae) #define VD55G1_DUSTER_ENABLE BIT(0) #define VD55G1_DUSTER_DISABLE 0 #define VD55G1_DUSTER_DYN_ENABLE BIT(1) #define VD55G1_DUSTER_RING_ENABLE BIT(4) -#define VD55G1_REG_AE_TARGET_PERCENTAGE CCI_REG8(0x0486) -#define VD55G1_REG_NEXT_CTX CCI_REG16_LE(0x03e4) -#define VD55G1_REG_EXPOSURE_USE_CASES CCI_REG8(0x0312) #define VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT BIT(2) -#define VD55G1_REG_EXPOSURE_MAX_COARSE CCI_REG16_LE(0x0372) #define VD55G1_EXPOSURE_MAX_COARSE_DEF 0x7fff #define VD55G1_EXPOSURE_MAX_COARSE_SUB 446 -#define VD55G1_REG_CTX_REPEAT_COUNT_CTX0 CCI_REG16_LE(0x03dc) -#define VD55G1_REG_CTX_REPEAT_COUNT_CTX1 CCI_REG16_LE(0x03de) - -#define VD55G1_REG_EXP_MODE(ctx) \ - CCI_REG8(0x0500 + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_FRAME_LENGTH(ctx) \ - CCI_REG32_LE(0x050c + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_X_START(ctx) \ - CCI_REG16_LE(0x0514 + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_X_WIDTH(ctx) \ - CCI_REG16_LE(0x0516 + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_Y_START(ctx) \ - CCI_REG16_LE(0x0510 + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_Y_HEIGHT(ctx) \ - CCI_REG16_LE(0x0512 + VD55G1_CTX_OFFSET * (ctx)) -#define VD55G1_REG_GPIO_0_CTRL(ctx) \ - CCI_REG8(0x051d + VD55G1_CTX_OFFSET * (ctx)) + #define VD55G1_GPIO_MODE_FSYNC_OUT 0x00 #define VD55G1_GPIO_MODE_IN 0x01 #define VD55G1_GPIO_MODE_STROBE 0x02 -#define VD55G1_REG_VT_MODE(ctx) \ - CCI_REG8(0x0536 + VD55G1_CTX_OFFSET * (ctx)) #define VD55G1_VT_MODE_NORMAL 0 #define VD55G1_VT_MODE_SUBTRACTION 1 -#define VD55G1_REG_MASK_FRAME_CTRL(ctx) \ - CCI_REG8(0x0537 + VD55G1_CTX_OFFSET * (ctx)) #define VD55G1_MASK_FRAME_CTRL_OUTPUT 0 #define VD55G1_MASK_FRAME_CTRL_MASK 1 -#define VD55G1_REG_EXPOSURE_INSTANCE(ctx) \ - CCI_REG32_LE(0x52D + VD55G1_CTX_OFFSET * (ctx)) +#define VD55G0_WIDTH 644 +#define VD55G0_HEIGHT 604 #define VD55G1_WIDTH 804 #define VD55G1_HEIGHT 704 #define VD55G1_MODE_IDX_DEF 0 @@ -121,19 +68,21 @@ #define VD55G1_EXPO_DEF 500 #define VD55G1_LINE_LENGTH_MIN 1128 #define VD55G1_LINE_LENGTH_SUB_MIN 1344 +#define VD55G0_LINE_LENGTH_SAFE_MIN 1200 #define VD55G1_VBLANK_MIN 86 #define VD55G1_VBLANK_MAX 0xffff #define VD55G1_FRAME_LENGTH_DEF 1860 /* 60 fps */ #define VD55G1_MIPI_MARGIN 900 #define VD55G1_CTX_OFFSET 0x50 -#define VD55G1_FWPATCH_REVISION_MAJOR 2 -#define VD55G1_FWPATCH_REVISION_MINOR 9 #define VD55G1_XCLK_FREQ_MIN (6 * HZ_PER_MHZ) #define VD55G1_XCLK_FREQ_MAX (27 * HZ_PER_MHZ) +#define VD55G0_MIPI_RATE_MIN (750 * MEGA) +#define VD55G0_MIPI_RATE_MAX (1200 * MEGA) #define VD55G1_MIPI_RATE_MIN (250 * MEGA) #define VD55G1_MIPI_RATE_MAX (1200 * MEGA) enum vd55g1_model_id { + VD55G1_MODEL_ID_0 = 0x53354730, VD55G1_MODEL_ID_2 = 0x53354731, VD55G1_MODEL_ID_3 = 0x53354733, }; @@ -143,28 +92,168 @@ enum vd55g1_color_version { VD55G1_COLOR_VERSION_BAYER, }; -struct vd55g1_version { - char *name; - enum vd55g1_model_id id; - enum vd55g1_color_version color; +enum vd55g1_reg_id { + REG_MODEL_ID, + REG_COLOR_VERSION, + REG_REVISION, + REG_FWPATCH_START_ADDR, + REG_BOOT, + REG_STBY, + REG_STREAMING, + REG_EXT_CLOCK, + REG_LINE_LENGTH, + REG_ORIENTATION, + REG_FORMAT_CTRL, + REG_OIF_CTRL, + REG_OIF_IMG_CTRL, + REG_MIPI_DATA_RATE, + + /* Model-specific */ + REG_SYSTEM_FSM, + REG_ISL_ENABLE, + REG_FWPATCH_REVISION, + REG_PATGEN_CTRL, + REG_READOUT_CTRL, + REG_DUSTER_CTRL, + REG_AE_TARGET_PERCENTAGE, + REG_MANUAL_ANALOG_GAIN, + REG_MANUAL_COARSE_EXPOSURE, + REG_MANUAL_DIGITAL_GAIN_CH0, + REG_MANUAL_DIGITAL_GAIN_CH1, + REG_MANUAL_DIGITAL_GAIN_CH2, + REG_MANUAL_DIGITAL_GAIN_CH3, + REG_APPLIED_COARSE_EXPOSURE, + REG_APPLIED_ANALOG_GAIN, + REG_APPLIED_DIGITAL_GAIN, + REG_AE_FORCE_COLDSTART, + REG_AE_COLDSTART_EXPOSURE_US, + REG_AE_COLDSTART_COARSE_EXPOSURE, + /* HDR-specific */ + REG_NEXT_CTX, + REG_EXPOSURE_USE_CASES, + REG_EXPOSURE_MAX_COARSE, + REG_CTX_REPEAT_COUNT_CTX0, + REG_CTX_REPEAT_COUNT_CTX1, + + /* Context-specific register bases */ + REG_CTX_STRIDE, /* Not a real register, stores the context bank offset */ + REG_CTX_REPEAT_COUNT, + REG_CTX_EXP_MODE, + REG_CTX_FRAME_LENGTH, + REG_CTX_X_START, + REG_CTX_X_WIDTH, + REG_CTX_X_END, + REG_CTX_Y_START, + REG_CTX_Y_HEIGHT, + REG_CTX_Y_END, + REG_CTX_GPIO_0_CTRL, + /* HDR-specific */ + REG_CTX_VT_MODE, + REG_CTX_MASK_FRAME_CTRL, + REG_CTX_EXPOSURE_INSTANCE, + + REG_MAX_INDEX /* Keeps track of enum size */ }; -static const struct vd55g1_version vd55g1_versions[] = { - { - .name = "vd55g1", - .id = VD55G1_MODEL_ID_2, - .color = VD55G1_COLOR_VERSION_MONO, - }, - { - .name = "vd55g4", - .id = VD55G1_MODEL_ID_3, - .color = VD55G1_COLOR_VERSION_MONO, - }, - { - .name = "vd65g4", - .id = VD55G1_MODEL_ID_3, - .color = VD55G1_COLOR_VERSION_BAYER, - }, +static const int vd55g0_reg_map[REG_MAX_INDEX] = { + [REG_MODEL_ID] = CCI_REG32_LE(0x0000), + [REG_COLOR_VERSION] = CCI_REG32_LE(0x0670), + [REG_REVISION] = CCI_REG16_LE(0x0004), + [REG_FWPATCH_START_ADDR] = CCI_REG8(0x2000), + [REG_BOOT] = CCI_REG8(0x0200), + [REG_STBY] = CCI_REG8(0x0201), + [REG_STREAMING] = CCI_REG8(0x0202), + [REG_EXT_CLOCK] = CCI_REG32_LE(0x0220), + [REG_LINE_LENGTH] = CCI_REG16_LE(0x0300), + [REG_ORIENTATION] = CCI_REG8(0x0302), + [REG_FORMAT_CTRL] = CCI_REG8(0x030a), + [REG_OIF_CTRL] = CCI_REG16_LE(0x030c), + [REG_OIF_IMG_CTRL] = CCI_REG8(0x030f), + [REG_MIPI_DATA_RATE] = CCI_REG32_LE(0x0224), + [REG_SYSTEM_FSM] = CCI_REG8(0x002c), + [REG_ISL_ENABLE] = CCI_REG8(0x0329), + [REG_FWPATCH_REVISION] = CCI_REG16_LE(0x0022), + [REG_PATGEN_CTRL] = CCI_REG16_LE(0x0400), + [REG_READOUT_CTRL] = CCI_REG8(0x047a), + [REG_DUSTER_CTRL] = CCI_REG8(0x0316), + [REG_AE_TARGET_PERCENTAGE] = CCI_REG8(0x0440), + [REG_MANUAL_ANALOG_GAIN] = CCI_REG8(0x044d), + [REG_MANUAL_COARSE_EXPOSURE] = CCI_REG16_LE(0x044e), + [REG_MANUAL_DIGITAL_GAIN_CH0] = CCI_REG16_LE(0x0450), + [REG_MANUAL_DIGITAL_GAIN_CH1] = CCI_REG16_LE(0x0452), + [REG_MANUAL_DIGITAL_GAIN_CH2] = CCI_REG16_LE(0x0454), + [REG_MANUAL_DIGITAL_GAIN_CH3] = CCI_REG16_LE(0x0456), + [REG_APPLIED_COARSE_EXPOSURE] = CCI_REG16_LE(0x0064), + [REG_APPLIED_ANALOG_GAIN] = CCI_REG8(0x0066), + [REG_APPLIED_DIGITAL_GAIN] = CCI_REG16_LE(0x0068), + [REG_AE_FORCE_COLDSTART] = CCI_REG8(0x042c), + [REG_AE_COLDSTART_COARSE_EXPOSURE] = CCI_REG16_LE(0x042e), + [REG_NEXT_CTX] = CCI_REG16_LE(0x0478), + [REG_CTX_REPEAT_COUNT_CTX0] = CCI_REG16_LE(0x0476), + [REG_CTX_REPEAT_COUNT_CTX1] = CCI_REG16_LE(0x04a6), + + /* Context properties */ + [REG_CTX_STRIDE] = 0x30, + [REG_CTX_EXP_MODE] = CCI_REG8(0x044c), + [REG_CTX_FRAME_LENGTH] = CCI_REG16_LE(0x0458), + [REG_CTX_X_START] = CCI_REG16_LE(0x045e), + [REG_CTX_X_END] = CCI_REG16_LE(0x0460), + [REG_CTX_Y_START] = CCI_REG16_LE(0x0462), + [REG_CTX_Y_END] = CCI_REG16_LE(0x0464), + [REG_CTX_GPIO_0_CTRL] = CCI_REG8(0x0467), +}; + +static const int vd55g1_reg_map[REG_MAX_INDEX] = { + [REG_MODEL_ID] = CCI_REG32_LE(0x0000), + [REG_COLOR_VERSION] = CCI_REG32_LE(0x0670), + [REG_REVISION] = CCI_REG16_LE(0x0004), + [REG_FWPATCH_START_ADDR] = CCI_REG8(0x2000), + [REG_BOOT] = CCI_REG8(0x0200), + [REG_STBY] = CCI_REG8(0x0201), + [REG_STREAMING] = CCI_REG8(0x0202), + [REG_EXT_CLOCK] = CCI_REG32_LE(0x0220), + [REG_LINE_LENGTH] = CCI_REG16_LE(0x0300), + [REG_ORIENTATION] = CCI_REG8(0x0302), + [REG_FORMAT_CTRL] = CCI_REG8(0x030a), + [REG_OIF_CTRL] = CCI_REG16_LE(0x030c), + [REG_OIF_IMG_CTRL] = CCI_REG8(0x030f), + [REG_MIPI_DATA_RATE] = CCI_REG32_LE(0x0224), + [REG_SYSTEM_FSM] = CCI_REG8(0x001c), + [REG_ISL_ENABLE] = CCI_REG8(0x0326), + [REG_FWPATCH_REVISION] = CCI_REG16_LE(0x0012), + [REG_PATGEN_CTRL] = CCI_REG16_LE(0x0304), + [REG_READOUT_CTRL] = CCI_REG8(0x057e), + [REG_DUSTER_CTRL] = CCI_REG8(0x03ae), + [REG_AE_TARGET_PERCENTAGE] = CCI_REG8(0x0486), + [REG_MANUAL_ANALOG_GAIN] = CCI_REG8(0x0501), + [REG_MANUAL_COARSE_EXPOSURE] = CCI_REG16_LE(0x0502), + [REG_MANUAL_DIGITAL_GAIN_CH0] = CCI_REG16_LE(0x0504), + [REG_MANUAL_DIGITAL_GAIN_CH1] = CCI_REG16_LE(0x0506), + [REG_MANUAL_DIGITAL_GAIN_CH2] = CCI_REG16_LE(0x0508), + [REG_MANUAL_DIGITAL_GAIN_CH3] = CCI_REG16_LE(0x050a), + [REG_APPLIED_COARSE_EXPOSURE] = CCI_REG16_LE(0x00e8), + [REG_APPLIED_ANALOG_GAIN] = CCI_REG16_LE(0x00ea), + [REG_APPLIED_DIGITAL_GAIN] = CCI_REG16_LE(0x00ec), + [REG_AE_FORCE_COLDSTART] = CCI_REG8(0x0308), + [REG_AE_COLDSTART_EXPOSURE_US] = CCI_REG32_LE(0x0374), + [REG_NEXT_CTX] = CCI_REG16_LE(0x03e4), + [REG_EXPOSURE_USE_CASES] = CCI_REG8(0x0312), + [REG_EXPOSURE_MAX_COARSE] = CCI_REG16_LE(0x0372), + [REG_CTX_REPEAT_COUNT_CTX0] = CCI_REG16_LE(0x03dc), + [REG_CTX_REPEAT_COUNT_CTX1] = CCI_REG16_LE(0x03de), + + /* Context properties */ + [REG_CTX_STRIDE] = 0x50, + [REG_CTX_EXP_MODE] = CCI_REG8(0x0500), + [REG_CTX_FRAME_LENGTH] = CCI_REG32_LE(0x050c), + [REG_CTX_X_START] = CCI_REG16_LE(0x0514), + [REG_CTX_X_WIDTH] = CCI_REG16_LE(0x0516), + [REG_CTX_Y_START] = CCI_REG16_LE(0x0510), + [REG_CTX_Y_HEIGHT] = CCI_REG16_LE(0x0512), + [REG_CTX_GPIO_0_CTRL] = CCI_REG8(0x051d), + [REG_CTX_VT_MODE] = CCI_REG8(0x0536), + [REG_CTX_MASK_FRAME_CTRL] = CCI_REG8(0x0537), + [REG_CTX_EXPOSURE_INSTANCE] = CCI_REG32_LE(0x052d), }; static const u8 vd55g1_patch_array[] = { @@ -463,6 +552,561 @@ static const u8 vd55g1_patch_array[] = { 0x00, 0xb9, 0x00, 0x00, 0xb6, 0x85, 0x00, 0x00, }; +static const u8 vd55g0_cut1_patch_array[] = { +0x81, 0x06, 0x0b, 0x02, 0x00, 0x09, 0x42, 0x00, 0xd2, 0x08, 0x42, 0x00, 0xe6, +0x08, 0x42, 0x00, 0x12, 0x09, 0x42, 0x00, 0xe0, 0x3f, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x40, 0x01, 0x60, 0x10, 0x40, 0x11, +0x81, 0x16, 0x80, 0x93, 0xdd, 0x4c, 0x04, 0x04, 0xfa, 0x06, 0x4c, 0x94, 0x60, +0xe6, 0x0e, 0xdc, 0xe0, 0x04, 0x18, 0x70, 0x02, 0x08, 0xe7, 0xce, 0x44, 0x01, +0x60, 0x10, 0x40, 0x31, 0x85, 0x1e, 0x08, 0x4e, 0x43, 0x01, 0x60, 0x10, 0xc0, +0x31, 0x40, 0x20, 0xe0, 0x04, 0x98, 0x29, 0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24, +0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84, 0x2b, 0xa1, 0x15, 0xfe, 0xd8, 0x41, +0x00, 0xe8, 0xf8, 0x47, 0x00, 0xe8, 0x78, 0x44, 0x40, 0xef, 0x0a, 0x9c, 0x1a, +0x9e, 0x2a, 0xdc, 0x3a, 0xde, 0x03, 0x4e, 0x7c, 0xf9, 0x13, 0x4e, 0x78, 0xf9, +0x23, 0x4e, 0x74, 0xf9, 0x33, 0x4e, 0x70, 0xf9, 0x18, 0x50, 0x40, 0xef, 0x38, +0x49, 0x44, 0xef, 0x04, 0x18, 0x11, 0x83, 0x11, 0x40, 0x20, 0xe0, 0x4c, 0x0c, +0x04, 0xf2, 0x93, 0xdd, 0x86, 0x44, 0x88, 0xe0, 0x05, 0x04, 0x40, 0xf8, 0x09, +0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0x60, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80, +0x65, 0x86, 0x31, 0x01, 0x06, 0x02, 0x0c, 0xe1, 0x33, 0x40, 0xc8, 0xe2, 0x33, +0x40, 0x70, 0xe2, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0x55, 0x8a, 0x0a, 0x02, +0x08, 0x70, 0x16, 0x43, 0x58, 0xe0, 0x26, 0x08, 0x00, 0x40, 0x16, 0x0a, 0x00, +0xc0, 0x41, 0x44, 0x08, 0x40, 0x06, 0x46, 0x08, 0xe0, 0x51, 0x44, 0x04, 0xc0, +0x45, 0x85, 0x66, 0x02, 0x04, 0xe0, 0x13, 0x40, 0x70, 0x62, 0x06, 0x02, 0x0c, +0xe1, 0x14, 0x84, 0x18, 0x48, 0x1b, 0xa3, 0xb5, 0x86, 0x0a, 0x02, 0x08, 0x70, +0xa6, 0x02, 0x68, 0xe0, 0x26, 0x0a, 0x00, 0x40, 0x16, 0x18, 0x00, 0xc0, 0x31, +0x01, 0xc6, 0x09, 0x0c, 0xe0, 0x51, 0x44, 0x08, 0xc0, 0xc1, 0x44, 0x04, 0xc0, +0x95, 0x83, 0x0a, 0x02, 0x04, 0xf0, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x44, 0x08, +0xc0, 0x11, 0x44, 0x04, 0x40, 0x11, 0x81, 0x33, 0x40, 0x18, 0xe3, 0x15, 0x83, +0x0a, 0x02, 0x04, 0x70, 0x11, 0x81, 0x16, 0x0a, 0x00, 0x40, 0x31, 0x9d, 0x51, +0x44, 0x08, 0x40, 0x06, 0x09, 0x04, 0xe0, 0x51, 0x44, 0x04, 0xc0, 0x43, 0x40, +0x18, 0xe3, 0x33, 0x40, 0xc8, 0xe2, 0x13, 0x40, 0xc0, 0x62, 0xa6, 0x05, 0x78, +0xe0, 0x23, 0x40, 0xd0, 0xe3, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x7c, 0xfa, +0x86, 0x42, 0x84, 0x60, 0x06, 0x00, 0x0c, 0xe1, 0x14, 0x04, 0xf6, 0x50, 0x78, +0xe3, 0x04, 0x00, 0x26, 0x0f, 0x28, 0xe2, 0x16, 0x15, 0x2c, 0x64, 0xb1, 0xbb, +0x6a, 0x84, 0x0b, 0xa3, 0x36, 0x10, 0x10, 0x41, 0x2e, 0x54, 0x08, 0xd8, 0x16, +0x4f, 0x44, 0x43, 0x06, 0x16, 0x00, 0xc0, 0x0b, 0x10, 0x1b, 0x8c, 0xb8, 0x4f, +0x00, 0x6f, 0xa4, 0x12, 0x80, 0xfb, 0x06, 0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc, +0x73, 0x03, 0xc0, 0x83, 0x22, 0x0e, 0x02, 0xc1, 0x7f, 0x1f, 0xc0, 0x87, 0xd4, +0x8f, 0x02, 0x1b, 0x8c, 0x8f, 0x20, 0x0b, 0x8e, 0x98, 0x4e, 0x00, 0xef, 0x06, +0x42, 0x1a, 0x60, 0x00, 0x40, 0xfc, 0xf0, 0x73, 0xa2, 0x77, 0xcc, 0x70, 0x42, +0x01, 0x73, 0xff, 0xc7, 0x12, 0x4e, 0x80, 0x79, 0x0b, 0x92, 0xb8, 0x4d, 0x00, +0x6f, 0x1b, 0x8c, 0x06, 0x42, 0x94, 0x60, 0x06, 0x46, 0x1a, 0xe0, 0x24, 0x04, +0x00, 0x40, 0xfd, 0x73, 0x03, 0xc0, 0xa3, 0xa6, 0xa7, 0xd4, 0xb3, 0x44, 0x60, +0xe3, 0x85, 0x44, 0x9c, 0xe0, 0x24, 0x84, 0x75, 0x44, 0xa0, 0x60, 0x0e, 0x04, +0xc0, 0x7f, 0x1f, 0xc0, 0x14, 0x04, 0xaf, 0x84, 0xaf, 0xa0, 0xa5, 0x42, 0xa4, +0xe0, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x0c, 0x04, 0x0c, 0xfa, 0x46, 0x0d, +0xcc, 0xe0, 0x09, 0x0c, 0xbc, 0xee, 0x0a, 0x40, 0x80, 0xe0, 0x45, 0x82, 0xa6, +0x40, 0x08, 0xe0, 0x06, 0x98, 0x08, 0x7f, 0x74, 0xef, 0x0b, 0xa1, 0xb5, 0xfe, +0x01, 0x87, 0x06, 0x98, 0x06, 0x4c, 0x94, 0x60, 0x06, 0x42, 0x0c, 0xe0, 0x05, +0x0c, 0x14, 0xe0, 0x11, 0x40, 0x30, 0x62, 0x26, 0x42, 0x00, 0xe0, 0x05, 0x0c, +0x14, 0xe0, 0x11, 0x40, 0xa0, 0x62, 0x56, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14, +0xe0, 0x11, 0x40, 0x40, 0x62, 0x76, 0x43, 0x04, 0xe0, 0x05, 0x0c, 0x14, 0xe0, +0x11, 0x40, 0x50, 0xe2, 0x04, 0x98, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x42, 0x88, +0xe0, 0xc5, 0x80, 0x28, 0x67, 0x54, 0x6f, 0x66, 0x00, 0xa0, 0xe0, 0x04, 0x98, +0x19, 0x00, 0x30, 0xe0, 0x19, 0xc4, 0x11, 0x40, 0x30, 0x60, 0x11, 0x83, 0x04, +0x9a, 0x11, 0x40, 0x10, 0xe0, 0x04, 0x98, 0x11, 0x40, 0x20, 0x60, 0x11, 0x81, +0x04, 0x98, 0x11, 0x40, 0xb0, 0xe7, 0x19, 0x00, 0x50, 0xeb, 0x4a, 0x02, 0x0c, +0xfc, 0x85, 0xfe, 0xc6, 0x4c, 0xb4, 0xe0, 0x60, 0x10, 0xb1, 0x64, 0x01, 0xc0, +0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8c, 0x04, 0xe0, 0x0b, 0xa1, 0x25, 0x8c, 0xd8, +0x43, 0x5c, 0xef, 0x0a, 0xe0, 0x0b, 0xa1, 0x95, 0x8a, 0x04, 0xe0, 0x0b, 0xa1, +0x25, 0x8a, 0xc6, 0x4e, 0x84, 0xe0, 0x04, 0x9c, 0x09, 0x00, 0x20, 0xe1, 0x4a, +0x40, 0x80, 0xe0, 0x25, 0x86, 0x08, 0x22, 0x46, 0x43, 0x00, 0xe0, 0x06, 0x40, +0x06, 0xe0, 0x63, 0x80, 0x09, 0x0c, 0x40, 0xe2, 0x09, 0x86, 0x0b, 0xa5, 0xd5, +0x82, 0x68, 0x4e, 0x48, 0x6f, 0x01, 0x85, 0x78, 0x44, 0x48, 0x6f, 0x06, 0x00, +0x80, 0xe1, 0xf8, 0x75, 0x4c, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x78, 0x48, 0x48, +0xef, 0x09, 0x0e, 0x48, 0xe0, 0x0b, 0xa1, 0xb5, 0x80, 0x58, 0x7b, 0x3c, 0xef, +0x78, 0x74, 0x40, 0xef, 0xa6, 0x40, 0x08, 0xe0, 0x0c, 0x0c, 0x0c, 0xf2, 0x18, +0x12, 0x50, 0xff, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x7b, 0x00, 0x06, 0x01, +0x08, 0xe1, 0x08, 0x00, 0x6b, 0x82, 0x0b, 0xa1, 0x15, 0x82, 0xa6, 0x41, 0x84, +0xe0, 0x18, 0x00, 0x01, 0x89, 0x18, 0x42, 0x00, 0xef, 0x63, 0x80, 0x04, 0x1c, +0x64, 0x02, 0x84, 0xe0, 0x78, 0x40, 0x00, 0x68, 0x00, 0x00, 0x40, 0xe6, 0x4c, +0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x1e, 0x80, 0x93, 0xdd, 0x0c, 0x04, 0x3c, 0xfa, +0xf0, 0x1e, 0x40, 0x6c, 0x86, 0x0f, 0xe0, 0xe0, 0x01, 0x05, 0x91, 0x81, 0x06, +0x02, 0x00, 0x62, 0x81, 0xb1, 0x01, 0x4e, 0xd8, 0x69, 0x0e, 0x00, 0x00, 0x60, +0x01, 0xc0, 0x0e, 0x7c, 0x6b, 0x9e, 0x93, 0x4e, 0x14, 0x65, 0x86, 0x00, 0x30, +0xe1, 0x93, 0x4e, 0x18, 0x65, 0x2b, 0x8c, 0x91, 0x4e, 0x50, 0xe1, 0x81, 0x4e, +0xc8, 0xe9, 0x1e, 0xbe, 0x1e, 0x7e, 0x11, 0x8f, 0x9e, 0xbc, 0xd8, 0x55, 0x30, +0xef, 0x06, 0x00, 0x00, 0x64, 0x46, 0x02, 0x54, 0xe1, 0x0e, 0x3e, 0x06, 0x00, +0x20, 0xe0, 0x0e, 0x7e, 0x86, 0x00, 0x30, 0xe1, 0x1e, 0x7c, 0x11, 0x91, 0x9e, +0x3c, 0x2b, 0x8c, 0xd8, 0x54, 0x30, 0xef, 0x06, 0x00, 0x00, 0x66, 0xa1, 0x83, +0x0e, 0x3e, 0xf6, 0x41, 0xfc, 0xe9, 0x0e, 0x7e, 0x06, 0x00, 0x01, 0x60, 0x20, +0xc0, 0x0e, 0x7c, 0x86, 0x00, 0x30, 0xe1, 0xae, 0x3c, 0x11, 0x93, 0xc8, 0x53, +0x30, 0x6f, 0x2b, 0x8c, 0x06, 0x40, 0x88, 0x60, 0xf6, 0x45, 0xfc, 0x63, 0xff, +0xc3, 0x14, 0x00, 0xf1, 0xa0, 0x95, 0x4e, 0xa8, 0xe2, 0xa1, 0x4e, 0x44, 0xea, +0x81, 0x4e, 0xf0, 0x69, 0x10, 0x42, 0x00, 0x7c, 0x00, 0xcc, 0x1f, 0x04, 0x21, +0x8b, 0x1e, 0x00, 0x86, 0x40, 0xfc, 0xe7, 0x05, 0x4e, 0xa4, 0x62, 0xc6, 0x01, +0x5c, 0xe0, 0x05, 0x4e, 0xac, 0x62, 0x9e, 0x00, 0x00, 0x60, 0x02, 0xc0, 0x05, +0x4e, 0x94, 0x62, 0x86, 0x40, 0x3c, 0x60, 0x10, 0xe7, 0x0e, 0x9c, 0x09, 0x0e, +0x0c, 0xe1, 0x15, 0x4e, 0x3c, 0x79, 0x06, 0x02, 0x80, 0xe0, 0x13, 0x4e, 0xf4, +0x64, 0x86, 0x02, 0x00, 0x60, 0x02, 0xc0, 0x21, 0x4e, 0x5c, 0x69, 0x00, 0x40, +0xc0, 0xf3, 0x1e, 0x9e, 0x01, 0x4e, 0x0c, 0xe1, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, +0xdd, 0xc6, 0x01, 0xcc, 0x60, 0x06, 0x42, 0x88, 0xe0, 0x29, 0x00, 0x98, 0xee, +0x34, 0x84, 0x4a, 0x44, 0x80, 0x60, 0x86, 0x04, 0x30, 0xe1, 0x24, 0x08, 0x30, +0x46, 0xfd, 0x73, 0xff, 0xc3, 0x3e, 0x04, 0xa6, 0x08, 0xa0, 0x80, 0x34, 0x06, +0xc8, 0x9c, 0x45, 0x44, 0xa0, 0x80, 0x35, 0x42, 0x00, 0x00, 0x06, 0x06, 0x00, +0xc0, 0x35, 0x44, 0xa0, 0xc0, 0x35, 0x02, 0x00, 0xc0, 0x3e, 0x80, 0x93, 0xdd, +0x4c, 0x00, 0x00, 0xfa, 0x88, 0x40, 0x00, 0xe8, 0x86, 0x02, 0x0c, 0xe1, 0x0e, +0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, +0x00, 0xf8, 0xc6, 0x40, 0xb4, 0xe0, 0x00, 0x02, 0xb1, 0x64, 0x01, 0xc0, 0x2a, +0xc4, 0x2b, 0xa1, 0x35, 0x92, 0x24, 0xc4, 0x2b, 0xa1, 0xc5, 0x90, 0x38, 0x06, +0x46, 0x45, 0x00, 0xe0, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0x49, 0x06, 0xf0, +0xe2, 0x4d, 0xe6, 0xc6, 0x40, 0x48, 0x41, 0x16, 0x02, 0x00, 0xc0, 0x11, 0x40, +0x00, 0xc0, 0xd5, 0x8c, 0xc9, 0x06, 0x44, 0x62, 0x06, 0x4b, 0x00, 0xe0, 0x46, +0x48, 0x16, 0x60, 0x86, 0x4a, 0xc8, 0xe0, 0x53, 0x08, 0xc0, 0x58, 0x80, 0xf3, +0x49, 0x0a, 0xa4, 0xe7, 0x6b, 0x0a, 0xd8, 0xe3, 0x49, 0xbe, 0xcf, 0xa8, 0xc1, +0x46, 0x44, 0xe2, 0x48, 0x86, 0xc9, 0x0a, 0xa0, 0xe7, 0x46, 0x48, 0x0a, 0xe0, +0xc1, 0x46, 0x40, 0xe2, 0xcb, 0x0a, 0xe0, 0x63, 0x43, 0x80, 0x63, 0x48, 0x28, +0xe1, 0x38, 0x86, 0x36, 0x46, 0x0a, 0xe0, 0x33, 0x80, 0xc3, 0x46, 0x30, 0xe1, +0x18, 0x86, 0xcb, 0x0a, 0xd4, 0xe3, 0x2b, 0x0a, 0xdc, 0x63, 0x16, 0x42, 0x0a, +0xe0, 0x5b, 0x0a, 0xe4, 0xe3, 0xc3, 0x48, 0x24, 0x61, 0x03, 0x82, 0x23, 0x46, +0x2c, 0xe1, 0x53, 0x40, 0x34, 0xe1, 0x4c, 0x0c, 0x00, 0xf0, 0x93, 0xdd, 0xc3, +0xc1, 0x0c, 0x00, 0x80, 0xfa, 0x86, 0x43, 0xcc, 0x60, 0x0b, 0xa3, 0x29, 0x02, +0xc4, 0xee, 0xb5, 0x84, 0x4a, 0x04, 0xfc, 0xfb, 0x45, 0x84, 0xc6, 0x40, 0x84, +0xe0, 0x14, 0x82, 0x18, 0x84, 0x19, 0x86, 0x1b, 0xa5, 0x85, 0x8c, 0x04, 0x80, +0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0xc5, 0x8a, 0x0c, 0x08, 0x80, +0xf2, 0x08, 0x34, 0x44, 0xff, 0x0b, 0xa5, 0x15, 0x8a, 0x0a, 0x04, 0x04, 0xf0, +0xb5, 0x88, 0xc6, 0x40, 0x84, 0xe0, 0x24, 0x82, 0x28, 0x88, 0x29, 0x86, 0x2b, +0xa5, 0xc5, 0x86, 0x04, 0x80, 0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, +0x05, 0x86, 0x09, 0x02, 0x24, 0xee, 0x0b, 0xa3, 0xd5, 0x80, 0x08, 0x84, 0x0a, +0x40, 0x80, 0xe0, 0x75, 0xf8, 0xf5, 0x81, 0x38, 0x72, 0x44, 0xef, 0xf8, 0x51, +0xfc, 0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0xa5, +0x80, 0x0c, 0x08, 0x80, 0xf2, 0xb8, 0x11, 0xfc, 0xff, 0x0c, 0x08, 0x80, 0xf2, +0x93, 0xdd, 0xcc, 0x04, 0x0c, 0xfa, 0x06, 0x4f, 0x84, 0x60, 0x86, 0x50, 0x4c, +0xe1, 0x04, 0x1c, 0x80, 0x0c, 0x21, 0x6f, 0x01, 0xc0, 0xf0, 0x04, 0x50, 0x64, +0x11, 0x81, 0x09, 0x00, 0x6c, 0xe0, 0x06, 0x29, 0x0b, 0x8c, 0x58, 0x4c, 0x58, +0xef, 0x04, 0x9c, 0x19, 0x00, 0x6c, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3, +0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f, +0xa2, 0x21, 0x40, 0x6c, 0xe0, 0x86, 0x43, 0xcc, 0xe0, 0x29, 0x02, 0x24, 0xee, +0x2b, 0xa3, 0xd5, 0x80, 0x18, 0x84, 0x4a, 0x42, 0x80, 0x60, 0x11, 0x8b, 0x65, +0x80, 0x19, 0x00, 0x70, 0xe0, 0x16, 0x29, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x03, +0x0b, 0x8c, 0x28, 0x4a, 0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x70, 0xe0, 0x10, +0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, +0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40, 0x70, 0xe0, 0x09, 0x00, 0x74, +0x60, 0xf0, 0x04, 0x50, 0xe4, 0x11, 0x85, 0x06, 0x29, 0x0b, 0x8c, 0xa8, 0x48, +0x58, 0xef, 0x04, 0x9c, 0x19, 0x00, 0x74, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, +0xa3, 0x35, 0x82, 0x28, 0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, +0x2f, 0xa2, 0x21, 0x40, 0x74, 0xe0, 0x09, 0x00, 0x78, 0x60, 0xf0, 0x04, 0x50, +0xe4, 0x11, 0x87, 0x06, 0x29, 0x0b, 0x8c, 0x28, 0x47, 0x58, 0xef, 0x04, 0x9c, +0x19, 0x00, 0x78, 0xe0, 0x10, 0x44, 0x3c, 0xf0, 0x2b, 0xa3, 0x35, 0x82, 0x28, +0x2b, 0x10, 0x42, 0xbc, 0xf3, 0x27, 0xc8, 0x29, 0xa0, 0x2f, 0xa2, 0x21, 0x40, +0x78, 0xe0, 0x19, 0x0e, 0xd0, 0xe0, 0x1b, 0xa1, 0x85, 0x86, 0x05, 0x00, 0x24, +0xe0, 0x1a, 0xa2, 0x04, 0x04, 0xc0, 0xe1, 0x2b, 0x83, 0xd5, 0x80, 0x19, 0x10, +0x10, 0x60, 0x09, 0x9e, 0x0b, 0x83, 0x85, 0x84, 0x08, 0xa0, 0x0b, 0xa1, 0x38, +0x61, 0x74, 0xcf, 0x58, 0x49, 0x34, 0xef, 0x14, 0x9c, 0x25, 0x02, 0x24, 0xe0, +0x24, 0x02, 0xc0, 0x61, 0x29, 0x9e, 0x18, 0x58, 0x74, 0xef, 0x55, 0x81, 0x08, +0xa0, 0x0b, 0xa1, 0x48, 0x60, 0x74, 0xcf, 0x28, 0x72, 0x4c, 0x6f, 0x46, 0x01, +0x08, 0xe0, 0xcc, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x65, 0x5e, 0x04, +0xfa, 0xf0, 0x0c, 0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc, +0xfc, 0x07, 0x80, 0xfe, 0xf0, 0x1e, 0x20, 0x6c, 0xe6, 0x40, 0x00, 0xe0, 0xc8, +0x70, 0x4c, 0xef, 0x0b, 0xa1, 0xb5, 0x88, 0xc6, 0x0d, 0xdc, 0xe0, 0x09, 0x0c, +0x94, 0xea, 0x0b, 0xa3, 0xf5, 0x80, 0x09, 0x0c, 0x91, 0x79, 0x01, 0xc0, 0x0a, +0x40, 0x80, 0xe0, 0xe5, 0x82, 0xc8, 0x71, 0x44, 0xef, 0x04, 0x18, 0x86, 0x02, +0x84, 0xe0, 0x24, 0x04, 0x31, 0x83, 0x01, 0x82, 0x0e, 0x18, 0xe6, 0x00, 0x48, +0xe1, 0x36, 0x00, 0x21, 0x82, 0x2e, 0x84, 0x46, 0x00, 0x2c, 0x61, 0x21, 0x83, +0x14, 0x00, 0x46, 0x40, 0x34, 0xe1, 0x25, 0x42, 0x44, 0xe0, 0x28, 0x51, 0x28, +0xef, 0x88, 0x40, 0x00, 0xe8, 0xf1, 0x90, 0xfc, 0x0f, 0x80, 0xf6, 0xf4, 0xbc, +0x64, 0xfd, 0x53, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x01, 0x03, 0x11, 0x81, 0x48, +0x79, 0x08, 0xef, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00, +0x00, 0xfa, 0x0d, 0x62, 0x8e, 0x45, 0xf0, 0xff, 0x75, 0x82, 0x4e, 0x05, 0x01, +0x60, 0x10, 0x40, 0x07, 0xc8, 0x24, 0x88, 0x23, 0x80, 0x1e, 0x8a, 0x78, 0x40, +0x00, 0xe8, 0x21, 0x81, 0x0b, 0x84, 0x4c, 0x08, 0x00, 0xf2, 0x93, 0xdd, 0x83, +0xc1, 0x93, 0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x41, 0xb9, 0x06, 0x48, 0x12, 0xe0, +0x33, 0x88, 0x45, 0x06, 0x14, 0xe0, 0x4b, 0xa3, 0xc5, 0x80, 0x35, 0x06, 0x18, +0xe0, 0x3b, 0x23, 0x31, 0x81, 0x55, 0x80, 0x31, 0x83, 0x0d, 0xe6, 0x04, 0x00, +0x88, 0x00, 0x4e, 0x08, 0x00, 0x00, 0x10, 0x80, 0x05, 0x00, 0x10, 0x9c, 0x08, +0x40, 0x00, 0x98, 0x25, 0x0b, 0x8e, 0x40, 0xf0, 0xff, 0x3b, 0xa1, 0x45, 0x8a, +0x2b, 0x23, 0x06, 0x4c, 0x00, 0xe0, 0x45, 0x90, 0x2b, 0x25, 0x6e, 0x40, 0xf0, +0xff, 0x25, 0x87, 0x3b, 0xa1, 0x25, 0x8a, 0x2b, 0x23, 0x06, 0x0c, 0x08, 0xe0, +0x05, 0x8e, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x0c, 0x06, 0x0c, 0x04, +0xe0, 0x95, 0x87, 0x3b, 0xa1, 0xc5, 0x88, 0x2b, 0x23, 0x61, 0x91, 0x85, 0x8a, +0x2b, 0x25, 0x7e, 0x40, 0xf0, 0xff, 0x25, 0x83, 0x2b, 0xa3, 0xc5, 0x08, 0x06, +0x0c, 0x08, 0x80, 0x2b, 0x25, 0x4e, 0x40, 0xf0, 0xff, 0x15, 0x8a, 0xf5, 0x03, +0x06, 0x0c, 0x04, 0xe0, 0x2b, 0x23, 0x61, 0x91, 0x45, 0x86, 0x2b, 0x25, 0x6e, +0x40, 0xf0, 0xff, 0xc5, 0x04, 0x61, 0x89, 0xc5, 0x83, 0x2b, 0x23, 0x06, 0x4c, +0x00, 0xe0, 0x05, 0x84, 0x2b, 0x25, 0x5e, 0x40, 0xf0, 0xff, 0x85, 0x02, 0x61, +0xa1, 0x25, 0x83, 0x2b, 0x23, 0x61, 0x85, 0xe5, 0x80, 0x2b, 0x25, 0x7e, 0x40, +0xf0, 0xff, 0x65, 0x00, 0x61, 0x83, 0x95, 0x81, 0x0b, 0x02, 0x1b, 0x8c, 0x48, +0x78, 0xfc, 0xef, 0x0b, 0xa1, 0x00, 0x4c, 0x00, 0xdc, 0x0b, 0x8c, 0x0c, 0x0c, +0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x04, 0xfa, 0x6b, 0x00, 0xc6, +0x40, 0xdc, 0xe0, 0x04, 0x00, 0xf0, 0x1e, 0x30, 0xec, 0x48, 0x55, 0xfc, 0x6e, +0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0xb8, 0x68, 0xfc, 0xee, 0x58, 0x71, 0x08, +0xef, 0x78, 0x68, 0xfc, 0x6e, 0x0b, 0x8c, 0x71, 0x01, 0x5b, 0x80, 0x7e, 0x3c, +0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x11, 0x03, 0x21, 0x81, 0x31, 0x05, 0x41, +0x81, 0xa8, 0x77, 0x08, 0xef, 0x0e, 0x3e, 0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, +0x48, 0x4b, 0x0c, 0xef, 0x0e, 0x7c, 0x11, 0x81, 0x04, 0x3e, 0x21, 0x83, 0x68, +0x76, 0xfc, 0x6f, 0x0e, 0x06, 0x04, 0x60, 0x10, 0xc0, 0xce, 0x0d, 0x01, 0x60, +0x10, 0xc0, 0x76, 0x98, 0x04, 0xfc, 0xc8, 0x59, 0x0c, 0xef, 0x13, 0xc3, 0x08, +0x98, 0x0b, 0xa1, 0xa5, 0xfe, 0x04, 0xfc, 0x18, 0x5c, 0x0c, 0xef, 0x14, 0x3e, +0x0e, 0x00, 0x05, 0x60, 0x10, 0xc0, 0x38, 0x46, 0x0c, 0xef, 0x01, 0x01, 0xf1, +0x98, 0x0c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x65, 0x5e, 0x04, 0xfa, 0xf0, 0x0c, +0x10, 0xec, 0x60, 0x5e, 0x1c, 0x74, 0x61, 0x88, 0x6e, 0xbc, 0xfc, 0x03, 0x80, +0xfe, 0x26, 0x00, 0x4c, 0x61, 0x11, 0x83, 0x16, 0x00, 0xce, 0x01, 0x01, 0x60, +0x10, 0xc0, 0x16, 0x00, 0x46, 0x00, 0x2c, 0xe1, 0x04, 0x00, 0xf0, 0x1e, 0x30, +0xec, 0x14, 0xc0, 0x14, 0x02, 0x20, 0xfc, 0x1e, 0xc0, 0x98, 0x40, 0x00, 0xe8, +0xf1, 0x98, 0xfc, 0x0b, 0x80, 0xf6, 0xf4, 0xbc, 0x64, 0xfd, 0x53, 0xc1, 0xc3, +0xc1, 0x06, 0x00, 0x01, 0x60, 0x00, 0xe0, 0x06, 0x00, 0x62, 0xf4, 0x93, 0xdd, +0x4c, 0x04, 0x7c, 0xfa, 0xc6, 0x54, 0x84, 0x60, 0x06, 0x0f, 0xdc, 0xe0, 0x04, +0x2a, 0xae, 0x4d, 0x01, 0x60, 0x10, 0xc0, 0x29, 0x14, 0x70, 0x60, 0x8e, 0x51, +0x00, 0x60, 0x10, 0xc0, 0x3b, 0x0e, 0x9c, 0x65, 0x06, 0x52, 0x94, 0xe0, 0x1b, +0x00, 0x20, 0x60, 0x0e, 0x57, 0x08, 0x60, 0x10, 0xc0, 0x0b, 0x00, 0x1c, 0xe0, +0x13, 0x20, 0x01, 0x81, 0x13, 0x84, 0x13, 0x86, 0x11, 0x9e, 0x1c, 0x98, 0x0c, +0xa0, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0xac, 0x0a, 0x98, 0x1a, 0xac, +0x0d, 0xc3, 0xf5, 0x82, 0x04, 0xa4, 0x0b, 0x00, 0xa0, 0xe5, 0x0c, 0x2c, 0x01, +0x8b, 0x08, 0x56, 0x2c, 0xef, 0x0a, 0xa0, 0x01, 0x82, 0x0c, 0xa0, 0x0a, 0xa0, +0x8a, 0x00, 0x24, 0xe7, 0xf5, 0xfa, 0x05, 0x0e, 0xbc, 0x61, 0x06, 0x04, 0x00, +0x60, 0x00, 0xf0, 0x13, 0x0e, 0xb8, 0x79, 0x06, 0x06, 0x00, 0x60, 0x00, 0xe0, +0x2f, 0x80, 0x2b, 0x07, 0x0e, 0x45, 0x01, 0x60, 0x10, 0xc0, 0x1c, 0x88, 0x95, +0x86, 0x07, 0xe1, 0x09, 0xbe, 0x0b, 0xa1, 0x05, 0x86, 0x21, 0xff, 0x1d, 0x84, +0x85, 0x02, 0x00, 0x00, 0x08, 0x8c, 0x10, 0x04, 0x18, 0xec, 0x8a, 0x04, 0x14, +0xed, 0xb5, 0x80, 0x8a, 0x02, 0x40, 0xe1, 0x75, 0x82, 0x01, 0x84, 0x14, 0x24, +0x09, 0xbe, 0x29, 0x02, 0x00, 0xe3, 0x20, 0x44, 0x80, 0xf3, 0x2f, 0xa0, 0x21, +0x42, 0x00, 0xe3, 0x8e, 0x41, 0x09, 0x60, 0x10, 0x40, 0xc6, 0x0d, 0x30, 0xe1, +0x04, 0x80, 0x0b, 0xa3, 0x75, 0xa0, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc, +0xfb, 0xc5, 0x9e, 0xa8, 0x54, 0x00, 0x68, 0x01, 0x81, 0x04, 0x24, 0x11, 0x85, +0x11, 0x40, 0x20, 0x60, 0x4e, 0x43, 0x00, 0x60, 0x10, 0xc0, 0x04, 0xa4, 0x29, +0x00, 0x50, 0xeb, 0x2e, 0x84, 0x24, 0x84, 0x29, 0x86, 0x2e, 0x84, 0x24, 0x84, +0x2b, 0xa1, 0x15, 0xfe, 0xa8, 0x52, 0x44, 0xef, 0x04, 0xaa, 0x08, 0x80, 0x0b, +0xa5, 0xe5, 0x80, 0x04, 0xa8, 0x09, 0x00, 0x20, 0xe1, 0x0b, 0xa1, 0x48, 0x4a, +0x44, 0x8f, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0x2c, +0xfc, 0x1e, 0xc0, 0x14, 0xc0, 0x14, 0x02, 0x08, 0xfc, 0x1e, 0x40, 0x11, 0x83, +0x04, 0xa4, 0x11, 0x40, 0xb0, 0x67, 0x11, 0x81, 0x04, 0xa6, 0x11, 0x40, 0x10, +0xe0, 0xb8, 0x73, 0x74, 0xef, 0x84, 0x20, 0x70, 0x06, 0x00, 0xe7, 0x8e, 0x17, +0x01, 0x60, 0x10, 0x40, 0xce, 0x54, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xe0, 0x04, +0x00, 0x04, 0xfc, 0x0e, 0xe0, 0x04, 0xe0, 0x04, 0x00, 0x0c, 0xfc, 0x0e, 0xe0, +0x04, 0xe0, 0x04, 0x00, 0x10, 0xfc, 0x0e, 0xe0, 0x45, 0x10, 0x2c, 0xe0, 0x44, +0x00, 0x00, 0xfc, 0x05, 0x50, 0x2c, 0x60, 0x4e, 0x41, 0x08, 0x60, 0x10, 0xc0, +0x1b, 0x0e, 0xa0, 0xe1, 0x04, 0x80, 0x2b, 0x0e, 0x9c, 0xe1, 0x1b, 0xc2, 0x03, +0x02, 0xce, 0x42, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x04, 0x03, 0x84, 0x00, 0x00, +0xf4, 0xec, 0x0e, 0xac, 0x09, 0x0e, 0x00, 0xe3, 0x4e, 0x8f, 0x0b, 0xa5, 0x0b, +0x0e, 0x10, 0xd8, 0x95, 0x96, 0x0b, 0x0e, 0x84, 0xe1, 0x15, 0x0e, 0x10, 0xe0, +0x03, 0x4e, 0x10, 0xf8, 0x1d, 0xc1, 0x13, 0x4e, 0x10, 0x58, 0x10, 0x40, 0x00, +0xdc, 0x1a, 0x5e, 0x04, 0x04, 0x80, 0xfb, 0x2d, 0xc3, 0x13, 0x4e, 0x10, 0x58, +0x10, 0x40, 0x00, 0xdc, 0x85, 0x89, 0x0b, 0xa1, 0x98, 0x0c, 0x00, 0xc0, 0x68, +0x7b, 0x68, 0x6f, 0x01, 0x85, 0x60, 0x00, 0x10, 0x64, 0x11, 0x81, 0xc8, 0x71, +0x54, 0xef, 0x28, 0x45, 0x70, 0xef, 0x09, 0x0e, 0xe8, 0xea, 0x4a, 0x40, 0x90, +0xe0, 0x06, 0x00, 0x00, 0xc0, 0xa8, 0x4b, 0x00, 0xc8, 0x09, 0x0e, 0x5c, 0xeb, +0x0a, 0x40, 0x80, 0xe0, 0x25, 0xac, 0x19, 0x0c, 0xcc, 0xe5, 0x4a, 0x02, 0xfc, +0xfb, 0x85, 0xaa, 0x0a, 0x40, 0x90, 0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1, +0x04, 0x80, 0x14, 0xc0, 0x14, 0x02, 0x2c, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c, +0xeb, 0x0a, 0x40, 0x84, 0xe0, 0x25, 0x82, 0xb8, 0x68, 0x30, 0x6f, 0x01, 0x81, +0xc8, 0x58, 0x2c, 0x6f, 0x0b, 0x8c, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x94, +0xe0, 0x45, 0x82, 0x46, 0x00, 0x74, 0xe1, 0x04, 0x80, 0x14, 0xc0, 0x14, 0x02, +0x08, 0xfc, 0x1e, 0xc0, 0x09, 0x0e, 0x5c, 0xeb, 0x0a, 0x40, 0x98, 0xe0, 0x18, +0x49, 0x00, 0xc8, 0x45, 0x91, 0x14, 0x2c, 0x04, 0x00, 0x80, 0xfb, 0x13, 0x20, +0x06, 0x41, 0x2c, 0xe0, 0x16, 0x40, 0x02, 0x60, 0x46, 0x42, 0x04, 0xe0, 0xc8, +0x7a, 0xf8, 0xee, 0x0e, 0x2c, 0x0e, 0x4c, 0x01, 0x60, 0x10, 0xc0, 0x04, 0xac, +0x00, 0x00, 0xd1, 0x6f, 0x0f, 0xc0, 0x0e, 0xac, 0x05, 0x10, 0x40, 0xe0, 0x04, +0x00, 0x38, 0xfc, 0x0e, 0x98, 0x05, 0x50, 0x40, 0xe0, 0x04, 0xac, 0xd8, 0x63, +0xfc, 0xef, 0x46, 0x10, 0x74, 0xe1, 0x04, 0xa0, 0x15, 0x00, 0x40, 0xe0, 0x14, +0x02, 0xb8, 0xfc, 0x1e, 0x98, 0x15, 0x40, 0x40, 0xe0, 0x98, 0x52, 0x30, 0xef, +0x0b, 0xa1, 0xa5, 0xfe, 0x34, 0x28, 0xc6, 0x0d, 0x30, 0xe1, 0x08, 0x8c, 0x0b, +0xa5, 0x0b, 0x0e, 0x04, 0xd8, 0x1b, 0x0e, 0x00, 0xc0, 0x2b, 0x0e, 0x04, 0xc0, +0x3b, 0x0e, 0x08, 0xc0, 0x75, 0x84, 0x08, 0x8e, 0x19, 0x0e, 0x10, 0xeb, 0x12, +0x40, 0x80, 0xfb, 0x01, 0x4e, 0x10, 0xf8, 0x0a, 0xcc, 0x03, 0x4e, 0x04, 0xf8, +0x1a, 0xce, 0x1c, 0x9c, 0x2b, 0x06, 0x10, 0xe0, 0x2c, 0x9e, 0x3b, 0x06, 0x14, +0xe0, 0x3c, 0xdc, 0xa4, 0x8f, 0x03, 0x4e, 0x80, 0xf9, 0x13, 0x4e, 0x7c, 0xf9, +0x23, 0x4e, 0x78, 0xf9, 0x33, 0x4e, 0x74, 0xf9, 0xc8, 0x6d, 0x38, 0xef, 0xe8, +0x66, 0x3c, 0xef, 0x04, 0x20, 0xa9, 0xc2, 0xa5, 0x40, 0x2c, 0xe0, 0x19, 0x0c, +0x40, 0xe6, 0x1b, 0xa1, 0x55, 0x88, 0x14, 0xc0, 0x19, 0xd0, 0x1e, 0xc0, 0x14, +0xc0, 0x19, 0xe0, 0x1e, 0xc0, 0x14, 0xc0, 0x19, 0xc4, 0x1e, 0x40, 0x01, 0x83, +0xa8, 0x41, 0x00, 0xe8, 0x04, 0xa0, 0x14, 0xc0, 0x14, 0x02, 0xac, 0xfc, 0x1e, +0xc0, 0x14, 0xc0, 0x19, 0xc8, 0x1e, 0xc0, 0x48, 0x51, 0x74, 0xef, 0x04, 0x26, +0x11, 0x83, 0x11, 0x40, 0x10, 0xe0, 0x04, 0xa4, 0x11, 0x40, 0x20, 0x60, 0x11, +0x81, 0x04, 0xa4, 0x11, 0x40, 0xb0, 0xe7, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, +0x1b, 0x00, 0x06, 0x01, 0x38, 0xe1, 0x04, 0x80, 0xc8, 0x20, 0x04, 0x60, 0x01, +0xb8, 0xc3, 0xc1, 0x83, 0xc1, 0x13, 0xc3, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, +0x00, 0xfa, 0xe6, 0x41, 0x84, 0x60, 0x8e, 0x42, 0x00, 0x60, 0x10, 0xc0, 0x08, +0x80, 0x14, 0x84, 0x0b, 0x21, 0xa6, 0x41, 0x04, 0xe1, 0x1c, 0x80, 0x05, 0x82, +0x28, 0x47, 0x64, 0xef, 0x88, 0x7b, 0x28, 0x6f, 0x46, 0x40, 0x04, 0xe0, 0x35, +0x81, 0x38, 0x56, 0x40, 0xef, 0xd8, 0x67, 0x64, 0xef, 0x38, 0x63, 0x64, 0xef, +0xd8, 0x44, 0x44, 0xef, 0x06, 0x40, 0x94, 0xe0, 0x04, 0x80, 0x09, 0x00, 0x50, +0xeb, 0x0a, 0x40, 0x88, 0xe0, 0x76, 0x00, 0xa0, 0xc0, 0x68, 0x4e, 0x50, 0xcf, +0xc6, 0x0d, 0x30, 0xe1, 0x09, 0x0c, 0x30, 0xe7, 0x0b, 0xa1, 0x78, 0x6c, 0x70, +0xcf, 0x98, 0x7f, 0x6c, 0xef, 0x38, 0x4b, 0x2c, 0x6f, 0x0b, 0x8c, 0xc8, 0x5a, +0x30, 0x6f, 0x01, 0x81, 0x58, 0x49, 0x30, 0xef, 0xb8, 0x5f, 0x70, 0xef, 0x01, +0x83, 0x01, 0x4c, 0xc0, 0xe5, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, +0x0c, 0x04, 0x84, 0xfa, 0x06, 0x4e, 0x88, 0x60, 0x06, 0x0c, 0x0c, 0xe1, 0x05, +0x0e, 0x38, 0x78, 0x06, 0x04, 0x00, 0x70, 0x40, 0xc0, 0x19, 0x0c, 0x94, 0x78, +0x06, 0x06, 0x10, 0x60, 0x40, 0xc0, 0x08, 0xc0, 0x4a, 0x02, 0x00, 0xe0, 0x09, +0x86, 0x07, 0xc6, 0x2f, 0x61, 0x3f, 0xe1, 0x21, 0x4e, 0x10, 0xf8, 0x0c, 0x00, +0x00, 0xe2, 0x31, 0x4e, 0x0c, 0xf8, 0x18, 0x62, 0x30, 0xef, 0x05, 0x0e, 0x38, +0xf8, 0x14, 0x98, 0x09, 0x00, 0x24, 0xe0, 0x4a, 0x40, 0x88, 0xe0, 0x65, 0x82, +0x0b, 0x0c, 0x28, 0x78, 0x1b, 0xa1, 0x0c, 0xde, 0x05, 0x84, 0x0a, 0x01, 0x20, +0xec, 0xf5, 0x84, 0x55, 0x03, 0x01, 0x93, 0x0b, 0x0c, 0x24, 0x78, 0x1b, 0xa1, +0x0c, 0xde, 0x65, 0x82, 0x0a, 0x01, 0x24, 0xec, 0xb5, 0x82, 0xb5, 0x01, 0x01, +0x95, 0x0a, 0x01, 0x48, 0xec, 0x15, 0x82, 0x65, 0x01, 0x01, 0xa7, 0x0a, 0x01, +0x50, 0xec, 0x75, 0x80, 0x01, 0xab, 0x0c, 0xde, 0x15, 0x0c, 0x20, 0x79, 0x06, +0x04, 0x00, 0x60, 0x00, 0xf0, 0x49, 0x0c, 0x64, 0x78, 0x06, 0x06, 0x00, 0x60, +0x00, 0xe0, 0x2f, 0x82, 0x3b, 0x0c, 0x8c, 0x78, 0x2b, 0x87, 0x30, 0x42, 0x00, +0x5c, 0x4a, 0x08, 0x14, 0xfc, 0x14, 0x04, 0x80, 0x7a, 0xce, 0x43, 0x00, 0x60, +0x10, 0xc0, 0x2c, 0x04, 0x8e, 0x48, 0x01, 0x60, 0x10, 0xc0, 0x3e, 0x90, 0x85, +0x88, 0x45, 0x0e, 0x30, 0xf8, 0x3b, 0x08, 0x18, 0xe0, 0x5b, 0x08, 0x20, 0xe0, +0x4b, 0x08, 0x1c, 0xe0, 0x32, 0x4a, 0x94, 0xf1, 0x53, 0x88, 0x8a, 0x0a, 0x89, +0x63, 0x05, 0xc0, 0xb5, 0x84, 0x54, 0x1a, 0x8e, 0x59, 0x09, 0x60, 0x10, 0xc0, +0x71, 0x83, 0x7e, 0x30, 0x2e, 0x59, 0x09, 0x60, 0x10, 0xc0, 0x5c, 0x30, 0x54, +0x08, 0x80, 0xfb, 0x4d, 0x44, 0x4e, 0x45, 0x09, 0x60, 0x10, 0xc0, 0x3e, 0x88, +0x43, 0x42, 0x00, 0xc0, 0x65, 0x81, 0x8e, 0x43, 0x09, 0x60, 0x10, 0x40, 0x21, +0x81, 0x2e, 0x84, 0x03, 0x4c, 0xd5, 0x79, 0x02, 0x40, 0xd6, 0x41, 0x00, 0xe0, +0x0c, 0x0c, 0x84, 0xf2, 0x18, 0x34, 0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0xfc, +0xfa, 0x6b, 0x00, 0x06, 0x41, 0x08, 0xe0, 0x98, 0x73, 0x48, 0xef, 0x0b, 0xa1, +0x78, 0x14, 0x00, 0xc0, 0x86, 0x52, 0x84, 0xe0, 0x74, 0xe4, 0x08, 0x9c, 0x09, +0x86, 0x0b, 0xa3, 0x36, 0x40, 0x98, 0x40, 0x06, 0x02, 0x00, 0xc0, 0x11, 0x40, +0x00, 0xc0, 0xf5, 0x80, 0x21, 0x01, 0x36, 0x40, 0x98, 0xe0, 0x2e, 0x18, 0x11, +0x83, 0x16, 0x80, 0x84, 0x26, 0x16, 0x44, 0x98, 0xe0, 0x18, 0x9c, 0x09, 0x10, +0xb4, 0xe0, 0x0a, 0x02, 0x0c, 0xfc, 0x0c, 0x02, 0x00, 0xe2, 0x01, 0x4c, 0x10, +0xe1, 0x0b, 0x10, 0x80, 0xe0, 0x16, 0x88, 0xe8, 0x75, 0xf8, 0xee, 0x05, 0x4c, +0x48, 0xe0, 0x08, 0x9c, 0x00, 0x42, 0x08, 0xfc, 0x19, 0x86, 0x1b, 0xa5, 0xd5, +0x80, 0x4a, 0x00, 0x0c, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x0e, 0x98, 0x09, 0x10, +0xb0, 0xe0, 0x98, 0x51, 0x00, 0x68, 0x09, 0x82, 0x01, 0x4c, 0x18, 0x60, 0x11, +0x9f, 0x0b, 0x10, 0x88, 0xe0, 0x78, 0x51, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c, +0x4c, 0xe0, 0x0b, 0x02, 0x84, 0x60, 0x11, 0x91, 0xf8, 0x50, 0x00, 0xe8, 0xb4, +0xa6, 0x05, 0x4c, 0x50, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x0a, 0x40, 0x84, 0x60, +0x06, 0x00, 0x00, 0x60, 0x48, 0xd0, 0x56, 0x01, 0x55, 0x15, 0x41, 0xd0, 0x05, +0x4c, 0x34, 0xe0, 0x09, 0x16, 0xd0, 0xe0, 0x68, 0x4f, 0x00, 0x68, 0x09, 0x82, +0x14, 0x64, 0x06, 0x54, 0xdc, 0xe0, 0x01, 0x4c, 0x10, 0x60, 0xce, 0x41, 0x00, +0x60, 0x10, 0xc0, 0x0a, 0x80, 0x2b, 0x02, 0x18, 0xe0, 0x23, 0xa0, 0x23, 0x4c, +0x60, 0xe0, 0x09, 0x14, 0x70, 0xea, 0x22, 0x40, 0x80, 0xf1, 0x03, 0x4c, 0x60, +0xe0, 0x25, 0x14, 0xe0, 0xe2, 0x2b, 0xa3, 0x35, 0x88, 0x24, 0xa4, 0x3b, 0x02, +0x18, 0xe0, 0x4b, 0x02, 0x20, 0xe0, 0x1b, 0x02, 0x1c, 0xe0, 0x59, 0x04, 0x24, +0xe0, 0x33, 0xa8, 0x32, 0x44, 0x84, 0x70, 0x2e, 0x47, 0x08, 0x60, 0x10, 0xc0, +0x20, 0x02, 0xfc, 0x6c, 0x0a, 0x4a, 0x88, 0xe0, 0x1c, 0x0c, 0x20, 0x02, 0x14, +0xcc, 0x10, 0x02, 0xf0, 0xcc, 0x13, 0x46, 0x00, 0x40, 0x0a, 0x41, 0x04, 0xec, +0x13, 0x4c, 0x60, 0x40, 0x10, 0x40, 0x00, 0xdc, 0x15, 0x14, 0xdc, 0x62, 0x04, +0x00, 0x80, 0xfb, 0x86, 0x05, 0x74, 0xe1, 0x1d, 0xc1, 0x13, 0x4c, 0x60, 0x40, +0x14, 0x00, 0x80, 0xdb, 0x14, 0xa4, 0x0e, 0xa8, 0x04, 0x88, 0x7a, 0x84, 0x73, +0x4c, 0x64, 0xe0, 0xe8, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x8e, 0x40, 0xb1, 0x66, +0x9b, 0xd3, 0x08, 0x6d, 0xf8, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x85, 0x4c, 0x3c, +0xe0, 0x28, 0x6d, 0xf8, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0x28, 0x6d, 0xf8, 0xee, +0x48, 0x6c, 0xf8, 0x6e, 0x0e, 0x02, 0x00, 0x60, 0x1e, 0xd1, 0x05, 0x4c, 0x40, +0x60, 0x11, 0x91, 0x0b, 0x16, 0x64, 0xe0, 0x7b, 0x16, 0x5c, 0xe0, 0x89, 0x16, +0xc0, 0xe0, 0x08, 0x49, 0x00, 0xe8, 0x3b, 0x00, 0x80, 0x44, 0x7c, 0xf0, 0x81, +0x01, 0x11, 0x81, 0xb8, 0x40, 0x40, 0x6f, 0x0b, 0x8e, 0x4b, 0x0c, 0x60, 0x60, +0x06, 0x0e, 0x00, 0x60, 0xe0, 0xcf, 0x0e, 0x5a, 0x11, 0x81, 0x21, 0x01, 0x06, +0x06, 0x01, 0x60, 0xe0, 0xcf, 0xd8, 0x7f, 0x3c, 0x6f, 0x0b, 0x88, 0x14, 0x64, +0x86, 0x57, 0x94, 0xe1, 0x05, 0x4c, 0x2c, 0xe0, 0x0a, 0x86, 0x83, 0x4c, 0xe4, +0xe0, 0x03, 0x4c, 0xe0, 0xe0, 0x08, 0x86, 0x09, 0xbe, 0x03, 0x4c, 0xe8, 0xe0, +0x08, 0x86, 0x09, 0xbe, 0x07, 0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x54, +0xe0, 0x0a, 0xc4, 0x03, 0x4c, 0xec, 0xe0, 0x0a, 0x44, 0x11, 0x91, 0xe8, 0x45, +0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x58, 0xe0, 0x0a, 0x46, 0x11, 0x91, 0x78, +0x45, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x5c, 0xe0, 0x0b, 0x02, 0x10, 0x60, +0x11, 0x91, 0xf8, 0x44, 0x00, 0xe8, 0x14, 0xe4, 0x05, 0x4c, 0x60, 0xe0, 0x0b, +0x02, 0x14, 0x60, 0x11, 0x91, 0x78, 0x44, 0x00, 0xe8, 0x14, 0xa6, 0x05, 0x4c, +0x64, 0xe0, 0x85, 0x4c, 0x14, 0xe0, 0x85, 0x4c, 0x18, 0xe0, 0x0b, 0x02, 0x7c, +0x60, 0x11, 0x91, 0xb8, 0x43, 0x00, 0xe8, 0x05, 0x4c, 0x10, 0x60, 0x11, 0x91, +0x81, 0x4c, 0x14, 0xe0, 0x75, 0x4c, 0x1c, 0xe0, 0x09, 0x14, 0xd0, 0xea, 0x07, +0xc4, 0x05, 0x00, 0x2c, 0xfc, 0x05, 0x4c, 0x20, 0xe0, 0x0b, 0x14, 0x94, 0xe5, +0x98, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x38, 0x60, 0x11, 0x91, 0x0b, 0x14, 0x64, +0xe5, 0x28, 0x42, 0x00, 0xe8, 0x05, 0x4c, 0x28, 0x60, 0x11, 0x81, 0x09, 0x14, +0x8c, 0x6a, 0x21, 0x81, 0x0b, 0x12, 0x7c, 0x60, 0x0a, 0x40, 0x88, 0xe0, 0xd6, +0x0e, 0xcd, 0x0c, 0x73, 0xcf, 0x75, 0x4c, 0x24, 0x60, 0x3b, 0x8e, 0x28, 0x79, +0x3c, 0xef, 0x0e, 0xd8, 0x16, 0x41, 0x08, 0xe0, 0x0c, 0x0c, 0xfc, 0xf2, 0xb8, +0x1e, 0x48, 0xff, 0xc3, 0xc1, 0x4a, 0x00, 0x00, 0xe0, 0x0c, 0x00, 0x00, 0xe2, +0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x00, 0x00, 0xfa, 0x88, 0x61, 0x28, 0xef, 0x4c, +0x08, 0x00, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x88, 0x5d, +0x48, 0x6f, 0xf6, 0x40, 0x04, 0xe0, 0x0b, 0xa1, 0x85, 0x80, 0x0c, 0x0c, 0x00, +0xf2, 0x93, 0xdd, 0xb8, 0x6b, 0x54, 0xef, 0xc6, 0x4c, 0x84, 0xe0, 0x04, 0x98, +0x09, 0x00, 0x20, 0xe1, 0x4a, 0x40, 0x80, 0xe0, 0x85, 0x88, 0xc6, 0x40, 0xb4, +0x60, 0x46, 0x45, 0x00, 0xe0, 0x19, 0x00, 0xb4, 0xe4, 0x16, 0x42, 0x0a, 0xe0, +0x03, 0x82, 0x09, 0x00, 0x40, 0xe2, 0x0b, 0xa5, 0xf5, 0x84, 0x48, 0x56, 0x40, +0x6f, 0x01, 0x85, 0x58, 0x4c, 0x40, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0xd8, 0x7d, +0x44, 0x6f, 0x06, 0x00, 0x80, 0xe1, 0x58, 0x50, 0x40, 0xef, 0x18, 0x70, 0xf4, +0xef, 0x4e, 0x40, 0x01, 0x60, 0x10, 0xc0, 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x70, +0xf4, 0xcf, 0x09, 0x0c, 0x48, 0xe0, 0x0b, 0xa1, 0x95, 0x84, 0xe8, 0x70, 0x38, +0xef, 0x06, 0x40, 0x94, 0x60, 0x16, 0x07, 0x04, 0xe1, 0x04, 0x80, 0x1b, 0x00, +0x18, 0xe1, 0x2b, 0x00, 0x50, 0xe1, 0x48, 0x8c, 0x39, 0x06, 0xac, 0xe0, 0x13, +0xa4, 0x13, 0xa8, 0x13, 0xa6, 0x15, 0x40, 0x2c, 0xe0, 0xc6, 0x00, 0x2c, 0x61, +0x11, 0xa1, 0x04, 0x80, 0x13, 0x40, 0xd0, 0x65, 0xe6, 0x40, 0xe0, 0xe0, 0x0a, +0x00, 0x06, 0x42, 0x94, 0xe0, 0x14, 0x84, 0x03, 0x42, 0x10, 0x62, 0x06, 0x41, +0x04, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x98, 0x17, 0x48, 0xff, 0xc3, 0xc1, 0x66, +0x01, 0xcc, 0xe0, 0x18, 0x80, 0x0a, 0x42, 0x84, 0xe0, 0x10, 0x42, 0xf8, 0xd3, +0x11, 0x40, 0x00, 0xc0, 0x93, 0xdd, 0x4a, 0x40, 0x80, 0xe0, 0x55, 0x84, 0x14, +0x04, 0xa0, 0x60, 0x0e, 0x06, 0x00, 0x70, 0x3f, 0xc0, 0x2f, 0x06, 0x14, 0x06, +0xa0, 0xe1, 0x14, 0x00, 0xe0, 0x61, 0x30, 0x46, 0x00, 0x70, 0xff, 0xc0, 0x17, +0x70, 0x3f, 0xa0, 0x1f, 0xa4, 0x1f, 0xa6, 0x93, 0x5d, 0x0b, 0x82, 0x4c, 0x04, +0x04, 0xfa, 0x86, 0x0d, 0xcc, 0x60, 0x71, 0x81, 0x7c, 0x98, 0x88, 0x55, 0x68, +0xef, 0x0a, 0x98, 0x00, 0x42, 0xf1, 0x73, 0xff, 0xc0, 0x19, 0xc2, 0x1c, 0x18, +0x60, 0x02, 0xe1, 0x65, 0x02, 0xc0, 0x65, 0x81, 0x24, 0x84, 0x2b, 0xa1, 0xf5, +0x80, 0x71, 0x02, 0x11, 0x88, 0x0a, 0x0e, 0x00, 0xe1, 0x35, 0xfe, 0x75, 0x81, +0x77, 0x50, 0x00, 0x40, 0xf0, 0xf3, 0x0f, 0xae, 0x09, 0xc4, 0x0c, 0x98, 0x4c, +0x0c, 0x04, 0xf2, 0x93, 0xdd, 0x4c, 0x04, 0x7c, 0xfa, 0xbb, 0x06, 0x6b, 0x82, +0x14, 0x2c, 0x9b, 0x80, 0x74, 0x24, 0xab, 0x84, 0xa4, 0x00, 0xa0, 0x61, 0x8b, +0x88, 0x1b, 0x81, 0x65, 0x82, 0x0e, 0x2c, 0x0b, 0x8e, 0x78, 0x56, 0x30, 0x6f, +0x1b, 0x8c, 0x24, 0x2c, 0x0b, 0x8e, 0x28, 0x59, 0x30, 0x6f, 0x1b, 0x8c, 0x04, +0x18, 0xb1, 0x83, 0x11, 0x01, 0x21, 0x83, 0x38, 0x44, 0x00, 0x68, 0x31, 0x85, +0xa4, 0x02, 0x80, 0x7a, 0x21, 0x8b, 0xd8, 0x43, 0x00, 0x68, 0x06, 0x46, 0xfc, +0xe1, 0xab, 0x00, 0x0b, 0x8e, 0x78, 0x40, 0xf8, 0x6f, 0x1b, 0x94, 0x09, 0x12, +0x10, 0x60, 0x1b, 0x90, 0x98, 0x79, 0xfc, 0x6f, 0x70, 0x12, 0x10, 0xe4, 0x1b, +0x00, 0x0b, 0x92, 0xb8, 0x7f, 0xf4, 0xef, 0x11, 0x03, 0x21, 0x85, 0x31, 0x09, +0x0b, 0x94, 0x58, 0x42, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x18, 0x7f, 0xf4, +0x6f, 0x1b, 0x90, 0x70, 0x12, 0x50, 0x64, 0x36, 0x54, 0x48, 0xe1, 0x08, 0x42, +0x00, 0x68, 0x0b, 0x92, 0x0a, 0x00, 0x00, 0xfb, 0xb1, 0x54, 0x00, 0x40, 0x0a, +0x40, 0xa0, 0xe0, 0xe5, 0xfc, 0x11, 0x01, 0x21, 0x85, 0x31, 0x09, 0x0b, 0x90, +0xc8, 0x40, 0x00, 0xe8, 0x8b, 0x00, 0x0b, 0x8e, 0x88, 0x7d, 0xf4, 0x6f, 0x1b, +0x90, 0x8e, 0x98, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1f, 0x25, +0x32, 0x40, 0x80, 0xf8, 0x1f, 0x86, 0x93, 0x5d, 0x0f, 0xa2, 0x04, 0x80, 0x93, +0xdd, 0x0c, 0x00, 0x80, 0xfa, 0xa8, 0x4c, 0x48, 0x6f, 0xe6, 0x40, 0x08, 0xe0, +0x0b, 0xa1, 0x95, 0x84, 0xc8, 0x46, 0x40, 0xef, 0x86, 0x02, 0x84, 0x60, 0xc6, +0x04, 0x84, 0xe0, 0x14, 0x04, 0x5a, 0x00, 0x08, 0xe0, 0x24, 0x88, 0x4a, 0x42, +0x08, 0x60, 0x46, 0x02, 0x84, 0xe0, 0x0a, 0x42, 0x80, 0xe1, 0x0c, 0x00, 0x00, +0xe2, 0x06, 0x84, 0xf6, 0x40, 0x08, 0xe0, 0x0c, 0x08, 0x80, 0xf2, 0xd8, 0x0a, +0x48, 0xff, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0xf0, 0x1e, 0x60, 0x6c, 0xa6, +0x01, 0x04, 0xe0, 0x48, 0x4a, 0x48, 0xef, 0x0b, 0xa1, 0x15, 0x90, 0xc8, 0x5d, +0x2c, 0xef, 0xe8, 0x7a, 0xf4, 0x6e, 0x0e, 0x02, 0x24, 0x74, 0x03, 0xc0, 0x86, +0x50, 0x84, 0xe0, 0x69, 0x10, 0x44, 0xe0, 0x58, 0x7a, 0xf4, 0x6e, 0x1b, 0x8c, +0x04, 0x60, 0x7b, 0x80, 0x1b, 0x00, 0x28, 0xe0, 0x0b, 0x00, 0x24, 0xe0, 0x13, +0xa0, 0x10, 0x00, 0x04, 0x64, 0x1b, 0x8c, 0x88, 0x79, 0xf4, 0xee, 0x14, 0xa0, +0x19, 0x02, 0x28, 0xe0, 0x19, 0xbe, 0x06, 0x44, 0x06, 0x60, 0x06, 0x41, 0xdc, +0xe0, 0x04, 0x00, 0x16, 0x42, 0x1e, 0xe0, 0x21, 0xbe, 0x1e, 0x7c, 0x27, 0xcb, +0x2c, 0x7c, 0x0e, 0x02, 0x25, 0x74, 0x03, 0xc0, 0x48, 0x78, 0xf4, 0xee, 0x0e, +0x7e, 0x11, 0x81, 0x1e, 0x09, 0xf0, 0x00, 0x10, 0xe4, 0x1c, 0x95, 0x88, 0x7e, +0x1c, 0xef, 0x0a, 0x95, 0x18, 0x41, 0x00, 0x68, 0x01, 0xa0, 0x06, 0x00, 0x38, +0x61, 0x11, 0x83, 0x78, 0x7c, 0x1c, 0xef, 0x18, 0x46, 0x48, 0x6f, 0xb6, 0x01, +0x04, 0xe0, 0xf1, 0xb0, 0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x1b, +0x00, 0x06, 0x00, 0x38, 0xe1, 0x04, 0x80, 0x08, 0x35, 0xf4, 0x7f, 0x01, 0xc8, +0xc3, 0xc1, 0x0c, 0x04, 0x3c, 0xfa, 0x86, 0x4e, 0x84, 0x60, 0xf0, 0x1e, 0x20, +0xee, 0x84, 0x9c, 0x19, 0x0e, 0x48, 0xe0, 0x0a, 0xe2, 0x2a, 0xe0, 0x03, 0xa4, +0x01, 0x82, 0x38, 0x75, 0xf4, 0x6e, 0x04, 0x00, 0x80, 0xfb, 0x0b, 0x10, 0x64, +0x60, 0x6b, 0x80, 0x0b, 0xa1, 0x05, 0x0e, 0x04, 0xc0, 0x99, 0x00, 0xa0, 0xc0, +0xd5, 0x84, 0x09, 0x10, 0x24, 0x60, 0x06, 0x03, 0xd0, 0xe0, 0x1a, 0x84, 0x07, +0xc5, 0x09, 0x82, 0x1f, 0x21, 0x06, 0x00, 0x04, 0xe0, 0x10, 0x42, 0x01, 0x70, +0xff, 0xc0, 0x17, 0xd1, 0x1b, 0xa1, 0x78, 0x73, 0xf4, 0x6e, 0x16, 0x02, 0x00, +0x80, 0x00, 0x12, 0x04, 0xec, 0x18, 0x42, 0x48, 0x6f, 0x36, 0x01, 0x04, 0xe0, +0x0b, 0xa1, 0xb5, 0xb0, 0x04, 0x1c, 0x06, 0x11, 0xd0, 0xe0, 0x19, 0x10, 0x24, +0x6e, 0x46, 0x05, 0xb8, 0xe0, 0xa4, 0x9e, 0x09, 0x00, 0xb0, 0xe0, 0x18, 0x08, +0x5a, 0x02, 0x00, 0xe0, 0x1c, 0x06, 0x00, 0xe2, 0x36, 0x3e, 0x09, 0x86, 0x0b, +0x21, 0x19, 0x82, 0x0c, 0x04, 0x00, 0x62, 0x0b, 0xa5, 0x16, 0x7c, 0x64, 0x02, +0x80, 0xfb, 0x26, 0x3c, 0x6b, 0x9e, 0x1e, 0xbe, 0x19, 0x0e, 0x60, 0xe0, 0x2b, +0x10, 0x30, 0xf8, 0x0c, 0x00, 0x00, 0xe2, 0x16, 0x1b, 0x11, 0x81, 0x06, 0x7e, +0x2b, 0xa3, 0x19, 0x14, 0x5c, 0xc0, 0x09, 0x0e, 0x80, 0xe0, 0x25, 0x10, 0xa4, +0xe3, 0x35, 0x10, 0xa8, 0x63, 0x10, 0x42, 0x3c, 0xd0, 0x0c, 0x0b, 0x01, 0x81, +0x16, 0x1d, 0x11, 0x81, 0x26, 0x1f, 0x46, 0x05, 0x04, 0xe0, 0x0c, 0x89, 0x06, +0x99, 0x06, 0xa5, 0x0c, 0x91, 0x3e, 0x8b, 0x0e, 0x91, 0x0e, 0x8f, 0x0e, 0x0d, +0x60, 0x00, 0xa0, 0xe4, 0x91, 0x5e, 0x90, 0xe0, 0xc8, 0x7a, 0xf4, 0xee, 0x09, +0x14, 0x58, 0xe0, 0x19, 0x10, 0xf0, 0xf8, 0x0e, 0x3f, 0xfe, 0x41, 0x3d, 0x70, +0xff, 0xc0, 0x1b, 0x25, 0xf6, 0x43, 0x3c, 0xe0, 0x05, 0x5e, 0x80, 0x60, 0x16, +0x00, 0x00, 0x80, 0x15, 0x5e, 0x84, 0xe0, 0x01, 0x5e, 0x34, 0x80, 0x05, 0x14, +0x18, 0xe0, 0x38, 0x47, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a, +0x20, 0x11, 0x91, 0x0e, 0x97, 0x78, 0x5d, 0xfc, 0x6f, 0x0b, 0x84, 0x19, 0x10, +0xc0, 0xe9, 0x0e, 0xb7, 0x4a, 0x42, 0x80, 0xe0, 0xc5, 0x82, 0x60, 0x00, 0xc0, +0x65, 0x11, 0xfb, 0x75, 0x81, 0x24, 0x37, 0x11, 0x82, 0x2e, 0xc1, 0x24, 0x97, +0x25, 0x40, 0x44, 0xf8, 0x1b, 0xa1, 0x35, 0xfe, 0xe5, 0x85, 0x04, 0x9e, 0x05, +0x00, 0x1c, 0xe0, 0x28, 0x45, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, +0x14, 0x9e, 0x0e, 0x99, 0x05, 0x02, 0x20, 0xe0, 0x98, 0x44, 0x00, 0x68, 0x00, +0x40, 0xfc, 0x73, 0xff, 0xc7, 0x14, 0x9e, 0x0e, 0x9b, 0x05, 0x02, 0x24, 0xe0, +0x08, 0x44, 0x00, 0x68, 0x00, 0x40, 0xfc, 0x73, 0xff, 0xc7, 0x2a, 0x22, 0x11, +0x91, 0x0e, 0x9d, 0x48, 0x5a, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x60, 0x11, 0x91, +0x0e, 0xb9, 0xe8, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x2a, 0x62, 0x11, 0x91, 0x0e, +0xbb, 0x88, 0x59, 0xfc, 0x6f, 0x0b, 0x84, 0x0e, 0xbd, 0x04, 0x9c, 0x09, 0x00, +0x24, 0xe0, 0x0a, 0x40, 0x88, 0xe0, 0xa5, 0x84, 0x04, 0xb7, 0xf8, 0x7a, 0xf4, +0x6e, 0x1b, 0x80, 0x14, 0xb9, 0x0e, 0xb7, 0xa8, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, +0x14, 0xbb, 0x0e, 0xb9, 0x58, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x14, 0xbd, 0x0e, +0xbb, 0x08, 0x7a, 0xf4, 0x6e, 0x0b, 0x82, 0x0e, 0xbd, 0x86, 0x41, 0x30, 0x61, +0x1b, 0x9e, 0xd8, 0x4c, 0x08, 0xef, 0x78, 0x75, 0x44, 0x6f, 0x46, 0x01, 0x04, +0xe0, 0xf0, 0x1e, 0x20, 0xe6, 0x0c, 0x0c, 0x3c, 0xf2, 0x93, 0xdd, 0x4c, 0x00, +0x00, 0xfa, 0x08, 0x74, 0x24, 0x6f, 0x11, 0x8f, 0x4c, 0x08, 0x00, 0xf2, 0x93, +0xdd, 0x0c, 0x04, 0x7c, 0xfe, 0x6b, 0x04, 0xf0, 0x1e, 0x60, 0xec, 0xb5, 0x0c, +0x10, 0xe0, 0x85, 0x0c, 0x18, 0xe0, 0x79, 0x0c, 0x24, 0xe0, 0xa4, 0xda, 0x1e, +0x0b, 0x11, 0x81, 0x0e, 0x7e, 0x0b, 0x96, 0x68, 0x79, 0xf4, 0xee, 0x0b, 0xa1, +0x15, 0x82, 0x14, 0x18, 0x0b, 0x96, 0x88, 0x79, 0xf4, 0xee, 0x0b, 0xa1, 0xb5, +0x4c, 0x00, 0xc0, 0x11, 0x01, 0x0b, 0x94, 0x98, 0x78, 0xf4, 0xee, 0x94, 0x18, +0x0b, 0xa1, 0x05, 0x1e, 0x14, 0xc0, 0xbe, 0xbe, 0x75, 0x82, 0x0b, 0x14, 0x1b, +0x92, 0xf8, 0x78, 0xf4, 0xee, 0x04, 0x0b, 0x0b, 0xa1, 0xa5, 0x4c, 0x00, 0x40, +0xa0, 0x52, 0x00, 0xdc, 0xae, 0x7c, 0xab, 0x80, 0x77, 0x42, 0x0b, 0x92, 0x15, +0x14, 0x1c, 0xfe, 0x58, 0x75, 0xf4, 0xee, 0x0b, 0x14, 0x44, 0x60, 0x9b, 0x80, +0x88, 0x75, 0xf4, 0xee, 0xbb, 0x00, 0x0b, 0x92, 0x08, 0x77, 0xf4, 0x6e, 0x1b, +0x96, 0x0b, 0xa1, 0x90, 0x56, 0x00, 0x1c, 0xa0, 0x12, 0x88, 0xe4, 0xc8, 0x75, +0xf4, 0x6e, 0x0b, 0x96, 0xab, 0x00, 0x01, 0x81, 0x01, 0x4c, 0x20, 0x60, 0x8b, +0xa1, 0x04, 0x7e, 0x76, 0x03, 0x71, 0x7b, 0x34, 0xce, 0xac, 0xd8, 0x03, 0x8e, +0x00, 0x00, 0x70, 0xe7, 0x0e, 0x89, 0x65, 0x88, 0x8b, 0xa3, 0x35, 0x08, 0x06, +0x02, 0x01, 0x00, 0xc0, 0xcf, 0x58, 0x74, 0xf4, 0x6e, 0x0b, 0x96, 0x28, 0x73, +0xf4, 0x6e, 0x8b, 0x80, 0x1b, 0x00, 0x0b, 0x96, 0x58, 0x73, 0xf4, 0xee, 0xd8, +0x49, 0x00, 0xe8, 0x18, 0x74, 0xf4, 0x6e, 0x76, 0x03, 0x70, 0x7b, 0x34, 0xce, +0x0b, 0xa1, 0x25, 0x84, 0x11, 0x01, 0x0b, 0x96, 0x48, 0x74, 0xf4, 0xee, 0x0b, +0xa1, 0x80, 0x14, 0x04, 0x04, 0x80, 0x54, 0x00, 0xdc, 0x75, 0x81, 0xc8, 0x70, +0xf4, 0x6e, 0x0b, 0x96, 0x58, 0x72, 0xf4, 0xee, 0xab, 0x80, 0xac, 0xd8, 0x0a, +0xa4, 0x08, 0x71, 0xf4, 0xee, 0x04, 0x0b, 0x9b, 0x80, 0x73, 0x80, 0x0b, 0x0e, +0x40, 0xe0, 0x98, 0x70, 0xf4, 0xee, 0xbb, 0x00, 0xa4, 0x00, 0x80, 0xfb, 0x48, +0x70, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x96, 0x68, 0x49, 0x00, 0xe8, 0x1b, 0x00, +0x0b, 0x92, 0xc8, 0x47, 0x00, 0xe8, 0xa8, 0x70, 0xf4, 0xee, 0x04, 0x09, 0xab, +0x80, 0xac, 0xd8, 0x9a, 0x80, 0x9c, 0xda, 0x0b, 0x0e, 0x4c, 0xe0, 0x18, 0x6f, +0xf4, 0xee, 0x0b, 0x0e, 0x48, 0x60, 0xbb, 0x80, 0xc8, 0x6e, 0xf4, 0xee, 0x8b, +0x00, 0x0b, 0x92, 0x88, 0x6e, 0xf4, 0xee, 0x1b, 0x00, 0x0b, 0x90, 0xa8, 0x47, +0x00, 0xe8, 0x1b, 0x00, 0x0b, 0x96, 0x08, 0x46, 0x00, 0xe8, 0xe8, 0x6e, 0xf4, +0xee, 0xb4, 0x3e, 0x9b, 0x80, 0x84, 0xfc, 0x9c, 0xda, 0x1b, 0x96, 0xf8, 0x6d, +0xf4, 0x6e, 0x0b, 0x90, 0x68, 0x44, 0x00, 0xe8, 0xe8, 0x6e, 0xf4, 0x6e, 0x11, +0x81, 0x0b, 0xa1, 0x85, 0x8e, 0x11, 0x01, 0x0b, 0x96, 0xf8, 0x6d, 0xf4, 0xee, +0x2a, 0x00, 0x00, 0x60, 0x0b, 0x94, 0x24, 0x7e, 0x1b, 0x92, 0x34, 0x8b, 0xf8, +0x74, 0x18, 0xef, 0x58, 0x01, 0x01, 0x40, 0x00, 0x40, 0x9b, 0x80, 0x0b, 0x16, +0x1b, 0x92, 0x78, 0x6d, 0xf4, 0xee, 0x0b, 0xa1, 0x85, 0x82, 0x1a, 0xd8, 0x09, +0x0c, 0x20, 0xe0, 0x11, 0x82, 0x04, 0x09, 0x0b, 0xa1, 0x1c, 0x58, 0x70, 0x00, +0x90, 0xc4, 0x0a, 0x80, 0x0c, 0xda, 0x11, 0x01, 0x0b, 0x90, 0xe8, 0x6b, 0xf4, +0xee, 0x0b, 0xa1, 0xb5, 0x84, 0x0b, 0x10, 0x1b, 0x92, 0x88, 0x6c, 0xf4, 0xee, +0x0b, 0xa1, 0xe5, 0x82, 0x09, 0x0c, 0x20, 0xe0, 0x1a, 0xd8, 0x0b, 0xa1, 0x70, +0x0e, 0x98, 0x44, 0x10, 0x00, 0x04, 0xec, 0x75, 0x5e, 0x10, 0xc0, 0x0c, 0xd8, +0x04, 0x89, 0x0a, 0x80, 0x0c, 0xda, 0xf1, 0xb0, 0x0c, 0x0c, 0x7c, 0xf6, 0x93, +0xdd, 0x0c, 0x04, 0x00, 0xfa, 0x6b, 0x00, 0x11, 0x81, 0xe8, 0x6a, 0xf4, 0xee, +0x0b, 0xa1, 0x64, 0x0c, 0x7c, 0xdd, 0x0b, 0x8c, 0x0c, 0x0c, 0x00, 0xf2, 0x93, +0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x0c, 0xfa, 0x6b, 0x02, 0x7b, 0x80, 0x08, 0x6a, +0xf4, 0xee, 0x8b, 0x00, 0x0b, 0x8e, 0x48, 0x69, 0xf4, 0x6e, 0x1b, 0x8c, 0x0b, +0xa1, 0x70, 0x4c, 0x00, 0x1c, 0x8b, 0xa1, 0x70, 0x4c, 0x00, 0xdc, 0x0b, 0x8c, +0x0c, 0x0c, 0x0c, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x4c, 0x04, 0x04, 0xfa, 0x6b, +0x02, 0x7b, 0x80, 0xa8, 0x68, 0xf4, 0xee, 0x0b, 0xa1, 0x70, 0x4c, 0x00, 0x9c, +0x0b, 0x8c, 0x4c, 0x0c, 0x04, 0xf2, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, +0xfa, 0x6b, 0x00, 0x21, 0x81, 0x04, 0x18, 0x31, 0x83, 0x68, 0x53, 0xfc, 0xef, +0x0e, 0x98, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x18, 0xa9, 0x00, 0x00, 0x38, +0x90, 0x40, 0x00, 0x08, 0x14, 0xb8, 0xe0, 0x80, 0x2c, 0x40, 0x00, 0xa8, 0x3a, +0xe8, 0xe0, 0x08, 0x9f, 0x40, 0x00, 0x28, 0x28, 0xb0, 0xe0, 0x18, 0x72, 0x40, +0x00, 0x88, 0x19, 0xc8, 0xe0, 0xe0, 0xbb, 0x40, 0x00, 0x08, 0x0c, 0xa4, 0xe0, +0xa8, 0xe3, 0x40, 0x00, 0x28, 0x13, 0x90, 0xe0, 0xf0, 0xec, 0x40, 0x00, 0x68, +0x0d, 0x8c, 0xe0, 0x48, 0xa8, 0x40, 0x00, 0xa8, 0x3c, 0xac, 0xe0, 0x28, 0xe4, +0x40, 0x00, 0x08, 0x0e, 0x94, 0xe0, 0xb8, 0xe2, 0x40, 0x00, 0x28, 0x3d, 0x90, +0xe0, 0x90, 0xa7, 0x40, 0x00, 0xc8, 0x13, 0xb0, 0xe0, 0x18, 0xc1, 0x40, 0x00, +0xa8, 0x2a, 0xa4, 0xe0, 0xa8, 0x91, 0x40, 0x00, 0x68, 0x2f, 0xbc, 0xe0, 0x88, +0xf1, 0x40, 0x00, 0x08, 0x1c, 0x88, 0xe0, 0xd0, 0x8e, 0x40, 0x00, 0xa8, 0x19, +0xb8, 0xe0, 0x38, 0x82, 0x40, 0x00, 0x28, 0x38, 0xbc, 0xe0, 0x88, 0xe0, 0x40, +0x00, 0xa8, 0x0e, 0x98, 0xe0, 0xd8, 0xe4, 0x40, 0x00, 0x68, 0x32, 0x94, 0xe0, +0x18, 0xd7, 0x40, 0x00, 0x28, 0x2d, 0x9c, 0xe0, 0x40, 0xe8, 0x40, 0x00, 0x28, +0x19, 0x94, 0xe0, 0xa8, 0x78, 0x40, 0x00, 0x28, 0x18, 0xcc, 0xe0, 0xf0, 0x95, +0x40, 0x00, 0x48, 0x34, 0xbc, 0xe0, 0xa8, 0xd2, 0x40, 0x00, 0x48, 0x16, 0xa0, +0xe0, 0x18, 0x52, 0x40, 0x00, 0x68, 0x2b, 0xe0, 0xe0, 0x00, 0xb9, 0x00, 0x00, +0x23, 0xdb, 0x00, 0x00 +}; + +static const u8 vd55g0_cut2_patch_array[] = { +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, +}; + static const char * const vd55g1_tp_menu[] = { "Disabled", "Diagonal Grey Scale", @@ -522,6 +1166,25 @@ static const u32 vd55g1_mbus_formats_bayer[][4] = { }, }; +static const struct vd55g1_mode vd55g0_supported_modes[] = { + { + .width = VD55G0_WIDTH, + .height = VD55G0_HEIGHT, + }, + { + .width = 640, + .height = 600, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 320, + .height = 240, + }, +}; + static const struct vd55g1_mode vd55g1_supported_modes[] = { { .width = VD55G1_WIDTH, @@ -559,15 +1222,191 @@ struct vd55g1_vblank_limits { u16 max; }; +struct vd55g1_frame_timings { + u16 frame_length; + u16 expo_max; +}; + +#define VD55G1_REVISION_ANY 0 + +struct vd55g1_revision { + u64 id; + bool needs_patch; + const struct vd55g1_firmware *builtin_fw; +}; + +struct vd55g1_model { + const char *name; + unsigned int id; + enum vd55g1_color_version color; + const int *reg_map; + const struct vd55g1_revision *revisions; + u16 num_revisions; + const struct vd55g1_mode *modes; + u16 num_modes; + u32 mipi_rate_min; + u32 mipi_rate_max; + u16 width; + u16 height; + bool bayer; + bool hdr; + bool absolute_endpoints; + bool ae_coldstart_exposure_in_us; + bool limit_flash_duty_cycle; + bool implicit_patch_boot; +}; + +struct vd55g1_firmware { + size_t size; + const u8 *data; + u8 v_major; + u8 v_minor; +}; + +static const struct vd55g1_firmware vd55g0_cut1_builtin_fw = { + .data = vd55g0_cut1_patch_array, + .size = ARRAY_SIZE(vd55g0_cut1_patch_array), + .v_major = 2, + .v_minor = 11, +}; + +static const struct vd55g1_firmware vd55g0_cut2_builtin_fw = { + .data = vd55g0_cut2_patch_array, + .size = ARRAY_SIZE(vd55g0_cut2_patch_array), + .v_major = 0, + .v_minor = 5, +}; + +static const struct vd55g1_firmware vd55g1_builtin_fw = { + .data = vd55g1_patch_array, + .size = ARRAY_SIZE(vd55g1_patch_array), + .v_major = 2, + .v_minor = 9, +}; + +static const struct vd55g1_revision vd55g0_revisions[] = { + { + .id = 0x1111, + .needs_patch = true, + .builtin_fw = &vd55g0_cut1_builtin_fw, + }, + { + .id = 0x1120, + .needs_patch = true, + .builtin_fw = &vd55g0_cut2_builtin_fw, + }, +}; + +static const struct vd55g1_revision vd55g1_revisions[] = { + { + .id = 0x2020, + .needs_patch = true, + .builtin_fw = &vd55g1_builtin_fw + }, +}; + +static const struct vd55g1_revision vd55g4_revisions[] = { + { .id = VD55G1_REVISION_ANY, .needs_patch = false }, +}; + +static const struct vd55g1_revision vd65g4_revisions[] = { + { .id = 0x3030, .needs_patch = false }, +}; + +static const struct vd55g1_model vd55g0_model = { + .name = "vd55g0", + .id = VD55G1_MODEL_ID_0, + .color = VD55G1_COLOR_VERSION_MONO, + .reg_map = vd55g0_reg_map, + .revisions = vd55g0_revisions, + .num_revisions = ARRAY_SIZE(vd55g0_revisions), + .modes = vd55g0_supported_modes, + .num_modes = ARRAY_SIZE(vd55g0_supported_modes), + .mipi_rate_min = VD55G0_MIPI_RATE_MIN, + .mipi_rate_max = VD55G0_MIPI_RATE_MAX, + .width = VD55G0_WIDTH, + .height = VD55G0_HEIGHT, + .bayer = false, + .hdr = false, + .absolute_endpoints = true, + .ae_coldstart_exposure_in_us = false, + .limit_flash_duty_cycle = true, + .implicit_patch_boot = false, +}; + +static const struct vd55g1_model vd55g1_model = { + .name = "vd55g1", + .id = VD55G1_MODEL_ID_2, + .color = VD55G1_COLOR_VERSION_MONO, + .reg_map = vd55g1_reg_map, + .revisions = vd55g1_revisions, + .num_revisions = ARRAY_SIZE(vd55g1_revisions), + .modes = vd55g1_supported_modes, + .num_modes = ARRAY_SIZE(vd55g1_supported_modes), + .mipi_rate_min = VD55G1_MIPI_RATE_MIN, + .mipi_rate_max = VD55G1_MIPI_RATE_MAX, + .width = VD55G1_WIDTH, + .height = VD55G1_HEIGHT, + .bayer = false, + .hdr = true, + .absolute_endpoints = false, + .ae_coldstart_exposure_in_us = true, + .limit_flash_duty_cycle = false, + .implicit_patch_boot = true, +}; + +static const struct vd55g1_model vd55g4_model = { + .name = "vd55g4", + .id = VD55G1_MODEL_ID_3, + .color = VD55G1_COLOR_VERSION_MONO, + .reg_map = vd55g1_reg_map, + .revisions = vd55g4_revisions, + .num_revisions = ARRAY_SIZE(vd55g4_revisions), + .modes = vd55g1_supported_modes, + .num_modes = ARRAY_SIZE(vd55g1_supported_modes), + .mipi_rate_min = VD55G1_MIPI_RATE_MIN, + .mipi_rate_max = VD55G1_MIPI_RATE_MAX, + .width = VD55G1_WIDTH, + .height = VD55G1_HEIGHT, + .bayer = false, + .hdr = true, + .absolute_endpoints = false, + .ae_coldstart_exposure_in_us = true, + .limit_flash_duty_cycle = false, + .implicit_patch_boot = true, +}; + +static const struct vd55g1_model vd65g4_model = { + .name = "vd65g4", + .id = VD55G1_MODEL_ID_3, + .color = VD55G1_COLOR_VERSION_BAYER, + .reg_map = vd55g1_reg_map, + .revisions = vd65g4_revisions, + .num_revisions = ARRAY_SIZE(vd65g4_revisions), + .modes = vd55g1_supported_modes, + .num_modes = ARRAY_SIZE(vd55g1_supported_modes), + .mipi_rate_min = VD55G1_MIPI_RATE_MIN, + .mipi_rate_max = VD55G1_MIPI_RATE_MAX, + .width = VD55G1_WIDTH, + .height = VD55G1_HEIGHT, + .bayer = true, + .hdr = true, + .absolute_endpoints = false, + .ae_coldstart_exposure_in_us = true, + .limit_flash_duty_cycle = false, + .implicit_patch_boot = true, +}; + struct vd55g1 { struct device *dev; - const struct vd55g1_version *version; struct v4l2_subdev sd; struct media_pad pad; struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)]; struct gpio_desc *reset_gpio; struct clk *xclk; struct regmap *regmap; + const struct vd55g1_model *model; + const struct vd55g1_revision *revision; u32 xclk_freq; u16 oif_ctrl; u8 gpios[VD55G1_NB_GPIOS]; @@ -575,6 +1414,7 @@ struct vd55g1 { u32 mipi_rate; u32 pixel_clock; u64 link_freq; + s64 link_freq_menu; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *pixel_rate_ctrl; struct v4l2_ctrl *vblank_ctrl; @@ -655,7 +1495,7 @@ static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code) u32 fallback_code; unsigned int i, j; - if (sensor->version->color == VD55G1_COLOR_VERSION_MONO) { + if (!sensor->model->bayer) { fallback_code = vd55g1_mbus_formats_mono[0]; for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_mono); i++) if (vd55g1_mbus_formats_mono[i] == code) @@ -704,9 +1544,23 @@ static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor, /* Absolute time required for ADCs to convert pixels */ min_line_length = VD55G1_LINE_LENGTH_MIN; - if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + if (sensor->model->hdr && + sensor->hdr_ctrl->val == VD55G1_HDR_SUB) min_line_length = VD55G1_LINE_LENGTH_SUB_MIN; + /* + * On VD55G0, a line length of 1128 pixels at MIPI rates below + * 900 MHz lets the sensor enter streaming but produce no frames, + * with a stalled stop. Windows programs a 1200-pixel line length + * over the 1128-pixel native width on this board (Chromatix + * frame-timing record, evidence/vd55g0-report.md); SP11 hardware + * testing confirmed continuous capture once raised. + */ + if (sensor->model->id == VD55G1_MODEL_ID_0 && + sensor->mipi_rate < 900 * MEGA) + min_line_length = max_t(u32, min_line_length, + VD55G0_LINE_LENGTH_SAFE_MIN); + /* Respect both constraint */ min_line_length = max(min_line_length, mipi_req_line_length); @@ -722,33 +1576,129 @@ static void vd55g1_get_vblank_limits(struct vd55g1 *sensor, limits->max = VD55G1_VBLANK_MAX - crop->height; } +static void vd55g1_get_frame_timings(struct vd55g1 *sensor, + struct v4l2_rect *crop, + struct vd55g1_frame_timings *timings) +{ + timings->frame_length = crop->height + sensor->vblank_ctrl->val; + + timings->expo_max = timings->frame_length - VD55G1_EXPO_MAX_TERM; + + if (sensor->model->limit_flash_duty_cycle && sensor->led_ctrl && + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_FLASH) { + timings->expo_max = timings->expo_max / 2; + } +} + +static inline int vd55g1_get_ctx_addr(struct vd55g1 *sensor, + enum vd55g1_reg_id reg, u8 ctx, u8 offset, u32 *addr) +{ + u32 base_addr; + u32 stride; + int ret = 0; + + base_addr = sensor->model->reg_map[reg]; + + if (!base_addr) { + dev_dbg(sensor->dev, + "Register %d not supported on this variant\n", reg); + ret = -EOPNOTSUPP; + goto out; + } + + stride = sensor->model->reg_map[REG_CTX_STRIDE]; + + *addr = base_addr + ctx * stride + offset; + +out: + return ret; +} + #define vd55g1_read(sensor, reg, val, err) \ - cci_read((sensor)->regmap, reg, val, err) + vd55g1_read_ctx(sensor, reg, 0, val, err) #define vd55g1_write(sensor, reg, val, err) \ - cci_write((sensor)->regmap, reg, val, err) + vd55g1_write_ctx(sensor, reg, 0, val, err) -static int vd55g1_write_array(struct vd55g1 *sensor, u32 reg, unsigned int len, +#define vd55g1_read_ctx(sensor, reg, ctx, val, err) \ + vd55g1_read_ctx_offset(sensor, reg, ctx, 0, val, err) + +#define vd55g1_write_ctx(sensor, reg, ctx, val, err) \ + vd55g1_write_ctx_offset(sensor, reg, ctx, 0, val, err) + +static int vd55g1_read_ctx_offset(struct vd55g1 *sensor, + enum vd55g1_reg_id reg, u8 ctx, u8 offset, + u64 *val, int *err) +{ + u32 addr; + int ret; + + if (err && *err) + return *err; + + ret = vd55g1_get_ctx_addr(sensor, reg, ctx, offset, &addr); + if (ret) + goto out; + + return cci_read(sensor->regmap, addr, val, err); + +out: + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_write_ctx_offset(struct vd55g1 *sensor, + enum vd55g1_reg_id reg, u8 ctx, u8 offset, + u32 val, int *err) +{ + u32 addr; + int ret; + + if (err && *err) + return *err; + + ret = vd55g1_get_ctx_addr(sensor, reg, ctx, offset, &addr); + if (ret) + goto out; + + return cci_write(sensor->regmap, addr, val, err); + +out: + if (ret && err) + *err = ret; + + return ret; +} + +static int vd55g1_write_array(struct vd55g1 *sensor, + enum vd55g1_reg_id reg, unsigned int len, const u8 *array, int *err) { unsigned int chunk_sz = 1024; unsigned int sz; + u32 addr; int ret = 0; if (err && *err) return *err; + ret = vd55g1_get_ctx_addr(sensor, reg, 0, 0, &addr); + if (ret) + goto out; + /* * This loop isn't necessary but in certains conditions (platforms, cpu * load, etc.) it has been observed that the bulk write could timeout. */ while (len) { sz = min(len, chunk_sz); - ret = regmap_bulk_write(sensor->regmap, reg, array, sz); + ret = regmap_bulk_write(sensor->regmap, addr, array, sz); if (ret < 0) goto out; len -= sz; - reg += sz; + addr += sz; array += sz; } @@ -759,19 +1709,24 @@ static int vd55g1_write_array(struct vd55g1 *sensor, u32 reg, unsigned int len, return ret; } -static int vd55g1_poll_reg(struct vd55g1 *sensor, u32 reg, u8 poll_val, - int *err) +static int vd55g1_poll_reg(struct vd55g1 *sensor, enum vd55g1_reg_id reg, u8 poll_val, int *err) { unsigned int val = 0; + u32 addr; int ret; if (err && *err) return *err; - ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(reg), val, + ret = vd55g1_get_ctx_addr(sensor, reg, 0, 0, &addr); + if (ret) + goto out; + + ret = regmap_read_poll_timeout(sensor->regmap, CCI_REG_ADDR(addr), val, (val == poll_val), 2000, 500 * USEC_PER_MSEC); +out: if (ret && err) *err = ret; @@ -780,7 +1735,7 @@ static int vd55g1_poll_reg(struct vd55g1 *sensor, u32 reg, u8 poll_val, static int vd55g1_wait_state(struct vd55g1 *sensor, int state, int *err) { - return vd55g1_poll_reg(sensor, VD55G1_REG_SYSTEM_FSM, state, err); + return vd55g1_poll_reg(sensor, REG_SYSTEM_FSM, state, err); } static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) @@ -789,7 +1744,7 @@ static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) if (sensor->xclk_freq < VD55G1_XCLK_FREQ_MIN || sensor->xclk_freq > VD55G1_XCLK_FREQ_MAX) { - dev_err(sensor->dev, + dev_dbg(sensor->dev, "Only %luMhz-%luMhz clock range supported. Provided %lu MHz\n", VD55G1_XCLK_FREQ_MIN / HZ_PER_MHZ, VD55G1_XCLK_FREQ_MAX / HZ_PER_MHZ, @@ -797,15 +1752,24 @@ static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) return -EINVAL; } - /* MIPI bus is double data rate */ + /* Validate the full-width input before doubling and narrowing to u32. */ + if (sensor->link_freq < + DIV_ROUND_UP((u64)sensor->model->mipi_rate_min, 2) || + sensor->link_freq > sensor->model->mipi_rate_max / 2) { + dev_dbg(sensor->dev, "Unsupported link frequency: %llu Hz\n", + sensor->link_freq); + return -EINVAL; + } + + /* MIPI bus is double data rate; the validated value fits in u32. */ sensor->mipi_rate = sensor->link_freq * 2; - if (sensor->mipi_rate < VD55G1_MIPI_RATE_MIN || - sensor->mipi_rate > VD55G1_MIPI_RATE_MAX) { - dev_err(sensor->dev, + if (sensor->mipi_rate < sensor->model->mipi_rate_min || + sensor->mipi_rate > sensor->model->mipi_rate_max) { + dev_dbg(sensor->dev, "Only %luMbps-%luMbps data rate range supported. Provided %lu Mbps\n", - VD55G1_MIPI_RATE_MIN / MEGA, - VD55G1_MIPI_RATE_MAX / MEGA, + sensor->model->mipi_rate_min / MEGA, + sensor->model->mipi_rate_max / MEGA, sensor->mipi_rate / MEGA); return -EINVAL; } @@ -828,6 +1792,9 @@ static int vd55g1_prepare_clock_tree(struct vd55g1 *sensor) sensor->pixel_clock = sys_clk / pixel_div; + /* Keep the signed integer-menu storage alive for the control lifetime. */ + sensor->link_freq_menu = sensor->link_freq; + return 0; } @@ -850,8 +1817,8 @@ static int vd55g1_update_patgen(struct vd55g1 *sensor, u32 patgen_index) duster = VD55G1_DUSTER_DISABLE; } - vd55g1_write(sensor, VD55G1_REG_DUSTER_CTRL, duster, &ret); - vd55g1_write(sensor, VD55G1_REG_PATGEN_CTRL, reg, &ret); + vd55g1_write(sensor, REG_DUSTER_CTRL, duster, &ret); + vd55g1_write(sensor, REG_PATGEN_CTRL, reg, &ret); return ret; } @@ -862,33 +1829,45 @@ static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto) VD55G1_EXP_MANUAL; int ret = 0; - if (sensor->ae_ctrl->is_new) - vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + if (sensor->ae_ctrl->is_new) { + u32 expo_max = sensor->expo_ctrl->maximum; - if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB && + if (sensor->model->hdr && sensor->hdr_ctrl && + sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + expo_max = VD55G1_EXPOSURE_MAX_COARSE_SUB; + + vd55g1_write_ctx(sensor, REG_CTX_EXP_MODE, 0, expo_state, + &ret); + if (sensor->model != &vd55g0_model) + vd55g1_write(sensor, REG_EXPOSURE_MAX_COARSE, + expo_max, &ret); + } + + if (sensor->model->hdr && + sensor->hdr_ctrl->val == VD55G1_HDR_SUB && sensor->hdr_ctrl->is_new) { - vd55g1_write(sensor, VD55G1_REG_EXP_MODE(1), VD55G1_EXP_BYPASS, - &ret); + vd55g1_write_ctx(sensor, REG_CTX_EXP_MODE, 1, + VD55G1_EXP_BYPASS, &ret); if (ret) return ret; } if (!is_auto && sensor->expo_ctrl->is_new) - vd55g1_write(sensor, VD55G1_REG_MANUAL_COARSE_EXPOSURE, + vd55g1_write(sensor, REG_MANUAL_COARSE_EXPOSURE, sensor->expo_ctrl->val, &ret); if (!is_auto && sensor->again_ctrl->is_new) - vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN, + vd55g1_write(sensor, REG_MANUAL_ANALOG_GAIN, sensor->again_ctrl->val, &ret); if (!is_auto && sensor->dgain_ctrl->is_new) { - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0, + vd55g1_write(sensor, REG_MANUAL_DIGITAL_GAIN_CH0, sensor->dgain_ctrl->val, &ret); - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1, + vd55g1_write(sensor, REG_MANUAL_DIGITAL_GAIN_CH1, sensor->dgain_ctrl->val, &ret); - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2, + vd55g1_write(sensor, REG_MANUAL_DIGITAL_GAIN_CH2, sensor->dgain_ctrl->val, &ret); - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3, + vd55g1_write(sensor, REG_MANUAL_DIGITAL_GAIN_CH3, sensor->dgain_ctrl->val, &ret); } @@ -903,7 +1882,8 @@ static int vd55g1_lock_exposure(struct vd55g1 *sensor, u32 lock_val) int ret = 0; if (sensor->ae_ctrl->val == V4L2_EXPOSURE_AUTO) - vd55g1_write(sensor, VD55G1_REG_EXP_MODE(0), expo_state, &ret); + vd55g1_write_ctx(sensor, REG_CTX_EXP_MODE, 0, expo_state, + &ret); return ret; } @@ -915,10 +1895,9 @@ static int vd55g1_read_expo_cluster(struct vd55g1 *sensor) u64 dgain = 0; int ret = 0; - vd55g1_read(sensor, VD55G1_REG_APPLIED_COARSE_EXPOSURE, &exposure, - &ret); - vd55g1_read(sensor, VD55G1_REG_APPLIED_ANALOG_GAIN, &again, &ret); - vd55g1_read(sensor, VD55G1_REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); + vd55g1_read(sensor, REG_APPLIED_COARSE_EXPOSURE, &exposure, &ret); + vd55g1_read(sensor, REG_APPLIED_ANALOG_GAIN, &again, &ret); + vd55g1_read(sensor, REG_APPLIED_DIGITAL_GAIN, &dgain, &ret); if (ret) return ret; @@ -930,14 +1909,16 @@ static int vd55g1_read_expo_cluster(struct vd55g1 *sensor) } static int vd55g1_update_frame_length(struct vd55g1 *sensor, - unsigned int frame_length) + unsigned int frame_length) { int ret = 0; - if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) - vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(1), frame_length, - &ret); - vd55g1_write(sensor, VD55G1_REG_FRAME_LENGTH(0), frame_length, &ret); + if (sensor->model->hdr && + sensor->hdr_ctrl->val == VD55G1_HDR_SUB) + vd55g1_write_ctx(sensor, REG_CTX_FRAME_LENGTH, 1, + frame_length, &ret); + vd55g1_write_ctx(sensor, REG_CTX_FRAME_LENGTH, 0, frame_length, + &ret); return ret; } @@ -953,30 +1934,39 @@ static int vd55g1_update_exposure_target(struct vd55g1 *sensor, int index) }; int exposure_target = index2exposure_target[index]; - return vd55g1_write(sensor, VD55G1_REG_AE_TARGET_PERCENTAGE, - exposure_target, NULL); + return vd55g1_write(sensor, REG_AE_TARGET_PERCENTAGE, + exposure_target, NULL); } static int vd55g1_apply_cold_start(struct vd55g1 *sensor, struct v4l2_rect *crop) { - /* - * Cold start register is a single register expressed as exposure time - * in us. This differ from status registers being a combination of - * exposure, digital gain, and analog gain, requiring the following - * format conversion. - */ - unsigned int line_length = crop->width + sensor->hblank_ctrl->val; - unsigned int line_time_us = DIV_ROUND_UP(line_length * MEGA, - sensor->pixel_clock); - u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8); - u8 a_gain = DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val)); - unsigned int expo_us = sensor->expo_ctrl->val * d_gain * a_gain * - line_time_us; int ret = 0; - vd55g1_write(sensor, VD55G1_REG_AE_FORCE_COLDSTART, 1, &ret); - vd55g1_write(sensor, VD55G1_REG_AE_COLDSTART_EXP_TIME, expo_us, &ret); + if (sensor->model->ae_coldstart_exposure_in_us) { + /* + * Cold start register is a single register expressed as exposure time + * in us. This differ from status registers being a combination of + * exposure, digital gain, and analog gain, requiring the following + * format conversion. + */ + unsigned int line_length = + crop->width + sensor->hblank_ctrl->val; + unsigned int line_time_us = + DIV_ROUND_UP(line_length * MEGA, sensor->pixel_clock); + u8 d_gain = DIV_ROUND_CLOSEST(sensor->dgain_ctrl->val, 1 << 8); + u8 a_gain = + DIV_ROUND_CLOSEST(32, (32 - sensor->again_ctrl->val)); + unsigned int expo_us = + sensor->expo_ctrl->val * d_gain * a_gain * line_time_us; + vd55g1_write(sensor, REG_AE_COLDSTART_EXPOSURE_US, expo_us, + &ret); + } else { + vd55g1_write(sensor, REG_AE_COLDSTART_COARSE_EXPOSURE, + sensor->expo_ctrl->val, &ret); + } + + vd55g1_write(sensor, REG_AE_FORCE_COLDSTART, 1, &ret); return ret; } @@ -999,40 +1989,45 @@ static int vd55g1_update_hdr_mode(struct vd55g1 *sensor) { int ret = 0; + if (!sensor->model->hdr) + return -EINVAL; + switch (sensor->hdr_ctrl->val) { case VD55G1_NO_HDR: - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, - VD55G1_EXPOSURE_MAX_COARSE_DEF, &ret); - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, 0, &ret); - vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0, &ret); + vd55g1_write(sensor, REG_EXPOSURE_MAX_COARSE, + sensor->expo_ctrl->maximum, &ret); + vd55g1_write(sensor, REG_EXPOSURE_USE_CASES, 0, &ret); + vd55g1_write(sensor, REG_NEXT_CTX, 0x0, &ret); - vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 0, &ret); + vd55g1_write(sensor, REG_CTX_REPEAT_COUNT_CTX0, 0, &ret); - vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), - VD55G1_VT_MODE_NORMAL, &ret); - vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), - VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + vd55g1_write_ctx(sensor, REG_CTX_VT_MODE, 0, + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write_ctx(sensor, REG_CTX_MASK_FRAME_CTRL, 0, + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); break; case VD55G1_HDR_SUB: - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_MAX_COARSE, + vd55g1_write(sensor, REG_EXPOSURE_MAX_COARSE, VD55G1_EXPOSURE_MAX_COARSE_SUB, &ret); - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_USE_CASES, + vd55g1_write(sensor, REG_EXPOSURE_USE_CASES, VD55G1_EXPOSURE_USE_CASES_MULTI_CONTEXT, &ret); - vd55g1_write(sensor, VD55G1_REG_NEXT_CTX, 0x0001, &ret); - - vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX0, 1, &ret); - vd55g1_write(sensor, VD55G1_REG_CTX_REPEAT_COUNT_CTX1, 1, &ret); - - vd55g1_write(sensor, VD55G1_REG_VT_MODE(0), - VD55G1_VT_MODE_NORMAL, &ret); - vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(0), - VD55G1_MASK_FRAME_CTRL_MASK, &ret); - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(0), 0, &ret); - vd55g1_write(sensor, VD55G1_REG_VT_MODE(1), - VD55G1_VT_MODE_SUBTRACTION, &ret); - vd55g1_write(sensor, VD55G1_REG_MASK_FRAME_CTRL(1), - VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); - vd55g1_write(sensor, VD55G1_REG_EXPOSURE_INSTANCE(1), 1, &ret); + vd55g1_write(sensor, REG_NEXT_CTX, 0x0001, &ret); + + vd55g1_write(sensor, REG_CTX_REPEAT_COUNT_CTX0, 1, &ret); + vd55g1_write(sensor, REG_CTX_REPEAT_COUNT_CTX1, 1, &ret); + + vd55g1_write_ctx(sensor, REG_CTX_VT_MODE, 0, + VD55G1_VT_MODE_NORMAL, &ret); + vd55g1_write_ctx(sensor, REG_CTX_MASK_FRAME_CTRL, 0, + VD55G1_MASK_FRAME_CTRL_MASK, &ret); + vd55g1_write_ctx(sensor, REG_CTX_EXPOSURE_INSTANCE, 0, 0, + &ret); + vd55g1_write_ctx(sensor, REG_CTX_VT_MODE, 1, + VD55G1_VT_MODE_SUBTRACTION, &ret); + vd55g1_write_ctx(sensor, REG_CTX_MASK_FRAME_CTRL, 1, + VD55G1_MASK_FRAME_CTRL_OUTPUT, &ret); + vd55g1_write_ctx(sensor, REG_CTX_EXPOSURE_INSTANCE, 1, 1, + &ret); break; default: ret = -EINVAL; @@ -1048,9 +2043,9 @@ static int vd55g1_set_framefmt(struct vd55g1 *sensor, u8 binning; int ret = 0; - vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL, + vd55g1_write(sensor, REG_FORMAT_CTRL, vd55g1_get_fmt_bpp(format->code), &ret); - vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL, + vd55g1_write(sensor, REG_OIF_IMG_CTRL, vd55g1_get_fmt_data_type(format->code), &ret); switch (crop->width / format->width) { @@ -1062,17 +2057,34 @@ static int vd55g1_set_framefmt(struct vd55g1 *sensor, binning = VD55G1_READOUT_CTRL_BIN_MODE_DIGITAL_X2; break; } - vd55g1_write(sensor, VD55G1_REG_READOUT_CTRL, binning, &ret); + vd55g1_write(sensor, REG_READOUT_CTRL, binning, &ret); - vd55g1_write(sensor, VD55G1_REG_X_START(0), crop->left, &ret); - vd55g1_write(sensor, VD55G1_REG_X_WIDTH(0), crop->width, &ret); - vd55g1_write(sensor, VD55G1_REG_Y_START(0), crop->top, &ret); - vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(0), crop->height, &ret); + vd55g1_write_ctx(sensor, REG_CTX_X_START, 0, crop->left, &ret); + vd55g1_write_ctx(sensor, REG_CTX_Y_START, 0, crop->top, &ret); - vd55g1_write(sensor, VD55G1_REG_X_START(1), crop->left, &ret); - vd55g1_write(sensor, VD55G1_REG_X_WIDTH(1), crop->width, &ret); - vd55g1_write(sensor, VD55G1_REG_Y_START(1), crop->top, &ret); - vd55g1_write(sensor, VD55G1_REG_Y_HEIGHT(1), crop->height, &ret); + if (sensor->model->absolute_endpoints) { + vd55g1_write_ctx(sensor, REG_CTX_X_END, 0, + crop->left + crop->width - 1, &ret); + vd55g1_write_ctx(sensor, REG_CTX_Y_END, 0, + crop->top + crop->height - 1, &ret); + } else { + vd55g1_write_ctx(sensor, REG_CTX_X_WIDTH, 0, + crop->width, &ret); + vd55g1_write_ctx(sensor, REG_CTX_Y_HEIGHT, 0, + crop->height, &ret); + } + + if (sensor->model->hdr && + !sensor->model->absolute_endpoints) { + vd55g1_write_ctx(sensor, REG_CTX_X_START, 1, + crop->left, &ret); + vd55g1_write_ctx(sensor, REG_CTX_Y_START, 1, + crop->top, &ret); + vd55g1_write_ctx(sensor, REG_CTX_X_WIDTH, 1, + crop->width, &ret); + vd55g1_write_ctx(sensor, REG_CTX_Y_HEIGHT, 1, + crop->height, &ret); + } return ret; } @@ -1087,18 +2099,21 @@ static int vd55g1_update_gpios(struct vd55g1 *sensor, unsigned long gpio_mask) gpio_val = sensor->gpios[io]; if (gpio_val == VD55G1_GPIO_MODE_STROBE && - sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE) { + (!sensor->led_ctrl || + sensor->led_ctrl->val == V4L2_FLASH_LED_MODE_NONE)) { gpio_val = VD55G1_GPIO_MODE_IN; - if (sensor->hdr_ctrl->val == VD55G1_HDR_SUB) { + if (sensor->model->hdr && + sensor->hdr_ctrl->val == VD55G1_HDR_SUB) { /* Make its context 1 counterpart strobe too */ - vd55g1_write(sensor, - VD55G1_REG_GPIO_0_CTRL(1) + io, - gpio_val, &ret); + vd55g1_write_ctx_offset(sensor, + REG_CTX_GPIO_0_CTRL, + 1, io, + gpio_val, &ret); } } - ret = vd55g1_write(sensor, VD55G1_REG_GPIO_0_CTRL(0) + io, - gpio_val, &ret); + ret = vd55g1_write_ctx_offset(sensor, REG_CTX_GPIO_0_CTRL, 0, io, + gpio_val, &ret); } return ret; @@ -1106,17 +2121,17 @@ static int vd55g1_update_gpios(struct vd55g1 *sensor, unsigned long gpio_mask) static int vd55g1_ro_ctrls_setup(struct vd55g1 *sensor, struct v4l2_rect *crop) { - return vd55g1_write(sensor, VD55G1_REG_LINE_LENGTH, - crop->width + sensor->hblank_ctrl->val, NULL); + return vd55g1_write(sensor, REG_LINE_LENGTH, + crop->width + sensor->hblank_ctrl->val, NULL); } static void vd55g1_grab_ctrls(struct vd55g1 *sensor, bool enable) { /* These settings cannot change during stream */ - v4l2_ctrl_grab(sensor->hflip_ctrl, enable); - v4l2_ctrl_grab(sensor->vflip_ctrl, enable); - v4l2_ctrl_grab(sensor->patgen_ctrl, enable); - v4l2_ctrl_grab(sensor->hdr_ctrl, enable); + __v4l2_ctrl_grab(sensor->hflip_ctrl, enable); + __v4l2_ctrl_grab(sensor->vflip_ctrl, enable); + __v4l2_ctrl_grab(sensor->patgen_ctrl, enable); + __v4l2_ctrl_grab(sensor->hdr_ctrl, enable); } static int vd55g1_enable_streams(struct v4l2_subdev *sd, @@ -1135,10 +2150,9 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd, return ret; /* Configure output */ - vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE, - sensor->mipi_rate, &ret); - vd55g1_write(sensor, VD55G1_REG_OIF_CTRL, sensor->oif_ctrl, &ret); - vd55g1_write(sensor, VD55G1_REG_ISL_ENABLE, 0, &ret); + vd55g1_write(sensor, REG_MIPI_DATA_RATE, sensor->mipi_rate, &ret); + vd55g1_write(sensor, REG_OIF_CTRL, sensor->oif_ctrl, &ret); + vd55g1_write(sensor, REG_ISL_ENABLE, 0, &ret); if (ret) goto err_rpm_put; @@ -1166,8 +2180,8 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd, goto err_rpm_put; /* Start streaming */ - vd55g1_write(sensor, VD55G1_REG_STBY, VD55G1_STBY_START_STREAM, &ret); - vd55g1_poll_reg(sensor, VD55G1_REG_STBY, 0, &ret); + vd55g1_write(sensor, REG_STBY, VD55G1_STBY_START_STREAM, &ret); + vd55g1_poll_reg(sensor, REG_STBY, 0, &ret); vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_STREAMING, &ret); if (ret) goto err_rpm_put; @@ -1186,68 +2200,72 @@ static int vd55g1_disable_streams(struct v4l2_subdev *sd, u64 streams_mask) { struct vd55g1 *sensor = to_vd55g1(sd); - int ret = 0; + int stop_ret = 0; + int ret; /* Retrieve Expo cluster to enable coldstart of AE */ ret = vd55g1_read_expo_cluster(sensor); + if (ret) + dev_warn(sensor->dev, "Failed to save exposure: %d\n", ret); - vd55g1_write(sensor, VD55G1_REG_STREAMING, VD55G1_STREAMING_STOP_STREAM, - &ret); - vd55g1_poll_reg(sensor, VD55G1_REG_STREAMING, 0, &ret); - vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &ret); + /* A failed exposure read must not suppress the independent stop. */ + vd55g1_write(sensor, REG_STREAMING, VD55G1_STREAMING_STOP_STREAM, + &stop_ret); + vd55g1_poll_reg(sensor, REG_STREAMING, 0, &stop_ret); + vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, &stop_ret); - if (ret) + if (stop_ret) { dev_warn(sensor->dev, "Can't disable stream\n"); + return stop_ret; + } vd55g1_grab_ctrls(sensor, false); pm_runtime_put_autosuspend(sensor->dev); - return ret; + return 0; } static int vd55g1_patch(struct vd55g1 *sensor) { + const struct vd55g1_firmware *fw = sensor->revision->builtin_fw; u64 patch; int ret = 0; - /* Version 2 needs a patch while version 3 does not */ - if (sensor->version->id == VD55G1_MODEL_ID_2) { - vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR, - sizeof(vd55g1_patch_array), - vd55g1_patch_array, &ret); - vd55g1_write(sensor, VD55G1_REG_BOOT, + if (sensor->revision->needs_patch) { + vd55g1_write_array(sensor, REG_FWPATCH_START_ADDR, + fw->size, fw->data, &ret); + vd55g1_write(sensor, REG_BOOT, VD55G1_BOOT_PATCH_AND_BOOT, &ret); - vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret); + vd55g1_poll_reg(sensor, REG_BOOT, 0, &ret); + vd55g1_read(sensor, REG_FWPATCH_REVISION, &patch, &ret); if (ret) { - dev_err(sensor->dev, "Failed to apply patch\n"); + dev_dbg(sensor->dev, "Failed to apply patch\n"); return ret; } - vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret); - if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) + - VD55G1_FWPATCH_REVISION_MINOR) { - dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n", - VD55G1_FWPATCH_REVISION_MAJOR, - VD55G1_FWPATCH_REVISION_MINOR, + if (patch != (fw->v_major << 8) + fw->v_minor) { + dev_dbg(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n", + fw->v_major, fw->v_minor, (u8)(patch >> 8), (u8)(patch & 0xff)); return -ENODEV; } dev_dbg(sensor->dev, "patch %d.%d applied\n", (u8)(patch >> 8), (u8)(patch & 0xff)); + } - } else { - vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_BOOT, &ret); - vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret); + if (!sensor->model->implicit_patch_boot || !sensor->revision->needs_patch) { + vd55g1_write(sensor, REG_BOOT, VD55G1_BOOT_BOOT, &ret); + vd55g1_poll_reg(sensor, REG_BOOT, 0, &ret); if (ret) { - dev_err(sensor->dev, "Failed to boot\n"); + dev_dbg(sensor->dev, "Failed to boot\n"); return ret; } } ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL); if (ret) { - dev_err(sensor->dev, "Sensor waiting after boot failed\n"); + dev_dbg(sensor->dev, "Sensor waiting after boot failed\n"); return ret; } @@ -1258,6 +2276,7 @@ static int vd55g1_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { + struct vd55g1 *sensor = to_vd55g1(sd); const struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, 0); switch (sel->target) { @@ -1269,8 +2288,8 @@ static int vd55g1_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.top = 0; sel->r.left = 0; - sel->r.width = VD55G1_WIDTH; - sel->r.height = VD55G1_HEIGHT; + sel->r.width = sensor->model->width; + sel->r.height = sensor->model->height; return 0; } @@ -1284,7 +2303,7 @@ static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd, struct vd55g1 *sensor = to_vd55g1(sd); u32 base_code; - if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER) { + if (!sensor->model->bayer) { if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono)) return -EINVAL; base_code = vd55g1_mbus_formats_mono[code->index]; @@ -1303,9 +2322,8 @@ static int vd55g1_new_format_change_controls(struct vd55g1 *sensor, struct v4l2_rect *crop) { struct vd55g1_vblank_limits vblank; + struct vd55g1_frame_timings timings; unsigned int hblank; - unsigned int frame_length = 0; - unsigned int expo_max; int ret; /* Reset vblank and frame length to default */ @@ -1316,10 +2334,9 @@ static int vd55g1_new_format_change_controls(struct vd55g1 *sensor, return ret; /* Max exposure changes with vblank */ - frame_length = crop->height + sensor->vblank_ctrl->val; - expo_max = frame_length - VD55G1_EXPO_MAX_TERM; - ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, 1, - VD55G1_EXPO_DEF); + vd55g1_get_frame_timings(sensor, crop, &timings); + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, timings.expo_max, + 1, VD55G1_EXPO_DEF); if (ret) return ret; @@ -1341,9 +2358,14 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd, struct v4l2_rect pad_crop; unsigned int binning; - new_mode = v4l2_find_nearest_size(vd55g1_supported_modes, - ARRAY_SIZE(vd55g1_supported_modes), - width, height, sd_fmt->format.width, + if (sd_fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && + v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + new_mode = v4l2_find_nearest_size(sensor->model->modes, + sensor->model->num_modes, + width, height, + sd_fmt->format.width, sd_fmt->format.height); vd55g1_update_pad_fmt(sensor, new_mode, sd_fmt->format.code, @@ -1353,13 +2375,13 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd, * Use binning to maximize the crop rectangle size, and centre it in the * sensor. */ - binning = min(VD55G1_WIDTH / sd_fmt->format.width, - VD55G1_HEIGHT / sd_fmt->format.height); + binning = min(sensor->model->width / sd_fmt->format.width, + sensor->model->height / sd_fmt->format.height); binning = min(binning, 2U); pad_crop.width = sd_fmt->format.width * binning; pad_crop.height = sd_fmt->format.height * binning; - pad_crop.left = (VD55G1_WIDTH - pad_crop.width) / 2; - pad_crop.top = (VD55G1_HEIGHT - pad_crop.height) / 2; + pad_crop.left = (sensor->model->width - pad_crop.width) / 2; + pad_crop.top = (sensor->model->height - pad_crop.height) / 2; format = v4l2_subdev_state_get_format(sd_state, sd_fmt->pad); @@ -1394,13 +2416,13 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, if (ret) return ret; - if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER) + if (!sensor->model->bayer) code = vd55g1_mbus_formats_mono[0]; else code = vd55g1_mbus_formats_bayer[0][0]; fmt.format.code = vd55g1_get_fmt_code(sensor, code); - fmt.format.width = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].width; - fmt.format.height = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].height; + fmt.format.width = sensor->model->modes[VD55G1_MODE_IDX_DEF].width; + fmt.format.height = sensor->model->modes[VD55G1_MODE_IDX_DEF].height; return vd55g1_set_pad_fmt(sd, sd_state, &fmt); } @@ -1412,16 +2434,16 @@ static int vd55g1_enum_frame_size(struct v4l2_subdev *sd, struct vd55g1 *sensor = to_vd55g1(sd); u32 code; - if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes)) + if (fse->index >= sensor->model->num_modes) return -EINVAL; code = vd55g1_get_fmt_code(sensor, fse->code); if (fse->code != code) return -EINVAL; - fse->min_width = vd55g1_supported_modes[fse->index].width; + fse->min_width = sensor->model->modes[fse->index].width; fse->max_width = fse->min_width; - fse->min_height = vd55g1_supported_modes[fse->index].height; + fse->min_height = sensor->model->modes[fse->index].height; fse->max_height = fse->min_height; return 0; @@ -1476,8 +2498,6 @@ static int vd55g1_g_volatile_ctrl(struct v4l2_ctrl *ctrl) static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) { struct vd55g1 *sensor = ctrl_to_vd55g1(ctrl); - unsigned int frame_length = 0; - unsigned int expo_max; struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(&sensor->sd); struct v4l2_rect *crop = @@ -1485,6 +2505,7 @@ static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(state, 0); unsigned int hblank = vd55g1_get_hblank_min(sensor, format, crop); + struct vd55g1_frame_timings timings; bool is_auto = false; int ret = 0; @@ -1494,10 +2515,11 @@ static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) /* Update controls state, range, etc. whatever the state of the HW */ switch (ctrl->id) { case V4L2_CID_VBLANK: - frame_length = crop->height + ctrl->val; - expo_max = frame_length - VD55G1_EXPO_MAX_TERM; - ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, expo_max, - 1, VD55G1_EXPO_DEF); + case V4L2_CID_FLASH_LED_MODE: + vd55g1_get_frame_timings(sensor, crop, &timings); + ret = __v4l2_ctrl_modify_range(sensor->expo_ctrl, 0, + timings.expo_max, 1, + VD55G1_EXPO_DEF); break; case V4L2_CID_EXPOSURE_AUTO: is_auto = (ctrl->val == V4L2_EXPOSURE_AUTO); @@ -1527,7 +2549,7 @@ static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_HFLIP: - ret = vd55g1_write(sensor, VD55G1_REG_ORIENTATION, + ret = vd55g1_write(sensor, REG_ORIENTATION, sensor->hflip_ctrl->val | (sensor->vflip_ctrl->val << 1), NULL); @@ -1549,7 +2571,7 @@ static int vd55g1_s_ctrl(struct v4l2_ctrl *ctrl) ret = vd55g1_update_exposure_target(sensor, ctrl->val); break; case V4L2_CID_VBLANK: - ret = vd55g1_update_frame_length(sensor, frame_length); + ret = vd55g1_update_frame_length(sensor, timings.frame_length); break; case V4L2_CID_FLASH_LED_MODE: ret = vd55g1_update_gpios(sensor, sensor->ext_leds_mask); @@ -1579,16 +2601,17 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) struct v4l2_ctrl *ctrl; struct v4l2_fwnode_device_properties fwnode_props; struct vd55g1_vblank_limits vblank; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt format; + struct v4l2_rect crop; unsigned int hblank; - struct v4l2_subdev_state *state = - v4l2_subdev_lock_and_get_active_state(&sensor->sd); - struct v4l2_rect *crop = - v4l2_subdev_state_get_crop(state, 0); - struct v4l2_mbus_framefmt *format = - v4l2_subdev_state_get_format(state, 0); int ret; - v4l2_ctrl_handler_init(hdl, 16); + /* Control constructors acquire the shared state/handler lock. */ + state = v4l2_subdev_lock_and_get_active_state(&sensor->sd); + crop = *v4l2_subdev_state_get_crop(state, 0); + format = *v4l2_subdev_state_get_format(state, 0); + v4l2_subdev_unlock_state(state); /* Flip cluster */ sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, @@ -1599,6 +2622,8 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) 0, 1, 1, 0); if (sensor->vflip_ctrl) sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + if (hdl->error) + return hdl->error; v4l2_ctrl_cluster(2, &sensor->hflip_ctrl); /* Exposition cluster */ @@ -1614,6 +2639,8 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) VD55G1_FRAME_LENGTH_DEF - VD55G1_EXPO_MAX_TERM, 1, VD55G1_EXPO_DEF); + if (hdl->error) + return hdl->error; v4l2_ctrl_auto_cluster(4, &sensor->ae_ctrl, V4L2_EXPOSURE_MANUAL, true); sensor->patgen_ctrl = @@ -1621,7 +2648,7 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) ARRAY_SIZE(vd55g1_tp_menu) - 1, 0, 0, vd55g1_tp_menu); ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, - 0, 0, &sensor->link_freq); + 0, 0, &sensor->link_freq_menu); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, @@ -1638,17 +2665,22 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) ARRAY_SIZE(vd55g1_ev_bias_menu) - 1, ARRAY_SIZE(vd55g1_ev_bias_menu) / 2, vd55g1_ev_bias_menu); - sensor->hdr_ctrl = - v4l2_ctrl_new_std_menu_items(hdl, ops, - V4L2_CID_HDR_SENSOR_MODE, - ARRAY_SIZE(vd55g1_hdr_menu) - 1, 0, - VD55G1_NO_HDR, vd55g1_hdr_menu); - hblank = vd55g1_get_hblank_min(sensor, format, crop); + if (sensor->model->hdr) { + sensor->hdr_ctrl = + v4l2_ctrl_new_std_menu_items(hdl, ops, + V4L2_CID_HDR_SENSOR_MODE, + ARRAY_SIZE(vd55g1_hdr_menu) - 1, + 0, VD55G1_NO_HDR, + vd55g1_hdr_menu); + } + if (hdl->error) + return hdl->error; + hblank = vd55g1_get_hblank_min(sensor, &format, &crop); 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; - vd55g1_get_vblank_limits(sensor, crop, &vblank); + vd55g1_get_vblank_limits(sensor, &crop, &vblank); sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, vblank.min, vblank.max, 1, vblank.def); @@ -1661,85 +2693,85 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) V4L2_FLASH_LED_MODE_FLASH, 0, V4L2_FLASH_LED_MODE_NONE); } + if (hdl->error) + return hdl->error; ret = v4l2_fwnode_device_parse(sensor->dev, &fwnode_props); if (ret) - goto free_ctrls; - - ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); - if (ret) - goto free_ctrls; - - sensor->sd.ctrl_handler = hdl; - goto unlock_state; + return ret; -free_ctrls: - v4l2_ctrl_handler_free(hdl); -unlock_state: - v4l2_subdev_unlock_state(state); - return ret; + return v4l2_ctrl_new_fwnode_properties(hdl, ops, &fwnode_props); } -static const struct vd55g1_version * -vd55g1_get_version(enum vd55g1_model_id id, - enum vd55g1_color_version color) +static int vd55g1_match_revision(struct vd55g1 *sensor, u64 rev) { - for (unsigned int i = 0; i < ARRAY_SIZE(vd55g1_versions); i++) { - if (vd55g1_versions[i].id == id && - vd55g1_versions[i].color == color) - return &vd55g1_versions[i]; + const struct vd55g1_revision *revision; + + for (int i = 0; i < sensor->model->num_revisions; i++) { + revision = &sensor->model->revisions[i]; + + if (revision->id == rev || + revision->id == VD55G1_REVISION_ANY) { + sensor->revision = revision; + return 0; + } } - return NULL; + return -ENOENT; } static int vd55g1_detect(struct vd55g1 *sensor) { - const struct vd55g1_version *dt_version = - device_get_match_data(sensor->dev); - const struct vd55g1_version *version; - u64 color, id; + u64 color, id, rev; int ret = 0; - vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, &ret); - vd55g1_read(sensor, VD55G1_REG_COLOR_VERSION, &color, &ret); - if (ret) + vd55g1_read(sensor, REG_MODEL_ID, &id, &ret); + vd55g1_read(sensor, REG_REVISION, &rev, &ret); + vd55g1_read(sensor, REG_COLOR_VERSION, &color, &ret); + if (ret) { + dev_dbg(sensor->dev, + "Failed to read sensor model: %d\n", ret); return ret; + } - version = vd55g1_get_version(id, color); - if (!version) { - dev_warn(sensor->dev, "Unsupported sensor version, expected %s\n", - dt_version->name); + if (id != sensor->model->id) { + dev_dbg(sensor->dev, + "Expected %s (0x%x), but detected mismatched sensor id 0x%x\n", + sensor->model->name, (u32)sensor->model->id, (u32)id); return -ENODEV; } - if (version->id != dt_version->id || - version->color != dt_version->color) { - dev_err(sensor->dev, "Probed sensor version %s and device tree definition %s mismatch", - version->name, dt_version->name); + if (color != sensor->model->color) { + dev_dbg(sensor->dev, + "Expected %s (0x%x), but detected mismatched color version 0x%x\n", + sensor->model->name, (u32)sensor->model->color, (u32)color); return -ENODEV; } - sensor->version = version; + ret = vd55g1_match_revision(sensor, rev); + if (ret == -ENOENT) { + dev_dbg(sensor->dev, + "Unsupported sensor revision 0x%x, expected %s\n", + (u16)rev, sensor->model->name); + return -ENODEV; + } return 0; } -static int vd55g1_power_on(struct device *dev) +static int vd55g1_power_on(struct vd55g1 *sensor) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct vd55g1 *sensor = to_vd55g1(sd); int ret; ret = regulator_bulk_enable(ARRAY_SIZE(vd55g1_supply_name), sensor->supplies); if (ret) { - dev_err(dev, "Failed to enable regulators %d\n", ret); + dev_dbg(sensor->dev, "Failed to enable regulators: %d\n", ret); return ret; } ret = clk_prepare_enable(sensor->xclk); if (ret) { - dev_err(dev, "Failed to enable clock %d\n", ret); + dev_dbg(sensor->dev, "Failed to enable clock: %d\n", ret); goto disable_bulk; } @@ -1747,23 +2779,32 @@ static int vd55g1_power_on(struct device *dev) usleep_range(5000, 10000); ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_READY_TO_BOOT, NULL); if (ret) { - dev_err(dev, "Sensor reset failed %d\n", ret); + dev_dbg(sensor->dev, "Sensor reset failed: %d\n", ret); goto disable_clock; } ret = vd55g1_detect(sensor); - if (ret) { - dev_err(dev, "Sensor detect failed %d\n", ret); + if (ret) goto disable_clock; - } /* Setup clock now to advance through system FSM states */ - vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret); + vd55g1_write(sensor, REG_EXT_CLOCK, sensor->xclk_freq, &ret); + if (ret) { + dev_dbg(sensor->dev, + "Failed to write external clock frequency: %d\n", + ret); + goto disable_clock; + } ret = vd55g1_patch(sensor); - if (ret) { - dev_err(dev, "Sensor patch failed %d\n", ret); + if (ret) goto disable_clock; + + /* Establish the sensor-only GPIO policy after firmware reaches standby. */ + if (sensor->model == &vd55g0_model) { + ret = vd55g1_update_gpios(sensor, GENMASK(VD55G1_NB_GPIOS - 1, 0)); + if (ret) + goto disable_clock; } return 0; @@ -1778,11 +2819,8 @@ static int vd55g1_power_on(struct device *dev) return ret; } -static int vd55g1_power_off(struct device *dev) +static int vd55g1_power_off(struct vd55g1 *sensor) { - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct vd55g1 *sensor = to_vd55g1(sd); - gpiod_set_value_cansleep(sensor->reset_gpio, 1); clk_disable_unprepare(sensor->xclk); regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); @@ -1790,6 +2828,32 @@ static int vd55g1_power_off(struct device *dev) return 0; } +static int vd55g1_pm_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + int ret; + + ret = vd55g1_power_on(sensor); + if (ret) + dev_err(dev, "Failed to power on during PM resume: %d\n", ret); + + return ret; +} + +static int vd55g1_pm_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct vd55g1 *sensor = to_vd55g1(sd); + int ret; + + ret = vd55g1_power_off(sensor); + if (ret) + dev_err(dev, "Failed to power off during PM suspend: %d\n", ret); + + return ret; +} + static int vd55g1_check_csi_conf(struct vd55g1 *sensor, struct fwnode_handle *endpoint) { @@ -1804,7 +2868,7 @@ static int vd55g1_check_csi_conf(struct vd55g1 *sensor, /* Check lanes number */ n_lanes = ep.bus.mipi_csi2.num_data_lanes; if (n_lanes != 1) { - dev_err(sensor->dev, "Sensor only supports 1 lane, found %d\n", + dev_dbg(sensor->dev, "Sensor only supports 1 lane, found %d\n", n_lanes); ret = -EINVAL; goto done; @@ -1812,7 +2876,7 @@ static int vd55g1_check_csi_conf(struct vd55g1 *sensor, /* Clock lane must be first */ if (ep.bus.mipi_csi2.clock_lane != 0) { - dev_err(sensor->dev, "Clock lane must be mapped to lane 0\n"); + dev_dbg(sensor->dev, "Clock lane must be mapped to lane 0\n"); ret = -EINVAL; goto done; } @@ -1823,12 +2887,12 @@ static int vd55g1_check_csi_conf(struct vd55g1 *sensor, /* Check the link frequency set in device tree */ if (!ep.nr_of_link_frequencies) { - dev_err(sensor->dev, "link-frequency property not found in DT\n"); + dev_dbg(sensor->dev, "link-frequency property not found in DT\n"); ret = -EINVAL; goto done; } if (ep.nr_of_link_frequencies != 1) { - dev_err(sensor->dev, "Multiple link frequencies not supported\n"); + dev_dbg(sensor->dev, "Multiple link frequencies not supported\n"); ret = -EINVAL; goto done; } @@ -1852,16 +2916,20 @@ static int vd55g1_parse_dt_gpios_array(struct vd55g1 *sensor, *nb = 0; return 0; } + if (*nb < 0) + return *nb; + if (!*nb || *nb > VD55G1_NB_GPIOS) + return -EINVAL; ret = device_property_read_u32_array(sensor->dev, prop_name, array, *nb); if (ret) { - dev_err(sensor->dev, "Failed to read %s prop\n", prop_name); + dev_dbg(sensor->dev, "Failed to read %s prop\n", prop_name); return ret; } for (i = 0; i < *nb; i++) { if (array[i] >= VD55G1_NB_GPIOS) { - dev_err(sensor->dev, "Invalid GPIO number %d\n", + dev_dbg(sensor->dev, "Invalid GPIO number %d\n", array[i]); return -EINVAL; } @@ -1888,6 +2956,10 @@ static int vd55g1_parse_dt_gpios(struct vd55g1 *sensor) if (ret) return ret; + /* Qualify VD55G0 illumination separately from the sensor-only port. */ + if (sensor->model == &vd55g0_model && nb_gpios_leds) + return -EOPNOTSUPP; + for (i = 0; i < nb_gpios_leds; i++) { sensor->gpios[led_gpios[i]] = VD55G1_GPIO_MODE_STROBE; set_bit(led_gpios[i], &sensor->ext_leds_mask); @@ -1929,14 +3001,19 @@ static int vd55g1_subdev_init(struct vd55g1 *sensor) sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); if (ret) { - dev_err(sensor->dev, "Failed to init media entity: %d\n", ret); + dev_dbg(sensor->dev, "Failed to init media entity: %d\n", ret); return ret; } + ret = v4l2_ctrl_handler_init(&sensor->ctrl_handler, 16); + if (ret) + goto err_media; + + sensor->sd.ctrl_handler = &sensor->ctrl_handler; sensor->sd.state_lock = sensor->ctrl_handler.lock; ret = v4l2_subdev_init_finalize(&sensor->sd); if (ret) { - dev_err(sensor->dev, "Subdev init error: %d\n", ret); + dev_dbg(sensor->dev, "Subdev init error: %d\n", ret); goto err_ctrls; } @@ -1946,16 +3023,17 @@ static int vd55g1_subdev_init(struct vd55g1 *sensor) */ ret = vd55g1_init_ctrls(sensor); if (ret) { - dev_err(sensor->dev, "Controls initialization failed %d\n", + dev_dbg(sensor->dev, "Controls initialization failed %d\n", ret); - goto err_media; + goto err_subdev; } return 0; +err_subdev: + v4l2_subdev_cleanup(&sensor->sd); err_ctrls: - v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); - + v4l2_ctrl_handler_free(&sensor->ctrl_handler); err_media: media_entity_cleanup(&sensor->sd.entity); return ret; @@ -1965,8 +3043,8 @@ static void vd55g1_subdev_cleanup(struct vd55g1 *sensor) { v4l2_async_unregister_subdev(&sensor->sd); v4l2_subdev_cleanup(&sensor->sd); + v4l2_ctrl_handler_free(&sensor->ctrl_handler); media_entity_cleanup(&sensor->sd.entity); - v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); } static int vd55g1_get_regulators(struct vd55g1 *sensor) @@ -1992,7 +3070,11 @@ static int vd55g1_probe(struct i2c_client *client) return -ENOMEM; sensor->dev = &client->dev; + sensor->model = device_get_match_data(dev); + v4l2_i2c_subdev_init(&sensor->sd, client, &vd55g1_subdev_ops); + /* Failed stops retain the stream PM reference and grabbed controls. */ + sensor->sd.flags |= V4L2_SUBDEV_FL_STOP_RETRY; ret = vd55g1_parse_dt(sensor); if (ret) @@ -2011,7 +3093,7 @@ static int vd55g1_probe(struct i2c_client *client) sensor->xclk_freq = clk_get_rate(sensor->xclk); ret = vd55g1_prepare_clock_tree(sensor); if (ret) - return ret; + return dev_err_probe(dev, ret, "Unsupported clock configuration\n"); sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); @@ -2025,39 +3107,53 @@ static int vd55g1_probe(struct i2c_client *client) "Failed to init regmap\n"); /* Detect if sensor is present and if its revision is supported */ - ret = vd55g1_power_on(dev); + ret = vd55g1_power_on(sensor); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to power on during probe\n"); - /* Enable pm_runtime and power off the sensor */ - pm_runtime_set_active(dev); + /* Retain the probe PM reference until the subdevice is published. */ + ret = pm_runtime_set_active(dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to set runtime PM active\n"); + goto err_power_off; + } pm_runtime_get_noresume(dev); pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, 4000); pm_runtime_use_autosuspend(dev); - pm_runtime_put_autosuspend(dev); ret = vd55g1_subdev_init(sensor); if (ret) { - dev_err(dev, "V4l2 init failed: %d\n", ret); - goto err_power_off; + dev_err_probe(dev, ret, "V4l2 subdev init failed\n"); + goto err_disable_pm; } - ret = v4l2_async_register_subdev(&sensor->sd); + /* + * Register as a sensor-style async subdev so that fwnode endpoint + * sub-notifiers (e.g. a "flash-leds" reference binding the PM8550 + * flash controller for the IR illuminator) are created for this + * device, matching v4l2-async SENSOR async types. Safe for all + * models in this family; illumination itself stays opt-in via + * the ext-leds device-tree mask. + */ + ret = v4l2_async_register_subdev_sensor(&sensor->sd); if (ret) { - dev_err(dev, "async subdev register failed %d\n", ret); + dev_err_probe(dev, ret, "async subdev register failed\n"); goto err_subdev; } + pm_runtime_put_autosuspend(dev); + return 0; err_subdev: vd55g1_subdev_cleanup(sensor); -err_power_off: +err_disable_pm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_dont_use_autosuspend(dev); - vd55g1_power_off(dev); +err_power_off: + vd55g1_power_off(sensor); return ret; } @@ -2071,26 +3167,34 @@ static void vd55g1_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); if (!pm_runtime_status_suspended(&client->dev)) - vd55g1_power_off(&client->dev); + vd55g1_power_off(sensor); pm_runtime_set_suspended(&client->dev); pm_runtime_dont_use_autosuspend(&client->dev); } +static const struct acpi_device_id vd55g1_acpi_match[] = { + { .id = "SMO55F0", .driver_data = (kernel_ulong_t)&vd55g0_model }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, vd55g1_acpi_match); + static const struct of_device_id vd55g1_dt_ids[] = { - { .compatible = "st,vd55g1", .data = (void *)&vd55g1_versions[0] }, - { .compatible = "st,vd55g4", .data = (void *)&vd55g1_versions[1] }, - { .compatible = "st,vd65g4", .data = (void *)&vd55g1_versions[2] }, + { .compatible = "st,vd55g0", .data = &vd55g0_model }, + { .compatible = "st,vd55g1", .data = &vd55g1_model }, + { .compatible = "st,vd55g4", .data = &vd55g4_model }, + { .compatible = "st,vd65g4", .data = &vd65g4_model }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, vd55g1_dt_ids); static const struct dev_pm_ops vd55g1_pm_ops = { - SET_RUNTIME_PM_OPS(vd55g1_power_off, vd55g1_power_on, NULL) + SET_RUNTIME_PM_OPS(vd55g1_pm_suspend, vd55g1_pm_resume, NULL) }; static struct i2c_driver vd55g1_i2c_driver = { .driver = { .name = "vd55g1", + .acpi_match_table = vd55g1_acpi_match, .of_match_table = vd55g1_dt_ids, .pm = &vd55g1_pm_ops, }, @@ -2102,5 +3206,6 @@ module_i2c_driver(vd55g1_i2c_driver); MODULE_AUTHOR("Benjamin Mugnier "); MODULE_AUTHOR("Sylvain Petinot "); +MODULE_AUTHOR("Peter Marshall "); MODULE_DESCRIPTION("VD55G1 camera subdev driver"); MODULE_LICENSE("GPL");