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");