diff options
85 files changed, 16150 insertions, 994 deletions
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx390.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx390.yaml new file mode 100644 index 000000000000..3b654948e2e5 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx390.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx390.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX390 Camera Sensor + +maintainers: + - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + +description: |- + Sony IMX390 camera sensor. + +properties: + compatible: + enum: + - sony,imx390 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: inck + + xclr-gpios: + maxItems: 1 + description: + Specifier for the GPIO connected to the XCLR (System Reset) pin. + + port: + $ref: /schemas/graph.yaml#/properties/port + additionalProperties: false + + properties: + endpoint: + $ref: ../video-interfaces.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + + camera@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&fixed_clock>; + clock-names = "inck"; + + xclr-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>; + + port { + camera1: endpoint { + remote-endpoint = <&vin1a_ep>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml new file mode 100644 index 000000000000..27cacdfa080f --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ti,ds90ub953.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DS90UB953 FPD-Link 3 Serializer + +maintainers: + - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + +description: |- + The TI DS90UB953 is an FPD-Link 3 video serializer for MIPI CSI-2. + +properties: + compatible: + enum: + - ti,ds90ub953-q1 + + '#gpio-cells': + const: 2 + + gpio-controller: true + + ports: true + +required: + - compatible + - '#gpio-cells' + - gpio-controller + - ports + +additionalProperties: false + +examples: + - | + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml new file mode 100644 index 000000000000..be2a92c1f754 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml @@ -0,0 +1,196 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ti,ds90ub960.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DS90UB960 FPD-Link 3 Deserializer Hub + +maintainers: + - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + +description: |- + The TI DS90UB960 is an FPD-Link 3 video deserializer with 4 FPD-Link 3 + inputs and 2 MIPI CSI-2 outputs. It also features I2C and GPIO forwarding. + +properties: + compatible: + enum: + - ti,ds90ub960-q1 + + reg: + minItems: 1 + maxItems: 5 + description: + i2c addresses for the deserializer and the serializers + + reg-names: + minItems: 1 + items: + - const: main + - enum: [ ser0, ser1, ser2, ser3 ] + - enum: [ ser0, ser1, ser2, ser3 ] + - enum: [ ser0, ser1, ser2, ser3 ] + - enum: [ ser0, ser1, ser2, ser3 ] + - enum: [ ser0, ser1, ser2, ser3 ] + + + clocks: + maxItems: 1 + description: + Reference clock connected to the REFCLK pin. + + powerdown-gpios: + maxItems: 1 + description: + Specifier for the GPIO connected to the PWDN pin. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + description: FPD-Link 3 input 0 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + + properties: + mode: + description: FPD3 Input Mode + bc-freq: + description: back channel frequency + + required: + - mode + - bc-freq + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + description: FPD-Link 3 input 1 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + + properties: + mode: + description: FPD3 Input Mode + bc-freq: + description: back channel frequency + + required: + - mode + - bc-freq + + port@2: + $ref: /schemas/graph.yaml#/$defs/port-base + description: FPD-Link 3 input 2 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + + properties: + mode: + description: FPD3 Input Mode + bc-freq: + description: back channel frequency + + required: + - mode + - bc-freq + + port@3: + $ref: /schemas/graph.yaml#/$defs/port-base + description: FPD-Link 3 input 3 + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + + properties: + mode: + description: FPD3 Input Mode + bc-freq: + description: back channel frequency + + required: + - mode + - bc-freq + + port@4: + $ref: /schemas/graph.yaml#/$defs/port-base + description: CSI-2 Output 0 + + port@5: + $ref: /schemas/graph.yaml#/$defs/port-base + description: CSI-2 Output 1 + +required: + - compatible + - reg + - clocks + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + + deser@3d { + compatible = "ti,ds90ub960-q1"; + + reg-names = "main", "ser0", "ser1", "ser2", "ser3"; + reg = <0x3d>, <0x44>, <0x45>, <0x46>, <0x47>; + + clocks = <&fixed_clock>; + + powerdown-gpios = <&pca9555 7 GPIO_ACTIVE_LOW>; + + i2c-alias-pool = /bits/ 16 <0x4a 0x4b 0x4c 0x4d 0x4e 0x4f>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + /* Port 0, Camera 0 */ + port@0 { + reg = <0>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub913_out>; + + mode = <0>; + bc-freq = <2500000>; + + }; + }; + + /* Port 4, CSI-2 TX */ + port@4 { + reg = <4>; + ds90ub960_0_csi_out: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + data-rate = <1600000000>; + remote-endpoint = <&csi2_phy0>; + }; + }; + }; + + ds90ub960_0_atr: i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; +... diff --git a/Documentation/driver-api/media/v4l2-subdev.rst b/Documentation/driver-api/media/v4l2-subdev.rst index bb5b1a7cdfd9..095610a58d95 100644 --- a/Documentation/driver-api/media/v4l2-subdev.rst +++ b/Documentation/driver-api/media/v4l2-subdev.rst @@ -491,6 +491,42 @@ The :c:func:`v4l2_i2c_new_subdev` function will call :c:type:`i2c_board_info` structure using the ``client_type`` and the ``addr`` to fill it. +Centrally managed subdev active state +------------------------------------- + +Traditionally V4L2 subdev drivers maintained internal state for the active +configuration for the subdev. This is often implemented as an array of struct +v4l2_mbus_framefmt, one entry for each pad. + +In addition to the active configuration, each subdev filehandle has contained +an array of struct v4l2_subdev_pad_config, managed by V4L2 core, which +contains the TRY configuration. + +To simplify the subdev drivers the V4L2 subdev API now optionally supports a +centrally managed active configuration. A subdev driver must use +v4l2_subdev_init_finalize() to initialize the active state between calls to +media_entity_pads_init() and v4l2_*_register_subdev(), and must call +v4l2_subdev_cleanup() to free the state. + +The active state must be locked before access, and can be done with +v4l2_subdev_lock_state() or v4l2_subdev_lock_active_state(). + +The V4L2 core will pass either the TRY or ACTIVE state to various subdev ops. +Unfortunately all the subdev drivers do not comply with this yet, and may pass +NULL for the ACTIVE case. This is only a problem for subdev drivers which use +the cetrally managed active state and are used in media pipelines with older +subdev drivers. In these cases the called subdev ops must also handle the NULL +case. This can be easily managed by the use of +v4l2_subdev_validate_and_lock_state() helper. + +Streams, multiplexed media pads and internal routing +---------------------------------------------------- + +A subdevice driver can implement support for multiplexed streams by setting +the V4L2_SUBDEV_FL_MULTIPLEXED subdev flag and implementing support for +centrally managed subdev active state, routing and stream based +configuration. + V4L2 sub-device functions and data structures --------------------------------------------- diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index 6c6a5ea8ba26..3f17709536ef 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -502,128 +502,166 @@ source pads. subdev-formats +Streams, multiplexed media pads and internal routing +---------------------------------------------------- + +Commonly V4L2 subdevices support only separate video streams, that is, each +link in the media grap and each pad in a subdevice passes through a single +video stream. Thus each pad contains a format configuration for that single +stream. In some cases a subdev can do stream processing and split a stream +into two or compose two streams into one, but the inputs and outputs for the +subdev are still a single stream per pad. + +Some hardware, e.g. MIPI CSI-2, support multiplexed streams, that is, a single +bus carries multiple streams. Thus a camera could output two streams, a pixel +stream and a metadata stream, and a bridge subdev could route the streams +from multiple input pads into a single output pad. + +Subdevice drivers that support multiplexed streams are compatible with +non-multiplexed subdev drivers, but, of course, requires such a routing +configuration where the link between those two types of drivers contain only +a single stream. + +Understanding streams +^^^^^^^^^^^^^^^^^^^^^ + +A stream is a stream of content (e.g. pixel data or metadata) flowing through +the media pipeline from a source (e.g. a sensor) towards the final sink +(e.g. a receiver in a SoC). Each media link carries all the streams from +one end of the link to the other, whereas subdevices have routing tables +which describe how the incoming streams from sink pads are routed to the +source pads. -Multiplexed media pads and internal routing -------------------------------------------- - -Routing Table -^^^^^^^^^^^^^ - -Subdevice drivers may expose the internal connections between media pads of an -entity by exposing a routing table that applications can inspect and manipulate. -A routing table is described by a struct :c:type:`v4l2_subdev_routing`, which -contains ``num_routes`` route entries, each one represented by a struct -:c:type:`v4l2_subdev_route`. - -Data routes do not just connect one pad to another in an entity, but they refer -instead to the ``streams`` a media pad provides. Streams are data connection -endpoints in a media pad. Multiplexed media pads expose multiple ``streams`` -which represent, when the underlying hardware technology allows that, logical -data flows transported over a single physical media bus. - -A noteworthy example of logical stream multiplexing techniques is represented -by the data interleaving mechanism implemented by mean of Virtual Channels as -defined by the MIPI CSI-2 media bus specifications. A subdevice that implements -support for Virtual Channel data interleaving might expose up to 4 data -``streams``, one for each available Virtual Channel, on the source media pad -that represents a CSI-2 connection endpoint. - -A route is defined as a connection between a ``(sink_pad, sink_stream)`` pair -and a ``(source_pad, source_stream)`` pair, where ``sink_pad`` and -``source_pad`` are the indexes of two media pads part of the same media entity, -and ``sink_stream`` and ``source_stream`` are the identifiers of the data -streams to be connected in the media pads. The stream identifiers are arbitrary -numbers, i.e. they have no relevance to the hardware, but they must be unique on -a single pad, and the entity on the other side of the link must have a matching -stream identifier. - -The current routes are reported to applications in a routing table which can be -inspected using the :ref:`routing <VIDIOC_SUBDEV_G_ROUTING>` ioctl. - -Routes can be added or removed by adding or removing them to/from the routing -table. Also, a route in the routing table can be activated and deactivated by -setting or clearing the ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag in the ``flags`` -field of struct :c:type:`v4l2_subdev_route`. - -A subdev driver may create routes that cannot be modified by applications. Such -routes are identified by the presence of the ``V4L2_SUBDEV_ROUTE_FL_IMMUTABLE`` -flag in the ``flags`` field of struct :c:type:`v4l2_subdev_route`. Immutable -routes are always active. - -A special type of a route is a "source route", marked with -``V4L2_SUBDEV_ROUTE_FL_SOURCE`` flag. Such routes exists in e.g. sensors as the -routes' origins are internal to the device. A source route has valid -``source_pad`` and ``source_stream``, but ``sink_pad`` and ``sink_stream`` are -not used. The purpose of a source route is to describe the streams. - -As an example, a subdevice with two sink pads and one output pad has the pads -defined as follows: - -.. flat-table:: - :header-rows: 1 +A stream ID (often just "stream") is a media link-local identifier for a +stream. In other words, configuration for a particular stream ID must exist +on both sides of a media link, but another stream ID can be used for the same +stream at the other side of the subdevice. - * - Pad Index - - Function - * - 0 - - SINK - * - 1 - - SINK - * - 2 - - SOURCE +A stream at a specific point in the media pipeline is identified with the +subdev and a (pad, stream) pair. For subdevices that do not support +multiplexed streams the 'stream' is always 0. -A case where the subdevice would receive a single stream via each sink pad, and -combine them to the source pad would result in a routing table as follows: +Configuring streams +^^^^^^^^^^^^^^^^^^^ -.. flat-table:: routing table - :header-rows: 1 +The configuration of the streams is done individually for each subdevice and +the validity of the streams between subdevices is validated when the pipeline +is started. - * - Sink Pad/Sink Stream - - Source Pad/Source Stream - * - 0/0 - - 2/0 - * - 1/0 - - 2/1 +There are three steps in configuring the streams: + +1) Set up links. Connect the pads between subdevices using the :ref:`Media +Controller API <media_controller>` + +2) Routing. The routing table for the subdevice must be set with +:ref:`VIDIOC_SUBDEV_S_ROUTING <VIDIOC_SUBDEV_G_ROUTING>` ioctl. + +3) Configure streams. Each route endpoint must be configured +with :ref:`VIDIOC_SUBDEV_S_FMT <VIDIOC_SUBDEV_G_FMT>`. + +Multiplexed streams setup example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A simple example of a multiplexed stream setup might be as follows: + +- Two identical sensors (Sensor A and Sensor B). Each sensor has a single + source pad (pad 0), and outputs two streams, pixel data and metadata. + +- Multiplexer bridge (Bridge). The bridge has two sink pads, connected to the + sensors (pads 0, 1), and one source pad (pad 2), which outputs all 4 + streams. + +- Receiver in the SoC (Receiver). The receiver has a single sink pad (pad 0), + connected to the bridge, and four source pads (pads 1-4), going to the DMA + engine. The receiver demultiplexes the incoming streams to the four source + pads. -Whereas if the same subdev would receive two streams via each sink pad, and -output the combined 4 streams would result in a routing table as follows: +- Four DMA Engines in the SoC (DMA Engine). Each DMA engine is connected to a + single source pad in the receiver. -.. flat-table:: routing table +The sensors, the bridge and the receiver are modeled as V4L2 subdevices, +exposed to userspace via /dev/v4l-subdevX device nodes. The DMA engines are +modeled as V4L2 devices, exposed to userspace via /dev/videoX nodes. + +To configure this pipeline, the userspace must take the following steps: + +1) Set up media links between entities: connect the sensors to the bridge, +bridge to the receiver, and the receiver to the DMA engines. This step does +not differ from normal non-multiplexed media controller setup. + +2) Configure routing. + +.. flat-table:: Sensor routing table (identical on both sensors) :header-rows: 1 - * - Sink Pad/Sink Stream - - Source Pad/Source Stream + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 0/0 (unused) + - 0/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE | V4L2_SUBDEV_ROUTE_FL_SOURCE + - Pixel data stream. Source route, i.e. the sink fields are unused. + * - 0/0 (unused) + - 0/1 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE | V4L2_SUBDEV_ROUTE_FL_SOURCE + - Metadata stream. Source route, i.e. the sink fields are unused. + +.. flat-table:: Bridge routing table + :header-rows: 1 + + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments * - 0/0 - 2/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from Sensor A * - 0/1 - 2/1 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from Sensor A * - 1/0 - 2/2 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from Sensor B * - 1/1 - 2/3 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from Sensor B -Some subdevices may have known set of routes, mutable or immutable, dictated by -the hardware. An example would be a sensor which produces pixel data and -metadata via CSI-2 bus. In such a case there can ever be only those two streams. - -A subdevice that does not produce the data is another matter. Consider a device -with two CSI-2 sink pads and two CSI-2 source pads, with the ability to route -streams freely between the sink and source pads based on HW configuration. Each -sink pad could receive streams for all four CSI-2 virtual channel. If we only -consider the virtual channels, we would have maximum number of routes of 8. - -But CSI-2 also defines a datatype for each CSI-2 packet, allowing one to send, -say, pixel data and metadata using the same virtual channel but different -datatype. Now we would have a maximum number of routes of 16. - -Generally speaking, the concept of "stream" is very flexible. As a contrived -example, you might even consider each line of a pixel frame to be a separate -stream, if your hardware would support such a thing. - -Multiplexed Streams -^^^^^^^^^^^^^^^^^^^ +.. flat-table:: Receiver routing table + :header-rows: 1 -When a subdevice exposes multiple streams in a single pad (multiplexed streams), -the subdevice driver needs to have ``V4L2_SUBDEV_FL_MULTIPLEXED`` flag set. This -flag indicates that the subdev supports the uAPI extensions needed to support -multiple streams, and the driver must handle the ``stream`` field in the various -subdev ioctls. + * - Sink Pad/Stream + - Source Pad/Stream + - Routing Flags + - Comments + * - 0/0 + - 1/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from Sensor A + * - 0/1 + - 2/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from Sensor A + * - 0/2 + - 3/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Pixel data stream from Sensor B + * - 0/3 + - 4/0 + - V4L2_SUBDEV_ROUTE_FL_ACTIVE + - Metadata stream from Sensor B + +3) Configure streams + +After configuring the routing table, the next step is configuring the streams. +This step is similar to configuring the pads in a non-multiplexed streams +setup, with the difference that we need to configure each (pad, stream) pair +(i.e. route endpoint), instead of just a pad. + +Presuming there are no format conversions in the pipeline, the userspace needs +to configure all the route endpoints using four formats (two pixel formats +and two metadata formats) with VIDIOC_SUBDEV_S_FMT. diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 6d953ee23287..41f4873c49f7 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -45,6 +45,10 @@ with the VIDIOC_SUBDEV_S_ROUTING ioctl, by adding or removing routes and setting or clearing the ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag of the ``flags`` field of a struct :c:type:`v4l2_subdev_route`. +A special case for routing are routes marked with +``V4L2_SUBDEV_ROUTE_FL_SOURCE`` flag. These routes are used to describe +source endpoints on sensors and the sink fields are unused. + When inspecting routes through VIDIOC_SUBDEV_G_ROUTING and the application provided ``num_routes`` is not big enough to contain all the available routes the subdevice exposes, drivers return the ENOSPC error code and adjust the diff --git a/arch/arm64/boot/dts/ti/Makefile b/arch/arm64/boot/dts/ti/Makefile index d30b73e4d6a1..2ac11a080ff2 100644 --- a/arch/arm64/boot/dts/ti/Makefile +++ b/arch/arm64/boot/dts/ti/Makefile @@ -44,3 +44,22 @@ dtb-$(CONFIG_ARCH_K3) += k3-am625-sk.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-skeleton.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-sk-lpmdemo.dtb dtb-$(CONFIG_ARCH_K3) += k3-am625-sk-csi2-ov5640.dtbo + +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-cpb-fusion.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-sk-fusion.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-0-0.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-0-1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-0-2.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-0-3.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-1-0.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-1-1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-1-2.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-cm-1-3.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-0-0.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-0-1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-0-2.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-0-3.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-1-0.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-1-1.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-1-2.dtbo +dtb-$(CONFIG_ARCH_K3) += k3-j721e-fpdlink-imx390-rcm-1-3.dtbo diff --git a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi index 9ff5cc05bcec..615abec16583 100644 --- a/arch/arm64/boot/dts/ti/k3-am64-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-am64-main.dtsi @@ -467,13 +467,11 @@ clock-names = "clk_ahb", "clk_xin"; mmc-ddr-1_8v; mmc-hs200-1_8v; - mmc-hs400-1_8v; ti,trm-icp = <0x2>; ti,otap-del-sel-legacy = <0x0>; ti,otap-del-sel-mmc-hs = <0x0>; ti,otap-del-sel-ddr52 = <0x6>; ti,otap-del-sel-hs200 = <0x7>; - ti,otap-del-sel-hs400 = <0x4>; }; sdhci1: mmc@fa00000 { diff --git a/arch/arm64/boot/dts/ti/k3-am642-sk.dts b/arch/arm64/boot/dts/ti/k3-am642-sk.dts index e536dc0c2e9b..6daac38e9553 100644 --- a/arch/arm64/boot/dts/ti/k3-am642-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-am642-sk.dts @@ -153,6 +153,15 @@ >; }; + main_uart0_pins_default: main-uart0-pins-default { + pinctrl-single,pins = < + AM64X_IOPAD(0x0238, PIN_INPUT, 0) /* (B16) UART0_CTSn */ + AM64X_IOPAD(0x023c, PIN_OUTPUT, 0) /* (A16) UART0_RTSn */ + AM64X_IOPAD(0x0230, PIN_INPUT, 0) /* (D15) UART0_RXD */ + AM64X_IOPAD(0x0234, PIN_OUTPUT, 0) /* (C16) UART0_TXD */ + >; + }; + main_usb0_pins_default: main-usb0-pins-default { pinctrl-single,pins = < AM64X_IOPAD(0x02a8, PIN_OUTPUT, 0) /* (E19) USB0_DRVVBUS */ @@ -238,6 +247,11 @@ status = "disabled"; }; +&main_uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&main_uart0_pins_default>; +}; + &main_uart1 { /* main_uart1 is reserved for firmware usage */ status = "reserved"; diff --git a/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi index 9f70925c8da8..66644f48d346 100644 --- a/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j7200-som-p0.dtsi @@ -153,6 +153,37 @@ flash@0,0 { compatible = "cypress,hyperflash", "cfi-flash"; reg = <0x00 0x00 0x4000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "hbmc.tiboot3"; + reg = <0x0 0x100000>; + }; + + partition@100000 { + label = "hbmc.tispl"; + reg = <0x100000 0x200000>; + }; + + partition@300000 { + label = "hbmc.u-boot"; + reg = <0x300000 0x400000>; + }; + + partition@700000 { + label = "hbmc.env"; + reg = <0x700000 0x40000>; + }; + + partition@800000 { + label = "hbmc.rootfs"; + reg = <0x800000 0x3800000>; + }; + }; }; }; @@ -271,7 +302,7 @@ pinctrl-names = "default"; pinctrl-0 = <&mcu_fss0_ospi0_pins_default>; - flash@0{ + flash@0 { compatible = "jedec,spi-nor"; reg = <0x0>; spi-tx-bus-width = <8>; @@ -284,5 +315,46 @@ cdns,read-delay = <4>; #address-cells = <1>; #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "ospi.tiboot3"; + reg = <0x0 0x100000>; + }; + + partition@100000 { + label = "ospi.tispl"; + reg = <0x100000 0x200000>; + }; + + partition@300000 { + label = "ospi.u-boot"; + reg = <0x300000 0x400000>; + }; + + partition@700000 { + label = "ospi.env"; + reg = <0x700000 0x40000>; + }; + + partition@740000 { + label = "ospi.env.backup"; + reg = <0x740000 0x40000>; + }; + + partition@800000 { + label = "ospi.rootfs"; + reg = <0x800000 0x37c0000>; + }; + + partition@3fc0000 { + label = "ospi.phypattern"; + reg = <0x3fc0000 0x40000>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-cpb-fusion.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-cpb-fusion.dts new file mode 100644 index 000000000000..ce4407040c80 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-cpb-fusion.dts @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_fusion_25M_fixed: fixed-clock-25M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + }; + }; +}; + +&main_i2c6 { + #address-cells = <1>; + #size-cells = <0>; + + deser@3d { + compatible = "ti,ds90ub960-q1"; + + reg-names = "main", "ser0", "ser1", "ser2", "ser3"; + reg = <0x3d>, <0x44>, <0x45>, <0x46>, <0x47>; + + clocks = <&clk_fusion_25M_fixed>; + + i2c-alias-pool = /bits/ 16 <0x4a 0x4b 0x4c 0x4d 0x4e 0x4f>; + + data-rate = <1600000000>; + + ds90ub960_0_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + /* CSI-2 */ + port@4 { + reg = <4>; + ds90ub960_0_csi_out: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi2_phy0>; + }; + }; + }; + + ds90ub960_0_atr: i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + deser@36 { + compatible = "ti,ds90ub960-q1"; + + reg-names = "main", "ser0", "ser1", "ser2", "ser3"; + reg = <0x36>, <0x54>, <0x55>, <0x56>, <0x57>; + + clocks = <&clk_fusion_25M_fixed>; + + i2c-alias-pool = /bits/ 16 <0x5a 0x5b 0x5c 0x5d 0x5e 0x5f>; + + data-rate = <1600000000>; + + ds90ub960_1_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + /* CSI-2 */ + port@4 { + reg = <4>; + ds90ub960_1_csi_out: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi2_phy1>; + }; + }; + }; + + ds90ub960_1_atr: i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + }; + }; +}; + +&csi0_port0 { + status = "okay"; + + csi2_phy0: endpoint { + remote-endpoint = <&ds90ub960_0_csi_out>; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + }; +}; + +&csi1_port0 { + status = "okay"; + + csi2_phy1: endpoint { + remote-endpoint = <&ds90ub960_1_csi_out>; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-0.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-0.dts new file mode 100644 index 000000000000..fff3678ab505 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-0.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-0-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 0 */ + port@0 { + reg = <0>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-1.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-1.dts new file mode 100644 index 000000000000..82c748f4d707 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-1.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 1 */ + port@1 { + reg = <1>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-2.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-2.dts new file mode 100644 index 000000000000..dd5ff95a1e3c --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-2.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-2-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 2 */ + port@2 { + reg = <2>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-3.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-3.dts new file mode 100644 index 000000000000..4dd45eec73ec --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-0-3.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-3-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 3 */ + port@3 { + reg = <3>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-0.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-0.dts new file mode 100644 index 000000000000..847499f3dab0 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-0.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-0-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 0 */ + port@0 { + reg = <0>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-1.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-1.dts new file mode 100644 index 000000000000..45a9b3d88628 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-1.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-1-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 1 */ + port@1 { + reg = <1>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-2.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-2.dts new file mode 100644 index 000000000000..799f63a86e7b --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-2.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-2-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 2 */ + port@2 { + reg = <2>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-3.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-3.dts new file mode 100644 index 000000000000..1e7b13117261 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-cm-1-3.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-3-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 3 */ + port@3 { + reg = <3>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@21 { + compatible = "sony,imx390"; + reg = <0x21>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 1 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-0.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-0.dts new file mode 100644 index 000000000000..e9b1526c5723 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-0.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-0-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 0 */ + port@0 { + reg = <0>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-1.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-1.dts new file mode 100644 index 000000000000..3cfebdaf0828 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-1.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 1 */ + port@1 { + reg = <1>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-2.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-2.dts new file mode 100644 index 000000000000..5dadbd168c01 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-2.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-2-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 2 */ + port@2 { + reg = <2>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-3.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-3.dts new file mode 100644 index 000000000000..9dbc84e361db --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-0-3.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-3-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_0_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 3 */ + port@3 { + reg = <3>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_0_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-0.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-0.dts new file mode 100644 index 000000000000..a6fc5693ff62 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-0.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-0-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 0 */ + port@0 { + reg = <0>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-1.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-1.dts new file mode 100644 index 000000000000..d4460ee6fcea --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-1.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-1-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 1 */ + port@1 { + reg = <1>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-2.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-2.dts new file mode 100644 index 000000000000..6545bf0676ba --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-2.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-2-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 2 */ + port@2 { + reg = <2>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-3.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-3.dts new file mode 100644 index 000000000000..32a71bf34af6 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-imx390-rcm-1-3.dts @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IMX390 FPD-Link 3 Camera Module + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +/dts-v1/; +/plugin/; + +#include <dt-bindings/gpio/gpio.h> + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_cam_27M: fixed-clock-1-3-27M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <27000000>; + }; + }; + }; +}; + +&ds90ub960_1_ports { + #address-cells = <1>; + #size-cells = <0>; + + /* FPDLink RX 3 */ + port@3 { + reg = <3>; + + ds90ub960_fpd3_in: endpoint { + remote-endpoint = <&ub953_out>; + + mode = <3>; + bc-freq = <50000000>; + + serializer: remote-chip { + compatible = "ti,ds90ub953-q1"; + + gpio-controller; + #gpio-cells = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ub953_in: endpoint { + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + + ub953_out: endpoint { + remote-endpoint = <&ds90ub960_fpd3_in>; + }; + }; + }; + }; + }; + }; +}; + +&ds90ub960_1_atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + sensor@1a { + compatible = "sony,imx390"; + reg = <0x1a>; + + clocks = <&clk_cam_27M>; + clock-names = "inck"; + + xclr-gpios = <&serializer 1 GPIO_ACTIVE_LOW>; + error0-gpios = <&serializer 2 GPIO_ACTIVE_HIGH>; + error1-gpios = <&serializer 3 GPIO_ACTIVE_HIGH>; + comready-gpios = <&serializer 0 GPIO_ACTIVE_HIGH>; + + port { + sensor_out: endpoint { + remote-endpoint = <&ub953_in>; + }; + }; + }; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-sk-fusion.dts b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-sk-fusion.dts new file mode 100644 index 000000000000..b4cb9c59a1f6 --- /dev/null +++ b/arch/arm64/boot/dts/ti/k3-j721e-fpdlink-sk-fusion.dts @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/ { + fragment@101 { + target-path = "/"; + + __overlay__ { + clk_fusion_25M_fixed: fixed-clock-25M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + }; + }; +}; + +&main_i2c3 { + #address-cells = <1>; + #size-cells = <0>; + + i2c-switch@70 { + compatible = "nxp,pca9543"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + + cam0_i2c: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + deser@38 { + compatible = "ti,ds90ub960-q1"; + + reg-names = "main", "ser0", "ser1", "ser2", "ser3"; + reg = <0x38>, <0x44>, <0x45>, <0x46>, <0x47>; + + clocks = <&clk_fusion_25M_fixed>; + + i2c-alias-pool = /bits/ 16 <0x4a 0x4b 0x4c 0x4d 0x4e 0x4f>; + + data-rate = <1600000000>; + + ds90ub960_0_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + /* CSI-2 */ + port@4 { + reg = <4>; + ds90ub960_0_csi_out: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi2_phy0>; + }; + }; + }; + + ds90ub960_0_atr: i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + deser@30 { + compatible = "ti,ds90ub960-q1"; + + reg-names = "main", "ser0", "ser1", "ser2", "ser3"; + reg = <0x30>, <0x54>, <0x55>, <0x56>, <0x57>; + + clocks = <&clk_fusion_25M_fixed>; + + i2c-alias-pool = /bits/ 16 <0x5a 0x5b 0x5c 0x5d 0x5e 0x5f>; + + data-rate = <1600000000>; + + ds90ub960_1_ports: ports { + #address-cells = <1>; + #size-cells = <0>; + + /* CSI-2 */ + port@4 { + reg = <4>; + ds90ub960_1_csi_out: endpoint { + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&csi2_phy1>; + }; + }; + }; + + ds90ub960_1_atr: i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + }; + }; + }; + }; +}; + +&csi0_port0 { + status = "okay"; + + csi2_phy0: endpoint { + remote-endpoint = <&ds90ub960_0_csi_out>; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + }; +}; + +&csi1_port0 { + status = "okay"; + + csi2_phy1: endpoint { + remote-endpoint = <&ds90ub960_1_csi_out>; + clock-lanes = <0>; + data-lanes = <1 2 3 4>; + }; +}; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-sk.dts b/arch/arm64/boot/dts/ti/k3-j721e-sk.dts index 30b57f4222e8..3038723afb9c 100644 --- a/arch/arm64/boot/dts/ti/k3-j721e-sk.dts +++ b/arch/arm64/boot/dts/ti/k3-j721e-sk.dts @@ -638,6 +638,52 @@ cdns,read-delay = <4>; #address-cells = <1>; #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "ospi.tiboot3"; + reg = <0x0 0x80000>; + }; + + partition@80000 { + label = "ospi.tispl"; + reg = <0x80000 0x200000>; + }; + + partition@280000 { + label = "ospi.u-boot"; + reg = <0x280000 0x400000>; + }; + + partition@680000 { + label = "ospi.env"; + reg = <0x680000 0x40000>; + }; + + partition@6c0000 { + label = "ospi.sysfw"; + reg = <0x6c0000 0x100000>; + }; + + partition@7c0000 { + label = "ospi.env.backup"; + reg = <0x7c0000 0x40000>; + }; + + partition@800000 { + label = "ospi.rootfs"; + reg = <0x800000 0x37c0000>; + }; + + partition@3fc0000 { + label = "ospi.phypattern"; + reg = <0x3fc0000 0x40000>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi index 906d16a240f2..1fb3bb479677 100644 --- a/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721e-som-p0.dtsi @@ -196,6 +196,52 @@ cdns,read-delay = <0>; #address-cells = <1>; #size-cells = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "ospi.tiboot3"; + reg = <0x0 0x80000>; + }; + + partition@80000 { + label = "ospi.tispl"; + reg = <0x80000 0x200000>; + }; + + partition@280000 { + label = "ospi.u-boot"; + reg = <0x280000 0x400000>; + }; + + partition@680000 { + label = "ospi.env"; + reg = <0x680000 0x20000>; + }; + + partition@6a0000 { + label = "ospi.env.backup"; + reg = <0x6a0000 0x20000>; + }; + + partition@6c0000 { + label = "ospi.sysfw"; + reg = <0x6c0000 0x100000>; + }; + + partition@800000 { + label = "ospi.rootfs"; + reg = <0x800000 0x37c0000>; + }; + + partition@3fe0000 { + label = "ospi.phypattern"; + reg = <0x3fe0000 0x20000>; + }; + }; }; }; diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts index c90bd74b3c81..c095e213cfa8 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts +++ b/arch/arm64/boot/dts/ti/k3-j721s2-common-proc-board.dts @@ -544,14 +544,14 @@ * VP3 - DPI1 */ - assigned-clocks = <&k3_clks 158 35>, - <&k3_clks 158 17>, - <&k3_clks 158 26>, - <&k3_clks 158 0>; - assigned-clock-parents = <&k3_clks 158 36>, - <&k3_clks 158 19>, - <&k3_clks 158 28>, - <&k3_clks 158 4>; + assigned-clocks = <&k3_clks 158 2>, + <&k3_clks 158 5>, + <&k3_clks 158 14>, + <&k3_clks 158 18>; + assigned-clock-parents = <&k3_clks 158 3>, + <&k3_clks 158 7>, + <&k3_clks 158 16>, + <&k3_clks 158 22>; }; &dss_ports { diff --git a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi index 937ba065a156..38843f866720 100644 --- a/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi +++ b/arch/arm64/boot/dts/ti/k3-j721s2-main.dtsi @@ -1381,11 +1381,11 @@ "vp1", "vp2", "vp3", "vp4", "wb"; - clocks = <&k3_clks 158 11>, - <&k3_clks 158 35>, - <&k3_clks 158 17>, - <&k3_clks 158 26>, - <&k3_clks 158 0>; + clocks = <&k3_clks 158 0>, + <&k3_clks 158 2>, + <&k3_clks 158 5>, + <&k3_clks 158 14>, + <&k3_clks 158 18>; clock-names = "fck", "vp1", "vp2", "vp3", "vp4"; power-domains = <&k3_pds 158 TI_SCI_PD_EXCLUSIVE>; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 438905e2a1d0..c6d1a345ea6d 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -71,6 +71,15 @@ config I2C_MUX source "drivers/i2c/muxes/Kconfig" +config I2C_ATR + tristate "I2C Address Translator (ATR) support" + help + Enable support for I2C Address Translator (ATR) chips. + + An ATR allows accessing multiple I2C busses from a single + physical bus via address translation instead of bus selection as + i2c-muxes do. + config I2C_HELPER_AUTO bool "Autoselect pertinent helper modules" default y diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index c1d493dc9bac..3f71ce4711e3 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -13,6 +13,7 @@ i2c-core-$(CONFIG_OF) += i2c-core-of.o obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_MUX) += i2c-mux.o +obj-$(CONFIG_I2C_ATR) += i2c-atr.o obj-y += algos/ busses/ muxes/ obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c new file mode 100644 index 000000000000..699446be1e90 --- /dev/null +++ b/drivers/i2c/i2c-atr.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * I2C Address Translator + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * + * An I2C Address Translator (ATR) is a device with an I2C slave parent + * ("upstream") port and N I2C master child ("downstream") ports, and + * forwards transactions from upstream to the appropriate downstream port + * with a modified slave address. The address used on the parent bus is + * called the "alias" and is (potentially) different from the physical + * slave address of the child bus. Address translation is done by the + * hardware. + * + * An ATR looks similar to an i2c-mux except: + * - the address on the parent and child busses can be different + * - there is normally no need to select the child port; the alias used on + * the parent bus implies it + * + * The ATR functionality can be provided by a chip with many other + * features. This file provides a helper to implement an ATR within your + * driver. + * + * The ATR creates a new I2C "child" adapter on each child bus. Adding + * devices on the child bus ends up in invoking the driver code to select + * an available alias. Maintaining an appropriate pool of available aliases + * and picking one for each new device is up to the driver implementer. The + * ATR maintains an table of currently assigned alias and uses it to modify + * all I2C transactions directed to devices on the child buses. + * + * A typical example follows. + * + * Topology: + * + * Slave X @ 0x10 + * .-----. | + * .-----. | |---+---- B + * | CPU |--A--| ATR | + * `-----' | |---+---- C + * `-----' | + * Slave Y @ 0x10 + * + * Alias table: + * + * Client Alias + * ------------- + * X 0x20 + * Y 0x30 + * + * Transaction: + * + * - Slave X driver sends a transaction (on adapter B), slave address 0x10 + * - ATR driver rewrites messages with address 0x20, forwards to adapter A + * - Physical I2C transaction on bus A, slave address 0x20 + * - ATR chip propagates transaction on bus B with address translated to 0x10 + * - Slave X chip replies on bus B + * - ATR chip forwards reply on bus A + * - ATR driver rewrites messages with address 0x10 + * - Slave X driver gets back the msgs[], with reply and address 0x10 + * + * Usage: + * + * 1. In your driver (typically in the probe function) add an ATR by + * calling i2c_atr_new() passing your attach/detach callbacks + * 2. When the attach callback is called pick an appropriate alias, + * configure it in your chip and return the chosen alias in the + * alias_id parameter + * 3. When the detach callback is called, deconfigure the alias from + * your chip and put it back in the pool for later usage + * + * Originally based on i2c-mux.c + */ + +#include <linux/i2c.h> +#include <linux/i2c-atr.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/slab.h> + +/** + * struct i2c_atr_cli2alias_pair - Hold the alias assigned to a client. + * @node: List node + * @client: Pointer to the client on the child bus + * @alias: I2C alias address assigned by the driver. + * This is the address that will be used to issue I2C transactions + * on the parent (physical) bus. + */ +struct i2c_atr_cli2alias_pair { + struct list_head node; + const struct i2c_client *client; + u16 alias; +}; + +/* + * Data for each channel (child bus) + */ +struct i2c_atr_chan { + struct i2c_adapter adap; + struct i2c_atr *atr; + u32 chan_id; + + struct list_head alias_list; + + u16 *orig_addrs; + unsigned int orig_addrs_size; + struct mutex orig_addrs_lock; /* Lock orig_addrs during xfer */ +}; + +static struct i2c_atr_cli2alias_pair * +i2c_atr_find_mapping_by_client(struct list_head *list, + const struct i2c_client *client) +{ + struct i2c_atr_cli2alias_pair *c2a; + + list_for_each_entry(c2a, list, node) { + if (c2a->client == client) + return c2a; + } + + return NULL; +} + +static struct i2c_atr_cli2alias_pair * +i2c_atr_find_mapping_by_addr(struct list_head *list, + u16 phys_addr) +{ + struct i2c_atr_cli2alias_pair *c2a; + + list_for_each_entry(c2a, list, node) { + if (c2a->client->addr == phys_addr) + return c2a; + } + + return NULL; +} + +/* + * Replace all message addresses with their aliases, saving the original + * addresses. + * + * This function is internal for use in i2c_atr_master_xfer(). It must be + * followed by i2c_atr_unmap_msgs() to restore the original addresses. + */ +static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, + struct i2c_msg msgs[], int num) + +{ + struct i2c_atr *atr = chan->atr; + static struct i2c_atr_cli2alias_pair *c2a; + int i; + + /* Ensure we have enough room to save the original addresses */ + if (unlikely(chan->orig_addrs_size < num)) { + void *new_buf = kmalloc(num * sizeof(chan->orig_addrs[0]), + GFP_KERNEL); + if (new_buf == NULL) + return -ENOMEM; + + kfree(chan->orig_addrs); + chan->orig_addrs = new_buf; + chan->orig_addrs_size = num; + } + + for (i = 0; i < num; i++) { + chan->orig_addrs[i] = msgs[i].addr; + + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, + msgs[i].addr); + if (c2a) { + msgs[i].addr = c2a->alias; + } else { + dev_err(atr->dev, "client 0x%02x not mapped!\n", + msgs[i].addr); + return -ENXIO; + } + } + + return 0; +} + +/* + * Restore all message address aliases with the original addresses. + * + * This function is internal for use in i2c_atr_master_xfer(). + * + * @see i2c_atr_map_msgs() + */ +static void i2c_atr_unmap_msgs(struct i2c_atr_chan *chan, + struct i2c_msg msgs[], int num) +{ + int i; + + for (i = 0; i < num; i++) + msgs[i].addr = chan->orig_addrs[i]; +} + +static int i2c_atr_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_adapter *parent = atr->parent; + int ret = 0; + + /* Switch to the right atr port */ + if (atr->ops->select) { + ret = atr->ops->select(atr, chan->chan_id); + if (ret < 0) + goto out; + } + + /* Translate addresses */ + mutex_lock(&chan->orig_addrs_lock); + ret = i2c_atr_map_msgs(chan, msgs, num); + if (ret < 0) { + mutex_unlock(&chan->orig_addrs_lock); + goto out; + } + + /* Perform the transfer */ + ret = i2c_transfer(parent, msgs, num); + + /* Restore addresses */ + i2c_atr_unmap_msgs(chan, msgs, num); + mutex_unlock(&chan->orig_addrs_lock); + +out: + if (atr->ops->deselect) + atr->ops->deselect(atr, chan->chan_id); + + return ret; +} + +static int i2c_atr_smbus_xfer(struct i2c_adapter *adap, + u16 addr, unsigned short flags, + char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_adapter *parent = atr->parent; + struct i2c_atr_cli2alias_pair *c2a; + int err = 0; + + c2a = i2c_atr_find_mapping_by_addr(&chan->alias_list, addr); + if (!c2a) { + dev_err(atr->dev, "client 0x%02x not mapped!\n", addr); + return -ENXIO; + } + + if (atr->ops->select) + err = atr->ops->select(atr, chan->chan_id); + if (!err) + err = i2c_smbus_xfer(parent, c2a->alias, flags, + read_write, command, size, data); + if (atr->ops->deselect) + atr->ops->deselect(atr, chan->chan_id); + + return err; +} + +static u32 i2c_atr_functionality(struct i2c_adapter *adap) +{ + struct i2c_atr_chan *chan = adap->algo_data; + struct i2c_adapter *parent = chan->atr->parent; + + return parent->algo->functionality(parent); +} + +static void i2c_atr_lock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + mutex_lock(&atr->lock); +} + +static int i2c_atr_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + return mutex_trylock(&atr->lock); +} + +static void i2c_atr_unlock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + + mutex_unlock(&atr->lock); +} + +static const struct i2c_lock_operations i2c_atr_lock_ops = { + .lock_bus = i2c_atr_lock_bus, + .trylock_bus = i2c_atr_trylock_bus, + .unlock_bus = i2c_atr_unlock_bus, +}; + +static int i2c_atr_attach_client(struct i2c_adapter *adapter, + const struct i2c_board_info *info, + const struct i2c_client *client) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_atr_cli2alias_pair *c2a; + u16 alias_id = 0; + int err = 0; + + c2a = kzalloc(sizeof(struct i2c_atr_cli2alias_pair), GFP_KERNEL); + if (!c2a) { + err = -ENOMEM; + goto err_alloc; + } + + err = atr->ops->attach_client(atr, chan->chan_id, info, client, + &alias_id); + if (err) + goto err_attach; + if (alias_id == 0) { + err = -EINVAL; + goto err_attach; + } + + c2a->client = client; + c2a->alias = alias_id; + list_add(&c2a->node, &chan->alias_list); + + return 0; + +err_attach: + kfree(c2a); +err_alloc: + return err; +} + +static void i2c_atr_detach_client(struct i2c_adapter *adapter, + const struct i2c_client *client) +{ + struct i2c_atr_chan *chan = adapter->algo_data; + struct i2c_atr *atr = chan->atr; + struct i2c_atr_cli2alias_pair *c2a; + + atr->ops->detach_client(atr, chan->chan_id, client); + + c2a = i2c_atr_find_mapping_by_client(&chan->alias_list, client); + if (c2a != NULL) { + list_del(&c2a->node); + kfree(c2a); + } +} + +static const struct i2c_attach_operations i2c_atr_attach_ops = { + .attach_client = i2c_atr_attach_client, + .detach_client = i2c_atr_detach_client, +}; + +/** + * i2c_atr_add_adapter - Create a child ("downstream") I2C bus. + * @atr: The I2C ATR + * @chan_id: Index of the new adapter (0 .. max_adapters-1). This value is + * passed to the callbacks in `struct i2c_atr_ops`. + * + * After calling this function a new i2c bus will appear. Adding and + * removing devices on the downstream bus will result in calls to the + * `attach_client` and `detach_client` callbacks for the driver to assign + * an alias to the device. + * + * If there is a device tree node under "i2c-atr" whose "reg" property + * equals chan_id, the new adapter will receive that node and perhaps start + * adding devices under it. The callbacks for those additions will be made + * before i2c_atr_add_adapter() returns. + * + * Call i2c_atr_del_adapter() to remove the adapter. + * + * Return: 0 on success, a negative error code otherwise. + */ +int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id) +{ + struct i2c_adapter *parent = atr->parent; + struct device *dev = atr->dev; + struct i2c_atr_chan *chan; + char symlink_name[20]; + int err; + + if (chan_id >= atr->max_adapters) + return -EINVAL; + + if (atr->adapter[chan_id]) { + dev_err(dev, "Adapter %d already present\n", chan_id); + return -EEXIST; + } + + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + chan->atr = atr; + chan->chan_id = chan_id; + INIT_LIST_HEAD(&chan->alias_list); + mutex_init(&chan->orig_addrs_lock); + + snprintf(chan->adap.name, sizeof(chan->adap.name), + "i2c-%d-atr-%d", i2c_adapter_id(parent), chan_id); + chan->adap.owner = THIS_MODULE; + chan->adap.algo = &atr->algo; + chan->adap.algo_data = chan; + chan->adap.dev.parent = dev; + chan->adap.retries = parent->retries; + chan->adap.timeout = parent->timeout; + chan->adap.quirks = parent->quirks; + chan->adap.lock_ops = &i2c_atr_lock_ops; + chan->adap.attach_ops = &i2c_atr_attach_ops; + + if (dev->of_node) { + struct device_node *atr_node; + struct device_node *child; + u32 reg; + + atr_node = of_get_child_by_name(dev->of_node, "i2c-atr"); + + for_each_child_of_node(atr_node, child) { + err = of_property_read_u32(child, "reg", ®); + if (err) + continue; + if (chan_id == reg) + break; + } + + chan->adap.dev.of_node = child; + of_node_put(atr_node); + } + + err = i2c_add_adapter(&chan->adap); + if (err) { + dev_err(dev, "failed to add atr-adapter %u (error=%d)\n", + chan_id, err); + goto err_add_adapter; + } + + WARN(sysfs_create_link(&chan->adap.dev.kobj, &dev->kobj, "atr_device"), + "can't create symlink to atr device\n"); + snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id); + WARN(sysfs_create_link(&dev->kobj, &chan->adap.dev.kobj, symlink_name), + "can't create symlink for channel %u\n", chan_id); + + dev_dbg(dev, "Added ATR child bus %d\n", i2c_adapter_id(&chan->adap)); + + atr->adapter[chan_id] = &chan->adap; + return 0; + +err_add_adapter: + mutex_destroy(&chan->orig_addrs_lock); + kfree(chan); + return err; +} +EXPORT_SYMBOL_GPL(i2c_atr_add_adapter); + +/** + * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by + * i2c_atr_del_adapter(). + * @atr: The I2C ATR + * @chan_id: Index of the `adapter to be removed (0 .. max_adapters-1) + */ +void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id) +{ + char symlink_name[20]; + + struct i2c_adapter *adap = atr->adapter[chan_id]; + struct i2c_atr_chan *chan = adap->algo_data; + struct device_node *np = adap->dev.of_node; + struct device *dev = atr->dev; + + if (atr->adapter[chan_id] == NULL) { + dev_err(dev, "Adapter %d does not exist\n", chan_id); + return; + } + + dev_dbg(dev, "Removing ATR child bus %d\n", i2c_adapter_id(adap)); + + atr->adapter[chan_id] = NULL; + + snprintf(symlink_name, sizeof(symlink_name), + "channel-%u", chan->chan_id); + sysfs_remove_link(&dev->kobj, symlink_name); + sysfs_remove_link(&chan->adap.dev.kobj, "atr_device"); + + i2c_del_adapter(adap); + of_node_put(np); + mutex_destroy(&chan->orig_addrs_lock); + kfree(chan->orig_addrs); + kfree(chan); +} +EXPORT_SYMBOL_GPL(i2c_atr_del_adapter); + +/** + * i2c_atr_new() - Allocate and initialize an I2C ATR helper. + * @parent: The parent (upstream) adapter + * @dev: The device acting as an ATR + * @ops: Driver-specific callbacks + * @max_adapters: Maximum number of child adapters + * + * The new ATR helper is connected to the parent adapter but has no child + * adapters. Call i2c_atr_add_adapter() to add some. + * + * Call i2c_atr_delete() to remove. + * + * Return: pointer to the new ATR helper object, or ERR_PTR + */ +struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, + const struct i2c_atr_ops *ops, int max_adapters) +{ + struct i2c_atr *atr; + + if (!ops || !ops->attach_client || !ops->detach_client) + return ERR_PTR(-EINVAL); + + atr = devm_kzalloc(dev, sizeof(*atr) + + max_adapters * sizeof(atr->adapter[0]), + GFP_KERNEL); + if (!atr) + return ERR_PTR(-ENOMEM); + + mutex_init(&atr->lock); + + atr->parent = parent; + atr->dev = dev; + atr->ops = ops; + atr->max_adapters = max_adapters; + + if (parent->algo->master_xfer) + atr->algo.master_xfer = i2c_atr_master_xfer; + if (parent->algo->smbus_xfer) + atr->algo.smbus_xfer = i2c_atr_smbus_xfer; + atr->algo.functionality = i2c_atr_functionality; + + return atr; +} +EXPORT_SYMBOL_GPL(i2c_atr_new); + +/** + * i2c_atr_delete - Delete an I2C ATR helper. + * @atr: I2C ATR helper to be deleted. + * + * Precondition: all the adapters added with i2c_atr_add_adapter() mumst be + * removed by calling i2c_atr_del_adapter(). + */ +void i2c_atr_delete(struct i2c_atr *atr) +{ + mutex_destroy(&atr->lock); +} +EXPORT_SYMBOL_GPL(i2c_atr_delete); + +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_DESCRIPTION("I2C Address Translator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index bdce6d3e5327..7fab4214dc93 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -921,15 +921,23 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf } } + if (adap->attach_ops && + adap->attach_ops->attach_client && + adap->attach_ops->attach_client(adap, info, client) != 0) + goto out_free_props; + status = device_register(&client->dev); if (status) - goto out_free_props; + goto out_detach_client; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; +out_detach_client: + if (adap->attach_ops && adap->attach_ops->detach_client) + adap->attach_ops->detach_client(adap, client); out_free_props: if (info->properties) device_remove_properties(&client->dev); @@ -952,9 +960,17 @@ EXPORT_SYMBOL_GPL(i2c_new_client_device); */ void i2c_unregister_device(struct i2c_client *client) { + struct i2c_adapter *adap; + if (IS_ERR_OR_NULL(client)) return; + adap = client->adapter; + + if (adap->attach_ops && + adap->attach_ops->detach_client) + adap->attach_ops->detach_client(adap, client); + if (client->dev.of_node) { of_node_clear_flag(client->dev.of_node, OF_POPULATED); of_node_put(client->dev.of_node); diff --git a/drivers/media/dvb-frontends/ascot2e.h b/drivers/media/dvb-frontends/ascot2e.h index f886fab1283f..d86b3de85c6a 100644 --- a/drivers/media/dvb-frontends/ascot2e.h +++ b/drivers/media/dvb-frontends/ascot2e.h @@ -33,7 +33,7 @@ struct ascot2e_config { #if IS_REACHABLE(CONFIG_DVB_ASCOT2E) /** - * Attach an ascot2e tuner + * ascot2e_attach - Attach an ascot2e tuner * * @fe: frontend to be attached * @config: pointer to &struct ascot2e_config with tuner configuration. diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h index a28b8754932b..4aa6cf4fb913 100644 --- a/drivers/media/dvb-frontends/cxd2820r.h +++ b/drivers/media/dvb-frontends/cxd2820r.h @@ -96,7 +96,7 @@ struct cxd2820r_config { #if IS_REACHABLE(CONFIG_DVB_CXD2820R) /** - * Attach a cxd2820r demod + * cxd2820r_attach - Attach a cxd2820r demod * * @config: pointer to &struct cxd2820r_config with demod configuration. * @i2c: i2c adapter to use. diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h index ee06e89187e4..69fdca00f364 100644 --- a/drivers/media/dvb-frontends/drxk.h +++ b/drivers/media/dvb-frontends/drxk.h @@ -54,7 +54,7 @@ struct drxk_config { #if IS_REACHABLE(CONFIG_DVB_DRXK) /** - * Attach a drxk demod + * drxk_attach - Attach a drxk demod * * @config: pointer to &struct drxk_config with demod configuration. * @i2c: i2c adapter to use. diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h index 973a66a82e27..71838888743b 100644 --- a/drivers/media/dvb-frontends/dvb-pll.h +++ b/drivers/media/dvb-frontends/dvb-pll.h @@ -38,7 +38,7 @@ struct dvb_pll_config { #if IS_REACHABLE(CONFIG_DVB_PLL) /** - * Attach a dvb-pll to the supplied frontend structure. + * dvb_pll_attach - Attach a dvb-pll to the supplied frontend structure. * * @fe: Frontend to attach to. * @pll_addr: i2c address of the PLL (if used). diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h index c026bdcf548d..32e0b1fb268c 100644 --- a/drivers/media/dvb-frontends/helene.h +++ b/drivers/media/dvb-frontends/helene.h @@ -44,7 +44,7 @@ struct helene_config { #if IS_REACHABLE(CONFIG_DVB_HELENE) /** - * Attach a helene tuner (terrestrial and cable standards) + * helene_attach - Attach a helene tuner (terrestrial and cable standards) * * @fe: frontend to be attached * @config: pointer to &struct helene_config with tuner configuration. @@ -57,7 +57,7 @@ extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c); /** - * Attach a helene tuner (satellite standards) + * helene_attach_s - Attach a helene tuner (satellite standards) * * @fe: frontend to be attached * @config: pointer to &struct helene_config with tuner configuration. diff --git a/drivers/media/dvb-frontends/horus3a.h b/drivers/media/dvb-frontends/horus3a.h index 366c399e3329..91dbe20169cd 100644 --- a/drivers/media/dvb-frontends/horus3a.h +++ b/drivers/media/dvb-frontends/horus3a.h @@ -33,7 +33,7 @@ struct horus3a_config { #if IS_REACHABLE(CONFIG_DVB_HORUS3A) /** - * Attach a horus3a tuner + * horus3a_attach - Attach a horus3a tuner * * @fe: frontend to be attached * @config: pointer to &struct helene_config with tuner configuration. diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h index 671c0e0959f7..175569131365 100644 --- a/drivers/media/dvb-frontends/ix2505v.h +++ b/drivers/media/dvb-frontends/ix2505v.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** +/* * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner * * Copyright (C) 2010 Malcolm Priestley @@ -31,7 +31,7 @@ struct ix2505v_config { #if IS_REACHABLE(CONFIG_DVB_IX2505V) /** - * Attach a ix2505v tuner to the supplied frontend structure. + * ix2505v_attach - Attach a ix2505v tuner to the supplied frontend structure. * * @fe: Frontend to attach to. * @config: pointer to &struct ix2505v_config diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h index 46b722495e4c..e32b68c0df70 100644 --- a/drivers/media/dvb-frontends/m88ds3103.h +++ b/drivers/media/dvb-frontends/m88ds3103.h @@ -128,7 +128,7 @@ struct m88ds3103_config { #if defined(CONFIG_DVB_M88DS3103) || \ (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE)) /** - * Attach a m88ds3103 demod + * m88ds3103_attach - Attach a m88ds3103 demod * * @config: pointer to &struct m88ds3103_config with demod configuration. * @i2c: i2c adapter to use. diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h index 00a6b6e9b5e4..d20d22bf7580 100644 --- a/drivers/media/dvb-frontends/mb86a20s.h +++ b/drivers/media/dvb-frontends/mb86a20s.h @@ -26,7 +26,7 @@ struct mb86a20s_config { #if IS_REACHABLE(CONFIG_DVB_MB86A20S) /** - * Attach a mb86a20s demod + * mb86a20s_attach - Attach a mb86a20s demod * * @config: pointer to &struct mb86a20s_config with demod configuration. * @i2c: i2c adapter to use. diff --git a/drivers/media/dvb-frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h index 570a4b1d07d6..38da55af7ea9 100644 --- a/drivers/media/dvb-frontends/stb6000.h +++ b/drivers/media/dvb-frontends/stb6000.h @@ -15,7 +15,7 @@ #if IS_REACHABLE(CONFIG_DVB_STB6000) /** - * Attach a stb6000 tuner to the supplied frontend structure. + * stb6000_attach - Attach a stb6000 tuner to the supplied frontend structure. * * @fe: Frontend to attach to. * @addr: i2c address of the tuner. diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h index bb575a251b04..e1d33edbb8ec 100644 --- a/drivers/media/dvb-frontends/tda826x.h +++ b/drivers/media/dvb-frontends/tda826x.h @@ -14,7 +14,7 @@ #include <media/dvb_frontend.h> /** - * Attach a tda826x tuner to the supplied frontend structure. + * tda826x_attach - Attach a tda826x tuner to the supplied frontend structure. * * @fe: Frontend to attach to. * @addr: i2c address of the tuner. diff --git a/drivers/media/dvb-frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h index 91eea777eaf1..ad83e6344e7f 100644 --- a/drivers/media/dvb-frontends/zl10036.h +++ b/drivers/media/dvb-frontends/zl10036.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** +/* * Driver for Zarlink ZL10036 DVB-S silicon tuner * * Copyright (C) 2006 Tino Reichardt @@ -19,7 +19,7 @@ struct zl10036_config { #if IS_REACHABLE(CONFIG_DVB_ZL10036) /** - * Attach a zl10036 tuner to the supplied frontend structure. + * zl10036_attach - Attach a zl10036 tuner to the supplied frontend structure. * * @fe: Frontend to attach to. * @config: zl10036_config structure. diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index edd79d2bf28a..8f86b43b3e78 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -825,6 +825,18 @@ config VIDEO_IMX355 To compile this driver as a module, choose M here: the module will be called imx355. +config VIDEO_IMX390 + tristate "Sony IMX390 sensor support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor driver for the Sony + IMX390 camera. + + To compile this driver as a module, choose M here: the + module will be called imx390. + config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C @@ -1347,4 +1359,26 @@ config VIDEO_LM3646 flash, torch LEDs. endmenu +# +# Video serializers and deserializers (e.g. FPDLink) +# + +menu "Video serializers and deserializers" + +config VIDEO_DS90UB953 + tristate "TI DS90UB953 Serializer" + help + Device driver for the Texas Instruments DS90UB953 + FPD-Link III Serializer. + +config VIDEO_DS90UB960 + tristate "TI DS90UB960 Deserializer" + depends on OF_GPIO + select I2C_ATR + help + Device driver for the Texas Instruments DS90UB960 + FPD-Link III Deserializer + +endmenu + endif # VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8577522bd553..469f0f0d44d2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -120,9 +120,12 @@ obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o +obj-$(CONFIG_VIDEO_IMX390) += imx390.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o rdacm20-camera_module-objs := rdacm20.o max9271.o obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_SDR_MAX2175) += max2175.o +obj-$(CONFIG_VIDEO_DS90UB953) += ds90ub953.o +obj-$(CONFIG_VIDEO_DS90UB960) += ds90ub960.o diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c new file mode 100644 index 000000000000..4b51117bbec4 --- /dev/null +++ b/drivers/media/i2c/ds90ub953.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB953 video serializer + * + * Based on a driver from Luca Ceresoli <luca@lucaceresoli.net> + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/delay.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regmap.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#define UB953_PAD_SINK 0 +#define UB953_PAD_SOURCE 1 + +#define UB953_NUM_GPIOS 4 + +#define UB953_REG_RESET_CTL 0x01 +#define UB953_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) +#define UB953_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) + +#define UB953_REG_GENERAL_CFG 0x02 +#define UB953_REG_MODE_SEL 0x03 + +#define UB953_REG_CLKOUT_CTRL0 0x06 +#define UB953_REG_CLKOUT_CTRL1 0x07 + +#define UB953_REG_SCL_HIGH_TIME 0x0B +#define UB953_REG_SCL_LOW_TIME 0x0C + +#define UB953_REG_LOCAL_GPIO_DATA 0x0d +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_RMTEN(n) BIT(4 + (n)) +#define UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(n) BIT(0 + (n)) + +#define UB953_REG_GPIO_INPUT_CTRL 0x0e +#define UB953_REG_GPIO_INPUT_CTRL_OUT_EN(n) BIT(4 + (n)) +#define UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(n) BIT(0 + (n)) + +#define UB953_REG_REV_MASK_ID 0x50 + +#define UB953_REG_GPIO_PIN_STS 0x53 +#define UB953_REG_GPIO_PIN_STS_GPIO_STS(n) BIT(0 + (n)) + +#define UB953_REG_IND_ACC_CTL 0xb0 +#define UB953_REG_IND_ACC_CTL_IA_AUTO_INC BIT(1) +#define UB953_REG_IND_ACC_CTL_IA_SEL_MASK GENMASK(4, 2) +#define UB953_REG_IND_ACC_ADDR 0xb1 +#define UB953_REG_IND_ACC_DATA 0xb2 + +#define UB953_IND_PGEN_CTL 0x01 +#define UB953_IND_PGEN_CTL_PGEN_ENABLE BIT(0) +#define UB953_IND_PGEN_CFG 0x02 +#define UB953_IND_PGEN_CSI_DI 0x03 +#define UB953_IND_PGEN_LINE_SIZE1 0x04 +#define UB953_IND_PGEN_LINE_SIZE0 0x05 +#define UB953_IND_PGEN_BAR_SIZE1 0x06 +#define UB953_IND_PGEN_BAR_SIZE0 0x07 +#define UB953_IND_PGEN_ACT_LPF1 0x08 +#define UB953_IND_PGEN_ACT_LPF0 0x09 +#define UB953_IND_PGEN_TOT_LPF1 0x0A +#define UB953_IND_PGEN_TOT_LPF0 0x0B +#define UB953_IND_PGEN_LINE_PD1 0x0C +#define UB953_IND_PGEN_LINE_PD0 0x0D +#define UB953_IND_PGEN_VBP 0x0E +#define UB953_IND_PGEN_VFP 0x0F +#define UB953_IND_PGEN_COLOR(n) (0x10 + (n)) /* n <= 15 */ + +struct ub953_data { + struct i2c_client *client; + struct regmap *regmap; + + u32 gpio_func[UB953_NUM_GPIOS]; + + struct gpio_chip gpio_chip; + char gpio_chip_name[64]; + + struct v4l2_subdev sd; + struct media_pad pads[2]; + + struct v4l2_async_notifier notifier; + + struct v4l2_subdev *source_sd; + + struct v4l2_ctrl_handler ctrl_handler; + + bool streaming; + + struct device_node *tx_ep_np; +}; + +static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub953_data, sd); +} + +/* + * HW Access + */ + +static int ub953_read(const struct ub953_data *priv, u8 reg, u8 *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret < 0) { + dev_err(&priv->client->dev, + "Cannot read register 0x%02x: %d!\n", reg, ret); + return ret; + } + + *val = v; + return 0; +} + +static int ub953_write(const struct ub953_data *priv, u8 reg, u8 val) +{ + int ret; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(&priv->client->dev, + "Cannot write register 0x%02x: %d!\n", reg, ret); + + return ret; +} + +static int ub953_write_ind8(const struct ub953_data *priv, u8 reg, u8 val) +{ + int ret; + + ret = ub953_write(priv, UB953_REG_IND_ACC_ADDR, reg); + if (!ret) + ret = ub953_write(priv, UB953_REG_IND_ACC_DATA, val); + return ret; +} + +/* Assumes IA_AUTO_INC is set in UB953_REG_IND_ACC_CTL */ +static int ub953_write_ind16(const struct ub953_data *priv, u8 reg, u16 val) +{ + int ret; + + ret = ub953_write(priv, UB953_REG_IND_ACC_ADDR, reg); + if (!ret) + ret = ub953_write(priv, UB953_REG_IND_ACC_DATA, val >> 8); + if (!ret) + ret = ub953_write(priv, UB953_REG_IND_ACC_DATA, val & 0xff); + return ret; +} + +/* + * GPIO chip + */ +static int ub953_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_INPUT_CTRL, &v); + if (ret) + return ret; + + if (v & UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)) + return GPIO_LINE_DIRECTION_IN; + else + return GPIO_LINE_DIRECTION_OUT; +} + +static int ub953_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + return regmap_update_bits( + priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset)); +} + +static int ub953_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + + ret = regmap_update_bits( + priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : 0); + + if (ret) + return ret; + + return regmap_update_bits( + priv->regmap, UB953_REG_GPIO_INPUT_CTRL, + UB953_REG_GPIO_INPUT_CTRL_INPUT_EN(offset) | + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset), + UB953_REG_GPIO_INPUT_CTRL_OUT_EN(offset)); +} + +static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_GPIO_PIN_STS, &v); + if (ret) + return ret; + + return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset)); +} + +static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct ub953_data *priv = gpiochip_get_data(gc); + + regmap_update_bits( + priv->regmap, UB953_REG_LOCAL_GPIO_DATA, + UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset), + value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : 0); +} + +static int ub953_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0]; +} + +static int ub953_gpiochip_probe(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int ret; + + /* Set all GPIOs to local mode */ + ub953_write(priv, UB953_REG_LOCAL_GPIO_DATA, 0); + + scnprintf(priv->gpio_chip_name, sizeof(priv->gpio_chip_name), "%s", + dev_name(dev)); + + gc->label = priv->gpio_chip_name; + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->base = -1; + gc->can_sleep = 1; + gc->ngpio = UB953_NUM_GPIOS; + gc->get_direction = ub953_gpio_get_direction; + gc->direction_input = ub953_gpio_direction_in; + gc->direction_output = ub953_gpio_direction_out; + gc->get = ub953_gpio_get; + gc->set = ub953_gpio_set; + gc->of_xlate = ub953_gpio_of_xlate; + gc->of_node = priv->client->dev.of_node; + gc->of_gpio_n_cells = 2; + + ret = gpiochip_add_data(gc, priv); + if (ret) { + dev_err(dev, "Failed to add GPIOs: %d\n", ret); + return ret; + } + + return 0; +} + +static void ub953_gpiochip_remove(struct ub953_data *priv) +{ + gpiochip_remove(&priv->gpio_chip); +} + +/* + * V4L2 + */ + +static int ub953_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ub953_data *priv = sd_to_ub953(sd); + int ret; + + priv->streaming = enable; + + ret = v4l2_subdev_call(priv->source_sd, video, s_stream, enable); + if (ret && enable) + priv->streaming = false; + + return ret; +} + +static const struct v4l2_subdev_video_ops ub953_video_ops = { + .s_stream = ub953_s_stream, +}; + +static int _ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_routing_simple_verify(routing); + if (ret) + return ret; + + v4l2_subdev_lock_state(state); + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + + v4l2_subdev_unlock_state(state); + + if (ret) + return ret; + + return 0; +} + + +static int ub953_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub953_data *priv = sd_to_ub953(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + return _ub953_set_routing(sd, state, routing); +} + +static int ub953_get_source_frame_desc(struct ub953_data *priv, + struct v4l2_mbus_frame_desc *desc) +{ + struct media_pad *pad; + int ret; + + pad = media_entity_remote_pad(&priv->pads[UB953_PAD_SINK]); + if (!pad) + return -EPIPE; + + ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, pad->index, + desc); + if (ret) + return ret; + + return 0; +} + +static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub953_data *priv = sd_to_ub953(sd); + const struct v4l2_subdev_krouting *routing; + struct v4l2_mbus_frame_desc source_fd; + struct v4l2_subdev_state *state; + unsigned int i; + int ret = 0; + + if (pad != 1) /* first tx pad */ + return -EINVAL; + + ret = ub953_get_source_frame_desc(priv, &source_fd); + if (ret) + return ret; + + state = v4l2_subdev_lock_active_state(sd); + + routing = &state->routing; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + unsigned int j; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (route->source_pad != pad) + continue; + + for (j = 0; j < source_fd.num_entries; ++j) + if (source_fd.entry[j].stream == route->sink_stream) { + source_entry = &source_fd.entry[j]; + break; + } + + if (!source_entry) { + dev_err(&priv->client->dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + + fd->entry[fd->num_entries].flags = + V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode; + fd->entry[fd->num_entries].bus.csi2.vc = + source_entry->bus.csi2.vc; + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + + fd->num_entries++; + } + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub953_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub953_data *priv = sd_to_ub953(sd); + struct v4l2_mbus_framefmt *fmt; + int ret = 0; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == 1) + return v4l2_subdev_get_fmt(sd, state, format); + + v4l2_subdev_lock_state(state); + + /* Set sink format */ + fmt = v4l2_state_get_stream_format(state, format->pad, format->stream); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + + /* Propagate to source format */ + fmt = v4l2_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub953_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = 1, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub953_set_routing(sd, state, &routing); +} + +static const struct v4l2_subdev_pad_ops ub953_pad_ops = { + .set_routing = ub953_set_routing, + .get_frame_desc = ub953_get_frame_desc, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub953_set_fmt, + .init_cfg = ub953_init_cfg, +}; + +static const struct v4l2_subdev_ops ub953_subdev_ops = { + .video = &ub953_video_ops, + .pad = &ub953_pad_ops, +}; + +static const struct media_entity_operations ub953_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +enum { + TEST_PATTERN_DISABLED = 0, + TEST_PATTERN_V_COLOR_BARS_1, + TEST_PATTERN_V_COLOR_BARS_2, + TEST_PATTERN_V_COLOR_BARS_4, + TEST_PATTERN_V_COLOR_BARS_8, +}; + +static const char *const ub953_tpg_qmenu[] = { + "Disabled", + "1 vertical color bar", + "2 vertical color bars", + "4 vertical color bars", + "8 vertical color bars", +}; + +static void ub953_enable_tpg(struct ub953_data *priv, int tpg_num) +{ + struct v4l2_subdev *sd = &priv->sd; + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + u8 vbp, vfp; + u16 blank_lines; + u16 width; + u16 height; + + u16 bytespp = 2; /* For MEDIA_BUS_FMT_UYVY8_1X16 */ + u8 cbars_idx = tpg_num - TEST_PATTERN_V_COLOR_BARS_1; + u8 num_cbars = 1 << cbars_idx; + + u16 line_size; /* Line size [bytes] */ + u16 bar_size; /* cbar size [bytes] */ + u16 act_lpf; /* active lines/frame */ + u16 tot_lpf; /* tot lines/frame */ + u16 line_pd; /* Line period in 10-ns units */ + + u16 fps = 30; + + vbp = 33; + vfp = 10; + blank_lines = vbp + vfp + 2; /* total blanking lines */ + + state = v4l2_subdev_lock_active_state(sd); + + fmt = v4l2_state_get_stream_format(state, UB953_PAD_SOURCE, 0); + + width = fmt->width; + height = fmt->height; + + line_size = width * bytespp; + bar_size = line_size / num_cbars; + act_lpf = height; + tot_lpf = act_lpf + blank_lines; + line_pd = 100000000 / fps / tot_lpf; + + /* Access Indirect Pattern Gen */ + ub953_write(priv, UB953_REG_IND_ACC_CTL, + UB953_REG_IND_ACC_CTL_IA_AUTO_INC | (0 << 2)); + + ub953_write_ind8(priv, UB953_IND_PGEN_CTL, + UB953_IND_PGEN_CTL_PGEN_ENABLE); + + /* YUV422 8bit: 2 bytes/block, CSI-2 data type 0x1e */ + ub953_write_ind8(priv, UB953_IND_PGEN_CFG, cbars_idx << 4 | 0x2); + ub953_write_ind8(priv, UB953_IND_PGEN_CSI_DI, 0x1e); + + ub953_write_ind16(priv, UB953_IND_PGEN_LINE_SIZE1, line_size); + ub953_write_ind16(priv, UB953_IND_PGEN_BAR_SIZE1, bar_size); + ub953_write_ind16(priv, UB953_IND_PGEN_ACT_LPF1, act_lpf); + ub953_write_ind16(priv, UB953_IND_PGEN_TOT_LPF1, tot_lpf); + ub953_write_ind16(priv, UB953_IND_PGEN_LINE_PD1, line_pd); + ub953_write_ind8(priv, UB953_IND_PGEN_VBP, vbp); + ub953_write_ind8(priv, UB953_IND_PGEN_VFP, vfp); + + v4l2_subdev_unlock_state(state); +} + +static void ub953_disable_tpg(struct ub953_data *priv) +{ + ub953_write_ind8(priv, UB953_IND_PGEN_CTL, 0x00); +} + +static int ub953_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ub953_data *priv = + container_of(ctrl->handler, struct ub953_data, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + if (ctrl->val == 0) + ub953_disable_tpg(priv); + else + ub953_enable_tpg(priv, ctrl->val); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ub953_ctrl_ops = { + .s_ctrl = ub953_s_ctrl, +}; + +static int ub953_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_subdev *asd) +{ + struct ub953_data *priv = sd_to_ub953(notifier->sd); + struct device *dev = &priv->client->dev; + unsigned int src_pad; + int ret; + + dev_dbg(dev, "Bind %s\n", source_subdev->name); + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + src_pad = ret; + + ret = media_create_pad_link( + &source_subdev->entity, src_pad, &priv->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, src_pad, priv->sd.name); + return ret; + } + + dev_dbg(dev, "Bound %s:%u\n", source_subdev->name, src_pad); + + dev_dbg(dev, "All subdevs bound\n"); + + return 0; +} + +static void ub953_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_subdev *asd) +{ + struct ub953_data *priv = sd_to_ub953(notifier->sd); + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "Unbind %s\n", source_subdev->name); +} + +static const struct v4l2_async_notifier_operations ub953_notify_ops = { + .bound = ub953_notify_bound, + .unbind = ub953_notify_unbind, +}; + +static int ub953_v4l2_notifier_register(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_subdev *asd; + struct device_node *ep_node; + int ret; + + dev_dbg(dev, "register async notif\n"); + + ep_node = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (!ep_node) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_notifier_init(&priv->notifier); + + asd = v4l2_async_notifier_add_fwnode_remote_subdev( + &priv->notifier, of_fwnode_handle(ep_node), + sizeof(*asd)); + + of_node_put(ep_node); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_notifier_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &ub953_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub953_v4l2_notifier_unregister(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "Unregister async notif\n"); + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); +} + +/* + * Probing + */ + +static void ub953_soft_reset(struct ub953_data *priv) +{ + struct device *dev = &priv->client->dev; + int retries; + + ub953_write(priv, UB953_REG_RESET_CTL, + UB953_REG_RESET_CTL_DIGITAL_RESET_1); + + usleep_range(10000, 30000); + + retries = 10; + while (retries-- > 0) { + int ret; + u8 v; + + ret = ub953_read(priv, UB953_REG_RESET_CTL, &v); + + if (ret >= 0 && + (v & UB953_REG_RESET_CTL_DIGITAL_RESET_1) == 0) { + dev_dbg(dev, "reset done\n"); + break; + } + + usleep_range(1000, 3000); + } + + if (retries == 0) + dev_err(dev, "reset timeout\n"); +} + +static int ub953_i2c_init(struct ub953_data *priv) +{ + /* i2c fast mode */ + u32 scl_high = 915; /* ns */ + u32 scl_low = 1641; /* ns */ + u32 ref = 25000000; /* TODO: get refclock from deserializer */ + int ret = 0; + + scl_high = div64_u64((u64)scl_high * ref, 1000000000) - 5; + scl_low = div64_u64((u64)scl_low * ref, 1000000000) - 5; + + ret = ub953_write(priv, UB953_REG_SCL_HIGH_TIME, scl_high); + if (ret) + return ret; + + ret = ub953_write(priv, UB953_REG_SCL_LOW_TIME, scl_low); + if (ret) + return ret; + + return 0; +} + +static int ub953_parse_dt(struct ub953_data *priv) +{ + struct device_node *np = priv->client->dev.of_node; + struct device *dev = &priv->client->dev; + int ret; + + if (!np) { + dev_err(dev, "OF: no device tree node!\n"); + return -ENOENT; + } + + /* optional, if absent all GPIO pins are unused */ + ret = of_property_read_u32_array(np, "gpio-functions", priv->gpio_func, + ARRAY_SIZE(priv->gpio_func)); + if (ret && ret != -EINVAL) + dev_err(dev, "DT: invalid gpio-functions property (%d)", ret); + + return 0; +} + +static const struct regmap_config ub953_regmap_config = { + .name = "ds90ub953", + .reg_bits = 8, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_DEFAULT, + .val_format_endian = REGMAP_ENDIAN_DEFAULT, +}; + +static int ub953_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub953_data *priv; + int ret; + u8 rev; + + dev_dbg(dev, "probing, addr 0x%02x\n", client->addr); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->regmap = devm_regmap_init_i2c(client, &ub953_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(dev, "Failed to init regmap\n"); + return PTR_ERR(priv->regmap); + } + + ret = ub953_parse_dt(priv); + if (ret) + return ret; + + ub953_soft_reset(priv); + + ret = ub953_read(priv, UB953_REG_REV_MASK_ID, &rev); + if (ret) { + dev_err(dev, "Failed to read revision: %d", ret); + return ret; + } + + dev_info(dev, "Found rev %u, mask %u\n", rev >> 4, rev & 0xf); + + ret = ub953_i2c_init(priv); + if (ret) { + dev_err(dev, "i2c init failed: %d\n", ret); + return ret; + } + + ret = ub953_gpiochip_probe(priv); + if (ret) { + dev_err(dev, "Failed to init gpiochip\n"); + return ret; + } + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub953_subdev_ops); + + v4l2_ctrl_handler_init(&priv->ctrl_handler, + ARRAY_SIZE(ub953_tpg_qmenu) - 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, &ub953_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ub953_tpg_qmenu) - 1, 0, 0, + ub953_tpg_qmenu); + + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_gpiochip_remove; + } + + priv->sd.flags |= + V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_MULTIPLEXED; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub953_entity_ops; + + priv->pads[0].flags = MEDIA_PAD_FL_SINK; + priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) { + dev_err(dev, "Failed to init pads\n"); + goto err_remove_ctrls; + } + + priv->tx_ep_np = of_graph_get_endpoint_by_regs(dev->of_node, 1, 0); + priv->sd.fwnode = of_fwnode_handle(priv->tx_ep_np); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub953_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + /* + * TODO compute these hard-coded values + * + * SET CLK_OUT: + * MODE = 0 = CSI-2 sync (strap, reg 0x03) -> refclk from deser + * REFCLK = 23..26 MHz (REFCLK pin @ remote deserializer) + * FC = fwd channel data rate = 160 x REFCLK + * CLK_OUT = FC * M / (HS_CLK_DIV * N) + * = FC * 1 / (4 * 20) = 2 * REFCLK + */ + ub953_write(priv, UB953_REG_CLKOUT_CTRL0, 0x41); /* div by 4, M=1 */ + ub953_write(priv, UB953_REG_CLKOUT_CTRL1, 0x25); /* N */ + + ub953_write(priv, UB953_REG_GENERAL_CFG, + (1 << 6) | /* continuous clk */ + (3 << 4) | /* 4 lanes */ + (1 << 1)); /* CRC TX gen */ + + dev_dbg(dev, "Successfully probed\n"); + + return 0; + +err_unreg_notif: + ub953_v4l2_notifier_unregister(priv); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + if (priv->tx_ep_np) + of_node_put(priv->tx_ep_np); + + media_entity_cleanup(&priv->sd.entity); +err_remove_ctrls: + v4l2_ctrl_handler_free(&priv->ctrl_handler); +err_gpiochip_remove: + ub953_gpiochip_remove(priv); + + return ret; +} + +static int ub953_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub953_data *priv = sd_to_ub953(sd); + + dev_dbg(&client->dev, "Removing\n"); + + ub953_v4l2_notifier_unregister(priv); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + of_node_put(priv->tx_ep_np); + + media_entity_cleanup(&priv->sd.entity); + + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + ub953_gpiochip_remove(priv); + + return 0; +} + +static const struct i2c_device_id ub953_id[] = { { "ds90ub953-q1", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, ub953_id); + +#ifdef CONFIG_OF +static const struct of_device_id ub953_dt_ids[] = { + { .compatible = "ti,ds90ub953-q1", }, + {} +}; +MODULE_DEVICE_TABLE(of, ub953_dt_ids); +#endif + +static struct i2c_driver ds90ub953_driver = { + .probe_new = ub953_probe, + .remove = ub953_remove, + .id_table = ub953_id, + .driver = { + .name = "ds90ub953", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ub953_dt_ids), + }, +}; + +module_i2c_driver(ds90ub953_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments DS90UB953 serializer driver"); +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c new file mode 100644 index 000000000000..26079edd9096 --- /dev/null +++ b/drivers/media/i2c/ds90ub960.c @@ -0,0 +1,2381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Texas Instruments DS90UB960-Q1 video deserializer + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c-atr.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#define UB960_MAX_RX_NPORTS 4 +#define UB960_MAX_TX_NPORTS 2 +#define UB960_MAX_NPORTS (UB960_MAX_RX_NPORTS + UB960_MAX_TX_NPORTS) + +#define UB960_NUM_SLAVE_ALIASES 8 +#define UB960_MAX_POOL_ALIASES (UB960_MAX_RX_NPORTS * UB960_NUM_SLAVE_ALIASES) + +/* + * Register map + * + * 0x00-0x32 Shared (UB960_SR) + * 0x33-0x3A CSI-2 TX (per-port paged on DS90UB960, shared on 954) (UB960_TR) + * 0x4C Shared (UB960_SR) + * 0x4D-0x7F FPD-Link RX, per-port paged (UB960_RR) + * 0xB0-0xBF Shared (UB960_SR) + * 0xD0-0xDF FPD-Link RX, per-port paged (UB960_RR) + * 0xF0-0xF5 Shared (UB960_SR) + * 0xF8-0xFB Shared (UB960_SR) + * All others Reserved + * + * Register prefixes: + * UB960_SR_* = Shared register + * UB960_RR_* = FPD-Link RX, per-port paged register + * UB960_TR_* = CSI-2 TX, per-port paged register + * UB960_XR_* = Reserved register + * UB960_IR_* = Indirect register + */ + +#define UB960_SR_I2C_DEV_ID 0x00 +#define UB960_SR_RESET 0x01 +#define UB960_SR_GEN_CONFIG 0x02 +#define UB960_SR_REV_MASK 0x03 +#define UB960_SR_DEVICE_STS 0x04 +#define UB960_SR_PAR_ERR_THOLD_HI 0x05 +#define UB960_SR_PAR_ERR_THOLD_LO 0x06 +#define UB960_SR_BCC_WDOG_CTL 0x07 +#define UB960_SR_I2C_CTL1 0x08 +#define UB960_SR_I2C_CTL2 0x09 +#define UB960_SR_SCL_HIGH_TIME 0x0A +#define UB960_SR_SCL_LOW_TIME 0x0B +#define UB960_SR_RX_PORT_CTL 0x0C +#define UB960_SR_IO_CTL 0x0D +#define UB960_SR_GPIO_PIN_STS 0x0E +#define UB960_SR_GPIO_INPUT_CTL 0x0F +#define UB960_SR_GPIO_PIN_CTL(n) (0x10 + (n)) /* n < UB960_NUM_GPIOS */ +#define UB960_SR_FS_CTL 0x18 +#define UB960_SR_FS_HIGH_TIME_1 0x19 +#define UB960_SR_FS_HIGH_TIME_0 0x1A +#define UB960_SR_FS_LOW_TIME_1 0x1B +#define UB960_SR_FS_LOW_TIME_0 0x1C +#define UB960_SR_MAX_FRM_HI 0x1D +#define UB960_SR_MAX_FRM_LO 0x1E +#define UB960_SR_CSI_PLL_CTL 0x1F + +#define UB960_SR_FWD_CTL1 0x20 +#define UB960_SR_FWD_CTL1_PORT_DIS(n) BIT((n) + 4) + +#define UB960_SR_FWD_CTL2 0x21 +#define UB960_SR_FWD_STS 0x22 + +#define UB960_SR_INTERRUPT_CTL 0x23 +#define UB960_SR_INTERRUPT_CTL_INT_EN BIT(7) +#define UB960_SR_INTERRUPT_CTL_IE_CSI_TX0 BIT(4) +#define UB960_SR_INTERRUPT_CTL_IE_RX(n) BIT((n)) /* rxport[n] IRQ */ +#define UB960_SR_INTERRUPT_CTL_ALL 0x83 /* TODO 0x93 to enable CSI */ + +#define UB960_SR_INTERRUPT_STS 0x24 +#define UB960_SR_INTERRUPT_STS_INT BIT(7) +#define UB960_SR_INTERRUPT_STS_IS_CSI_TX(n) BIT(4 + (n)) /* txport[n] IRQ */ +#define UB960_SR_INTERRUPT_STS_IS_RX(n) BIT((n)) /* rxport[n] IRQ */ + +#define UB960_SR_TS_CONFIG 0x25 +#define UB960_SR_TS_CONTROL 0x26 +#define UB960_SR_TS_LINE_HI 0x27 +#define UB960_SR_TS_LINE_LO 0x28 +#define UB960_SR_TS_STATUS 0x29 +#define UB960_SR_TIMESTAMP_P0_HI 0x2A +#define UB960_SR_TIMESTAMP_P0_LO 0x2B +#define UB960_SR_TIMESTAMP_P1_HI 0x2C +#define UB960_SR_TIMESTAMP_P1_LO 0x2D + +#define UB960_SR_CSI_PORT_SEL 0x32 + +#define UB960_TR_CSI_CTL 0x33 +#define UB960_TR_CSI_CTL_CSI_CAL_EN BIT(6) +#define UB960_TR_CSI_CTL_CSI_ENABLE BIT(0) + +#define UB960_TR_CSI_CTL2 0x34 +#define UB960_TR_CSI_STS 0x35 +#define UB960_TR_CSI_TX_ICR 0x36 + +#define UB960_TR_CSI_TX_ISR 0x37 +#define UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR BIT(3) +#define UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR BIT(1) + +#define UB960_TR_CSI_TEST_CTL 0x38 +#define UB960_TR_CSI_TEST_PATT_HI 0x39 +#define UB960_TR_CSI_TEST_PATT_LO 0x3A + +#define UB960_XR_AEQ_CTL1 0x42 +#define UB960_XR_AEQ_ERR_THOLD 0x43 + +#define UB960_RR_BCC_ERR_CTL 0x46 +#define UB960_RR_BCC_STATUS 0x47 + +#define UB960_RR_FPD3_CAP 0x4A +#define UB960_RR_RAW_EMBED_DTYPE 0x4B + +#define UB960_SR_FPD3_PORT_SEL 0x4C + +#define UB960_RR_RX_PORT_STS1 0x4D +#define UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS1_LOCK_STS_CHG BIT(4) +#define UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS1_PARITY_ERROR BIT(2) +#define UB960_RR_RX_PORT_STS1_PORT_PASS BIT(1) +#define UB960_RR_RX_PORT_STS1_LOCK_STS BIT(0) + +#define UB960_RR_RX_PORT_STS2 0x4E +#define UB960_RR_RX_PORT_STS2_LINE_LEN_UNSTABLE BIT(7) +#define UB960_RR_RX_PORT_STS2_LINE_LEN_CHG BIT(6) +#define UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR BIT(5) +#define UB960_RR_RX_PORT_STS2_BUFFER_ERROR BIT(4) +#define UB960_RR_RX_PORT_STS2_CSI_ERROR BIT(3) +#define UB960_RR_RX_PORT_STS2_FREQ_STABLE BIT(2) +#define UB960_RR_RX_PORT_STS2_CABLE_FAULT BIT(1) +#define UB960_RR_RX_PORT_STS2_LINE_CNT_CHG BIT(0) + +#define UB960_RR_RX_FREQ_HIGH 0x4F +#define UB960_RR_RX_FREQ_LOW 0x50 +#define UB960_RR_SENSOR_STS_0 0x51 +#define UB960_RR_SENSOR_STS_1 0x52 +#define UB960_RR_SENSOR_STS_2 0x53 +#define UB960_RR_SENSOR_STS_3 0x54 +#define UB960_RR_RX_PAR_ERR_HI 0x55 +#define UB960_RR_RX_PAR_ERR_LO 0x56 +#define UB960_RR_BIST_ERR_COUNT 0x57 + +#define UB960_RR_BCC_CONFIG 0x58 +#define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH BIT(6) + +#define UB960_RR_DATAPATH_CTL1 0x59 +#define UB960_RR_DATAPATH_CTL2 0x5A +#define UB960_RR_SER_ID 0x5B +#define UB960_RR_SER_ALIAS_ID 0x5C + +/* For these two register sets: n < UB960_NUM_SLAVE_ALIASES */ +#define UB960_RR_SLAVE_ID(n) (0x5D + (n)) +#define UB960_RR_SLAVE_ALIAS(n) (0x65 + (n)) + +#define UB960_RR_PORT_CONFIG 0x6D +#define UB960_RR_BC_GPIO_CTL(n) (0x6E + (n)) /* n < 2 */ +#define UB960_RR_RAW10_ID 0x70 +#define UB960_RR_RAW12_ID 0x71 +#define UB960_RR_CSI_VC_MAP 0x72 +#define UB960_RR_LINE_COUNT_HI 0x73 +#define UB960_RR_LINE_COUNT_LO 0x74 +#define UB960_RR_LINE_LEN_1 0x75 +#define UB960_RR_LINE_LEN_0 0x76 +#define UB960_RR_FREQ_DET_CTL 0x77 +#define UB960_RR_MAILBOX_1 0x78 +#define UB960_RR_MAILBOX_2 0x79 + +#define UB960_RR_CSI_RX_STS 0x7A +#define UB960_RR_CSI_RX_STS_LENGTH_ERR BIT(3) +#define UB960_RR_CSI_RX_STS_CKSUM_ERR BIT(2) +#define UB960_RR_CSI_RX_STS_ECC2_ERR BIT(1) +#define UB960_RR_CSI_RX_STS_ECC1_ERR BIT(0) + +#define UB960_RR_CSI_ERR_COUNTER 0x7B +#define UB960_RR_PORT_CONFIG2 0x7C +#define UB960_RR_PORT_PASS_CTL 0x7D +#define UB960_RR_SEN_INT_RISE_CTL 0x7E +#define UB960_RR_SEN_INT_FALL_CTL 0x7F + +#define UB960_XR_REFCLK_FREQ 0xA5 + +#define UB960_SR_IND_ACC_CTL 0xB0 +#define UB960_SR_IND_ACC_CTL_IA_AUTO_INC BIT(1) + +#define UB960_SR_IND_ACC_ADDR 0xB1 +#define UB960_SR_IND_ACC_DATA 0xB2 +#define UB960_SR_BIST_CONTROL 0xB3 +#define UB960_SR_MODE_IDX_STS 0xB8 +#define UB960_SR_LINK_ERROR_COUNT 0xB9 +#define UB960_SR_FPD3_ENC_CTL 0xBA +#define UB960_SR_FV_MIN_TIME 0xBC +#define UB960_SR_GPIO_PD_CTL 0xBE + +#define UB960_RR_PORT_DEBUG 0xD0 +#define UB960_RR_AEQ_CTL2 0xD2 +#define UB960_RR_AEQ_STATUS 0xD3 +#define UB960_RR_AEQ_BYPASS 0xD4 +#define UB960_RR_AEQ_MIN_MAX 0xD5 +#define UB960_RR_PORT_ICR_HI 0xD8 +#define UB960_RR_PORT_ICR_LO 0xD9 +#define UB960_RR_PORT_ISR_HI 0xDA +#define UB960_RR_PORT_ISR_LO 0xDB +#define UB960_RR_FC_GPIO_STS 0xDC +#define UB960_RR_FC_GPIO_ICR 0xDD +#define UB960_RR_SEN_INT_RISE_STS 0xDE +#define UB960_RR_SEN_INT_FALL_STS 0xDF + +#define UB960_SR_FPD3_RX_ID0 0xF0 +#define UB960_SR_FPD3_RX_ID1 0xF1 +#define UB960_SR_FPD3_RX_ID2 0xF2 +#define UB960_SR_FPD3_RX_ID3 0xF3 +#define UB960_SR_FPD3_RX_ID4 0xF4 +#define UB960_SR_FPD3_RX_ID5 0xF5 +#define UB960_SR_I2C_RX_ID(n) (0xF8 + (n)) /* < UB960_FPD_RX_NPORTS */ + +/* UB960_IR_PGEN_*: Indirect Registers for Test Pattern Generator */ + +#define UB960_IR_PGEN_CTL 0x01 +#define UB960_IR_PGEN_CTL_PGEN_ENABLE BIT(0) + +#define UB960_IR_PGEN_CFG 0x02 +#define UB960_IR_PGEN_CSI_DI 0x03 +#define UB960_IR_PGEN_LINE_SIZE1 0x04 +#define UB960_IR_PGEN_LINE_SIZE0 0x05 +#define UB960_IR_PGEN_BAR_SIZE1 0x06 +#define UB960_IR_PGEN_BAR_SIZE0 0x07 +#define UB960_IR_PGEN_ACT_LPF1 0x08 +#define UB960_IR_PGEN_ACT_LPF0 0x09 +#define UB960_IR_PGEN_TOT_LPF1 0x0A +#define UB960_IR_PGEN_TOT_LPF0 0x0B +#define UB960_IR_PGEN_LINE_PD1 0x0C +#define UB960_IR_PGEN_LINE_PD0 0x0D +#define UB960_IR_PGEN_VBP 0x0E +#define UB960_IR_PGEN_VFP 0x0F +#define UB960_IRT_PGEN_COLOR(n) (0x10 + (n)) /* n < 15 */ + +struct ub960_hw_data { + u8 num_rxports; + u8 num_txports; +}; + +enum ub960_rxport_mode { + RXPORT_MODE_RAW10 = 0, + RXPORT_MODE_RAW12_HF = 1, + RXPORT_MODE_RAW12_LF = 2, + RXPORT_MODE_CSI2 = 3, +}; + +struct ub960_rxport { + struct ub960_data *priv; + u8 nport; /* RX port number, and index in priv->rxport[] */ + + struct v4l2_subdev *sd; /* Connected subdev */ + struct fwnode_handle *fwnode; + + enum ub960_rxport_mode mode; + + struct device_node *remote_of_node; /* "remote-chip" OF node */ + struct i2c_client *ser_client; /* remote serializer */ + unsigned short ser_alias; /* ser i2c alias (lower 7 bits) */ + bool locked; +}; + +struct ub960_asd { + struct v4l2_async_subdev base; + struct ub960_rxport *rxport; +}; + +static inline struct ub960_asd *to_ub960_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct ub960_asd, base); +} + +struct ub960_txport { + u32 num_data_lanes; +}; + +struct ub960_data { + const struct ub960_hw_data *hw_data; + struct i2c_client *client; /* for shared local registers */ + struct regmap *regmap; + struct gpio_desc *pd_gpio; + struct task_struct *kthread; + struct i2c_atr *atr; + struct ub960_rxport *rxports[UB960_MAX_RX_NPORTS]; + struct ub960_txport *txports[UB960_MAX_TX_NPORTS]; + + struct v4l2_subdev sd; + struct media_pad pads[UB960_MAX_NPORTS]; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + + unsigned long refclk; + + u32 tx_data_rate; /* Nominal data rate (Gb/s) */ + s64 tx_link_freq[1]; + + /* Address Translator alias-to-slave map table */ + size_t atr_alias_num; /* Number of aliases configured */ + u16 atr_alias_id[UB960_MAX_POOL_ALIASES]; /* 0 = no alias */ + u16 atr_slave_id[UB960_MAX_POOL_ALIASES]; /* 0 = not in use */ + struct mutex alias_table_lock; + + u8 current_read_rxport; + u8 current_write_rxport_mask; + + u8 current_read_csiport; + u8 current_write_csiport_mask; + + bool streaming; +}; + +static inline struct ub960_data *sd_to_ub960(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ub960_data, sd); +} + +enum { + TEST_PATTERN_DISABLED = 0, + TEST_PATTERN_V_COLOR_BARS_1, + TEST_PATTERN_V_COLOR_BARS_2, + TEST_PATTERN_V_COLOR_BARS_4, + TEST_PATTERN_V_COLOR_BARS_8, +}; + +static const char * const ub960_tpg_qmenu[] = { + "Disabled", + "1 vertical color bar", + "2 vertical color bars", + "4 vertical color bars", + "8 vertical color bars", +}; + +static inline bool ub960_pad_is_sink(struct ub960_data *priv, u32 pad) +{ + return pad < priv->hw_data->num_rxports; +} + +static inline bool ub960_pad_is_source(struct ub960_data *priv, u32 pad) +{ + return pad >= priv->hw_data->num_rxports && + pad < (priv->hw_data->num_rxports + priv->hw_data->num_txports); +} + +struct ub960_format_info { + u32 code; + u32 bpp; + u8 datatype; + bool meta; +}; + +static const struct ub960_format_info ub960_formats[] = { + { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, .datatype = 0x1e, }, + + /* Legacy */ + { .code = MEDIA_BUS_FMT_YUYV8_2X8, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_UYVY8_2X8, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_VYUY8_2X8, .bpp = 16, .datatype = 0x1e, }, + { .code = MEDIA_BUS_FMT_YVYU8_2X8, .bpp = 16, .datatype = 0x1e, }, + + /* RAW */ + { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .datatype = 0x2c, }, + { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .datatype = 0x2c, }, +}; + +static const struct ub960_format_info *ub960_find_format(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ub960_formats); ++i) { + if (ub960_formats[i].code == code) + return &ub960_formats[i]; + } + + return NULL; +} + +/* ----------------------------------------------------------------------------- + * Basic device access + */ + +static int ub960_read(const struct ub960_data *priv, u8 reg, u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + return ret; + } + + *val = v; + + return 0; +} + +static int ub960_write(const struct ub960_data *priv, u8 reg, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ret = regmap_write(priv->regmap, reg, val); + if (ret < 0) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +static int ub960_update_bits_shared(const struct ub960_data *priv, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + if (ret < 0) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +static int ub960_rxport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + if (priv->current_read_rxport == nport && + priv->current_write_rxport_mask == BIT(nport)) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_FPD3_PORT_SEL, + (nport << 4) | (1 << nport)); + if (ret) { + dev_err(dev, "%s: cannot select rxport %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->current_read_rxport = nport; + priv->current_write_rxport_mask = BIT(nport); + + return 0; +} + +static int ub960_rxport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + ub960_rxport_select(priv, nport); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + return ret; + } + + *val = v; + + return 0; +} + +static int ub960_rxport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ub960_rxport_select(priv, nport); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +static int ub960_rxport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ub960_rxport_select(priv, nport); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +static int ub960_csiport_select(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + int ret; + + if (priv->current_read_csiport == nport && + priv->current_write_csiport_mask == BIT(nport)) + return 0; + + ret = regmap_write(priv->regmap, UB960_SR_CSI_PORT_SEL, + (nport << 4) | (1 << nport)); + if (ret) { + dev_err(dev, "%s: cannot select csi port %d (%d)!\n", __func__, + nport, ret); + return ret; + } + + priv->current_read_csiport = nport; + priv->current_write_csiport_mask = BIT(nport); + + return 0; +} + +static int ub960_csiport_read(struct ub960_data *priv, u8 nport, u8 reg, + u8 *val) +{ + struct device *dev = &priv->client->dev; + unsigned int v; + int ret; + + ub960_csiport_select(priv, nport); + + ret = regmap_read(priv->regmap, reg, &v); + if (ret) { + dev_err(dev, "%s: cannot read register 0x%02x (%d)!\n", + __func__, reg, ret); + return ret; + } + + *val = v; + + return 0; +} + +static int ub960_csiport_write(struct ub960_data *priv, u8 nport, u8 reg, + u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ub960_csiport_select(priv, nport); + + ret = regmap_write(priv->regmap, reg, val); + if (ret) + dev_err(dev, "%s: cannot write register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +__maybe_unused +static int ub960_csiport_update_bits(struct ub960_data *priv, u8 nport, u8 reg, + u8 mask, u8 val) +{ + struct device *dev = &priv->client->dev; + int ret; + + ub960_csiport_select(priv, nport); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + + if (ret) + dev_err(dev, "%s: cannot update register 0x%02x (%d)!\n", + __func__, reg, ret); + + return ret; +} + +static int ub960_write_ind8(const struct ub960_data *priv, u8 reg, u8 val) +{ + int ret; + + ret = ub960_write(priv, UB960_SR_IND_ACC_ADDR, reg); + if (!ret) + ret = ub960_write(priv, UB960_SR_IND_ACC_DATA, val); + return ret; +} + +/* Assumes IA_AUTO_INC is set in UB960_SR_IND_ACC_CTL */ +static int ub960_write_ind16(const struct ub960_data *priv, u8 reg, u16 val) +{ + int ret; + + ret = ub960_write(priv, UB960_SR_IND_ACC_ADDR, reg); + if (!ret) + ret = ub960_write(priv, UB960_SR_IND_ACC_DATA, val >> 8); + if (!ret) + ret = ub960_write(priv, UB960_SR_IND_ACC_DATA, val & 0xff); + return ret; +} + +/* ----------------------------------------------------------------------------- + * I2C-ATR (address translator) + */ + +static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_board_info *info, + const struct i2c_client *client, + u16 *alias_id) +{ + struct ub960_data *priv = i2c_atr_get_clientdata(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + unsigned int pool_idx; + u16 alias = 0; + int ret = 0; + + dev_dbg(dev, "rx%d: %s\n", chan_id, __func__); + + mutex_lock(&priv->alias_table_lock); + + /* Find unused alias in table */ + + for (pool_idx = 0; pool_idx < priv->atr_alias_num; pool_idx++) + if (priv->atr_slave_id[pool_idx] == 0) + break; + + if (pool_idx == priv->atr_alias_num) { + dev_warn(dev, "rx%d: alias pool exhausted\n", rxport->nport); + ret = -EADDRNOTAVAIL; + goto out; + } + + alias = priv->atr_alias_id[pool_idx]; + + /* Find first unused alias register */ + + for (reg_idx = 0; reg_idx < UB960_NUM_SLAVE_ALIASES; reg_idx++) { + u8 regval; + + ret = ub960_rxport_read(priv, chan_id, + UB960_RR_SLAVE_ALIAS(reg_idx), ®val); + if (!ret && regval == 0) + break; + } + + if (reg_idx == UB960_NUM_SLAVE_ALIASES) { + dev_warn(dev, "rx%d: all aliases in use\n", rxport->nport); + ret = -EADDRNOTAVAIL; + goto out; + } + + /* Map alias to slave */ + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ID(reg_idx), + client->addr << 1); + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), + alias << 1); + + priv->atr_slave_id[pool_idx] = client->addr; + + *alias_id = alias; /* tell the atr which alias we chose */ + + dev_dbg(dev, "rx%d: client 0x%02x mapped at alias 0x%02x (%s)\n", + rxport->nport, client->addr, alias, client->name); + +out: + mutex_unlock(&priv->alias_table_lock); + return ret; +} + +static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client) +{ + struct ub960_data *priv = i2c_atr_get_clientdata(atr); + struct ub960_rxport *rxport = priv->rxports[chan_id]; + struct device *dev = &priv->client->dev; + unsigned int reg_idx; + unsigned int pool_idx; + u16 alias = 0; + + mutex_lock(&priv->alias_table_lock); + + /* Find alias mapped to this client */ + + for (pool_idx = 0; pool_idx < priv->atr_alias_num; pool_idx++) + if (priv->atr_slave_id[pool_idx] == client->addr) + break; + + if (pool_idx == priv->atr_alias_num) { + dev_err(dev, "rx%d: client 0x%02x is not mapped!\n", + rxport->nport, client->addr); + goto out; + } + + alias = priv->atr_alias_id[pool_idx]; + + /* Find alias register used for this client */ + + for (reg_idx = 0; reg_idx < UB960_NUM_SLAVE_ALIASES; reg_idx++) { + u8 regval; + int ret; + + ret = ub960_rxport_read(priv, chan_id, + UB960_RR_SLAVE_ALIAS(reg_idx), ®val); + if (!ret && regval == (alias << 1)) + break; + } + + if (reg_idx == UB960_NUM_SLAVE_ALIASES) { + dev_err(dev, + "rx%d: cannot find alias 0x%02x reg (client 0x%02x)!\n", + rxport->nport, alias, client->addr); + goto out; + } + + /* Unmap */ + + ub960_rxport_write(priv, chan_id, UB960_RR_SLAVE_ALIAS(reg_idx), 0); + priv->atr_slave_id[pool_idx] = 0; + + dev_dbg(dev, "rx%d: client 0x%02x unmapped from alias 0x%02x (%s)\n", + rxport->nport, client->addr, alias, client->name); + +out: + mutex_unlock(&priv->alias_table_lock); +} + +static const struct i2c_atr_ops ub960_atr_ops = { + .attach_client = ub960_atr_attach_client, + .detach_client = ub960_atr_detach_client, +}; + +/* ----------------------------------------------------------------------------- + * CSI ports + */ + +static int ub960_csiport_probe_one(struct ub960_data *priv, + const struct device_node *np, + u8 nport) +{ + struct device *dev = &priv->client->dev; + struct ub960_txport *txport; + int ret; + + if (priv->txports[nport]) { + dev_err(dev, "OF: %s: duplicate tx port\n", + of_node_full_name(np)); + return -EADDRINUSE; + } + + txport = kzalloc(sizeof(*txport), GFP_KERNEL); + if (!txport) + return -ENOMEM; + + priv->txports[nport] = txport; + + ret = of_property_count_u32_elems(np, "data-lanes"); + + if (ret <= 0) { + dev_err(dev, "OF: %s: failed to parse data-lanes: %d\n", + of_node_full_name(np), ret); + goto err_free_txport; + } + + txport->num_data_lanes = ret; + + return 0; + +err_free_txport: + kfree(txport); + + return ret; +} + +static void ub960_txport_remove_one(struct ub960_data *priv, u8 nport) +{ + struct ub960_txport *txport = priv->txports[nport]; + + kfree(txport); + priv->txports[nport] = NULL; +} + +static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 csi_tx_isr; + int ret; + + ret = ub960_csiport_read(priv, nport, UB960_TR_CSI_TX_ISR, &csi_tx_isr); + + if (!ret) { + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_SYNC_ERROR) + dev_warn(dev, "TX%u: CSI_SYNC_ERROR\n", nport); + + if (csi_tx_isr & UB960_TR_CSI_TX_ISR_IS_CSI_PASS_ERROR) + dev_warn(dev, "TX%u: CSI_PASS_ERROR\n", nport); + } +} + +/* ----------------------------------------------------------------------------- + * RX ports + */ + +/* + * Instantiate serializer and i2c adapter for a locked remote end. + * + * Must be called with priv->alias_table_lock not held! The added i2c adapter + * will probe new slaves, which can request i2c transfers, ending up in + * calling ub960_atr_attach_client() where the lock is taken. + */ +static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + struct device *dev = &priv->client->dev; + struct i2c_board_info ser_info = { + .of_node = rxport->remote_of_node, + }; + + /* + * Adding the serializer under rxport->adap would be cleaner, but it + * would need tweaks to bypass the alias table. Adding to the + * upstream adapter is way simpler. + */ + ser_info.addr = rxport->ser_alias; + rxport->ser_client = + i2c_new_client_device(priv->client->adapter, &ser_info); + if (!rxport->ser_client) { + dev_err(dev, "rx%d: cannot add %s i2c device", nport, + ser_info.type); + return -EIO; + } + + dev_dbg(dev, "rx%d: remote serializer at alias 0x%02x\n", nport, + rxport->ser_client->addr); + + return 0; +} + +static void ub960_rxport_remove_serializer(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (rxport->ser_client) { + i2c_unregister_device(rxport->ser_client); + rxport->ser_client = NULL; + } +} + +static int ub960_rxport_probe_serializers(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned long timeout; + u8 nport; + unsigned int missing = 0; + + timeout = jiffies + msecs_to_jiffies(750); + + while (time_before(jiffies, timeout)) { + missing = 0; + + for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) { + struct ub960_rxport *rxport = priv->rxports[nport]; + u8 rx_port_sts1; + int ret; + + /* No serializer in DT? */ + if (!rxport) + continue; + + /* Serializer already added? */ + if (rxport->ser_client) + continue; + + ret = ub960_rxport_read(priv, nport, + UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (ret) + return ret; + + /* Serializer not locked yet? */ + if (!(rx_port_sts1 & UB960_RR_RX_PORT_STS1_LOCK_STS)) { + missing++; + continue; + } + + ret = ub960_rxport_add_serializer(priv, nport); + if (ret) + return ret; + + rxport->locked = true; + } + + if (missing == 0) + return 0; + + usleep_range(500, 5000); + } + + dev_err(dev, "timeout, continuing with %u missing serializer(s)\n", + missing); + + return 0; +} + +/* + * Return the local alias for a given remote serializer. + */ +static int ub960_of_get_reg(struct device_node *np, const char *serializer_name) +{ + u32 alias; + int ret; + int idx; + + if (!np) + return -ENODEV; + + idx = of_property_match_string(np, "reg-names", serializer_name); + if (idx < 0) + return idx; + + ret = of_property_read_u32_index(np, "reg", idx, &alias); + if (ret) + return ret; + + return alias; +} + +static int ub960_rxport_probe_one(struct ub960_data *priv, + const struct device_node *np, + u8 nport) +{ + const char *ser_names[UB960_MAX_RX_NPORTS] = { "ser0", "ser1", "ser2", + "ser3" }; + struct device *dev = &priv->client->dev; + struct ub960_rxport *rxport; + u32 bc_freq, bc_freq_val; + int ret; + u32 mode; + + if (priv->rxports[nport]) { + dev_err(dev, "OF: %s: reg value %d is duplicated\n", + of_node_full_name(np), nport); + return -EADDRINUSE; + } + + rxport = kzalloc(sizeof(*rxport), GFP_KERNEL); + if (!rxport) + return -ENOMEM; + + priv->rxports[nport] = rxport; + + rxport->nport = nport; + rxport->priv = priv; + + ret = ub960_of_get_reg(priv->client->dev.of_node, ser_names[nport]); + if (ret < 0) + goto err_free_rxport; + + rxport->ser_alias = ret; + + ret = of_property_read_u32(np, "mode", &mode); + if (ret < 0) { + dev_err(dev, "Missing RX port mode: %d\n", ret); + goto err_free_rxport; + } + + if (mode > RXPORT_MODE_CSI2) { + dev_err(dev, "Bad RX port mode %u\n", mode); + goto err_free_rxport; + } + + rxport->mode = mode; + + ret = of_property_read_u32(np, "bc-freq", &bc_freq); + if (ret < 0) { + dev_err(dev, "Failed to read BC freq for port %u: %d\n", nport, + ret); + goto err_free_rxport; + } + + switch (bc_freq) { + case 2500000: + bc_freq_val = 0; break; + case 10000000: + bc_freq_val = 2; break; + case 50000000: + bc_freq_val = 6; break; + default: + dev_err(dev, "Bad BC freq %u\n", bc_freq); + goto err_free_rxport; + } + + rxport->remote_of_node = of_get_child_by_name(np, "remote-chip"); + if (!rxport->remote_of_node) { + dev_err(dev, "OF: %s: missing remote-chip child\n", + of_node_full_name(np)); + ret = -EINVAL; + goto err_free_rxport; + } + + rxport->fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(np)); + if (!rxport->fwnode) { + dev_err(dev, "No remote endpoint for rxport%d\n", nport); + ret = -ENODEV; + goto err_node_put; + } + + /* + * Back channel frequency select. + * Override FREQ_SELECT from the strap. + * 0 - 2.5 Mbps (DS90UB913A-Q1 / DS90UB933-Q1) + * 2 - 10 Mbps + * 6 - 50 Mbps (DS90UB953-Q1) + * + * Note that changing this setting will result in some errors on the back + * channel for a short period of time. + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, 0x7, + bc_freq_val); + + switch (rxport->mode) { + default: + WARN_ON(true); + fallthrough; + + case RXPORT_MODE_RAW10: + /* FPD3_MODE = RAW10 Mode (DS90UB913A-Q1 / DS90UB933-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, + 0x3); + + /* + * RAW10_8BIT_CTL = 0b11 : 8-bit processing using lower 8 bits + * 0b10 : 8-bit processing using upper 8 bits + */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, + 0x3 << 6, 0x2 << 6); + + break; + + case RXPORT_MODE_CSI2: + /* CSI-2 Mode (DS90UB953-Q1 compatible) */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG, 0x3, + 0x0); + + break; + } + + /* LV_POLARITY & FV_POLARITY */ + ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, 0x1); + + /* Enable all interrupt sources from this port */ + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07); + ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_LO, 0x7f); + + /* Enable I2C_PASS_THROUGH */ + ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH, + UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH); + + /* Enable I2C communication to the serializer via the alias addr */ + ub960_rxport_write(priv, nport, UB960_RR_SER_ALIAS_ID, + rxport->ser_alias << 1); + + dev_dbg(dev, "ser%d: at alias 0x%02x\n", nport, rxport->ser_alias); + + ret = i2c_atr_add_adapter(priv->atr, nport); + if (ret) { + dev_err(dev, "rx%d: cannot add adapter", nport); + goto err_node_put; + } + + return 0; + +err_node_put: + of_node_put(rxport->remote_of_node); +err_free_rxport: + priv->rxports[nport] = NULL; + kfree(rxport); + return ret; +} + +static void ub960_rxport_remove_one(struct ub960_data *priv, u8 nport) +{ + struct ub960_rxport *rxport = priv->rxports[nport]; + + i2c_atr_del_adapter(priv->atr, nport); + ub960_rxport_remove_serializer(priv, nport); + of_node_put(rxport->remote_of_node); + kfree(rxport); +} + +static int ub960_atr_probe(struct ub960_data *priv) +{ + struct i2c_adapter *parent_adap = priv->client->adapter; + struct device *dev = &priv->client->dev; + + priv->atr = i2c_atr_new(parent_adap, dev, &ub960_atr_ops, + priv->hw_data->num_rxports); + if (IS_ERR(priv->atr)) + return PTR_ERR(priv->atr); + + i2c_atr_set_clientdata(priv->atr, priv); + + return 0; +} + +static void ub960_atr_remove(struct ub960_data *priv) +{ + i2c_atr_delete(priv->atr); + priv->atr = NULL; +} + +static void ub960_rxport_handle_events(struct ub960_data *priv, u8 nport) +{ + struct device *dev = &priv->client->dev; + u8 rx_port_sts1; + u8 rx_port_sts2; + u8 csi_rx_sts; + u8 bcc_sts; + int ret = 0; + + /* Read interrupts (also clears most of them) */ + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, + &rx_port_sts1); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, + &rx_port_sts2); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, + &csi_rx_sts); + if (!ret) + ret = ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, + &bcc_sts); + + if (ret) + return; + + dev_dbg(dev, "Handle RX%d events: STS: %x, %x, %x, BCC %x\n", nport, + rx_port_sts1, rx_port_sts2, csi_rx_sts, bcc_sts); + + if (rx_port_sts1 & (UB960_RR_RX_PORT_STS1_BCC_CRC_ERROR | + UB960_RR_RX_PORT_STS1_BCC_SEQ_ERROR | + UB960_RR_RX_PORT_STS1_PARITY_ERROR)) + dev_err(dev, "RX%u STS1 error: %#02x\n", nport, rx_port_sts1); + + if (rx_port_sts2 & (UB960_RR_RX_PORT_STS2_FPD3_ENCODE_ERROR | + UB960_RR_RX_PORT_STS2_BUFFER_ERROR | + UB960_RR_RX_PORT_STS2_CSI_ERROR | + UB960_RR_RX_PORT_STS2_CABLE_FAULT)) + dev_err(dev, "RX%u STS2 error: %#02x\n", nport, rx_port_sts2); + + if (csi_rx_sts) + dev_err(dev, "RX%u CSI error: %#02x\n", nport, csi_rx_sts); + + if (bcc_sts) + dev_err(dev, "RX%u BCC error: %#02x\n", nport, bcc_sts); +} + +/* ----------------------------------------------------------------------------- + * V4L2 + */ + +static int ub960_start_streaming(struct ub960_data *priv) +{ + const struct v4l2_subdev_krouting *routing; + struct v4l2_subdev_state *state; + unsigned int i; + u8 nport; + int ret; + u32 csi_ctl; + u32 speed_select; + u32 fwd_ctl; + struct { + u32 num_streams; + u8 pixel_dt; + u8 meta_dt; + u32 meta_lines; + u32 tx_port; + } rx_data[UB960_MAX_RX_NPORTS] = { 0 }; + + ret = 0; + + state = v4l2_subdev_lock_active_state(&priv->sd); + + routing = &state->routing; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + u32 port = route->sink_pad; + struct ub960_rxport *rxport = priv->rxports[port]; + struct v4l2_mbus_framefmt *fmt; + const struct ub960_format_info *ub960_fmt; + + if (!rxport) + continue; + + rx_data[port].tx_port = + route->source_pad - priv->hw_data->num_rxports; + + /* For the rest, we are only interested in parallel busses */ + if (rxport->mode == RXPORT_MODE_CSI2) + continue; + + rx_data[port].num_streams++; + + if (rx_data[port].num_streams > 2) { + ret = -EPIPE; + break; + } + + fmt = v4l2_state_get_stream_format(state, port, + route->sink_stream); + if (!fmt) { + ret = -EPIPE; + break; + } + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) { + ret = -EPIPE; + break; + } + + if (ub960_fmt->meta) { + if (fmt->height > 3) { + dev_err(&priv->client->dev, + "Unsupported metadata height %u\n", + fmt->height); + ret = -EPIPE; + break; + } + + rx_data[port].meta_dt = ub960_fmt->datatype; + rx_data[port].meta_lines = fmt->height; + } else { + rx_data[port].pixel_dt = ub960_fmt->datatype; + } + } + + v4l2_subdev_unlock_state(state); + + if (ret) + return ret; + + switch (priv->tx_data_rate) { + case 1600000000: + default: + speed_select = 0; + break; + case 1200000000: + speed_select = 1; + break; + case 800000000: + speed_select = 2; + break; + case 400000000: + speed_select = 3; + break; + } + + ub960_write(priv, UB960_SR_CSI_PLL_CTL, speed_select); + + for (nport = 0; nport < priv->hw_data->num_txports; nport++) { + struct ub960_txport *txport = priv->txports[nport]; + + if (!txport) + continue; + + csi_ctl = UB960_TR_CSI_CTL_CSI_ENABLE; + + /* + * From the datasheet: "initial CSI Skew-Calibration + * sequence [...] should be set when operating at 1.6 Gbps" + */ + if (speed_select == 0) + csi_ctl |= UB960_TR_CSI_CTL_CSI_CAL_EN; + + csi_ctl |= (4 - txport->num_data_lanes) << 4; + + ub960_csiport_write(priv, nport, UB960_TR_CSI_CTL, csi_ctl); + } + + for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->locked) + continue; + + switch (rxport->mode) { + default: + WARN_ON(true); + fallthrough; + + case RXPORT_MODE_RAW10: + /* VC=nport */ + ub960_rxport_write(priv, nport, UB960_RR_RAW10_ID, + rx_data[nport].pixel_dt | + (nport << 6)); + + ub960_rxport_write(priv, rxport->nport, + UB960_RR_RAW_EMBED_DTYPE, + (rx_data[nport].meta_lines << 6) | + rx_data[nport].meta_dt); + + break; + + case RXPORT_MODE_CSI2: + /* Map all VCs from this port to VC(nport) */ + ub960_rxport_write(priv, nport, UB960_RR_CSI_VC_MAP, + (nport << 6) | (nport << 4) | + (nport << 2) | (nport << 0)); + + break; + } + } + + /* Start all cameras */ + + priv->streaming = true; + + for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->locked) + continue; + + ret = v4l2_subdev_call(rxport->sd, video, s_stream, 1); + if (ret) { + for (; nport > 0; --nport) { + rxport = priv->rxports[nport - 1]; + if (!rxport) + continue; + + v4l2_subdev_call(rxport->sd, video, s_stream, + 0); + } + + priv->streaming = false; + + return ret; + } + } + + /* Forwarding */ + + fwd_ctl = 0; + + for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) { + struct ub960_rxport *rxport = priv->rxports[nport]; + + if (!rxport || !rxport->locked) { + fwd_ctl |= BIT(4 + nport); /* forward disable */ + continue; + } + + if (rx_data[nport].tx_port == 1) + fwd_ctl |= BIT(nport); /* forward to TX1 */ + } + + ub960_write(priv, UB960_SR_FWD_CTL1, fwd_ctl); + + return 0; +} + +static int ub960_stop_streaming(struct ub960_data *priv) +{ + unsigned int i; + + /* Disable forwarding */ + ub960_write(priv, UB960_SR_FWD_CTL1, + (BIT(0) | BIT(1) | BIT(2) | BIT(3)) << 4); + + /* Stop all cameras */ + for (i = 0; i < priv->hw_data->num_rxports; ++i) { + struct ub960_rxport *rxport = priv->rxports[i]; + + if (!rxport || !rxport->locked) + continue; + + v4l2_subdev_call(rxport->sd, video, s_stream, 0); + } + + for (i = 0; i < priv->hw_data->num_txports; i++) { + struct ub960_txport *txport = priv->txports[i]; + + if (!txport) + continue; + + ub960_csiport_write(priv, i, UB960_TR_CSI_CTL, 0); + } + + priv->streaming = false; + + return 0; +} + +static int ub960_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + if (enable) + return ub960_start_streaming(priv); + else + return ub960_stop_streaming(priv); +} + +static const struct v4l2_subdev_video_ops ub960_video_ops = { + .s_stream = ub960_s_stream, +}; + +static int _ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_routing_simple_verify(routing); + if (ret) + return ret; + + v4l2_subdev_lock_state(state); + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + + v4l2_subdev_unlock_state(state); + + if (ret) + return ret; + + return 0; +} + +static int ub960_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + return _ub960_set_routing(sd, state, routing); +} + +static int ub960_get_source_frame_desc(struct ub960_data *priv, + struct v4l2_mbus_frame_desc *desc, + u8 nport) +{ + struct v4l2_subdev *sd; + struct media_pad *pad; + int ret; + + pad = media_entity_remote_pad(&priv->pads[nport]); + if (!pad) + return -EPIPE; + + sd = priv->rxports[nport]->sd; + + ret = v4l2_subdev_call(sd, pad, get_frame_desc, pad->index, + desc); + if (ret) + return ret; + + return 0; +} + +static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct ub960_data *priv = sd_to_ub960(sd); + const struct v4l2_subdev_krouting *routing; + struct v4l2_subdev_state *state; + int ret = 0; + unsigned int i; + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "%s for pad %d\n", __func__, pad); + + if (!ub960_pad_is_source(priv, pad)) + return -EINVAL; + + state = v4l2_subdev_lock_active_state(&priv->sd); + + routing = &state->routing; + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + struct v4l2_mbus_frame_desc_entry *source_entry = NULL; + struct v4l2_mbus_frame_desc source_fd; + unsigned int j; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (route->source_pad != pad) + continue; + + ret = ub960_get_source_frame_desc(priv, &source_fd, + route->sink_pad); + if (ret) { + dev_err(dev, + "Failed to get source frame desc for port %u\n", + route->sink_pad); + goto out; + } + + for (j = 0; j < source_fd.num_entries; ++j) + if (source_fd.entry[j].stream == route->sink_stream) { + source_entry = &source_fd.entry[j]; + break; + } + + if (!source_entry) { + dev_err(dev, + "Failed to find stream from source frame desc\n"); + ret = -EPIPE; + goto out; + } + + fd->entry[fd->num_entries].stream = route->source_stream; + + fd->entry[fd->num_entries].flags = + V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + fd->entry[fd->num_entries].length = source_entry->length; + fd->entry[fd->num_entries].pixelcode = + source_entry->pixelcode; + + /* Use the RX channel number as VC. See ub960_start_streaming() */ + fd->entry[fd->num_entries].bus.csi2.vc = route->sink_pad; + + if (source_fd.type == V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + fd->entry[fd->num_entries].bus.csi2.dt = + source_entry->bus.csi2.dt; + } else { + const struct ub960_format_info *ub960_fmt; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_state_get_stream_format( + state, pad, route->source_stream); + + if (!fmt) { + ret = -EINVAL; + goto out; + } + + ub960_fmt = ub960_find_format(fmt->code); + if (!ub960_fmt) { + dev_err(dev, "Unable to find format\n"); + ret = -EINVAL; + goto out; + } + + fd->entry[fd->num_entries].bus.csi2.dt = + ub960_fmt->datatype; + } + + fd->num_entries++; + } + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub960_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct ub960_data *priv = sd_to_ub960(sd); + struct v4l2_mbus_framefmt *fmt; + int ret = 0; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->streaming) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (ub960_pad_is_source(priv, format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + /* TODO: implement fmt validation */ + + v4l2_subdev_lock_state(state); + + fmt = v4l2_state_get_stream_format(state, format->pad, format->stream); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + + fmt = v4l2_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) { + ret = -EINVAL; + goto out; + } + + *fmt = format->format; + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int ub960_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct ub960_data *priv = sd_to_ub960(sd); + + struct v4l2_subdev_route routes[] = { + { + .sink_pad = 0, + .sink_stream = 0, + .source_pad = priv->hw_data->num_rxports, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _ub960_set_routing(sd, state, &routing); +} + +static const struct v4l2_subdev_pad_ops ub960_pad_ops = { + .set_routing = ub960_set_routing, + .get_frame_desc = ub960_get_frame_desc, + + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = ub960_set_fmt, + + .init_cfg = ub960_init_cfg, +}; + +static const struct v4l2_subdev_core_ops ub960_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ub960_subdev_ops = { + .core = &ub960_subdev_core_ops, + .video = &ub960_video_ops, + .pad = &ub960_pad_ops, +}; + +static const struct media_entity_operations ub960_entity_ops = { + .link_validate = v4l2_subdev_link_validate, + .has_route = v4l2_subdev_has_route +}; + +static void ub960_enable_tpg(struct ub960_data *priv, int tpg_num) +{ + /* + * Note: no need to write UB960_REG_IND_ACC_CTL: the only indirect + * registers target we use is "CSI-2 Pattern Generator & Timing + * Registers", which is the default + */ + + /* + * TPG can only provide a single stream per CSI TX port. If + * multiple streams are currently enabled, only the first + * one will use the TPG, other streams will be halted. + */ + + struct v4l2_mbus_framefmt *fmt; + u8 vbp, vfp; + u16 blank_lines; + u16 width; + u16 height; + + u16 bytespp = 2; /* For MEDIA_BUS_FMT_UYVY8_1X16 */ + u8 cbars_idx = tpg_num - TEST_PATTERN_V_COLOR_BARS_1; + u8 num_cbars = 1 << cbars_idx; + + u16 line_size; /* Line size [bytes] */ + u16 bar_size; /* cbar size [bytes] */ + u16 act_lpf; /* active lines/frame */ + u16 tot_lpf; /* tot lines/frame */ + u16 line_pd; /* Line period in 10-ns units */ + + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_active_state(&priv->sd); + + vbp = 33; + vfp = 10; + blank_lines = vbp + vfp + 2; /* total blanking lines */ + + fmt = v4l2_state_get_stream_format(state, 4, 0); + + width = fmt->width; + height = fmt->height; + + line_size = width * bytespp; + bar_size = line_size / num_cbars; + act_lpf = height; + tot_lpf = act_lpf + blank_lines; + line_pd = 100000000 / 60 / tot_lpf; + + /* Disable forwarding from FPD-3 RX ports */ + ub960_write(priv, UB960_SR_FWD_CTL1, + UB960_SR_FWD_CTL1_PORT_DIS(0) | + UB960_SR_FWD_CTL1_PORT_DIS(1)); + + /* Access Indirect Pattern Gen */ + ub960_write(priv, UB960_SR_IND_ACC_CTL, + UB960_SR_IND_ACC_CTL_IA_AUTO_INC | 0); + + ub960_write_ind8(priv, UB960_IR_PGEN_CTL, + UB960_IR_PGEN_CTL_PGEN_ENABLE); + + /* YUV422 8bit: 2 bytes/block, CSI-2 data type 0x1e */ + ub960_write_ind8(priv, UB960_IR_PGEN_CFG, cbars_idx << 4 | 0x2); + ub960_write_ind8(priv, UB960_IR_PGEN_CSI_DI, 0x1e); + + ub960_write_ind16(priv, UB960_IR_PGEN_LINE_SIZE1, line_size); + ub960_write_ind16(priv, UB960_IR_PGEN_BAR_SIZE1, bar_size); + ub960_write_ind16(priv, UB960_IR_PGEN_ACT_LPF1, act_lpf); + ub960_write_ind16(priv, UB960_IR_PGEN_TOT_LPF1, tot_lpf); + ub960_write_ind16(priv, UB960_IR_PGEN_LINE_PD1, line_pd); + ub960_write_ind8(priv, UB960_IR_PGEN_VBP, vbp); + ub960_write_ind8(priv, UB960_IR_PGEN_VFP, vfp); + + v4l2_subdev_unlock_state(state); +} + +static void ub960_disable_tpg(struct ub960_data *priv) +{ + /* TPG off, enable forwarding from FPD-3 RX ports */ + ub960_write(priv, UB960_SR_FWD_CTL1, 0x00); + + ub960_write_ind8(priv, UB960_IR_PGEN_CTL, 0x00); +} + +static int ub960_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ub960_data *priv = + container_of(ctrl->handler, struct ub960_data, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + if (ctrl->val == 0) + ub960_disable_tpg(priv); + else + ub960_enable_tpg(priv, ctrl->val); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ub960_ctrl_ops = { + .s_ctrl = ub960_s_ctrl, +}; + +/* ----------------------------------------------------------------------------- + * Core + */ + +static irqreturn_t ub960_handle_events(int irq, void *arg) +{ + struct ub960_data *priv = arg; + unsigned int i; + u8 int_sts; + int ret; + + ret = ub960_read(priv, UB960_SR_INTERRUPT_STS, &int_sts); + + if (!ret && int_sts) { + u8 fwd_sts; + + dev_dbg(&priv->client->dev, "INTERRUPT_STS %x\n", int_sts); + + ub960_read(priv, UB960_SR_FWD_STS, &fwd_sts); + + dev_dbg(&priv->client->dev, "FWD_STS %#x\n", fwd_sts); + + for (i = 0; i < priv->hw_data->num_txports; ++i) { + if (int_sts & UB960_SR_INTERRUPT_STS_IS_CSI_TX(i)) + ub960_csi_handle_events(priv, i); + } + + for (i = 0; i < priv->hw_data->num_rxports; i++) { + if (!priv->rxports[i] || !priv->rxports[i]->locked) + continue; + + if (int_sts & UB960_SR_INTERRUPT_STS_IS_RX(i)) + ub960_rxport_handle_events(priv, i); + } + } + + return IRQ_HANDLED; +} + +static int ub960_run(void *arg) +{ + struct ub960_data *priv = arg; + + while (!kthread_should_stop()) { + ub960_handle_events(0, priv); + + msleep(500); + } + + return 0; +} + +static void ub960_remove_ports(struct ub960_data *priv) +{ + unsigned int i; + + for (i = 0; i < priv->hw_data->num_rxports; i++) + if (priv->rxports[i]) + ub960_rxport_remove_one(priv, i); + + for (i = 0; i < priv->hw_data->num_txports; i++) + if (priv->txports[i]) + ub960_txport_remove_one(priv, i); +} + +static int ub960_parse_dt(struct ub960_data *priv) +{ + struct device_node *np = priv->client->dev.of_node; + struct device *dev = &priv->client->dev; + int ret = 0; + int n; + + if (!np) { + dev_err(dev, "OF: no device tree node!\n"); + return -ENOENT; + } + + n = of_property_read_variable_u16_array(np, "i2c-alias-pool", + priv->atr_alias_id, + 2, UB960_MAX_POOL_ALIASES); + if (n < 0) + dev_warn(dev, + "OF: no i2c-alias-pool, can't access remote I2C slaves"); + + priv->atr_alias_num = n; + + dev_dbg(dev, "i2c-alias-pool has %zu aliases", priv->atr_alias_num); + + if (of_property_read_u32(np, "data-rate", &priv->tx_data_rate) != 0) { + dev_err(dev, "OF: %s: missing \"data-rate\" node\n", + of_node_full_name(np)); + return -EINVAL; + } + + if (priv->tx_data_rate != 1600000000 && + priv->tx_data_rate != 1200000000 && + priv->tx_data_rate != 800000000 && + priv->tx_data_rate != 400000000) { + dev_err(dev, "OF: %s: invalid \"data-rate\" node\n", + of_node_full_name(np)); + return -EINVAL; + } + + priv->tx_link_freq[0] = priv->tx_data_rate / 2; + + dev_dbg(dev, "Nominal data rate: %u", priv->tx_data_rate); + + for (n = 0; n < priv->hw_data->num_rxports + priv->hw_data->num_txports; ++n) { + struct device_node *ep_np; + + ep_np = of_graph_get_endpoint_by_regs(np, n, 0); + if (!ep_np) + continue; + + if (n < priv->hw_data->num_rxports) + ret = ub960_rxport_probe_one(priv, ep_np, n); + else + ret = ub960_csiport_probe_one( + priv, ep_np, n - priv->hw_data->num_rxports); + + of_node_put(ep_np); + + if (ret) + break; + } + + if (ret) + ub960_remove_ports(priv); + + return ret; +} + +static int ub960_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct ub960_data *priv = sd_to_ub960(notifier->sd); + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + struct device *dev = &priv->client->dev; + u8 nport = rxport->nport; + unsigned int src_pad; + unsigned int i; + int ret; + + dev_dbg(dev, "Bind %s\n", subdev->name); + + ret = media_entity_get_fwnode_pad(&subdev->entity, rxport->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + rxport->sd = subdev; + src_pad = ret; + + ret = media_create_pad_link(&rxport->sd->entity, src_pad, + &priv->sd.entity, nport, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:%u\n", + rxport->sd->name, src_pad, priv->sd.name, nport); + return ret; + } + + dev_dbg(dev, "Bound %s pad: %u on index %u\n", subdev->name, src_pad, + nport); + + for (i = 0; i < priv->hw_data->num_rxports; ++i) { + if (priv->rxports[i] && rxport->locked && !priv->rxports[i]->sd) { + dev_dbg(dev, "Waiting for more subdevs to be bound\n"); + return 0; + } + } + + dev_dbg(dev, "All subdevs bound\n"); + + return 0; +} + +static void ub960_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct ub960_data *priv = sd_to_ub960(notifier->sd); + struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport; + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "Unbind %s\n", subdev->name); + + rxport->sd = NULL; +} + +static const struct v4l2_async_notifier_operations ub960_notify_ops = { + .bound = ub960_notify_bound, + .unbind = ub960_notify_unbind, +}; + +static int ub960_v4l2_notifier_register(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_async_notifier_init(&priv->notifier); + + for (i = 0; i < priv->hw_data->num_rxports; ++i) { + struct ub960_rxport *rxport = priv->rxports[i]; + struct v4l2_async_subdev *asd; + struct ub960_asd *ubasd; + + if (!rxport || !rxport->locked) + continue; + + asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, + rxport->fwnode, + sizeof(*ubasd)); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev for source %u: %ld", + i, PTR_ERR(asd)); + v4l2_async_notifier_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + ubasd = to_ub960_asd(asd); + ubasd->rxport = rxport; + } + + priv->notifier.ops = &ub960_notify_ops; + + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_notifier_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static void ub960_v4l2_notifier_unregister(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + + dev_dbg(dev, "Unregister async notif\n"); + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); +} + +static int ub960_create_subdev(struct ub960_data *priv) +{ + struct device *dev = &priv->client->dev; + unsigned int i; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub960_subdev_ops); + v4l2_ctrl_handler_init(&priv->ctrl_handler, + ARRAY_SIZE(ub960_tpg_qmenu) - 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, &ub960_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ub960_tpg_qmenu) - 1, 0, 0, + ub960_tpg_qmenu); + + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(priv->tx_link_freq) - 1, 0, + priv->tx_link_freq); + + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_MULTIPLEXED; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &ub960_entity_ops; + + for (i = 0; i < priv->hw_data->num_rxports + priv->hw_data->num_txports; i++) { + priv->pads[i].flags = ub960_pad_is_sink(priv, i) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + } + + ret = media_entity_pads_init(&priv->sd.entity, + priv->hw_data->num_rxports + + priv->hw_data->num_txports, + priv->pads); + if (ret) + goto err_free_ctrl; + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = ub960_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + ub960_v4l2_notifier_unregister(priv); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +} + +static void ub960_destroy_subdev(struct ub960_data *priv) +{ + ub960_v4l2_notifier_unregister(priv); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static const struct regmap_config ub960_regmap_config = { + .name = "ds90ub960", + + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, +}; + +static void ub960_sw_reset(struct ub960_data *priv) +{ + unsigned int i; + + ub960_write(priv, UB960_SR_RESET, BIT(1)); + + for (i = 0; i < 10; ++i) { + int ret; + u8 v; + + ret = ub960_read(priv, UB960_SR_RESET, &v); + + if (ret || v == 0) + break; + } +} + +static int ub960_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ub960_data *priv; + unsigned int nport; + struct clk *clk; + u8 rev_mask; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + priv->hw_data = of_device_get_match_data(dev); + if (!priv->hw_data) + return -ENODEV; + + mutex_init(&priv->alias_table_lock); + + priv->regmap = devm_regmap_init_i2c(client, &ub960_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + /* get power-down pin from DT */ + priv->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->pd_gpio)) { + ret = PTR_ERR(priv->pd_gpio); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Cannot get powerdown GPIO (%d)", ret); + return ret; + } + + if (priv->pd_gpio) { + gpiod_set_value_cansleep(priv->pd_gpio, 1); + /* wait min 2 ms for reset to complete */ + usleep_range(2000, 5000); + gpiod_set_value_cansleep(priv->pd_gpio, 0); + /* wait min 2 ms for power up to finish */ + usleep_range(2000, 5000); + } else { + /* Use SW reset if we don't have PD gpio */ + ub960_sw_reset(priv); + } + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Cannot get REFCLK (%d)", ret); + return ret; + } + priv->refclk = clk_get_rate(clk); + clk_put(clk); + dev_dbg(dev, "REFCLK %lu", priv->refclk); + + /* Runtime check register accessibility */ + ret = ub960_read(priv, UB960_SR_REV_MASK, &rev_mask); + if (ret) { + dev_err(dev, "Cannot read first register (%d), abort\n", ret); + goto err_reg_read; + } + + ret = ub960_atr_probe(priv); + if (ret) + goto err_atr_probe; + + ret = ub960_parse_dt(priv); + if (ret) + goto err_parse_dt; + + ret = ub960_rxport_probe_serializers(priv); + if (ret) + goto err_parse_dt; + + /* + * Clear any errors caused by switching the RX port settings while + * probing. + */ + for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) { + u8 dummy; + + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS1, &dummy); + ub960_rxport_read(priv, nport, UB960_RR_RX_PORT_STS2, &dummy); + ub960_rxport_read(priv, nport, UB960_RR_CSI_RX_STS, &dummy); + ub960_rxport_read(priv, nport, UB960_RR_BCC_STATUS, &dummy); + } + + ret = ub960_create_subdev(priv); + if (ret) + goto err_subdev; + + if (client->irq) { + dev_dbg(dev, "using IRQ %d\n", client->irq); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + ub960_handle_events, + IRQF_ONESHOT, client->name, + priv); + if (ret) { + dev_err(dev, "Cannot enable IRQ (%d)\n", ret); + goto err_irq; + } + + /* Disable GPIO3 as input */ + ub960_update_bits_shared(priv, UB960_SR_GPIO_INPUT_CTL, BIT(3), + 0); + /* Enable GPIO3 as output, active low interrupt */ + ub960_write(priv, UB960_SR_GPIO_PIN_CTL(3), 0xd1); + + ub960_write(priv, UB960_SR_INTERRUPT_CTL, + UB960_SR_INTERRUPT_CTL_ALL); + } else { + /* No IRQ, fallback to polling */ + + priv->kthread = kthread_run(ub960_run, priv, dev_name(dev)); + if (IS_ERR(priv->kthread)) { + ret = PTR_ERR(priv->kthread); + dev_err(dev, "Cannot create kthread (%d)\n", ret); + goto err_kthread; + } + dev_dbg(dev, "using polling mode\n"); + } + + dev_info(dev, "Successfully probed (rev/mask %02x)\n", rev_mask); + + return 0; + +err_kthread: +err_irq: + ub960_destroy_subdev(priv); +err_subdev: + ub960_remove_ports(priv); +err_parse_dt: + ub960_atr_remove(priv); +err_atr_probe: +err_reg_read: + if (priv->pd_gpio) + gpiod_set_value_cansleep(priv->pd_gpio, 1); + mutex_destroy(&priv->alias_table_lock); + return ret; +} + +static int ub960_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ub960_data *priv = sd_to_ub960(sd); + + dev_dbg(&client->dev, "Removing\n"); + + if (priv->kthread) + kthread_stop(priv->kthread); + ub960_destroy_subdev(priv); + ub960_remove_ports(priv); + ub960_atr_remove(priv); + if (priv->pd_gpio) + gpiod_set_value_cansleep(priv->pd_gpio, 1); + mutex_destroy(&priv->alias_table_lock); + + dev_dbg(&client->dev, "Remove done\n"); + + return 0; +} + +static const struct ub960_hw_data ds90ub960_hw = { + .num_rxports = 4, + .num_txports = 2, +}; + +static const struct i2c_device_id ub960_id[] = { + { "ds90ub960-q1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ub960_id); + +#ifdef CONFIG_OF +static const struct of_device_id ub960_dt_ids[] = { + { .compatible = "ti,ds90ub960-q1", .data = &ds90ub960_hw }, + { } +}; +MODULE_DEVICE_TABLE(of, ub960_dt_ids); +#endif + +static struct i2c_driver ds90ub960_driver = { + .probe_new = ub960_probe, + .remove = ub960_remove, + .id_table = ub960_id, + .driver = { + .name = "ds90ub960", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ub960_dt_ids), + }, +}; + +module_i2c_driver(ds90ub960_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Texas Instruments DS90UB960-Q1 FPDLink-3 deserializer driver"); +MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); diff --git a/drivers/media/i2c/imx390.c b/drivers/media/i2c/imx390.c new file mode 100644 index 000000000000..a81896401f00 --- /dev/null +++ b/drivers/media/i2c/imx390.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sony IMX390 CMOS Image Sensor Driver + * + * Copyright (c) 2021 Apurva Nandan <a-nandan@ti.com> + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-ctrls.h> + +#include "imx390.h" + +static inline struct imx390 *to_imx390(struct v4l2_subdev *sd) +{ + return container_of(sd, struct imx390, subdev); +} + +static int imx390_read(struct imx390 *imx390, u16 addr, u32 *val, size_t nbytes) +{ + int ret; + __le32 val_le = 0; + + ret = regmap_bulk_read(imx390->regmap, addr, &val_le, nbytes); + if (ret < 0) { + dev_err(imx390->dev, "%s: failed to read reg 0x%04x: %d\n", + __func__, addr, ret); + return ret; + } + + *val = le32_to_cpu(val_le); + return 0; +} + +static int imx390_write(struct imx390 *imx390, u16 addr, u32 val, size_t nbytes) +{ + int ret; + __le32 val_le = cpu_to_le32(val); + + ret = regmap_bulk_write(imx390->regmap, addr, &val_le, nbytes); + if (ret < 0) + dev_err(imx390->dev, "%s: failed to write reg 0x%04x: %d\n", + __func__, addr, ret); + return ret; +} + +static int imx390_update_bits(struct imx390 *imx390, u16 addr, u32 val, + u32 mask, size_t nbytes) +{ + int ret; + u32 cfg; + + ret = imx390_read(imx390, addr, &cfg, nbytes); + if (ret < 0) + return ret; + + cfg = (val) ? (cfg | mask) : (cfg & (~mask)); + + return imx390_write(imx390, addr, cfg, nbytes); +} + +static int imx390_write_table(struct imx390 *imx390, + const struct reg_sequence *regs, + unsigned int nr_regs) +{ + int ret; + + ret = regmap_multi_reg_write(imx390->regmap, regs, nr_regs); + if (ret < 0) + dev_err(imx390->dev, + "%s: failed to write reg table (%d)!\n", __func__, ret); + return ret; +} + +static void imx390_init_formats(struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_state_get_stream_format(state, 0, 0); + format->code = imx390_mbus_formats[0]; + format->width = imx390_framesizes[0].width; + format->height = imx390_framesizes[0].height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SMPTE170M; +} + +static int _imx390_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .source_pad = 0, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_IMMUTABLE | + V4L2_SUBDEV_ROUTE_FL_SOURCE | + V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + { + .source_pad = 0, + .source_stream = 1, + .flags = V4L2_SUBDEV_ROUTE_FL_IMMUTABLE | + V4L2_SUBDEV_ROUTE_FL_SOURCE, + } + }; + + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + int ret; + + ret = v4l2_subdev_set_routing(sd, state, &routing); + if (ret < 0) + return ret; + + imx390_init_formats(state); + + return 0; +} + +static int imx390_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + int ret; + + v4l2_subdev_lock_state(state); + + ret = _imx390_set_routing(sd, state); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int imx390_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(imx390_mbus_formats)) + return -EINVAL; + + code->code = imx390_mbus_formats[code->index]; + + return 0; +} + +static int imx390_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx390_mbus_formats); ++i) { + if (imx390_mbus_formats[i] == fse->code) + break; + } + + if (i == ARRAY_SIZE(imx390_mbus_formats)) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(imx390_framesizes)) + return -EINVAL; + + fse->min_width = imx390_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = imx390_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int imx390_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct imx390 *imx390 = to_imx390(sd); + struct v4l2_mbus_framefmt *format; + const struct v4l2_area *fsize; + unsigned int i; + u32 code; + int ret = 0; + + if (fmt->pad != 0) + return -EINVAL; + + if (fmt->stream != 0) + return -EINVAL; + + /* + * Validate the media bus code, defaulting to the first one if the + * requested code isn't supported. + */ + for (i = 0; i < ARRAY_SIZE(imx390_mbus_formats); ++i) { + if (imx390_mbus_formats[i] == fmt->format.code) { + code = fmt->format.code; + break; + } + } + + if (i == ARRAY_SIZE(imx390_mbus_formats)) + code = imx390_mbus_formats[0]; + + /* Find the nearest supported frame size. */ + fsize = v4l2_find_nearest_size(imx390_framesizes, + ARRAY_SIZE(imx390_framesizes), width, + height, fmt->format.width, + fmt->format.height); + + v4l2_subdev_lock_state(state); + + /* Update the stored format and return it. */ + format = v4l2_state_get_stream_format(state, fmt->pad, fmt->stream); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && imx390->streaming) { + ret = -EBUSY; + goto done; + } + + format->code = code; + format->width = fsize->width; + format->height = fsize->height; + + fmt->format = *format; + +done: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int imx390_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + u32 bpp; + int ret = 0; + + if (pad != 0) + return -EINVAL; + + state = v4l2_subdev_lock_active_state(sd); + + fmt = v4l2_state_get_stream_format(state, 0, 0); + if (!fmt) { + ret = -EPIPE; + goto out; + } + + memset(fd, 0, sizeof(*fd)); + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + /* pixel stream */ + + bpp = 12; + + fd->entry[fd->num_entries].stream = 0; + + fd->entry[fd->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + fd->entry[fd->num_entries].length = fmt->width * fmt->height * bpp / 8; + fd->entry[fd->num_entries].pixelcode = fmt->code; + fd->entry[fd->num_entries].bus.csi2.vc = 0; + fd->entry[fd->num_entries].bus.csi2.dt = 0x2c; /* SRGGB12 */ + + fd->num_entries++; + +out: + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int imx390_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + if (routing->num_routes == 0 || routing->num_routes > 1) + return -EINVAL; + + v4l2_subdev_lock_state(state); + + ret = _imx390_set_routing(sd, state); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int imx390_check_non_wdr_mode_fps(struct imx390 *imx390, bool enable) +{ + if (!enable && imx390->fps > IMX390_FRAMERATE_MAX_LINEAR) { + dev_err(imx390->dev, + "%s: failed, %dFPS unsupported in non-WDR mode\n", + __func__, imx390->fps); + return -EINVAL; + } + return 0; +} + +static int imx390_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx390 *imx390 = container_of(ctrl->handler, + struct imx390, ctrl.handler); + int ret; + + dev_dbg(imx390->dev, + "%s: %s, value: %d\n", __func__, ctrl->name, ctrl->val); + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(imx390->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = imx390_check_non_wdr_mode_fps(imx390, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + ret = imx390_write(imx390, IMX390_REG_CAT0_SHS1, + IMX390_EXPOSURE_SHS_VAL(ctrl->val, + imx390->fps), 3); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = imx390_write(imx390, IMX390_REG_CAT0_AGAIN_SP1H, + ctrl->val, 2); + if (ret < 0) + break; + + ret = imx390_write(imx390, IMX390_REG_CAT0_AGAIN_SP1L, + ctrl->val / IMX390_AGAIN_CONV_GAIN_RATIO, 2); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = imx390_write(imx390, IMX390_REG_CAT0_PGA_GAIN, + ctrl->val, 2); + break; + + case V4L2_CID_RED_BALANCE: + ret = imx390_write(imx390, IMX390_REG_CAT0_WBGAIN_R, + ctrl->val, 2); + break; + + case V4L2_CID_BLUE_BALANCE: + ret = imx390_write(imx390, IMX390_REG_CAT0_WBGAIN_B, + ctrl->val, 2); + break; + + case V4L2_CID_HFLIP: + ret = imx390_update_bits(imx390, IMX390_REG_CAT0_V_H_REVERSE, + ctrl->val, IMX390_H_REV_MASK, 1); + if (ret < 0) + break; + + ret = imx390_update_bits(imx390, IMX390_REG_SM_CFG_REVERSE_APL, + ctrl->val, IMX390_H_REV_APL_MASK, 1); + break; + + case V4L2_CID_VFLIP: + ret = imx390_update_bits(imx390, IMX390_REG_CAT0_V_H_REVERSE, + ctrl->val, IMX390_V_REV_MASK, 1); + if (ret < 0) + break; + + ret = imx390_update_bits(imx390, IMX390_REG_SM_CFG_REVERSE_APL, + ctrl->val, IMX390_V_REV_APL_MASK, 1); + break; + + case V4L2_CID_TEST_PATTERN: + ret = imx390_write(imx390, IMX390_REG_CAT0_PGMODE_PGREGEN, + imx390_pg_mode_reg_val[ctrl->val], 1); + if (ret < 0) + break; + + ret = imx390_update_bits(imx390, IMX390_SM_CFG_SM_PGREGEN_APL, + ctrl->val, IMX390_SM_PG_APL_MASK, 1); + break; + + default: + ret = -EINVAL; + } + + pm_runtime_put_noidle(imx390->dev); + return ret; +} + +static int imx390_detect(struct imx390 *imx390) +{ + int ret; + u32 id; + + ret = imx390_read(imx390, IMX390_REG_VERSION_ROM_VERSION, &id, 2); + if (ret < 0) + return ret; + + if (id != IMX390_ROM_VERSION) { + dev_err(imx390->dev, + "%s: unknown chip ID 0x%04x\n", __func__, id); + return -ENODEV; + } + + dev_dbg(imx390->dev, "%s: detected chip ID 0x%04x\n", __func__, id); + return 0; +} + +static int imx390_power_on(struct imx390 *imx390) +{ + int ret; + + ret = clk_prepare_enable(imx390->clk); + if (ret < 0) + return ret; + + if (imx390->xclr_gpio) { + gpiod_set_value_cansleep(imx390->xclr_gpio, 1); + /* Keep the XCLR pin on Low for 100 us or longer */ + usleep_range(100, 1000); + gpiod_set_value_cansleep(imx390->xclr_gpio, 0); + /* It takes max 30 ms for the sensor to be ready */ + msleep(30); + } + return 0; +} + +static void imx390_power_off(struct imx390 *imx390) +{ + if (imx390->xclr_gpio) { + gpiod_set_value_cansleep(imx390->xclr_gpio, 1); + /* Wait for the XCLR pin to be Low for atleast 1 us */ + usleep_range(1, 10); + } + + clk_disable_unprepare(imx390->clk); +} + +static int imx390_get_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx390 *imx390 = to_imx390(sd); + + fi->interval.numerator = 1; + fi->interval.denominator = imx390->fps; + return 0; +} + +static int imx390_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx390 *imx390 = to_imx390(sd); + struct v4l2_ctrl *ctrl = imx390->ctrl.exposure; + u32 req_fps; + int ret; + + mutex_lock(&imx390->lock); + + if (fi->interval.numerator == 0 || fi->interval.denominator == 0) { + fi->interval.denominator = IMX390_FRAMERATE_DEFAULT; + fi->interval.numerator = 1; + } + + req_fps = clamp_val(DIV_ROUND_CLOSEST(fi->interval.denominator, + fi->interval.numerator), + IMX390_FRAMERATE_MIN, IMX390_FRAMERATE_MAX); + + fi->interval.numerator = 1; + fi->interval.denominator = req_fps; + + imx390->fps = req_fps; + + ret = __v4l2_ctrl_modify_range(ctrl, 0, IMX390_EXPOSURE_MAX(req_fps), 1, + IMX390_EXPOSURE_DEFAULT); + if (ret < 0) { + dev_err(imx390->dev, + "%s: exposure ctrl range update failed %d\n", + __func__, ret); + } + + mutex_unlock(&imx390->lock); + dev_dbg(imx390->dev, "%s frame rate = %d\n", __func__, imx390->fps); + + return ret; +} + +static int imx390_start_stream(struct imx390 *imx390) +{ + int ret; + + if (!imx390->ctrl.wdr->val && + imx390->fps <= IMX390_FRAMERATE_MAX_LINEAR) + ret = imx390_write_table(imx390, imx390_linear_1936x1096, + ARRAY_SIZE(imx390_linear_1936x1096)); + else + ret = imx390_write_table(imx390, imx390_wdr_1936x1096, + ARRAY_SIZE(imx390_wdr_1936x1096)); + if (ret < 0) + return ret; + + msleep(100); + + /* Restore the V4L2 control values into the registers */ + ret = __v4l2_ctrl_handler_setup(imx390->subdev.ctrl_handler); + if (ret < 0) { + dev_err(imx390->dev, + "%s: failed to apply v4l2 ctrls: %d\n", __func__, ret); + return ret; + } + + ret = imx390_write(imx390, IMX390_REG_MODE_HMAX, + (u32)IMX390_FPS_TO_MODE_HMAX(imx390->fps), 2); + if (ret < 0) + return ret; + + /* Set active */ + ret = imx390_write(imx390, IMX390_REG_CAT0_STANDBY, 0, 1); + if (ret < 0) + return ret; + + /* No communication is possible for a while after exiting standby */ + msleep(20); + + return 0; +} + +static int imx390_stop_stream(struct imx390 *imx390) +{ + int ret; + + /* Set standby */ + ret = imx390_write(imx390, IMX390_REG_CAT0_STANDBY, 1, 1); + if (ret < 0) + return ret; + + /* No communication is possible for a while after entering standby */ + usleep_range(10000, 20000); + return 0; +} + +static int imx390_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx390 *imx390 = to_imx390(sd); + int ret; + + mutex_lock(&imx390->lock); + if (imx390->streaming == enable) { + mutex_unlock(&imx390->lock); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(imx390->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx390->dev); + goto err_unlock; + } + + ret = imx390_start_stream(imx390); + if (ret < 0) + goto err_runtime_put; + } else { + ret = imx390_stop_stream(imx390); + if (ret < 0) + goto err_runtime_put; + pm_runtime_mark_last_busy(imx390->dev); + pm_runtime_put_autosuspend(imx390->dev); + } + + imx390->streaming = enable; + /* WDR, HFLIP, VFLIP, TEST PATTERN cannot change during streaming */ + __v4l2_ctrl_grab(imx390->ctrl.wdr, enable); + __v4l2_ctrl_grab(imx390->ctrl.h_flip, enable); + __v4l2_ctrl_grab(imx390->ctrl.v_flip, enable); + __v4l2_ctrl_grab(imx390->ctrl.pg_mode, enable); + + mutex_unlock(&imx390->lock); + return 0; + +err_runtime_put: + pm_runtime_put(imx390->dev); + +err_unlock: + mutex_unlock(&imx390->lock); + dev_err(imx390->dev, + "%s: failed to setup streaming %d\n", __func__, ret); + return ret; +} + +static const struct v4l2_subdev_video_ops imx390_subdev_video_ops = { + .g_frame_interval = imx390_get_frame_interval, + .s_frame_interval = imx390_set_frame_interval, + .s_stream = imx390_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx390_subdev_pad_ops = { + .init_cfg = imx390_init_cfg, + .enum_mbus_code = imx390_enum_mbus_code, + .enum_frame_size = imx390_enum_frame_sizes, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx390_set_fmt, + .set_routing = imx390_set_routing, + .get_frame_desc = imx390_get_frame_desc, +}; + +static const struct v4l2_subdev_ops imx390_subdev_ops = { + .video = &imx390_subdev_video_ops, + .pad = &imx390_subdev_pad_ops, +}; + +static const struct v4l2_ctrl_ops imx390_ctrl_ops = { + .s_ctrl = imx390_set_ctrl, +}; + +static int imx390_probe(struct i2c_client *client) +{ + struct imx390 *imx390; + struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *ctrl_hdr; + int ret; + + imx390 = devm_kzalloc(&client->dev, sizeof(*imx390), GFP_KERNEL); + if (!imx390) + return -ENOMEM; + + imx390->dev = &client->dev; + + imx390->regmap = devm_regmap_init_i2c(client, &imx390_regmap_config); + if (IS_ERR(imx390->regmap)) + return PTR_ERR(imx390->regmap); + + imx390->xclr_gpio = devm_gpiod_get_optional(imx390->dev, + "xclr", GPIOD_OUT_LOW); + if (IS_ERR(imx390->xclr_gpio)) + return PTR_ERR(imx390->xclr_gpio); + + imx390->clk = devm_clk_get(imx390->dev, "inck"); + if (IS_ERR(imx390->clk)) + return PTR_ERR(imx390->clk); + + imx390->clk_rate = clk_get_rate(imx390->clk); + dev_info(imx390->dev, "inck rate: %lu Hz\n", imx390->clk_rate); + + if (imx390->clk_rate < 6000000 || imx390->clk_rate > 27000000) + return -EINVAL; + + ret = imx390_power_on(imx390); + if (ret < 0) + return ret; + + ret = imx390_detect(imx390); + if (ret < 0) + return ret; + + /* Initialize the subdev and its controls. */ + sd = &imx390->subdev; + v4l2_i2c_subdev_init(sd, client, &imx390_subdev_ops); + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS | + V4L2_SUBDEV_FL_MULTIPLEXED; + + /* Initialize the media entity. */ + imx390->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx390->pad); + if (ret < 0) { + dev_err(imx390->dev, + "%s: media entity init failed %d\n", __func__, ret); + return ret; + } + + /* Initialize controls */ + ctrl_hdr = &imx390->ctrl.handler; + ret = v4l2_ctrl_handler_init(ctrl_hdr, 9); + if (ret < 0) { + dev_err(imx390->dev, + "%s: ctrl handler init failed: %d\n", __func__, ret); + goto err_media_cleanup; + } + + mutex_init(&imx390->lock); + imx390->ctrl.handler.lock = &imx390->lock; + imx390->fps = IMX390_FRAMERATE_DEFAULT; + + /* Add new controls */ + imx390->ctrl.exposure = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_EXPOSURE, 0, + IMX390_EXPOSURE_MAX(imx390->fps), + 1, IMX390_EXPOSURE_DEFAULT); + + imx390->ctrl.again = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, 0, + IMX390_ANALOG_GAIN_MAX, 1, + IMX390_ANALOG_GAIN_DEFAULT); + + imx390->ctrl.dgain = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, 0, + IMX390_DIGITAL_GAIN_MAX, 1, + IMX390_DIGITAL_GAIN_DEFAULT); + + imx390->ctrl.r_balance = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, + IMX390_R_B_BALANCE_MAX, 1, + IMX390_R_B_BALANCE_DEFAULT); + + imx390->ctrl.b_balance = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, + IMX390_R_B_BALANCE_MAX, 1, + IMX390_R_B_BALANCE_DEFAULT); + + imx390->ctrl.wdr = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, + 0, 1, 1, 1); + + imx390->ctrl.h_flip = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + imx390->ctrl.v_flip = v4l2_ctrl_new_std(ctrl_hdr, &imx390_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + imx390->ctrl.pg_mode = v4l2_ctrl_new_std_menu_items(ctrl_hdr, + &imx390_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx390_ctrl_pg_qmenu) - 1, + 0, 0, imx390_ctrl_pg_qmenu); + + imx390->subdev.ctrl_handler = ctrl_hdr; + if (imx390->ctrl.handler.error) { + ret = imx390->ctrl.handler.error; + dev_err(imx390->dev, + "%s: failed to add the ctrls: %d\n", __func__, ret); + goto err_ctrl_free; + } + + pm_runtime_set_active(imx390->dev); + pm_runtime_enable(imx390->dev); + pm_runtime_set_autosuspend_delay(imx390->dev, IMX390_PM_IDLE_TIMEOUT); + pm_runtime_use_autosuspend(imx390->dev); + pm_runtime_get_noresume(imx390->dev); + + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) + goto err_pm_disable; + + /* Finally, register the subdev. */ + ret = v4l2_async_register_subdev(sd); + if (ret < 0) { + dev_err(imx390->dev, + "%s: v4l2 subdev register failed %d\n", __func__, ret); + goto err_subdev_cleanup; + } + + dev_info(imx390->dev, "imx390 probed\n"); + pm_runtime_mark_last_busy(imx390->dev); + pm_runtime_put_autosuspend(imx390->dev); + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(&imx390->subdev); + +err_pm_disable: + pm_runtime_dont_use_autosuspend(imx390->dev); + pm_runtime_put_noidle(imx390->dev); + pm_runtime_disable(imx390->dev); + +err_ctrl_free: + v4l2_ctrl_handler_free(ctrl_hdr); + mutex_destroy(&imx390->lock); + +err_media_cleanup: + media_entity_cleanup(&imx390->subdev.entity); + + return ret; +} + +static int __maybe_unused imx390_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx390 *imx390 = to_imx390(sd); + int ret; + + ret = imx390_power_on(imx390); + if (ret < 0) + return ret; + + return 0; +} + +static int __maybe_unused imx390_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx390 *imx390 = to_imx390(sd); + + imx390_power_off(imx390); + + return 0; +} + +static int __maybe_unused imx390_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx390 *imx390 = to_imx390(sd); + int ret; + + if (imx390->streaming) + imx390_stop_stream(imx390); + + ret = pm_runtime_force_suspend(dev); + if (ret < 0) + dev_warn(dev, "%s: failed to suspend: %i\n", __func__, ret); + + return 0; +} + +static int __maybe_unused imx390_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx390 *imx390 = to_imx390(sd); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + dev_warn(dev, "%s: failed to resume: %i\n", __func__, ret); + + if (imx390->streaming) + ret = imx390_start_stream(imx390); + + if (ret < 0) { + imx390_stop_stream(imx390); + imx390->streaming = 0; + return ret; + } + + return 0; +} + +static int imx390_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx390 *imx390 = to_imx390(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_ctrl_handler_free(&imx390->ctrl.handler); + v4l2_subdev_cleanup(&imx390->subdev); + media_entity_cleanup(&sd->entity); + mutex_destroy(&imx390->lock); + + pm_runtime_disable(imx390->dev); + pm_runtime_dont_use_autosuspend(imx390->dev); + pm_runtime_set_suspended(imx390->dev); + + return 0; +} + +static const struct dev_pm_ops imx390_pm_ops = { + SET_RUNTIME_PM_OPS(imx390_runtime_suspend, + imx390_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(imx390_suspend, imx390_resume) +}; + +static const struct of_device_id imx390_dt_id[] = { + { .compatible = "sony,imx390" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, imx390_dt_id); + +static struct i2c_driver imx390_i2c_driver = { + .driver = { + .name = "imx390", + .of_match_table = of_match_ptr(imx390_dt_id), + .pm = &imx390_pm_ops, + }, + .probe_new = imx390_probe, + .remove = imx390_remove, +}; + +module_i2c_driver(imx390_i2c_driver); + +MODULE_DESCRIPTION("Camera Sensor Driver for Sony IMX390"); +MODULE_AUTHOR("Apurva Nandan <a-nandan@ti.com>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx390.h b/drivers/media/i2c/imx390.h new file mode 100644 index 000000000000..30e518afb163 --- /dev/null +++ b/drivers/media/i2c/imx390.h @@ -0,0 +1,7158 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Sony IMX390 CMOS Image Sensor Driver + * + * Copyright (c) 2021 Apurva Nandan <a-nandan@ti.com> + * + * Copyright (c) 2021 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> + */ + +#include <linux/types.h> + +#define IMX390_ACTIVE_AREA_WIDTH 1936 +#define IMX390_ACTIVE_AREA_HEIGHT 1096 + +/* SMPG cannot be disabled, and datatype is the same as the pixel data */ +#define IMX390_SMPG_HEIGHT 4 + +#define IMX390_OUT_WIDTH IMX390_ACTIVE_AREA_WIDTH +#define IMX390_OUT_HEIGHT (IMX390_ACTIVE_AREA_HEIGHT + IMX390_SMPG_HEIGHT) + +#define IMX390_FRAMERATE_DEFAULT 30 +#define IMX390_FRAMERATE_MIN 25 +#define IMX390_FRAMERATE_MAX 60 +#define IMX390_FRAMERATE_MAX_LINEAR 30 + +#define IMX390_MODE_VMAX 0x465 +#define IMX390_MODE_HMAX_DEFAULT 0x1130 +#define IMX390_REG_MODE_HMAX 0x200C +#define IMX390_FPS_TO_MODE_HMAX(_fps) \ + ((IMX390_MODE_HMAX_DEFAULT * IMX390_FRAMERATE_DEFAULT) / (_fps)) + +#define IMX390_REG_VERSION_ROM_VERSION 0x0330 +#define IMX390_ROM_VERSION 0x3815 + +#define IMX390_REG_CAT0_STANDBY 0x0000 + +/* Exposure control */ +#define IMX390_REG_CAT0_SHS1 0x000C +#define IMX390_EXPOSURE_LINES_MAX (IMX390_MODE_VMAX - 2) +#define IMX390_EXPOSURE_MAX(_fps) \ + ((IMX390_EXPOSURE_LINES_MAX * 1000000) / (IMX390_MODE_VMAX * (_fps))) + +#define IMX390_EXPOSURE_SHS_VAL(_exp, _fps) \ + (IMX390_MODE_VMAX - ((_exp) * (_fps) * IMX390_MODE_VMAX) / 1000000) + +#define IMX390_EXPOSURE_DEFAULT 11111 + +/* Analog gain control */ +#define IMX390_REG_CAT0_AGAIN_SP1H 0x0018 +#define IMX390_REG_CAT0_AGAIN_SP1L 0x001A +#define IMX390_ANALOG_GAIN_MAX 0x3FF +#define IMX390_ANALOG_GAIN_DEFAULT 0x20 +#define IMX390_AGAIN_CONV_GAIN_RATIO 4 + +/* Digital gain control */ +#define IMX390_REG_CAT0_PGA_GAIN 0x0024 +#define IMX390_DIGITAL_GAIN_MAX 0x1FF +#define IMX390_DIGITAL_GAIN_DEFAULT 0x20 + +/* White Balance control */ +#define IMX390_REG_CAT0_WBGAIN_R 0x0030 +#define IMX390_REG_CAT0_WBGAIN_B 0x0036 +#define IMX390_R_B_BALANCE_MAX 0xFFF +#define IMX390_R_B_BALANCE_DEFAULT 0x200 + +/* Vertical and Horizontal Flip control */ +#define IMX390_REG_CAT0_V_H_REVERSE 0x0074 +#define IMX390_V_REV_MASK BIT(0) +#define IMX390_H_REV_MASK BIT(1) +#define IMX390_REG_SM_CFG_REVERSE_APL 0x03C0 +#define IMX390_V_REV_APL_MASK BIT(2) +#define IMX390_H_REV_APL_MASK BIT(3) + +/* Test Pattern control */ +#define IMX390_REG_CAT0_PGMODE_PGREGEN 0x01DB +#define IMX390_SM_CFG_SM_PGREGEN_APL 0x03C0 +#define IMX390_SM_PG_APL_MASK BIT(1) + +#define IMX390_PM_IDLE_TIMEOUT 1000 + +struct imx390_ctrl { + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *again; + struct v4l2_ctrl *dgain; + struct v4l2_ctrl *r_balance; + struct v4l2_ctrl *b_balance; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct v4l2_ctrl *pg_mode; +}; + +/* + * struct im390 - imx390 device structure + * @dev: Device handle + * @clk: Pointer to imx390 clock + * @client: Pointer to I2C client + * @regmap: Pointer to regmap structure + * @xclr_gpio: Pointer to XCLR gpio + * @subddev: V4L2 subdevice structure + * @format: V4L2 media bus frame format structure + * (width and height are in sync with the compose rect) + * @pad: Media pad structure + * @ctrl: imx390 control structure + * @frame_interval: Time period of one frame in seconds + * @clk_rate: Frequency of imx390 clock + * @lock: Mutex structure for V4L2 ctrl handler + * @streaming: Flag to store the streaming on/off status + */ +struct imx390 { + struct device *dev; + + struct clk *clk; + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *xclr_gpio; + + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt format; + struct media_pad pad; + + struct imx390_ctrl ctrl; + + unsigned long clk_rate; + u32 fps; + + /* mutex for V4L2 ctrl handler */ + struct mutex lock; + bool streaming; +}; + +static const struct v4l2_area imx390_framesizes[] = { + { + .width = IMX390_OUT_WIDTH, + .height = IMX390_OUT_HEIGHT, + }, +}; + +static const u32 imx390_mbus_formats[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +static const struct regmap_config imx390_regmap_config = { + .reg_bits = 16, + .val_bits = 8, +}; + +static const u32 imx390_pg_mode_reg_val[] = { + 0x00, + 0x32, + 0x33, + 0x35, + 0x3D, +}; + +static const char *const imx390_ctrl_pg_qmenu[] = { + "Disabled", + "Horizontal Color Bars", + "Vertical Color Bars", + "Gradation", + "Checker Pattern", +}; + +static const struct reg_sequence imx390_wdr_1936x1096[] = { + { 0x000C, 0x7F }, + { 0x000D, 0x01 }, + { 0x000E, 0x00 }, + { 0x0010, 0x7F }, + { 0x0011, 0x01 }, + { 0x0012, 0x00 }, + { 0x0018, 0x20 }, + { 0x0019, 0x00 }, + { 0x001A, 0x0C }, + { 0x001B, 0x00 }, + { 0x0038, 0x00 }, + { 0x003C, 0x00 }, + { 0x003D, 0x00 }, + { 0x003E, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0044, 0x00 }, + { 0x0045, 0x00 }, + { 0x0046, 0x00 }, + { 0x0048, 0x00 }, + { 0x0049, 0x00 }, + { 0x004A, 0x00 }, + { 0x004C, 0x00 }, + { 0x004D, 0x00 }, + { 0x004E, 0x00 }, + { 0x0050, 0x00 }, + { 0x0051, 0x00 }, + { 0x0052, 0x00 }, + { 0x0054, 0x00 }, + { 0x0055, 0x00 }, + { 0x0056, 0x00 }, + { 0x0058, 0x00 }, + { 0x0059, 0x00 }, + { 0x005A, 0x00 }, + { 0x005C, 0x00 }, + { 0x005D, 0x00 }, + { 0x005E, 0x00 }, + { 0x0060, 0x00 }, + { 0x0061, 0x00 }, + { 0x0062, 0x00 }, + { 0x0064, 0x00 }, + { 0x0065, 0x00 }, + { 0x0066, 0x00 }, + { 0x0068, 0x00 }, + { 0x0069, 0x00 }, + { 0x006A, 0x00 }, + { 0x0078, 0x01 }, + { 0x007C, 0x08 }, + { 0x007D, 0x00 }, + { 0x0080, 0x08 }, + { 0x0081, 0x00 }, + { 0x0090, 0x00 }, + { 0x00F4, 0x1C }, + { 0x00F5, 0xF8 }, + { 0x00F6, 0x01 }, + { 0x00F8, 0x03 }, + { 0x00F9, 0x01 }, + { 0x00FA, 0x00 }, + { 0x00FB, 0x02 }, + { 0x0114, 0x00 }, + { 0x0115, 0x01 }, + { 0x0118, 0x20 }, + { 0x0119, 0x03 }, + { 0x011A, 0x00 }, + { 0x011B, 0x41 }, + { 0x011C, 0x80 }, + { 0x011D, 0x00 }, + { 0x0120, 0x20 }, + { 0x0121, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x44 }, + { 0x0124, 0x00 }, + { 0x0125, 0x01 }, + { 0x0128, 0xAC }, + { 0x0129, 0x0D }, + { 0x012A, 0x00 }, + { 0x012B, 0xA4 }, + { 0x012C, 0x00 }, + { 0x012D, 0x01 }, + { 0x0130, 0xC4 }, + { 0x0131, 0x09 }, + { 0x0132, 0x00 }, + { 0x0133, 0xDA }, + { 0x0138, 0x00 }, /* DB_CMN_POST_PEDESTAL (0x0138: 0x0139) = 0 */ + { 0x0139, 0x00 }, /* DB_CMN_POST_PEDESTAL (0x0138: 0x0139) = 0 */ + { 0x013A, 0x00 }, + { 0x013B, 0x00 }, + { 0x013C, 0x00 }, + { 0x013D, 0x00 }, + { 0x013E, 0x00 }, + { 0x0140, 0x00 }, + { 0x0141, 0x00 }, + { 0x0142, 0x00 }, + /* Start of PWL Registers */ + { 0x0144, 0x00 }, /* pwl_cp1_x 2048 */ + { 0x0145, 0x08 }, /* pwl_cp1_x 2048 */ + { 0x0146, 0x00 }, /* pwl_cp1_x 2048 */ + { 0x0147, 0x00 }, /* pwl_cp1_x 2048 */ + { 0x0148, 0x00 }, /* pwl_cp1_y 51 */ + { 0x0149, 0x02 }, /* pwl_cp1_y 51 */ + { 0x014A, 0x00 }, /* pwl_cp1_y 51 */ + { 0x014B, 0x00 }, /* pwl_cp1_y 51 */ + { 0x014C, 0x00 }, /* pwl_cp2_x 1638 */ + { 0x014D, 0x40 }, /* pwl_cp2_x 1638 */ + { 0x014E, 0x00 }, /* pwl_cp2_x 1638 */ + { 0x014F, 0x00 }, /* pwl_cp2_x 1638 */ + { 0x0150, 0x80 }, /* pwl_cp2_y 1408 */ + { 0x0151, 0x05 }, /* pwl_cp2_y 1408 */ + { 0x0152, 0x00 }, /* pwl_cp2_y 1408 */ + { 0x0153, 0x00 }, /* pwl_cp2_y 1408 */ + { 0x0154, 0x00 }, /* pwl_cp3_x 6553 */ + { 0x0155, 0x00 }, /* pwl_cp3_x 6553 */ + { 0x0156, 0x01 }, /* pwl_cp3_x 6553 */ + { 0x0157, 0x00 }, /* pwl_cp3_x 6553 */ + { 0x0158, 0x80 }, /* pwl_cp3_y 2176 */ + { 0x0159, 0x08 }, /* pwl_cp3_y 2176 */ + { 0x015A, 0x00 }, /* pwl_cp3_y 2176 */ + { 0x015B, 0x00 }, /* pwl_cp3_y 2176 */ + { 0x015C, 0x00 }, /* pwl_cp4_x 0x100000 */ + { 0x015D, 0x00 }, /* pwl_cp4_x 0x100000 */ + { 0x015E, 0x10 }, /* pwl_cp4_x 0x100000 */ + { 0x015F, 0x00 }, /* pwl_cp4_x 0x100000 */ + { 0x0160, 0x00 }, /* pwl_cp4_y 0x1000 */ + { 0x0161, 0x10 }, /* pwl_cp4_y 0x1000 */ + { 0x0162, 0x00 }, /* pwl_cp4_y 0x1000 */ + { 0x0163, 0x00 }, /* pwl_cp4_y 0x1000 */ + { 0x0164, 0x00 }, /* pwl_cp5_x 0x100000 */ + { 0x0165, 0x00 }, /* pwl_cp5_x 0x100000 */ + { 0x0166, 0x10 }, /* pwl_cp5_x 0x100000 */ + { 0x0167, 0x00 }, /* pwl_cp5_x 0x100000 */ + { 0x0168, 0x00 }, /* pwl_cp5_y 0x1000 */ + { 0x0169, 0x10 }, /* pwl_cp5_y 0x1000 */ + { 0x016A, 0x00 }, /* pwl_cp5_y 0x1000 */ + { 0x016B, 0x00 }, /* pwl_cp5_y 0x1000 */ + { 0x016C, 0x00 }, /* pwl_cp6_x 0x100000 */ + { 0x016D, 0x00 }, /* pwl_cp6_x 0x100000 */ + { 0x016E, 0x10 }, /* pwl_cp6_x 0x100000 */ + { 0x016F, 0x00 }, /* pwl_cp6_x 0x100000 */ + { 0x0170, 0x00 }, /* pwl_cp6_y 0x1000 */ + { 0x0171, 0x10 }, /* pwl_cp6_y 0x1000 */ + { 0x0172, 0x00 }, /* pwl_cp6_y 0x1000 */ + { 0x0173, 0x00 }, /* pwl_cp6_y 0x1000 */ + { 0x0174, 0x00 }, /* pwl_cp7_x 0x100000 */ + { 0x0175, 0x00 }, /* pwl_cp7_x 0x100000 */ + { 0x0176, 0x10 }, /* pwl_cp7_x 0x100000 */ + { 0x0177, 0x00 }, /* pwl_cp7_x 0x100000 */ + { 0x0178, 0x00 }, /* pwl_cp7_y 0x1000 */ + { 0x0179, 0x10 }, /* pwl_cp7_y 0x1000 */ + { 0x017A, 0x00 }, /* pwl_cp7_y 0x1000 */ + { 0x017B, 0x00 }, /* pwl_cp7_y 0x1000 */ + { 0x017C, 0x00 }, /* pwl_cp8_x 0x100000 */ + { 0x017D, 0x00 }, /* pwl_cp8_x 0x100000 */ + { 0x017E, 0x10 }, /* pwl_cp8_x 0x100000 */ + { 0x017F, 0x00 }, /* pwl_cp8_x 0x100000 */ + { 0x0180, 0x00 }, /* pwl_cp8_y 0x1000 */ + { 0x0181, 0x10 }, /* pwl_cp8_y 0x1000 */ + { 0x0182, 0x00 }, /* pwl_cp8_y 0x1000 */ + { 0x0183, 0x00 }, /* pwl_cp8_y 0x1000 */ + { 0x0184, 0x00 }, /* pwl_cp9_x 0x100000 */ + { 0x0185, 0x00 }, /* pwl_cp9_x 0x100000 */ + { 0x0186, 0x10 }, /* pwl_cp9_x 0x100000 */ + { 0x0187, 0x00 }, /* pwl_cp9_x 0x100000 */ + { 0x0188, 0x00 }, /* pwl_cp9_y 0x1000 */ + { 0x0189, 0x10 }, /* pwl_cp9_y 0x1000 */ + { 0x018A, 0x00 }, /* pwl_cp9_y 0x1000 */ + { 0x018B, 0x00 }, /* pwl_cp9_y 0x1000 */ + { 0x018C, 0x00 }, /* pwl_cp10_x 0x10000 */ + { 0x018D, 0x00 }, /* pwl_cp10_x 0x10000 */ + { 0x018E, 0x10 }, /* pwl_cp10_x 0x10000 */ + { 0x018F, 0x00 }, /* pwl_cp10_x 0x10000 */ + { 0x0190, 0x00 }, /* pwl_cp10_y 0x100 */ + { 0x0191, 0x10 }, /* pwl_cp10_y 0x100 */ + { 0x0192, 0x00 }, /* pwl_cp10_y 0x100 */ + { 0x0193, 0x00 }, /* pwl_cp10_y 0x100 */ + { 0x0198, 0x00 }, /* pwl gain0 0x040 0000 */ + { 0x0199, 0x00 }, + { 0x019A, 0x40 }, + { 0x019B, 0x00 }, + { 0x019C, 0x00 }, /* pwl gain1 0x010 0000 */ + { 0x019D, 0x00 }, + { 0x019E, 0x10 }, + { 0x019F, 0x00 }, + { 0x01A0, 0x00 }, /* pwl gain2 0x004 0000 */ + { 0x01A1, 0x00 }, + { 0x01A2, 0x04 }, + { 0x01A3, 0x00 }, + { 0x01A4, 0x00 }, /* pwl gain3 0x000 8000 */ + { 0x01A5, 0x80 }, + { 0x01A6, 0x00 }, + { 0x01A7, 0x00 }, + { 0x01A8, 0x00 }, + { 0x01A9, 0x00 }, + { 0x01AA, 0x00 }, + { 0x01AB, 0x00 }, + /* End of PWL Registers*/ + { 0x01AC, 0x00 }, + { 0x01AD, 0x00 }, + { 0x01AE, 0x00 }, + { 0x01AF, 0x00 }, + { 0x01B0, 0x00 }, + { 0x01B1, 0x00 }, + { 0x01B2, 0x00 }, + { 0x01B3, 0x00 }, + { 0x01B4, 0x00 }, + { 0x01B5, 0x00 }, + { 0x01B6, 0x00 }, + { 0x01B7, 0x00 }, + { 0x01B8, 0x00 }, + { 0x01B9, 0x00 }, + { 0x01BA, 0x00 }, + { 0x01BB, 0x00 }, + { 0x01BC, 0x00 }, + { 0x01BD, 0x00 }, + { 0x01BE, 0x00 }, + { 0x01BF, 0x00 }, + { 0x01C0, 0x00 }, + { 0x01C1, 0x00 }, + { 0x01C2, 0x00 }, + { 0x01C3, 0x00 }, + { 0x01C4, 0x00 }, + { 0x01C5, 0x00 }, + { 0x01CC, 0x01 }, + { 0x01D0, 0x09 }, + { 0x01D4, 0x01 }, + { 0x0332, 0x67 }, + { 0x0333, 0x02 }, + { 0x0390, 0x00 }, + { 0x0391, 0x00 }, + { 0x0392, 0x00 }, + { 0x03C0, 0x01 }, + { 0x2000, 0x55 }, + { 0x2001, 0x55 }, + { 0x2002, 0x55 }, + { 0x2003, 0x05 }, + { 0x2004, 0x02 }, + { 0x2008, 0x65 }, + { 0x2009, 0x04 }, + { 0x200A, 0x00 }, + { 0x200C, 0x98 }, + { 0x200D, 0x08 }, + { 0x2010, 0x04 }, + { 0x2014, 0x00 }, + { 0x2018, 0x02 }, + { 0x2019, 0x04 }, + { 0x201A, 0x00 }, + { 0x201C, 0x21 }, + { 0x201D, 0x11 }, + { 0x201E, 0x00 }, + { 0x201F, 0x00 }, + { 0x2020, 0xBC }, + { 0x2021, 0x00 }, + { 0x2022, 0x7F }, + { 0x2023, 0x00 }, + { 0x2024, 0xBA }, + { 0x2025, 0x00 }, + { 0x2026, 0x81 }, + { 0x2027, 0x00 }, + { 0x2028, 0x7D }, + { 0x2029, 0x90 }, + { 0x202A, 0x05 }, + { 0x202C, 0xFC }, + { 0x202D, 0x02 }, + { 0x202E, 0x25 }, + { 0x202F, 0x03 }, + { 0x2030, 0x05 }, + { 0x2031, 0x02 }, + { 0x2032, 0xCA }, + { 0x2033, 0x02 }, + { 0x2034, 0xFC }, + { 0x2035, 0x02 }, + { 0x2036, 0x25 }, + { 0x2037, 0x03 }, + { 0x2038, 0xF8 }, + { 0x2039, 0xE4 }, + { 0x203A, 0xE3 }, + { 0x203B, 0x01 }, + { 0x203C, 0xF5 }, + { 0x203D, 0x8E }, + { 0x203E, 0x0C }, + { 0x203F, 0x2D }, + { 0x2040, 0x69 }, + { 0x2041, 0x01 }, + { 0x2042, 0x8E }, + { 0x2043, 0x01 }, + { 0x2044, 0x0C }, + { 0x2045, 0x02 }, + { 0x2046, 0x31 }, + { 0x2047, 0x02 }, + { 0x2048, 0x6A }, + { 0x2049, 0x01 }, + { 0x204A, 0x8E }, + { 0x204B, 0x01 }, + { 0x204C, 0x0D }, + { 0x204D, 0x02 }, + { 0x204E, 0x31 }, + { 0x204F, 0x02 }, + { 0x2050, 0x7B }, + { 0x2051, 0x00 }, + { 0x2052, 0x7D }, + { 0x2053, 0x00 }, + { 0x2054, 0x95 }, + { 0x2055, 0x00 }, + { 0x2056, 0x97 }, + { 0x2057, 0x00 }, + { 0x2058, 0xAD }, + { 0x2059, 0x00 }, + { 0x205A, 0xAF }, + { 0x205B, 0x00 }, + { 0x205C, 0x92 }, + { 0x205D, 0x00 }, + { 0x205E, 0x94 }, + { 0x205F, 0x00 }, + { 0x2060, 0x8E }, + { 0x2061, 0x00 }, + { 0x2062, 0x90 }, + { 0x2063, 0x00 }, + { 0x2064, 0xB1 }, + { 0x2065, 0x00 }, + { 0x2066, 0xB3 }, + { 0x2067, 0x00 }, + { 0x2068, 0x08 }, + { 0x2069, 0x00 }, + { 0x206A, 0x04 }, + { 0x206B, 0x00 }, + { 0x206C, 0x84 }, + { 0x206D, 0x00 }, + { 0x206E, 0x80 }, + { 0x206F, 0x00 }, + { 0x2070, 0x04 }, + { 0x2071, 0x00 }, + { 0x2072, 0x46 }, + { 0x2073, 0x00 }, + { 0x2074, 0xE9 }, + { 0x2075, 0x01 }, + { 0x2076, 0x74 }, + { 0x2077, 0x02 }, + { 0x2078, 0x80 }, + { 0x2079, 0x00 }, + { 0x207A, 0xC1 }, + { 0x207B, 0x00 }, + { 0x207C, 0xFF }, + { 0x207D, 0x03 }, + { 0x207E, 0xFF }, + { 0x207F, 0x03 }, + { 0x2080, 0x78 }, + { 0x2081, 0x00 }, + { 0x2082, 0x6A }, + { 0x2083, 0x01 }, + { 0x2084, 0xE4 }, + { 0x2085, 0x01 }, + { 0x2086, 0x2B }, + { 0x2087, 0x03 }, + { 0x2088, 0x00 }, + { 0x2089, 0x00 }, + { 0x208A, 0xFF }, + { 0x208B, 0x03 }, + { 0x208C, 0xFF }, + { 0x208D, 0x03 }, + { 0x208E, 0xFF }, + { 0x208F, 0x03 }, + { 0x2090, 0x7D }, + { 0x2091, 0x00 }, + { 0x2092, 0x62 }, + { 0x2093, 0x01 }, + { 0x2094, 0xE9 }, + { 0x2095, 0x01 }, + { 0x2096, 0x00 }, + { 0x2097, 0x00 }, + { 0x2098, 0x7C }, + { 0x2099, 0x00 }, + { 0x209A, 0x21 }, + { 0x209B, 0x03 }, + { 0x209C, 0xE9 }, + { 0x209D, 0x01 }, + { 0x209E, 0x21 }, + { 0x209F, 0x03 }, + { 0x20A0, 0xFF }, + { 0x20A1, 0x03 }, + { 0x20A2, 0xFF }, + { 0x20A3, 0x03 }, + { 0x20A4, 0xFF }, + { 0x20A5, 0x03 }, + { 0x20A6, 0xFF }, + { 0x20A7, 0x03 }, + { 0x20A8, 0xFF }, + { 0x20A9, 0x03 }, + { 0x20AA, 0xFF }, + { 0x20AB, 0x03 }, + { 0x20AC, 0xFF }, + { 0x20AD, 0x03 }, + { 0x20AE, 0xFF }, + { 0x20AF, 0x03 }, + { 0x20B0, 0xFF }, + { 0x20B1, 0x03 }, + { 0x20B2, 0xFF }, + { 0x20B3, 0x03 }, + { 0x20B4, 0x87 }, + { 0x20B5, 0xCC }, + { 0x20B6, 0x87 }, + { 0x20B7, 0x08 }, + { 0x20B8, 0xF4 }, + { 0x20B9, 0xA5 }, + { 0x20BA, 0x07 }, + { 0x20BC, 0x1F }, + { 0x20BD, 0x01 }, + { 0x20BE, 0xF6 }, + { 0x20BF, 0x00 }, + { 0x20C0, 0x90 }, + { 0x20C1, 0x01 }, + { 0x20C2, 0x67 }, + { 0x20C3, 0x01 }, + { 0x20C4, 0xFF }, + { 0x20C5, 0x03 }, + { 0x20C6, 0xFF }, + { 0x20C7, 0x03 }, + { 0x20C8, 0x33 }, + { 0x20C9, 0x02 }, + { 0x20CA, 0x0A }, + { 0x20CB, 0x02 }, + { 0x20CC, 0x7F }, + { 0x20CD, 0x00 }, + { 0x20CE, 0xD2 }, + { 0x20CF, 0x00 }, + { 0x20D0, 0x81 }, + { 0x20D1, 0x00 }, + { 0x20D2, 0x87 }, + { 0x20D3, 0x00 }, + { 0x20D4, 0x09 }, + { 0x20D5, 0x00 }, + { 0x20D8, 0x7F }, + { 0x20D9, 0x00 }, + { 0x20DA, 0x62 }, + { 0x20DB, 0x01 }, + { 0x20DC, 0x7F }, + { 0x20DD, 0x00 }, + { 0x20DE, 0x62 }, + { 0x20DF, 0x01 }, + { 0x20E0, 0x65 }, + { 0x20E1, 0x00 }, + { 0x20E2, 0x75 }, + { 0x20E3, 0x00 }, + { 0x20E4, 0xE0 }, + { 0x20E5, 0x00 }, + { 0x20E6, 0xF0 }, + { 0x20E7, 0x00 }, + { 0x20E8, 0x4C }, + { 0x20E9, 0x01 }, + { 0x20EA, 0x5C }, + { 0x20EB, 0x01 }, + { 0x20EC, 0xD1 }, + { 0x20ED, 0x01 }, + { 0x20EE, 0xE1 }, + { 0x20EF, 0x01 }, + { 0x20F0, 0x93 }, + { 0x20F1, 0x02 }, + { 0x20F2, 0xA3 }, + { 0x20F3, 0x02 }, + { 0x20F4, 0x0D }, + { 0x20F5, 0x03 }, + { 0x20F6, 0x1D }, + { 0x20F7, 0x03 }, + { 0x20F8, 0x57 }, + { 0x20F9, 0x00 }, + { 0x20FA, 0x7B }, + { 0x20FB, 0x00 }, + { 0x20FC, 0xD2 }, + { 0x20FD, 0x00 }, + { 0x20FE, 0xF6 }, + { 0x20FF, 0x00 }, + { 0x2100, 0x3E }, + { 0x2101, 0x01 }, + { 0x2102, 0x60 }, + { 0x2103, 0x01 }, + { 0x2104, 0xC3 }, + { 0x2105, 0x01 }, + { 0x2106, 0xE5 }, + { 0x2107, 0x01 }, + { 0x2108, 0x85 }, + { 0x2109, 0x02 }, + { 0x210A, 0xA9 }, + { 0x210B, 0x02 }, + { 0x210C, 0xFF }, + { 0x210D, 0x02 }, + { 0x210E, 0x21 }, + { 0x210F, 0x03 }, + { 0x2110, 0xFF }, + { 0x2111, 0x03 }, + { 0x2112, 0x00 }, + { 0x2113, 0x00 }, + { 0x2114, 0xFF }, + { 0x2115, 0x03 }, + { 0x2116, 0xFF }, + { 0x2117, 0x03 }, + { 0x2118, 0xFF }, + { 0x2119, 0x03 }, + { 0x211A, 0xFF }, + { 0x211B, 0x03 }, + { 0x211C, 0xFF }, + { 0x211D, 0x03 }, + { 0x211E, 0xFF }, + { 0x211F, 0x03 }, + { 0x2120, 0xFF }, + { 0x2121, 0x03 }, + { 0x2122, 0xFF }, + { 0x2123, 0x03 }, + { 0x2124, 0xFF }, + { 0x2125, 0x03 }, + { 0x2126, 0xFF }, + { 0x2127, 0x03 }, + { 0x2128, 0x7D }, + { 0x2129, 0x90 }, + { 0x212A, 0xD5 }, + { 0x212B, 0x07 }, + { 0x212C, 0x64 }, + { 0x212D, 0x01 }, + { 0x2130, 0x5F }, + { 0x2131, 0x7D }, + { 0x2132, 0x05 }, + { 0x2134, 0x78 }, + { 0x2135, 0x00 }, + { 0x2136, 0x76 }, + { 0x2137, 0x00 }, + { 0x2138, 0xF3 }, + { 0x2139, 0x00 }, + { 0x213A, 0xF1 }, + { 0x213B, 0x00 }, + { 0x213C, 0xA6 }, + { 0x213D, 0x02 }, + { 0x213E, 0xA4 }, + { 0x213F, 0x02 }, + { 0x2140, 0x7D }, + { 0x2141, 0x00 }, + { 0x2142, 0x8D }, + { 0x2143, 0x00 }, + { 0x2144, 0xA1 }, + { 0x2145, 0x01 }, + { 0x2146, 0xB1 }, + { 0x2147, 0x01 }, + { 0x2148, 0xAB }, + { 0x2149, 0x02 }, + { 0x214A, 0xBB }, + { 0x214B, 0x02 }, + { 0x214C, 0x17 }, + { 0x214D, 0x5C }, + { 0x214E, 0x00 }, + { 0x2150, 0x00 }, + { 0x2151, 0x00 }, + { 0x2152, 0xF8 }, + { 0x2153, 0x00 }, + { 0x2154, 0xBE }, + { 0x2155, 0x00 }, + { 0x2156, 0x7D }, + { 0x2157, 0x00 }, + { 0x2158, 0x25 }, + { 0x2159, 0x00 }, + { 0x215A, 0x7D }, + { 0x215B, 0x00 }, + { 0x215C, 0x62 }, + { 0x215D, 0x01 }, + { 0x215E, 0xFF }, + { 0x215F, 0x03 }, + { 0x2160, 0x26 }, + { 0x2161, 0x00 }, + { 0x2162, 0x7D }, + { 0x2163, 0x00 }, + { 0x2164, 0x63 }, + { 0x2165, 0x01 }, + { 0x2166, 0xFF }, + { 0x2167, 0x03 }, + { 0x2168, 0xCB }, + { 0x2169, 0x02 }, + { 0x216A, 0xCF }, + { 0x216B, 0x02 }, + { 0x216C, 0xFF }, + { 0x216D, 0x03 }, + { 0x216E, 0xFF }, + { 0x216F, 0x03 }, + { 0x2170, 0xFF }, + { 0x2171, 0x03 }, + { 0x2172, 0xFF }, + { 0x2173, 0x03 }, + { 0x2174, 0xFF }, + { 0x2175, 0x03 }, + { 0x2176, 0xFF }, + { 0x2177, 0x03 }, + { 0x2178, 0x7E }, + { 0x2179, 0x00 }, + { 0x217A, 0xBD }, + { 0x217B, 0x00 }, + { 0x217C, 0xEC }, + { 0x217D, 0x01 }, + { 0x217E, 0x7B }, + { 0x217F, 0x02 }, + { 0x2180, 0xD1 }, + { 0x2181, 0x02 }, + { 0x2182, 0x25 }, + { 0x2183, 0x03 }, + { 0x2184, 0x7F }, + { 0x2185, 0x00 }, + { 0x2186, 0xBD }, + { 0x2187, 0x00 }, + { 0x2188, 0xED }, + { 0x2189, 0x01 }, + { 0x218A, 0x7B }, + { 0x218B, 0x02 }, + { 0x218C, 0xD2 }, + { 0x218D, 0x02 }, + { 0x218E, 0x25 }, + { 0x218F, 0x03 }, + { 0x2190, 0xFF }, + { 0x2191, 0x03 }, + { 0x2192, 0xFF }, + { 0x2193, 0x03 }, + { 0x2194, 0xE9 }, + { 0x2195, 0x01 }, + { 0x2196, 0x21 }, + { 0x2197, 0x03 }, + { 0x2198, 0x17 }, + { 0x2199, 0xFC }, + { 0x219A, 0x7F }, + { 0x219B, 0x01 }, + { 0x219C, 0xFF }, + { 0x219D, 0x03 }, + { 0x21A0, 0x1B }, + { 0x21A1, 0x1B }, + { 0x21A2, 0x1B }, + { 0x21A3, 0x1B }, + { 0x21A4, 0x2B }, + { 0x21A5, 0x80 }, + { 0x21A6, 0x00 }, + { 0x21A8, 0x04 }, + { 0x21A9, 0x98 }, + { 0x21AA, 0x60 }, + { 0x21AB, 0x03 }, + { 0x21AC, 0x7F }, + { 0x21AD, 0x80 }, + { 0x21AE, 0x09 }, + { 0x21B0, 0x1C }, + { 0x21B1, 0x00 }, + { 0x21B2, 0xA0 }, + { 0x21B3, 0x00 }, + { 0x21B4, 0x0C }, + { 0x21B5, 0x00 }, + { 0x21B6, 0x2D }, + { 0x21B7, 0x00 }, + { 0x21B8, 0x20 }, + { 0x21B9, 0x00 }, + { 0x21BA, 0x02 }, + { 0x21BB, 0x00 }, + { 0x21BC, 0xCC }, + { 0x21BD, 0x00 }, + { 0x21BE, 0x4A }, + { 0x21BF, 0x00 }, + { 0x21C0, 0xD0 }, + { 0x21C1, 0x00 }, + { 0x21C2, 0x44 }, + { 0x21C3, 0x00 }, + { 0x21C4, 0x00 }, + { 0x21C5, 0xE0 }, + { 0x21C6, 0x00 }, + { 0x21C8, 0x11 }, + { 0x21C9, 0x00 }, + { 0x21CA, 0x02 }, + { 0x21CC, 0x08 }, + { 0x21CD, 0xC0 }, + { 0x21CE, 0x0C }, + { 0x21D0, 0x44 }, + { 0x21D1, 0x00 }, + { 0x21D2, 0x02 }, + { 0x21D4, 0x02 }, + { 0x21D5, 0x20 }, + { 0x21D6, 0x2C }, + { 0x21D8, 0xFE }, + { 0x21D9, 0x9D }, + { 0x21DA, 0xDF }, + { 0x21DB, 0x03 }, + { 0x21DC, 0x62 }, + { 0x21DD, 0x01 }, + { 0x21DE, 0x7F }, + { 0x21DF, 0x00 }, + { 0x21E0, 0xB7 }, + { 0x21E1, 0x01 }, + { 0x21E2, 0xB5 }, + { 0x21E3, 0x01 }, + { 0x21E4, 0xC1 }, + { 0x21E5, 0x02 }, + { 0x21E6, 0xBF }, + { 0x21E7, 0x02 }, + { 0x21E8, 0xB3 }, + { 0x21E9, 0x0D }, + { 0x21EA, 0x00 }, + { 0x21EB, 0x04 }, + { 0x21EC, 0x90 }, + { 0x21ED, 0x07 }, + { 0x21EE, 0x58 }, + { 0x21EF, 0x04 }, + { 0x21F0, 0x54 }, + { 0x21F1, 0x04 }, + { 0x21F4, 0x02 }, + { 0x21F5, 0x00 }, + { 0x21F6, 0x00 }, + { 0x21F8, 0x3C }, + { 0x21F9, 0x00 }, + { 0x21FC, 0x28 }, + { 0x21FD, 0x00 }, + { 0x21FE, 0x3C }, + { 0x21FF, 0x00 }, + { 0x2200, 0x00 }, + { 0x2204, 0x4C }, + { 0x2205, 0x04 }, + { 0x2206, 0x65 }, + { 0x2207, 0x04 }, + { 0x2208, 0x0A }, + { 0x2209, 0x00 }, + { 0x220C, 0x57 }, + { 0x220D, 0x00 }, + { 0x220E, 0x37 }, + { 0x220F, 0x00 }, + { 0x2210, 0x1F }, + { 0x2211, 0x00 }, + { 0x2212, 0x1F }, + { 0x2213, 0x00 }, + { 0x2214, 0x1F }, + { 0x2215, 0x00 }, + { 0x2216, 0x77 }, + { 0x2217, 0x00 }, + { 0x2218, 0x1F }, + { 0x2219, 0x00 }, + { 0x221A, 0x17 }, + { 0x221B, 0x00 }, + { 0x221C, 0x03 }, + { 0x2220, 0x24 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2224, 0xA7 }, + { 0x2225, 0xAA }, + { 0x2226, 0x80 }, + { 0x2227, 0x08 }, + { 0x2228, 0x01 }, + { 0x2260, 0xFF }, + { 0x2261, 0x1F }, + { 0x2262, 0x00 }, + { 0x2263, 0x00 }, + { 0x2264, 0x00 }, + { 0x2265, 0x00 }, + { 0x2266, 0xFF }, + { 0x2267, 0x1F }, + { 0x2268, 0x00 }, + { 0x2269, 0x00 }, + { 0x226A, 0xFF }, + { 0x226B, 0x1F }, + { 0x226C, 0x00 }, + { 0x226D, 0x00 }, + { 0x226E, 0xFF }, + { 0x226F, 0x1F }, + { 0x227C, 0xB2 }, + { 0x227D, 0x0C }, + { 0x227E, 0x6A }, + { 0x227F, 0x09 }, + { 0x2280, 0xD2 }, + { 0x2281, 0x0C }, + { 0x2282, 0x5A }, + { 0x2283, 0x09 }, + { 0x2284, 0xC4 }, + { 0x2285, 0x0C }, + { 0x2286, 0x54 }, + { 0x2287, 0x09 }, + { 0x22B2, 0x92 }, + { 0x22B4, 0x20 }, + { 0x22B5, 0x00 }, + { 0x22B6, 0x20 }, + { 0x22B7, 0x00 }, + { 0x22B8, 0x20 }, + { 0x22B9, 0x00 }, + { 0x22BA, 0x20 }, + { 0x22BB, 0x00 }, + { 0x22BC, 0x20 }, + { 0x22BD, 0x00 }, + { 0x22BE, 0x20 }, + { 0x22BF, 0x00 }, + { 0x22C0, 0x20 }, + { 0x22C1, 0x00 }, + { 0x22C2, 0x20 }, + { 0x22C3, 0x00 }, + { 0x22C4, 0x20 }, + { 0x22C5, 0x00 }, + { 0x22C6, 0x20 }, + { 0x22C7, 0x00 }, + { 0x22C8, 0x20 }, + { 0x22C9, 0x00 }, + { 0x22CA, 0x20 }, + { 0x22CB, 0x00 }, + { 0x22CC, 0x20 }, + { 0x22CD, 0x00 }, + { 0x22CE, 0x20 }, + { 0x22CF, 0x00 }, + { 0x22DA, 0x00 }, + { 0x22EF, 0x82 }, + { 0x2308, 0x01 }, + { 0x2310, 0x4B }, + { 0x2311, 0x09 }, + { 0x2318, 0x40 }, + { 0x2319, 0xCD }, + { 0x231A, 0x54 }, + { 0x2324, 0x20 }, + { 0x2325, 0x00 }, + { 0x2328, 0x00 }, + { 0x234A, 0x9F }, + { 0x234B, 0x07 }, + { 0x2354, 0x0C }, + { 0x23C0, 0x5D }, + { 0x244C, 0xFF }, + { 0x244D, 0x03 }, + { 0x244E, 0xFF }, + { 0x244F, 0x03 }, + { 0x24A0, 0x00 }, + { 0x24A4, 0x16 }, + { 0x24A5, 0x01 }, + { 0x24A6, 0xA6 }, + { 0x24A7, 0x02 }, + { 0x24A8, 0xD5 }, + { 0x24A9, 0x02 }, + { 0x24BC, 0x17 }, + { 0x24BD, 0x01 }, + { 0x24BE, 0xA7 }, + { 0x24BF, 0x02 }, + { 0x24C0, 0xD5 }, + { 0x24C1, 0x02 }, + { 0x24DA, 0x6F }, + { 0x24DB, 0x00 }, + { 0x24DC, 0x62 }, + { 0x24DD, 0x01 }, + { 0x24EA, 0x32 }, + { 0x24EB, 0x00 }, + { 0x24EC, 0xDC }, + { 0x24ED, 0x00 }, + { 0x24FA, 0x32 }, + { 0x24FB, 0x00 }, + { 0x24FC, 0xDD }, + { 0x24FD, 0x00 }, + { 0x254A, 0x15 }, + { 0x254B, 0x01 }, + { 0x255A, 0x15 }, + { 0x255B, 0x01 }, + { 0x2560, 0x01 }, + { 0x2561, 0x00 }, + { 0x2562, 0x2A }, + { 0x2563, 0x00 }, + { 0x2564, 0xF8 }, + { 0x2565, 0x00 }, + { 0x2566, 0x15 }, + { 0x2567, 0x01 }, + { 0x2568, 0x0C }, + { 0x2569, 0x02 }, + { 0x256A, 0x31 }, + { 0x256B, 0x02 }, + { 0x2578, 0x90 }, + { 0x2579, 0x01 }, + { 0x257A, 0x92 }, + { 0x257B, 0x01 }, + { 0x257C, 0xB8 }, + { 0x257D, 0x02 }, + { 0x257E, 0xBA }, + { 0x257F, 0x02 }, + { 0x2584, 0x90 }, + { 0x2585, 0x01 }, + { 0x2586, 0x92 }, + { 0x2587, 0x01 }, + { 0x2588, 0xB8 }, + { 0x2589, 0x02 }, + { 0x258A, 0xBA }, + { 0x258B, 0x02 }, + { 0x267A, 0xF8 }, + { 0x267B, 0x00 }, + { 0x267C, 0x16 }, + { 0x267D, 0x01 }, + { 0x267E, 0xA6 }, + { 0x267F, 0x02 }, + { 0x2680, 0xD5 }, + { 0x2681, 0x02 }, + { 0x2690, 0xF8 }, + { 0x2691, 0x00 }, + { 0x2694, 0xA6 }, + { 0x2695, 0x02 }, + { 0x2696, 0x16 }, + { 0x2697, 0x01 }, + { 0x269A, 0xD5 }, + { 0x269B, 0x02 }, + { 0x26B8, 0x10 }, + { 0x26B9, 0x00 }, + { 0x26BA, 0x33 }, + { 0x26BB, 0x00 }, + { 0x26BC, 0x89 }, + { 0x26BD, 0x00 }, + { 0x26BE, 0xB0 }, + { 0x26BF, 0x00 }, + { 0x26C4, 0x4E }, + { 0x26C5, 0x00 }, + { 0x26C8, 0xC9 }, + { 0x26C9, 0x00 }, + { 0x26CC, 0x35 }, + { 0x26CD, 0x01 }, + { 0x26D0, 0xBA }, + { 0x26D1, 0x01 }, + { 0x26D4, 0x7C }, + { 0x26D5, 0x02 }, + { 0x26D8, 0xF6 }, + { 0x26D9, 0x02 }, + { 0x26DE, 0x51 }, + { 0x26DF, 0x00 }, + { 0x26E0, 0x7F }, + { 0x26E1, 0x00 }, + { 0x26E2, 0xCC }, + { 0x26E3, 0x00 }, + { 0x26E4, 0xF8 }, + { 0x26E5, 0x00 }, + { 0x26E6, 0x38 }, + { 0x26E7, 0x01 }, + { 0x26E8, 0x65 }, + { 0x26E9, 0x01 }, + { 0x26EA, 0xBD }, + { 0x26EB, 0x01 }, + { 0x26EE, 0x7F }, + { 0x26EF, 0x02 }, + { 0x26F0, 0xAB }, + { 0x26F1, 0x02 }, + { 0x26F2, 0xF9 }, + { 0x26F3, 0x02 }, + { 0x2722, 0x59 }, + { 0x2723, 0x02 }, + { 0x2938, 0x55 }, + { 0x2939, 0x00 }, + { 0x293A, 0x17 }, + { 0x293B, 0x00 }, + { 0x293C, 0xD0 }, + { 0x293D, 0x00 }, + { 0x293E, 0x91 }, + { 0x293F, 0x00 }, + { 0x2940, 0x3C }, + { 0x2941, 0x01 }, + { 0x2942, 0x0C }, + { 0x2943, 0x01 }, + { 0x2944, 0xC1 }, + { 0x2945, 0x01 }, + { 0x2946, 0x76 }, + { 0x2947, 0x01 }, + { 0x2948, 0x83 }, + { 0x2949, 0x02 }, + { 0x294A, 0xFB }, + { 0x294B, 0x01 }, + { 0x294C, 0xFD }, + { 0x294D, 0x02 }, + { 0x294E, 0xBF }, + { 0x294F, 0x02 }, + { 0x2A06, 0x25 }, + { 0x2A07, 0x03 }, + { 0x2A0C, 0x45 }, + { 0x2A0D, 0x00 }, + { 0x2A0E, 0x00 }, + { 0x2A0F, 0x00 }, + { 0x2A20, 0x00 }, + { 0x2A21, 0x00 }, + { 0x2A22, 0x7D }, + { 0x2A23, 0x00 }, + { 0x2B11, 0x1A }, + { 0x2B13, 0x11 }, + { 0x2B14, 0x11 }, + { 0x2B15, 0x11 }, + { 0x2B16, 0x11 }, + { 0x2B17, 0x10 }, + { 0x2B18, 0x0F }, + { 0x2B19, 0x0E }, + { 0x2B1A, 0x0D }, + { 0x2B1B, 0x0C }, + { 0x2B1C, 0x0B }, + { 0x2B1D, 0x0B }, + { 0x2B1E, 0x0A }, + { 0x2B1F, 0x09 }, + { 0x2B20, 0x08 }, + { 0x2B21, 0x07 }, + { 0x2B22, 0x06 }, + { 0x2B23, 0x05 }, + { 0x2B24, 0x04 }, + { 0x2B25, 0x03 }, + { 0x2B26, 0x03 }, + { 0x2B38, 0x01 }, + { 0x2B45, 0xE3 }, + { 0x2B50, 0x01 }, + { 0x2B51, 0x00 }, + { 0x2B62, 0x66 }, + { 0x2B6D, 0x47 }, + { 0x2B70, 0x03 }, + { 0x2B71, 0x02 }, + { 0x2B72, 0x02 }, + { 0x2B7B, 0x42 }, + { 0x2B7F, 0x7F }, + { 0x2B80, 0x94 }, + { 0x2B81, 0x06 }, + { 0x2B87, 0x1B }, + { 0x2B88, 0x1A }, + { 0x2B89, 0x17 }, + { 0x2B8A, 0x17 }, + { 0x2B8B, 0x12 }, + { 0x2B8D, 0x2B }, + { 0x2B8E, 0x2B }, + { 0x2B8F, 0x2B }, + { 0x2B90, 0x7F }, + { 0x2B91, 0x0F }, + { 0x2B92, 0x31 }, + { 0x2B93, 0x07 }, + { 0x2B94, 0xFE }, + { 0x2B95, 0x26 }, + { 0x2B96, 0x84 }, + { 0x2B97, 0x0C }, + { 0x2B98, 0xFE }, + { 0x2B99, 0x56 }, + { 0x2B9B, 0x2A }, + { 0x2BA8, 0xBC }, + { 0x2BA9, 0x62 }, + { 0x2BC1, 0x70 }, + { 0x2BC5, 0x80 }, + { 0x2BD5, 0x30 }, + { 0x2BD6, 0xF0 }, + { 0x2BD8, 0xDB }, + { 0x2BD9, 0xF6 }, + { 0x2BDA, 0x21 }, + { 0x2BDB, 0x06 }, + { 0x2BDC, 0x57 }, + { 0x2BFE, 0x00 }, + { 0x2BFF, 0x00 }, + { 0x2C98, 0xE1 }, + { 0x2C99, 0x2E }, + { 0x2C9B, 0x80 }, + { 0x2CA9, 0x80 }, + { 0x2CAA, 0x01 }, + { 0x2CBF, 0x08 }, + { 0x2D39, 0x0E }, + { 0x2D50, 0x80 }, + { 0x2D54, 0x00 }, + { 0x2D5B, 0x58 }, + { 0x2DFD, 0x01 }, + { 0x2D64, 0x64 }, + { 0x2D65, 0x80 }, + { 0x3000, 0x00 }, + { 0x3001, 0x00 }, + { 0x3002, 0x23 }, + { 0x3003, 0xA1 }, + { 0x3004, 0x00 }, + { 0x3005, 0x20 }, + { 0x3006, 0x58 }, + { 0x3007, 0x00 }, + { 0x3008, 0x06 }, + { 0x3009, 0xB4 }, + { 0x300A, 0x1F }, + { 0x300B, 0x00 }, + { 0x300C, 0x00 }, + { 0x300D, 0x1B }, + { 0x300E, 0x90 }, + { 0x300F, 0x97 }, + { 0x3010, 0x00 }, + { 0x3011, 0x00 }, + { 0x3012, 0x20 }, + { 0x3013, 0x21 }, + { 0x3014, 0x00 }, + { 0x3015, 0x20 }, + { 0x3016, 0x84 }, + { 0x3017, 0x00 }, + { 0x3018, 0x30 }, + { 0x3019, 0x09 }, + { 0x301A, 0x46 }, + { 0x301B, 0x00 }, + { 0x3070, 0xC1 }, + { 0x3071, 0x81 }, + { 0x3072, 0x29 }, + { 0x3073, 0x81 }, + { 0x3080, 0xC4 }, + { 0x3081, 0x0C }, + { 0x3082, 0xD2 }, + { 0x3083, 0x0C }, + { 0x3084, 0x5C }, + { 0x3085, 0x00 }, + { 0x3086, 0x90 }, + { 0x3087, 0x00 }, + { 0x3088, 0x07 }, + { 0x3089, 0x0A }, + { 0x308A, 0x52 }, + { 0x308B, 0x09 }, + { 0x308C, 0x44 }, + { 0x308D, 0x03 }, + { 0x308E, 0x70 }, + { 0x308F, 0x03 }, + { 0x3090, 0x54 }, + { 0x3091, 0x09 }, + { 0x3092, 0x5A }, + { 0x3093, 0x09 }, + { 0x3094, 0x1C }, + { 0x3095, 0x00 }, + { 0x3096, 0x10 }, + { 0x3097, 0x00 }, + { 0x3098, 0x70 }, + { 0x3099, 0x03 }, + { 0x309A, 0xF8 }, + { 0x309B, 0x04 }, + { 0x309C, 0x74 }, + { 0x309D, 0x01 }, + { 0x309E, 0x60 }, + { 0x309F, 0x01 }, + { 0x3370, 0x01 }, + { 0x3374, 0xF0 }, + { 0x3375, 0x00 }, + { 0x3376, 0x01 }, + { 0x3377, 0x00 }, + { 0x3410, (IMX390_OUT_WIDTH & 0xFF) }, + { 0x3411, (IMX390_OUT_WIDTH >> 8) }, + { 0x3418, (IMX390_OUT_HEIGHT & 0xFF) }, + { 0x3419, (IMX390_OUT_HEIGHT >> 8) }, + { 0x34A0, 0x30 }, + { 0x34BE, 0x6A }, + { 0x34BF, 0x01 }, + { 0x34C0, 0x40 }, + { 0x34C1, 0x00 }, + { 0x34C2, 0x40 }, + { 0x34C3, 0x00 }, + { 0x34C4, 0x40 }, + { 0x34C5, 0x00 }, + { 0x34C6, 0x40 }, + { 0x34C7, 0x00 }, + { 0x34C8, 0x40 }, + { 0x34C9, 0x00 }, + { 0x34CA, 0x40 }, + { 0x34CB, 0x00 }, + { 0x34CC, 0x40 }, + { 0x34CD, 0x00 }, + { 0x34CE, 0x40 }, + { 0x34CF, 0x00 }, + { 0x3584, 0x00 }, + { 0x3586, 0x00 }, + { 0x3587, 0x01 }, + { 0x3588, 0xE6 }, + { 0x3589, 0x00 }, + { 0x3590, 0x00 }, + { 0x3591, 0x00 }, + { 0x3594, 0x40 }, + { 0x3598, 0x03 }, + { 0x3599, 0x00 }, + { 0x359A, 0x80 }, + { 0x359B, 0x00 }, + { 0x359C, 0x00 }, + { 0x359D, 0x01 }, + { 0x359E, 0x00 }, + { 0x359F, 0x02 }, + { 0x35A0, 0x00 }, + { 0x35A1, 0x04 }, + { 0x35A2, 0x20 }, + { 0x35A3, 0x00 }, + { 0x35A4, 0x40 }, + { 0x35A5, 0x00 }, + { 0x35A6, 0x80 }, + { 0x35A7, 0x00 }, + { 0x35A8, 0x00 }, + { 0x35A9, 0x01 }, + { 0x35AC, 0x80 }, + { 0x35AD, 0x00 }, + { 0x35AE, 0x00 }, + { 0x35AF, 0x01 }, + { 0x35B0, 0x00 }, + { 0x35B1, 0x02 }, + { 0x35B2, 0x00 }, + { 0x35B3, 0x04 }, + { 0x35B4, 0x02 }, + { 0x35B5, 0x00 }, + { 0x35B6, 0x04 }, + { 0x35B7, 0x00 }, + { 0x35B8, 0x08 }, + { 0x35B9, 0x00 }, + { 0x35BA, 0x10 }, + { 0x35BB, 0x00 }, + { 0x35C8, 0x00 }, + { 0x35C9, 0x01 }, + { 0x35CA, 0x00 }, + { 0x35CB, 0x04 }, + { 0x35CC, 0x00 }, + { 0x35CD, 0x10 }, + { 0x35CE, 0x00 }, + { 0x35CF, 0x40 }, + { 0x35D0, 0x00 }, + { 0x35D1, 0x0C }, + { 0x35D2, 0x00 }, + { 0x35D3, 0x0C }, + { 0x35D4, 0x00 }, + { 0x35D5, 0x0C }, + { 0x35D6, 0x00 }, + { 0x35D7, 0x0C }, + { 0x35D8, 0x00 }, + { 0x35D9, 0x00 }, + { 0x35DA, 0x08 }, + { 0x35DB, 0x00 }, + { 0x35DC, 0xD8 }, + { 0x35DD, 0x0E }, + { 0x35F0, 0x00 }, + { 0x35F1, 0x10 }, + { 0x35F2, 0x00 }, + { 0x35F3, 0x10 }, + { 0x35F4, 0x00 }, + { 0x35F5, 0x10 }, + { 0x35F6, 0x00 }, + { 0x35F7, 0x03 }, + { 0x35F8, 0x00 }, + { 0x35F9, 0x02 }, + { 0x35FA, 0x38 }, + { 0x35FB, 0x00 }, + { 0x35FC, 0xB3 }, + { 0x35FD, 0x01 }, + { 0x35FE, 0x00 }, + { 0x35FF, 0x00 }, + { 0x3600, 0x05 }, + { 0x3601, 0x06 }, + { 0x3604, 0x03 }, + { 0x3605, 0x00 }, + { 0x3608, 0x03 }, + { 0x3609, 0x00 }, + { 0x360C, 0x00 }, + { 0x360D, 0x00 }, + { 0x3610, 0x10 }, + { 0x3611, 0x01 }, + { 0x3612, 0x00 }, + { 0x3613, 0x00 }, + { 0x3614, 0x00 }, + { 0x3615, 0x00 }, + { 0x361C, 0x00 }, + { 0x361D, 0x01 }, + { 0x361E, 0x00 }, + { 0x361F, 0x01 }, + { 0x3620, 0x00 }, + { 0x3621, 0x00 }, + { 0x3622, 0xB0 }, + { 0x3623, 0x04 }, + { 0x3624, 0xDC }, + { 0x3625, 0x05 }, + { 0x3626, 0x00 }, + { 0x3627, 0x01 }, + { 0x3628, 0xFF }, + { 0x3629, 0x0F }, + { 0x362A, 0x00 }, + { 0x362B, 0x10 }, + { 0x362C, 0x00 }, + { 0x362D, 0x01 }, + { 0x3630, 0x41 }, + { 0x3631, 0x00 }, + { 0x3632, 0x41 }, + { 0x3633, 0x00 }, + { 0x3634, 0x41 }, + { 0x3635, 0x00 }, + { 0x3636, 0x41 }, + { 0x3637, 0x00 }, + { 0x3638, 0x44 }, + { 0x3639, 0x00 }, + { 0x363A, 0x47 }, + { 0x363B, 0x00 }, + { 0x363C, 0x47 }, + { 0x363D, 0x00 }, + { 0x363E, 0x44 }, + { 0x363F, 0x00 }, + { 0x36C4, 0xFF }, + { 0x36C5, 0x0F }, + { 0x36C6, 0xFF }, + { 0x36C7, 0x0F }, + { 0x36C8, 0xFF }, + { 0x36C9, 0x0F }, + { 0x36CC, 0x00 }, + { 0x36CD, 0x00 }, + { 0x36CE, 0x00 }, + { 0x36CF, 0x00 }, + { 0x36D0, 0x00 }, + { 0x36D1, 0x00 }, + { 0x36D4, 0xFF }, + { 0x36D5, 0x0F }, + { 0x36D6, 0xFF }, + { 0x36D7, 0x0F }, + { 0x36D8, 0xFF }, + { 0x36D9, 0x0F }, + { 0x36DC, 0xFF }, + { 0x36DD, 0x0F }, + { 0x36DE, 0xFF }, + { 0x36DF, 0x0F }, + { 0x36E0, 0xFF }, + { 0x36E1, 0x0F }, + { 0x36E4, 0xFF }, + { 0x36E5, 0x0F }, + { 0x36E6, 0xFF }, + { 0x36E7, 0x0F }, + { 0x36E8, 0xFF }, + { 0x36E9, 0x0F }, + { 0x36EE, 0x00 }, + { 0x36EF, 0x00 }, + { 0x36F0, 0x00 }, + { 0x36F1, 0x80 }, + { 0x36F8, 0x01 }, + { 0x3700, 0x03 }, + { 0x3701, 0x05 }, + { 0x3702, 0x03 }, + { 0x3703, 0x04 }, + { 0x3704, 0x08 }, + { 0x3705, 0x03 }, + { 0x3706, 0x03 }, + { 0x3707, 0x03 }, + { 0x3708, 0x03 }, + { 0x3709, 0x03 }, + { 0x370A, 0x03 }, + { 0x370B, 0x03 }, + { 0x370C, 0x03 }, + { 0x370D, 0x03 }, + { 0x370E, 0x0E }, + { 0x3718, 0x64 }, + { 0x3719, 0x47 }, + { 0x371A, 0x36 }, + { 0x371B, 0x1E }, + { 0x371C, 0x50 }, + { 0x371D, 0x41 }, + { 0x371E, 0x2F }, + { 0x371F, 0x1A }, + { 0x3720, 0x95 }, + { 0x3721, 0x9D }, + { 0x3722, 0xA5 }, + { 0x3723, 0xAD }, + { 0x3748, 0xA8 }, + { 0x3749, 0x9E }, + { 0x374A, 0x94 }, + { 0x374B, 0x80 }, + { 0x37C0, 0x00 }, + { 0x37C1, 0x00 }, + { 0x37C2, 0x00 }, + { 0x37C4, 0x00 }, + { 0x37C5, 0x00 }, + { 0x37C6, 0x00 }, + { 0x37C8, 0x00 }, + { 0x37C9, 0x00 }, + { 0x37CA, 0x00 }, + { 0x37CC, 0x00 }, + { 0x37CD, 0x00 }, + { 0x37CE, 0x00 }, + { 0x37D0, 0x00 }, + { 0x37D1, 0x00 }, + { 0x37D2, 0x00 }, + { 0x37D4, 0x00 }, + { 0x37D5, 0x00 }, + { 0x37D6, 0x00 }, + { 0x37D8, 0x00 }, + { 0x37D9, 0x00 }, + { 0x37DA, 0x00 }, + { 0x37DC, 0x00 }, + { 0x37DD, 0x00 }, + { 0x37DE, 0x00 }, + { 0x37E0, 0x00 }, + { 0x37E1, 0x00 }, + { 0x37E2, 0x00 }, + { 0x37E4, 0x00 }, + { 0x37E5, 0x00 }, + { 0x37E6, 0x00 }, + { 0x37E8, 0x00 }, + { 0x37E9, 0x00 }, + { 0x37EA, 0x00 }, + { 0x37EC, 0x00 }, + { 0x37ED, 0x00 }, + { 0x37EE, 0x00 }, + { 0x37F0, 0x00 }, + { 0x37F4, 0x00 }, + { 0x37F5, 0x1E }, + { 0x37F6, 0x34 }, + { 0x37F7, 0x00 }, + { 0x37F8, 0xFF }, + { 0x37F9, 0xFF }, + { 0x37FA, 0x03 }, + { 0x37FC, 0x00 }, + { 0x37FD, 0x00 }, + { 0x37FE, 0x04 }, + { 0x3800, 0xFF }, + { 0x3801, 0xFF }, + { 0x3802, 0x03 }, + { 0x3804, 0x00 }, + { 0x3805, 0x00 }, + { 0x3806, 0x04 }, + { 0x3808, 0x00 }, + { 0x3809, 0x00 }, + { 0x380A, 0x00 }, + { 0x380C, 0x00 }, + { 0x380D, 0x00 }, + { 0x380E, 0x00 }, + { 0x3810, 0x00 }, + { 0x3811, 0x00 }, + { 0x3812, 0x00 }, + { 0x3814, 0x00 }, + { 0x3815, 0x00 }, + { 0x3816, 0x00 }, + { 0x3818, 0x00 }, + { 0x3819, 0x00 }, + { 0x381A, 0x00 }, + { 0x381C, 0x00 }, + { 0x381D, 0x00 }, + { 0x381E, 0x00 }, + { 0x3820, 0x00 }, + { 0x3821, 0x00 }, + { 0x3822, 0x00 }, + { 0x3824, 0x00 }, + { 0x3825, 0x00 }, + { 0x3826, 0x00 }, + { 0x3828, 0x00 }, + { 0x3829, 0x00 }, + { 0x382A, 0x00 }, + { 0x382C, 0x00 }, + { 0x382D, 0x00 }, + { 0x382E, 0x00 }, + { 0x3830, 0x00 }, + { 0x3831, 0x00 }, + { 0x3832, 0x00 }, + { 0x3834, 0x00 }, + { 0x3835, 0x00 }, + { 0x3836, 0x00 }, + { 0x3838, 0x47 }, + { 0x3839, 0x00 }, + { 0x383A, 0x34 }, + { 0x383B, 0x00 }, + { 0x383C, 0x48 }, + { 0x383D, 0x00 }, + { 0x383E, 0x39 }, + { 0x383F, 0x00 }, + { 0x3840, 0x13 }, + { 0x3841, 0x00 }, + { 0x3842, 0x13 }, + { 0x3843, 0x00 }, + { 0x3844, 0x1D }, + { 0x3845, 0x00 }, + { 0x3846, 0x1D }, + { 0x3847, 0x00 }, + { 0x3848, 0x08 }, + { 0x3849, 0x00 }, + { 0x384A, 0x07 }, + { 0x384B, 0x00 }, + { 0x384C, 0x05 }, + { 0x384D, 0x00 }, + { 0x384E, 0x00 }, + { 0x384F, 0x00 }, + { 0x3850, 0xFF }, + { 0x3851, 0x0F }, + { 0x3852, 0x00 }, + { 0x3853, 0x10 }, + { 0x3854, 0xFF }, + { 0x3855, 0x0F }, + { 0x3856, 0x00 }, + { 0x3857, 0x10 }, + { 0x3858, 0xFF }, + { 0x3859, 0x0F }, + { 0x385A, 0x00 }, + { 0x385B, 0x10 }, + { 0x385C, 0x02 }, + { 0x385D, 0x00 }, + { 0x385E, 0x06 }, + { 0x385F, 0x00 }, + { 0x3860, 0x06 }, + { 0x3861, 0x00 }, + { 0x3862, 0x08 }, + { 0x3863, 0x00 }, + { 0x3864, 0x02 }, + { 0x3865, 0x00 }, + { 0x3870, 0x00 }, + { 0x3871, 0x01 }, + { 0x38A0, 0x01 }, + { 0x38A1, 0x01 }, + { 0x38A2, 0x00 }, + { 0x38A3, 0x01 }, + { 0x38A4, 0x07 }, + { 0x38A5, 0x00 }, + { 0x38A6, 0x04 }, + { 0x38A7, 0x04 }, + { 0x38A8, 0x00 }, + { 0x38A9, 0x00 }, + { 0x38AC, 0x00 }, + { 0x38AD, 0x00 }, + { 0x38AE, 0x01 }, + { 0x38B0, 0x02 }, + { 0x38B2, 0x43 }, + { 0x38B3, 0x00 }, + { 0x38B4, 0x10 }, + { 0x38B5, 0x00 }, + { 0x38B6, 0x09 }, + { 0x38B7, 0x00 }, + { 0x38B8, 0x09 }, + { 0x38B9, 0x00 }, + { 0x38BA, 0x47 }, + { 0x38BB, 0x00 }, + { 0x38BC, 0x16 }, + { 0x38BD, 0x00 }, + { 0x38BE, 0x0E }, + { 0x38BF, 0x00 }, + { 0x38C0, 0x0B }, + { 0x38C1, 0x00 }, + { 0x38C2, 0x4A }, + { 0x38C3, 0x00 }, + { 0x38C4, 0x1C }, + { 0x38C5, 0x00 }, + { 0x38C6, 0x12 }, + { 0x38C7, 0x00 }, + { 0x38C8, 0x0D }, + { 0x38C9, 0x00 }, + { 0x38CA, 0x51 }, + { 0x38CB, 0x00 }, + { 0x38CC, 0x24 }, + { 0x38CD, 0x00 }, + { 0x38CE, 0x19 }, + { 0x38CF, 0x00 }, + { 0x38D0, 0x10 }, + { 0x38D1, 0x00 }, + { 0x38D2, 0x5D }, + { 0x38D3, 0x00 }, + { 0x38D4, 0x30 }, + { 0x38D5, 0x00 }, + { 0x38D6, 0x23 }, + { 0x38D7, 0x00 }, + { 0x38D8, 0x17 }, + { 0x38D9, 0x00 }, + { 0x38DA, 0x72 }, + { 0x38DB, 0x00 }, + { 0x38DC, 0x43 }, + { 0x38DD, 0x00 }, + { 0x38DE, 0x31 }, + { 0x38DF, 0x00 }, + { 0x38E0, 0x20 }, + { 0x38E1, 0x00 }, + { 0x38E2, 0x96 }, + { 0x38E3, 0x00 }, + { 0x38E4, 0x5E }, + { 0x38E5, 0x00 }, + { 0x38E6, 0x46 }, + { 0x38E7, 0x00 }, + { 0x38E8, 0x2E }, + { 0x38E9, 0x00 }, + { 0x38EA, 0xD4 }, + { 0x38EB, 0x00 }, + { 0x38EC, 0x87 }, + { 0x38ED, 0x00 }, + { 0x38EE, 0x65 }, + { 0x38EF, 0x00 }, + { 0x38F0, 0x43 }, + { 0x38F1, 0x00 }, + { 0x38F2, 0x3F }, + { 0x38F3, 0x01 }, + { 0x38F4, 0xC4 }, + { 0x38F5, 0x00 }, + { 0x38F6, 0x94 }, + { 0x38F7, 0x00 }, + { 0x38F8, 0x64 }, + { 0x38F9, 0x00 }, + { 0x38FA, 0x00 }, + { 0x38FB, 0x01 }, + { 0x38FC, 0x00 }, + { 0x38FD, 0x01 }, + { 0x38FE, 0x00 }, + { 0x38FF, 0x01 }, + { 0x3900, 0x00 }, + { 0x3901, 0x01 }, + { 0x3902, 0x60 }, + { 0x3903, 0x00 }, + { 0x3904, 0x25 }, + { 0x3905, 0x00 }, + { 0x3906, 0x18 }, + { 0x3907, 0x00 }, + { 0x3908, 0x10 }, + { 0x3909, 0x00 }, + { 0x390A, 0xE6 }, + { 0x390B, 0x00 }, + { 0x390C, 0xD5 }, + { 0x390D, 0x00 }, + { 0x390E, 0xAA }, + { 0x390F, 0x00 }, + { 0x3910, 0x85 }, + { 0x3911, 0x00 }, + { 0x3912, 0xE6 }, + { 0x3913, 0x00 }, + { 0x3914, 0xD5 }, + { 0x3915, 0x00 }, + { 0x3916, 0xAA }, + { 0x3917, 0x00 }, + { 0x3918, 0x85 }, + { 0x3919, 0x00 }, + { 0x391A, 0xE6 }, + { 0x391B, 0x00 }, + { 0x391C, 0xD5 }, + { 0x391D, 0x00 }, + { 0x391E, 0xAA }, + { 0x391F, 0x00 }, + { 0x3920, 0x85 }, + { 0x3921, 0x00 }, + { 0x3922, 0x40 }, + { 0x3923, 0x00 }, + { 0x3924, 0x40 }, + { 0x3925, 0x00 }, + { 0x3926, 0x40 }, + { 0x3927, 0x00 }, + { 0x3928, 0x40 }, + { 0x3929, 0x00 }, + { 0x392A, 0x80 }, + { 0x392B, 0x00 }, + { 0x392C, 0x80 }, + { 0x392D, 0x00 }, + { 0x392E, 0x80 }, + { 0x392F, 0x00 }, + { 0x3930, 0x80 }, + { 0x3931, 0x00 }, + { 0x3932, 0x4C }, + { 0x3933, 0x4C }, + { 0x3934, 0x4C }, + { 0x3940, 0x01 }, + { 0x3941, 0x01 }, + { 0x3942, 0x00 }, + { 0x3943, 0x01 }, + { 0x3944, 0x07 }, + { 0x3945, 0x00 }, + { 0x3946, 0x04 }, + { 0x3947, 0x04 }, + { 0x3948, 0x00 }, + { 0x3949, 0x00 }, + { 0x394C, 0x00 }, + { 0x394D, 0x00 }, + { 0x394E, 0x01 }, + { 0x3950, 0x03 }, + { 0x3952, 0x1B }, + { 0x3953, 0x00 }, + { 0x3954, 0x0C }, + { 0x3955, 0x00 }, + { 0x3956, 0x09 }, + { 0x3957, 0x00 }, + { 0x3958, 0x07 }, + { 0x3959, 0x00 }, + { 0x395A, 0x1D }, + { 0x395B, 0x00 }, + { 0x395C, 0x0E }, + { 0x395D, 0x00 }, + { 0x395E, 0x0B }, + { 0x395F, 0x00 }, + { 0x3960, 0x08 }, + { 0x3961, 0x00 }, + { 0x3962, 0x1E }, + { 0x3963, 0x00 }, + { 0x3964, 0x10 }, + { 0x3965, 0x00 }, + { 0x3966, 0x0C }, + { 0x3967, 0x00 }, + { 0x3968, 0x09 }, + { 0x3969, 0x00 }, + { 0x396A, 0x21 }, + { 0x396B, 0x00 }, + { 0x396C, 0x13 }, + { 0x396D, 0x00 }, + { 0x396E, 0x0E }, + { 0x396F, 0x00 }, + { 0x3970, 0x0A }, + { 0x3971, 0x00 }, + { 0x3972, 0x25 }, + { 0x3973, 0x00 }, + { 0x3974, 0x19 }, + { 0x3975, 0x00 }, + { 0x3976, 0x12 }, + { 0x3977, 0x00 }, + { 0x3978, 0x0D }, + { 0x3979, 0x00 }, + { 0x397A, 0x2E }, + { 0x397B, 0x00 }, + { 0x397C, 0x21 }, + { 0x397D, 0x00 }, + { 0x397E, 0x19 }, + { 0x397F, 0x00 }, + { 0x3980, 0x11 }, + { 0x3981, 0x00 }, + { 0x3982, 0x3C }, + { 0x3983, 0x00 }, + { 0x3984, 0x2F }, + { 0x3985, 0x00 }, + { 0x3986, 0x23 }, + { 0x3987, 0x00 }, + { 0x3988, 0x19 }, + { 0x3989, 0x00 }, + { 0x398A, 0x56 }, + { 0x398B, 0x00 }, + { 0x398C, 0x44 }, + { 0x398D, 0x00 }, + { 0x398E, 0x35 }, + { 0x398F, 0x00 }, + { 0x3990, 0x27 }, + { 0x3991, 0x00 }, + { 0x3992, 0x84 }, + { 0x3993, 0x00 }, + { 0x3994, 0x68 }, + { 0x3995, 0x00 }, + { 0x3996, 0x53 }, + { 0x3997, 0x00 }, + { 0x3998, 0x40 }, + { 0x3999, 0x00 }, + { 0x399A, 0x00 }, + { 0x399B, 0x01 }, + { 0x399C, 0x00 }, + { 0x399D, 0x01 }, + { 0x399E, 0x00 }, + { 0x399F, 0x01 }, + { 0x39A0, 0x00 }, + { 0x39A1, 0x01 }, + { 0x39A2, 0x60 }, + { 0x39A3, 0x00 }, + { 0x39A4, 0x20 }, + { 0x39A5, 0x00 }, + { 0x39A6, 0x15 }, + { 0x39A7, 0x00 }, + { 0x39A8, 0x10 }, + { 0x39A9, 0x00 }, + { 0x39AA, 0xE6 }, + { 0x39AB, 0x00 }, + { 0x39AC, 0xD5 }, + { 0x39AD, 0x00 }, + { 0x39AE, 0xAA }, + { 0x39AF, 0x00 }, + { 0x39B0, 0x85 }, + { 0x39B1, 0x00 }, + { 0x39B2, 0xE6 }, + { 0x39B3, 0x00 }, + { 0x39B4, 0xD5 }, + { 0x39B5, 0x00 }, + { 0x39B6, 0xAA }, + { 0x39B7, 0x00 }, + { 0x39B8, 0x85 }, + { 0x39B9, 0x00 }, + { 0x39BA, 0xE6 }, + { 0x39BB, 0x00 }, + { 0x39BC, 0xD5 }, + { 0x39BD, 0x00 }, + { 0x39BE, 0xAA }, + { 0x39BF, 0x00 }, + { 0x39C0, 0x85 }, + { 0x39C1, 0x00 }, + { 0x39C2, 0x40 }, + { 0x39C3, 0x00 }, + { 0x39C4, 0x40 }, + { 0x39C5, 0x00 }, + { 0x39C6, 0x40 }, + { 0x39C7, 0x00 }, + { 0x39C8, 0x40 }, + { 0x39C9, 0x00 }, + { 0x39CA, 0x80 }, + { 0x39CB, 0x00 }, + { 0x39CC, 0x80 }, + { 0x39CD, 0x00 }, + { 0x39CE, 0x80 }, + { 0x39CF, 0x00 }, + { 0x39D0, 0x80 }, + { 0x39D1, 0x00 }, + { 0x39D2, 0x4C }, + { 0x39D3, 0x4C }, + { 0x39D4, 0x4C }, + { 0x39E0, 0x01 }, + { 0x39E1, 0x00 }, + { 0x39E4, 0x40 }, + { 0x39E5, 0x01 }, + { 0x39E6, 0x01 }, + { 0x39E8, 0x00 }, + { 0x39E9, 0x01 }, + { 0x39EA, 0x00 }, + { 0x39EB, 0x00 }, + { 0x39EC, 0x01 }, + { 0x39ED, 0x00 }, + { 0x39EE, 0x01 }, + { 0x39F0, 0x03 }, + { 0x39F1, 0x04 }, + { 0x39F2, 0x0E }, + { 0x39F4, 0x19 }, + { 0x39F5, 0x00 }, + { 0x39F6, 0x12 }, + { 0x39F7, 0x00 }, + { 0x39F8, 0x0D }, + { 0x39F9, 0x00 }, + { 0x39FA, 0x07 }, + { 0x39FB, 0x00 }, + { 0x39FC, 0x2B }, + { 0x39FD, 0x00 }, + { 0x39FE, 0x1B }, + { 0x39FF, 0x00 }, + { 0x3A00, 0x11 }, + { 0x3A01, 0x00 }, + { 0x3A02, 0x08 }, + { 0x3A03, 0x00 }, + { 0x3A04, 0x37 }, + { 0x3A05, 0x00 }, + { 0x3A06, 0x21 }, + { 0x3A07, 0x00 }, + { 0x3A08, 0x14 }, + { 0x3A09, 0x00 }, + { 0x3A0A, 0x09 }, + { 0x3A0B, 0x00 }, + { 0x3A0C, 0x4A }, + { 0x3A0D, 0x00 }, + { 0x3A0E, 0x2C }, + { 0x3A0F, 0x00 }, + { 0x3A10, 0x18 }, + { 0x3A11, 0x00 }, + { 0x3A12, 0x0B }, + { 0x3A13, 0x00 }, + { 0x3A14, 0x66 }, + { 0x3A15, 0x00 }, + { 0x3A16, 0x3B }, + { 0x3A17, 0x00 }, + { 0x3A18, 0x20 }, + { 0x3A19, 0x00 }, + { 0x3A1A, 0x0F }, + { 0x3A1B, 0x00 }, + { 0x3A1C, 0x8E }, + { 0x3A1D, 0x00 }, + { 0x3A1E, 0x51 }, + { 0x3A1F, 0x00 }, + { 0x3A20, 0x2B }, + { 0x3A21, 0x00 }, + { 0x3A22, 0x14 }, + { 0x3A23, 0x00 }, + { 0x3A24, 0xC8 }, + { 0x3A25, 0x00 }, + { 0x3A26, 0x72 }, + { 0x3A27, 0x00 }, + { 0x3A28, 0x3C }, + { 0x3A29, 0x00 }, + { 0x3A2A, 0x1B }, + { 0x3A2B, 0x00 }, + { 0x3A2C, 0x19 }, + { 0x3A2D, 0x01 }, + { 0x3A2E, 0xA0 }, + { 0x3A2F, 0x00 }, + { 0x3A30, 0x54 }, + { 0x3A31, 0x00 }, + { 0x3A32, 0x25 }, + { 0x3A33, 0x00 }, + { 0x3A34, 0x8D }, + { 0x3A35, 0x01 }, + { 0x3A36, 0xE1 }, + { 0x3A37, 0x00 }, + { 0x3A38, 0x76 }, + { 0x3A39, 0x00 }, + { 0x3A3A, 0x35 }, + { 0x3A3B, 0x00 }, + { 0x3A3C, 0x00 }, + { 0x3A3D, 0x01 }, + { 0x3A3E, 0x00 }, + { 0x3A3F, 0x01 }, + { 0x3A40, 0x00 }, + { 0x3A41, 0x01 }, + { 0x3A42, 0x00 }, + { 0x3A43, 0x01 }, + { 0x3A44, 0x70 }, + { 0x3A45, 0x00 }, + { 0x3A46, 0x25 }, + { 0x3A47, 0x00 }, + { 0x3A48, 0x18 }, + { 0x3A49, 0x00 }, + { 0x3A4A, 0x10 }, + { 0x3A4B, 0x00 }, + { 0x3A4C, 0xE6 }, + { 0x3A4D, 0x00 }, + { 0x3A4E, 0xD5 }, + { 0x3A4F, 0x00 }, + { 0x3A50, 0xAA }, + { 0x3A51, 0x00 }, + { 0x3A52, 0x85 }, + { 0x3A53, 0x00 }, + { 0x3A54, 0xE6 }, + { 0x3A55, 0x00 }, + { 0x3A56, 0xD5 }, + { 0x3A57, 0x00 }, + { 0x3A58, 0xAA }, + { 0x3A59, 0x00 }, + { 0x3A5A, 0x85 }, + { 0x3A5B, 0x00 }, + { 0x3A5C, 0xE6 }, + { 0x3A5D, 0x00 }, + { 0x3A5E, 0xD5 }, + { 0x3A5F, 0x00 }, + { 0x3A60, 0xAA }, + { 0x3A61, 0x00 }, + { 0x3A62, 0x85 }, + { 0x3A63, 0x00 }, + { 0x3A64, 0x19 }, + { 0x3A65, 0x00 }, + { 0x3A66, 0x12 }, + { 0x3A67, 0x00 }, + { 0x3A68, 0x0D }, + { 0x3A69, 0x00 }, + { 0x3A6A, 0x07 }, + { 0x3A6B, 0x00 }, + { 0x3A6C, 0x0C }, + { 0x3A6D, 0x00 }, + { 0x3A6E, 0x07 }, + { 0x3A6F, 0x00 }, + { 0x3A70, 0x05 }, + { 0x3A71, 0x00 }, + { 0x3A72, 0x04 }, + { 0x3A73, 0x00 }, + { 0x3A74, 0x1B }, + { 0x3A75, 0x00 }, + { 0x3A76, 0x15 }, + { 0x3A77, 0x00 }, + { 0x3A78, 0x0C }, + { 0x3A79, 0x00 }, + { 0x3A7A, 0x08 }, + { 0x3A7B, 0x00 }, + { 0x3A7C, 0x80 }, + { 0x3A7D, 0x00 }, + { 0x3A7E, 0x80 }, + { 0x3A7F, 0x00 }, + { 0x3A80, 0x80 }, + { 0x3A81, 0x00 }, + { 0x3A82, 0x80 }, + { 0x3A83, 0x00 }, + { 0x3A84, 0x09 }, + { 0x3A85, 0x00 }, + { 0x3A86, 0x06 }, + { 0x3A87, 0x00 }, + { 0x3A88, 0x04 }, + { 0x3A89, 0x00 }, + { 0x3A8A, 0x03 }, + { 0x3A8B, 0x00 }, + { 0x3A8C, 0xFA }, + { 0x3A8D, 0x00 }, + { 0x3A8E, 0xC8 }, + { 0x3A8F, 0x00 }, + { 0x3A90, 0x96 }, + { 0x3A91, 0x00 }, + { 0x3A92, 0x64 }, + { 0x3A93, 0x00 }, + { 0x3A94, 0xE1 }, + { 0x3A95, 0x00 }, + { 0x3A96, 0xC8 }, + { 0x3A97, 0x00 }, + { 0x3A98, 0x96 }, + { 0x3A99, 0x00 }, + { 0x3A9A, 0x64 }, + { 0x3A9B, 0x00 }, + { 0x3A9C, 0x08 }, + { 0x3A9D, 0x10 }, + { 0x3A9E, 0x4C }, + { 0x3A9F, 0x4C }, + { 0x3AA0, 0x4C }, + { 0x3AA1, 0x04 }, + { 0x3AA2, 0x04 }, + { 0x3AC0, 0x01 }, + { 0x3AC4, 0x81 }, + { 0x3AC5, 0x00 }, + { 0x3AC6, 0x00 }, + { 0x3AC7, 0x00 }, + { 0x3AC8, 0x00 }, + { 0x3AC9, 0x00 }, + { 0x3ACA, 0x00 }, + { 0x3ACB, 0x00 }, + { 0x3ACC, 0x02 }, + { 0x3ACD, 0x00 }, + { 0x3ACE, 0x81 }, + { 0x3ACF, 0x00 }, + { 0x3AD0, 0x00 }, + { 0x3AD1, 0x00 }, + { 0x3AD2, 0xFD }, + { 0x3AD3, 0x03 }, + { 0x3AD4, 0x02 }, + { 0x3AD5, 0x00 }, + { 0x3AD6, 0x00 }, + { 0x3AD7, 0x00 }, + { 0x3AD8, 0x81 }, + { 0x3AD9, 0x00 }, + { 0x3ADA, 0xFD }, + { 0x3ADB, 0x03 }, + { 0x3ADC, 0xFF }, + { 0x3ADD, 0x03 }, + { 0x3ADE, 0x01 }, + { 0x3ADF, 0x00 }, + { 0x3AE0, 0x01 }, + { 0x3AE1, 0x00 }, + { 0x3AE2, 0x7E }, + { 0x3AE3, 0x00 }, + { 0x3AF4, 0x00 }, + { 0x3AF6, 0x40 }, + { 0x3AF7, 0x1E }, + { 0x3AF8, 0x00 }, + { 0x3AFA, 0x00 }, + { 0x3AFB, 0x00 }, + { 0x3AFC, 0x00 }, + { 0x3AFD, 0x00 }, + { 0x3AFE, 0x00 }, + { 0x3AFF, 0x00 }, + { 0x3B00, 0x00 }, + { 0x3B01, 0x00 }, + { 0x3B02, 0x00 }, + { 0x3B03, 0x00 }, + { 0x3B04, 0x00 }, + { 0x3B05, 0x00 }, + { 0x3B06, 0x00 }, + { 0x3B07, 0x00 }, + { 0x3B08, 0x00 }, + { 0x3B09, 0x00 }, + { 0x3B0A, 0x00 }, + { 0x3B0B, 0x00 }, + { 0x3B0C, 0x00 }, + { 0x3B0D, 0x00 }, + { 0x3B0E, 0x00 }, + { 0x3B0F, 0x00 }, + { 0x3B10, 0x00 }, + { 0x3B11, 0x00 }, + { 0x3B12, 0x00 }, + { 0x3B13, 0x00 }, + { 0x3B14, 0x00 }, + { 0x3B15, 0x00 }, + { 0x3B16, 0x00 }, + { 0x3B17, 0x00 }, + { 0x3B18, 0x00 }, + { 0x3B19, 0x00 }, + { 0x3B1A, 0x00 }, + { 0x3B1B, 0x00 }, + { 0x3B1C, 0x00 }, + { 0x3B1D, 0x00 }, + { 0x3B1E, 0x00 }, + { 0x3B1F, 0x00 }, + { 0x3B20, 0x00 }, + { 0x3B21, 0x00 }, + { 0x3B22, 0x00 }, + { 0x3B23, 0x00 }, + { 0x3B24, 0x00 }, + { 0x3B25, 0x00 }, + { 0x3B26, 0x00 }, + { 0x3B27, 0x00 }, + { 0x3B28, 0x00 }, + { 0x3B29, 0x00 }, + { 0x3B2A, 0x00 }, + { 0x3B2C, 0x00 }, + { 0x3B2E, 0x00 }, + { 0x3B30, 0x00 }, + { 0x3B32, 0x0C }, + { 0x4000, 0xAF }, + { 0x4001, 0xA7 }, + { 0x4002, 0xA8 }, + { 0x4003, 0xA5 }, + { 0x4004, 0x98 }, + { 0x4005, 0x93 }, + { 0x4006, 0x94 }, + { 0x4007, 0x93 }, + { 0x4008, 0x8E }, + { 0x4009, 0x8C }, + { 0x400A, 0x8C }, + { 0x400B, 0x8C }, + { 0x400C, 0x89 }, + { 0x400D, 0x88 }, + { 0x400E, 0x89 }, + { 0x400F, 0x89 }, + { 0x4010, 0x87 }, + { 0x4011, 0x87 }, + { 0x4012, 0x87 }, + { 0x4013, 0x86 }, + { 0x4014, 0x88 }, + { 0x4015, 0x87 }, + { 0x4016, 0x87 }, + { 0x4017, 0x87 }, + { 0x4018, 0x8B }, + { 0x4019, 0x89 }, + { 0x401A, 0x89 }, + { 0x401B, 0x8A }, + { 0x401C, 0x92 }, + { 0x401D, 0x8F }, + { 0x401E, 0x8F }, + { 0x401F, 0x8F }, + { 0x4020, 0xA2 }, + { 0x4021, 0x9C }, + { 0x4022, 0x9B }, + { 0x4023, 0x9C }, + { 0x4024, 0xA1 }, + { 0x4025, 0x9A }, + { 0x4026, 0x9B }, + { 0x4027, 0x99 }, + { 0x4028, 0x94 }, + { 0x4029, 0x90 }, + { 0x402A, 0x90 }, + { 0x402B, 0x90 }, + { 0x402C, 0x8B }, + { 0x402D, 0x89 }, + { 0x402E, 0x89 }, + { 0x402F, 0x89 }, + { 0x4030, 0x86 }, + { 0x4031, 0x85 }, + { 0x4032, 0x86 }, + { 0x4033, 0x85 }, + { 0x4034, 0x84 }, + { 0x4035, 0x84 }, + { 0x4036, 0x84 }, + { 0x4037, 0x84 }, + { 0x4038, 0x85 }, + { 0x4039, 0x85 }, + { 0x403A, 0x85 }, + { 0x403B, 0x85 }, + { 0x403C, 0x88 }, + { 0x403D, 0x87 }, + { 0x403E, 0x87 }, + { 0x403F, 0x87 }, + { 0x4040, 0x8E }, + { 0x4041, 0x8C }, + { 0x4042, 0x8C }, + { 0x4043, 0x8C }, + { 0x4044, 0x98 }, + { 0x4045, 0x93 }, + { 0x4046, 0x93 }, + { 0x4047, 0x94 }, + { 0x4048, 0x9D }, + { 0x4049, 0x96 }, + { 0x404A, 0x97 }, + { 0x404B, 0x96 }, + { 0x404C, 0x91 }, + { 0x404D, 0x8C }, + { 0x404E, 0x8D }, + { 0x404F, 0x8C }, + { 0x4050, 0x89 }, + { 0x4051, 0x86 }, + { 0x4052, 0x87 }, + { 0x4053, 0x86 }, + { 0x4054, 0x83 }, + { 0x4055, 0x82 }, + { 0x4056, 0x82 }, + { 0x4057, 0x82 }, + { 0x4058, 0x80 }, + { 0x4059, 0x80 }, + { 0x405A, 0x80 }, + { 0x405B, 0x80 }, + { 0x405C, 0x82 }, + { 0x405D, 0x82 }, + { 0x405E, 0x82 }, + { 0x405F, 0x82 }, + { 0x4060, 0x86 }, + { 0x4061, 0x85 }, + { 0x4062, 0x85 }, + { 0x4063, 0x85 }, + { 0x4064, 0x8B }, + { 0x4065, 0x8A }, + { 0x4066, 0x89 }, + { 0x4067, 0x89 }, + { 0x4068, 0x94 }, + { 0x4069, 0x91 }, + { 0x406A, 0x90 }, + { 0x406B, 0x91 }, + { 0x406C, 0x9E }, + { 0x406D, 0x95 }, + { 0x406E, 0x96 }, + { 0x406F, 0x95 }, + { 0x4070, 0x91 }, + { 0x4071, 0x8C }, + { 0x4072, 0x8C }, + { 0x4073, 0x8C }, + { 0x4074, 0x89 }, + { 0x4075, 0x86 }, + { 0x4076, 0x86 }, + { 0x4077, 0x86 }, + { 0x4078, 0x83 }, + { 0x4079, 0x82 }, + { 0x407A, 0x82 }, + { 0x407B, 0x82 }, + { 0x407C, 0x80 }, + { 0x407D, 0x80 }, + { 0x407E, 0x80 }, + { 0x407F, 0x80 }, + { 0x4080, 0x82 }, + { 0x4081, 0x81 }, + { 0x4082, 0x81 }, + { 0x4083, 0x81 }, + { 0x4084, 0x85 }, + { 0x4085, 0x85 }, + { 0x4086, 0x85 }, + { 0x4087, 0x84 }, + { 0x4088, 0x8B }, + { 0x4089, 0x8A }, + { 0x408A, 0x89 }, + { 0x408B, 0x89 }, + { 0x408C, 0x93 }, + { 0x408D, 0x90 }, + { 0x408E, 0x8F }, + { 0x408F, 0x8F }, + { 0x4090, 0xA3 }, + { 0x4091, 0x99 }, + { 0x4092, 0x9A }, + { 0x4093, 0x99 }, + { 0x4094, 0x95 }, + { 0x4095, 0x8F }, + { 0x4096, 0x8F }, + { 0x4097, 0x8F }, + { 0x4098, 0x8B }, + { 0x4099, 0x87 }, + { 0x409A, 0x87 }, + { 0x409B, 0x87 }, + { 0x409C, 0x86 }, + { 0x409D, 0x84 }, + { 0x409E, 0x84 }, + { 0x409F, 0x84 }, + { 0x40A0, 0x84 }, + { 0x40A1, 0x83 }, + { 0x40A2, 0x83 }, + { 0x40A3, 0x82 }, + { 0x40A4, 0x84 }, + { 0x40A5, 0x84 }, + { 0x40A6, 0x83 }, + { 0x40A7, 0x83 }, + { 0x40A8, 0x88 }, + { 0x40A9, 0x87 }, + { 0x40AA, 0x86 }, + { 0x40AB, 0x86 }, + { 0x40AC, 0x8E }, + { 0x40AD, 0x8C }, + { 0x40AE, 0x8C }, + { 0x40AF, 0x8B }, + { 0x40B0, 0x9A }, + { 0x40B1, 0x96 }, + { 0x40B2, 0x96 }, + { 0x40B3, 0x95 }, + { 0x40B4, 0xBA }, + { 0x40B5, 0xAC }, + { 0x40B6, 0xAD }, + { 0x40B7, 0xAC }, + { 0x40B8, 0x99 }, + { 0x40B9, 0x90 }, + { 0x40BA, 0x91 }, + { 0x40BB, 0x90 }, + { 0x40BC, 0x90 }, + { 0x40BD, 0x8A }, + { 0x40BE, 0x8A }, + { 0x40BF, 0x8A }, + { 0x40C0, 0x89 }, + { 0x40C1, 0x86 }, + { 0x40C2, 0x86 }, + { 0x40C3, 0x87 }, + { 0x40C4, 0x87 }, + { 0x40C5, 0x85 }, + { 0x40C6, 0x85 }, + { 0x40C7, 0x85 }, + { 0x40C8, 0x87 }, + { 0x40C9, 0x86 }, + { 0x40CA, 0x85 }, + { 0x40CB, 0x85 }, + { 0x40CC, 0x8A }, + { 0x40CD, 0x88 }, + { 0x40CE, 0x88 }, + { 0x40CF, 0x87 }, + { 0x40D0, 0x92 }, + { 0x40D1, 0x8F }, + { 0x40D2, 0x8E }, + { 0x40D3, 0x8E }, + { 0x40D4, 0xA2 }, + { 0x40D5, 0x9D }, + { 0x40D6, 0x9D }, + { 0x40D7, 0x9B }, + { 0x4100, 0x80 }, + { 0x4101, 0x80 }, + { 0x4102, 0x80 }, + { 0x4103, 0x80 }, + { 0x4104, 0x80 }, + { 0x4105, 0x80 }, + { 0x4106, 0x80 }, + { 0x4107, 0x80 }, + { 0x4108, 0x80 }, + { 0x4109, 0x80 }, + { 0x410A, 0x80 }, + { 0x410B, 0x80 }, + { 0x410C, 0x80 }, + { 0x410D, 0x80 }, + { 0x410E, 0x80 }, + { 0x410F, 0x80 }, + { 0x4110, 0x80 }, + { 0x4111, 0x80 }, + { 0x4112, 0x80 }, + { 0x4113, 0x80 }, + { 0x4114, 0x80 }, + { 0x4115, 0x80 }, + { 0x4116, 0x80 }, + { 0x4117, 0x80 }, + { 0x4118, 0x80 }, + { 0x4119, 0x80 }, + { 0x411A, 0x80 }, + { 0x411B, 0x80 }, + { 0x411C, 0x80 }, + { 0x411D, 0x80 }, + { 0x411E, 0x80 }, + { 0x411F, 0x80 }, + { 0x4120, 0x80 }, + { 0x4121, 0x80 }, + { 0x4122, 0x80 }, + { 0x4123, 0x80 }, + { 0x4124, 0x80 }, + { 0x4125, 0x80 }, + { 0x4126, 0x80 }, + { 0x4127, 0x80 }, + { 0x4128, 0x80 }, + { 0x4129, 0x80 }, + { 0x412A, 0x80 }, + { 0x412B, 0x80 }, + { 0x412C, 0x80 }, + { 0x412D, 0x80 }, + { 0x412E, 0x80 }, + { 0x412F, 0x80 }, + { 0x4130, 0x80 }, + { 0x4131, 0x80 }, + { 0x4132, 0x80 }, + { 0x4133, 0x80 }, + { 0x4134, 0x80 }, + { 0x4135, 0x80 }, + { 0x4136, 0x80 }, + { 0x4137, 0x80 }, + { 0x4138, 0x80 }, + { 0x4139, 0x80 }, + { 0x413A, 0x80 }, + { 0x413B, 0x80 }, + { 0x413C, 0x80 }, + { 0x413D, 0x80 }, + { 0x413E, 0x80 }, + { 0x413F, 0x80 }, + { 0x4140, 0x80 }, + { 0x4141, 0x80 }, + { 0x4142, 0x80 }, + { 0x4143, 0x80 }, + { 0x4144, 0x80 }, + { 0x4145, 0x80 }, + { 0x4146, 0x80 }, + { 0x4147, 0x80 }, + { 0x4148, 0x80 }, + { 0x4149, 0x80 }, + { 0x414A, 0x80 }, + { 0x414B, 0x80 }, + { 0x414C, 0x80 }, + { 0x414D, 0x80 }, + { 0x414E, 0x80 }, + { 0x414F, 0x80 }, + { 0x4150, 0x80 }, + { 0x4151, 0x80 }, + { 0x4152, 0x80 }, + { 0x4153, 0x80 }, + { 0x4154, 0x80 }, + { 0x4155, 0x80 }, + { 0x4156, 0x80 }, + { 0x4157, 0x80 }, + { 0x4158, 0x80 }, + { 0x4159, 0x80 }, + { 0x415A, 0x80 }, + { 0x415B, 0x80 }, + { 0x415C, 0x80 }, + { 0x415D, 0x80 }, + { 0x415E, 0x80 }, + { 0x415F, 0x80 }, + { 0x4160, 0x80 }, + { 0x4161, 0x80 }, + { 0x4162, 0x80 }, + { 0x4163, 0x80 }, + { 0x4164, 0x80 }, + { 0x4165, 0x80 }, + { 0x4166, 0x80 }, + { 0x4167, 0x80 }, + { 0x4168, 0x80 }, + { 0x4169, 0x80 }, + { 0x416A, 0x80 }, + { 0x416B, 0x80 }, + { 0x416C, 0x80 }, + { 0x416D, 0x80 }, + { 0x416E, 0x80 }, + { 0x416F, 0x80 }, + { 0x4170, 0x80 }, + { 0x4171, 0x80 }, + { 0x4172, 0x80 }, + { 0x4173, 0x80 }, + { 0x4174, 0x80 }, + { 0x4175, 0x80 }, + { 0x4176, 0x80 }, + { 0x4177, 0x80 }, + { 0x4178, 0x80 }, + { 0x4179, 0x80 }, + { 0x417A, 0x80 }, + { 0x417B, 0x80 }, + { 0x417C, 0x80 }, + { 0x417D, 0x80 }, + { 0x417E, 0x80 }, + { 0x417F, 0x80 }, + { 0x4180, 0x80 }, + { 0x4181, 0x80 }, + { 0x4182, 0x80 }, + { 0x4183, 0x80 }, + { 0x4184, 0x80 }, + { 0x4185, 0x80 }, + { 0x4186, 0x80 }, + { 0x4187, 0x80 }, + { 0x4188, 0x80 }, + { 0x4189, 0x80 }, + { 0x418A, 0x80 }, + { 0x418B, 0x80 }, + { 0x418C, 0x80 }, + { 0x418D, 0x80 }, + { 0x418E, 0x80 }, + { 0x418F, 0x80 }, + { 0x4190, 0x80 }, + { 0x4191, 0x80 }, + { 0x4192, 0x80 }, + { 0x4193, 0x80 }, + { 0x4194, 0x80 }, + { 0x4195, 0x80 }, + { 0x4196, 0x80 }, + { 0x4197, 0x80 }, + { 0x4198, 0x80 }, + { 0x4199, 0x80 }, + { 0x419A, 0x80 }, + { 0x419B, 0x80 }, + { 0x419C, 0x80 }, + { 0x419D, 0x80 }, + { 0x419E, 0x80 }, + { 0x419F, 0x80 }, + { 0x41A0, 0x80 }, + { 0x41A1, 0x80 }, + { 0x41A2, 0x80 }, + { 0x41A3, 0x80 }, + { 0x41A4, 0x80 }, + { 0x41A5, 0x80 }, + { 0x41A6, 0x80 }, + { 0x41A7, 0x80 }, + { 0x41A8, 0x80 }, + { 0x41A9, 0x80 }, + { 0x41AA, 0x80 }, + { 0x41AB, 0x80 }, + { 0x41AC, 0x80 }, + { 0x41AD, 0x80 }, + { 0x41AE, 0x80 }, + { 0x41AF, 0x80 }, + { 0x41B0, 0x80 }, + { 0x41B1, 0x80 }, + { 0x41B2, 0x80 }, + { 0x41B3, 0x80 }, + { 0x41B4, 0x80 }, + { 0x41B5, 0x80 }, + { 0x41B6, 0x80 }, + { 0x41B7, 0x80 }, + { 0x41B8, 0x80 }, + { 0x41B9, 0x80 }, + { 0x41BA, 0x80 }, + { 0x41BB, 0x80 }, + { 0x41BC, 0x80 }, + { 0x41BD, 0x80 }, + { 0x41BE, 0x80 }, + { 0x41BF, 0x80 }, + { 0x41C0, 0x80 }, + { 0x41C1, 0x80 }, + { 0x41C2, 0x80 }, + { 0x41C3, 0x80 }, + { 0x41C4, 0x80 }, + { 0x41C5, 0x80 }, + { 0x41C6, 0x80 }, + { 0x41C7, 0x80 }, + { 0x41C8, 0x80 }, + { 0x41C9, 0x80 }, + { 0x41CA, 0x80 }, + { 0x41CB, 0x80 }, + { 0x41CC, 0x80 }, + { 0x41CD, 0x80 }, + { 0x41CE, 0x80 }, + { 0x41CF, 0x80 }, + { 0x41D0, 0x80 }, + { 0x41D1, 0x80 }, + { 0x41D2, 0x80 }, + { 0x41D3, 0x80 }, + { 0x41D4, 0x80 }, + { 0x41D5, 0x80 }, + { 0x41D6, 0x80 }, + { 0x41D7, 0x80 }, + { 0x4200, 0x80 }, + { 0x4201, 0x80 }, + { 0x4202, 0x80 }, + { 0x4203, 0x80 }, + { 0x4204, 0x80 }, + { 0x4205, 0x80 }, + { 0x4206, 0x80 }, + { 0x4207, 0x80 }, + { 0x4208, 0x80 }, + { 0x4209, 0x80 }, + { 0x420A, 0x80 }, + { 0x420B, 0x80 }, + { 0x420C, 0x80 }, + { 0x420D, 0x80 }, + { 0x420E, 0x80 }, + { 0x420F, 0x80 }, + { 0x4210, 0x80 }, + { 0x4211, 0x80 }, + { 0x4212, 0x80 }, + { 0x4213, 0x80 }, + { 0x4214, 0x80 }, + { 0x4215, 0x80 }, + { 0x4216, 0x80 }, + { 0x4217, 0x80 }, + { 0x4218, 0x80 }, + { 0x4219, 0x80 }, + { 0x421A, 0x80 }, + { 0x421B, 0x80 }, + { 0x421C, 0x80 }, + { 0x421D, 0x80 }, + { 0x421E, 0x80 }, + { 0x421F, 0x80 }, + { 0x4220, 0x80 }, + { 0x4221, 0x80 }, + { 0x4222, 0x80 }, + { 0x4223, 0x80 }, + { 0x4224, 0x80 }, + { 0x4225, 0x80 }, + { 0x4226, 0x80 }, + { 0x4227, 0x80 }, + { 0x4228, 0x80 }, + { 0x4229, 0x80 }, + { 0x422A, 0x80 }, + { 0x422B, 0x80 }, + { 0x422C, 0x80 }, + { 0x422D, 0x80 }, + { 0x422E, 0x80 }, + { 0x422F, 0x80 }, + { 0x4230, 0x80 }, + { 0x4231, 0x80 }, + { 0x4232, 0x80 }, + { 0x4233, 0x80 }, + { 0x4234, 0x80 }, + { 0x4235, 0x80 }, + { 0x4236, 0x80 }, + { 0x4237, 0x80 }, + { 0x4238, 0x80 }, + { 0x4239, 0x80 }, + { 0x423A, 0x80 }, + { 0x423B, 0x80 }, + { 0x423C, 0x80 }, + { 0x423D, 0x80 }, + { 0x423E, 0x80 }, + { 0x423F, 0x80 }, + { 0x4240, 0x80 }, + { 0x4241, 0x80 }, + { 0x4242, 0x80 }, + { 0x4243, 0x80 }, + { 0x4244, 0x80 }, + { 0x4245, 0x80 }, + { 0x4246, 0x80 }, + { 0x4247, 0x80 }, + { 0x4248, 0x80 }, + { 0x4249, 0x80 }, + { 0x424A, 0x80 }, + { 0x424B, 0x80 }, + { 0x424C, 0x80 }, + { 0x424D, 0x80 }, + { 0x424E, 0x80 }, + { 0x424F, 0x80 }, + { 0x4250, 0x80 }, + { 0x4251, 0x80 }, + { 0x4252, 0x80 }, + { 0x4253, 0x80 }, + { 0x4254, 0x80 }, + { 0x4255, 0x80 }, + { 0x4256, 0x80 }, + { 0x4257, 0x80 }, + { 0x4258, 0x80 }, + { 0x4259, 0x80 }, + { 0x425A, 0x80 }, + { 0x425B, 0x80 }, + { 0x425C, 0x80 }, + { 0x425D, 0x80 }, + { 0x425E, 0x80 }, + { 0x425F, 0x80 }, + { 0x4260, 0x80 }, + { 0x4261, 0x80 }, + { 0x4262, 0x80 }, + { 0x4263, 0x80 }, + { 0x4264, 0x80 }, + { 0x4265, 0x80 }, + { 0x4266, 0x80 }, + { 0x4267, 0x80 }, + { 0x4268, 0x80 }, + { 0x4269, 0x80 }, + { 0x426A, 0x80 }, + { 0x426B, 0x80 }, + { 0x426C, 0x80 }, + { 0x426D, 0x80 }, + { 0x426E, 0x80 }, + { 0x426F, 0x80 }, + { 0x4270, 0x80 }, + { 0x4271, 0x80 }, + { 0x4272, 0x80 }, + { 0x4273, 0x80 }, + { 0x4274, 0x80 }, + { 0x4275, 0x80 }, + { 0x4276, 0x80 }, + { 0x4277, 0x80 }, + { 0x4278, 0x80 }, + { 0x4279, 0x80 }, + { 0x427A, 0x80 }, + { 0x427B, 0x80 }, + { 0x427C, 0x80 }, + { 0x427D, 0x80 }, + { 0x427E, 0x80 }, + { 0x427F, 0x80 }, + { 0x4280, 0x80 }, + { 0x4281, 0x80 }, + { 0x4282, 0x80 }, + { 0x4283, 0x80 }, + { 0x4284, 0x80 }, + { 0x4285, 0x80 }, + { 0x4286, 0x80 }, + { 0x4287, 0x80 }, + { 0x4288, 0x80 }, + { 0x4289, 0x80 }, + { 0x428A, 0x80 }, + { 0x428B, 0x80 }, + { 0x428C, 0x80 }, + { 0x428D, 0x80 }, + { 0x428E, 0x80 }, + { 0x428F, 0x80 }, + { 0x4290, 0x80 }, + { 0x4291, 0x80 }, + { 0x4292, 0x80 }, + { 0x4293, 0x80 }, + { 0x4294, 0x80 }, + { 0x4295, 0x80 }, + { 0x4296, 0x80 }, + { 0x4297, 0x80 }, + { 0x4298, 0x80 }, + { 0x4299, 0x80 }, + { 0x429A, 0x80 }, + { 0x429B, 0x80 }, + { 0x429C, 0x80 }, + { 0x429D, 0x80 }, + { 0x429E, 0x80 }, + { 0x429F, 0x80 }, + { 0x42A0, 0x80 }, + { 0x42A1, 0x80 }, + { 0x42A2, 0x80 }, + { 0x42A3, 0x80 }, + { 0x42A4, 0x80 }, + { 0x42A5, 0x80 }, + { 0x42A6, 0x80 }, + { 0x42A7, 0x80 }, + { 0x42A8, 0x80 }, + { 0x42A9, 0x80 }, + { 0x42AA, 0x80 }, + { 0x42AB, 0x80 }, + { 0x42AC, 0x80 }, + { 0x42AD, 0x80 }, + { 0x42AE, 0x80 }, + { 0x42AF, 0x80 }, + { 0x42B0, 0x80 }, + { 0x42B1, 0x80 }, + { 0x42B2, 0x80 }, + { 0x42B3, 0x80 }, + { 0x42B4, 0x80 }, + { 0x42B5, 0x80 }, + { 0x42B6, 0x80 }, + { 0x42B7, 0x80 }, + { 0x42B8, 0x80 }, + { 0x42B9, 0x80 }, + { 0x42BA, 0x80 }, + { 0x42BB, 0x80 }, + { 0x42BC, 0x80 }, + { 0x42BD, 0x80 }, + { 0x42BE, 0x80 }, + { 0x42BF, 0x80 }, + { 0x42C0, 0x80 }, + { 0x42C1, 0x80 }, + { 0x42C2, 0x80 }, + { 0x42C3, 0x80 }, + { 0x42C4, 0x80 }, + { 0x42C5, 0x80 }, + { 0x42C6, 0x80 }, + { 0x42C7, 0x80 }, + { 0x42C8, 0x80 }, + { 0x42C9, 0x80 }, + { 0x42CA, 0x80 }, + { 0x42CB, 0x80 }, + { 0x42CC, 0x80 }, + { 0x42CD, 0x80 }, + { 0x42CE, 0x80 }, + { 0x42CF, 0x80 }, + { 0x42D0, 0x80 }, + { 0x42D1, 0x80 }, + { 0x42D2, 0x80 }, + { 0x42D3, 0x80 }, + { 0x42D4, 0x80 }, + { 0x42D5, 0x80 }, + { 0x42D6, 0x80 }, + { 0x42D7, 0x80 }, + { 0x42D8, 0x00 }, + { 0x42D9, 0x00 }, + { 0x4300, 0xA2 }, + { 0x4301, 0xAA }, + { 0x4302, 0xA7 }, + { 0x4303, 0xAD }, + { 0x4304, 0x8E }, + { 0x4305, 0x92 }, + { 0x4306, 0x90 }, + { 0x4307, 0x93 }, + { 0x4308, 0x86 }, + { 0x4309, 0x89 }, + { 0x430A, 0x87 }, + { 0x430B, 0x88 }, + { 0x430C, 0x82 }, + { 0x430D, 0x84 }, + { 0x430E, 0x83 }, + { 0x430F, 0x84 }, + { 0x4310, 0x80 }, + { 0x4311, 0x82 }, + { 0x4312, 0x82 }, + { 0x4313, 0x82 }, + { 0x4314, 0x83 }, + { 0x4315, 0x85 }, + { 0x4316, 0x84 }, + { 0x4317, 0x85 }, + { 0x4318, 0x8D }, + { 0x4319, 0x8D }, + { 0x431A, 0x8D }, + { 0x431B, 0x8D }, + { 0x431C, 0x99 }, + { 0x431D, 0x9A }, + { 0x431E, 0x9A }, + { 0x431F, 0x9A }, + { 0x4320, 0xAE }, + { 0x4321, 0xB4 }, + { 0x4322, 0xB4 }, + { 0x4323, 0xB5 }, + { 0x4324, 0x9A }, + { 0x4325, 0x9D }, + { 0x4326, 0x9B }, + { 0x4327, 0x9E }, + { 0x4328, 0x8C }, + { 0x4329, 0x8F }, + { 0x432A, 0x8D }, + { 0x432B, 0x8F }, + { 0x432C, 0x83 }, + { 0x432D, 0x85 }, + { 0x432E, 0x85 }, + { 0x432F, 0x85 }, + { 0x4330, 0x80 }, + { 0x4331, 0x81 }, + { 0x4332, 0x81 }, + { 0x4333, 0x81 }, + { 0x4334, 0x80 }, + { 0x4335, 0x80 }, + { 0x4336, 0x80 }, + { 0x4337, 0x81 }, + { 0x4338, 0x83 }, + { 0x4339, 0x83 }, + { 0x433A, 0x83 }, + { 0x433B, 0x83 }, + { 0x433C, 0x88 }, + { 0x433D, 0x88 }, + { 0x433E, 0x88 }, + { 0x433F, 0x88 }, + { 0x4340, 0x93 }, + { 0x4341, 0x93 }, + { 0x4342, 0x93 }, + { 0x4343, 0x93 }, + { 0x4344, 0xA2 }, + { 0x4345, 0xA4 }, + { 0x4346, 0xA4 }, + { 0x4347, 0xA4 }, + { 0x4348, 0x97 }, + { 0x4349, 0x99 }, + { 0x434A, 0x97 }, + { 0x434B, 0x97 }, + { 0x434C, 0x89 }, + { 0x434D, 0x8C }, + { 0x434E, 0x8B }, + { 0x434F, 0x8A }, + { 0x4350, 0x81 }, + { 0x4351, 0x83 }, + { 0x4352, 0x83 }, + { 0x4353, 0x83 }, + { 0x4354, 0x7F }, + { 0x4355, 0x80 }, + { 0x4356, 0x80 }, + { 0x4357, 0x80 }, + { 0x4358, 0x7F }, + { 0x4359, 0x7F }, + { 0x435A, 0x7F }, + { 0x435B, 0x7F }, + { 0x435C, 0x82 }, + { 0x435D, 0x81 }, + { 0x435E, 0x81 }, + { 0x435F, 0x82 }, + { 0x4360, 0x86 }, + { 0x4361, 0x86 }, + { 0x4362, 0x86 }, + { 0x4363, 0x87 }, + { 0x4364, 0x8F }, + { 0x4365, 0x8F }, + { 0x4366, 0x8F }, + { 0x4367, 0x90 }, + { 0x4368, 0x9E }, + { 0x4369, 0x9E }, + { 0x436A, 0x9E }, + { 0x436B, 0x9F }, + { 0x436C, 0x99 }, + { 0x436D, 0x9B }, + { 0x436E, 0x9A }, + { 0x436F, 0x98 }, + { 0x4370, 0x8B }, + { 0x4371, 0x8D }, + { 0x4372, 0x8D }, + { 0x4373, 0x8B }, + { 0x4374, 0x83 }, + { 0x4375, 0x84 }, + { 0x4376, 0x84 }, + { 0x4377, 0x83 }, + { 0x4378, 0x80 }, + { 0x4379, 0x81 }, + { 0x437A, 0x81 }, + { 0x437B, 0x80 }, + { 0x437C, 0x81 }, + { 0x437D, 0x80 }, + { 0x437E, 0x80 }, + { 0x437F, 0x80 }, + { 0x4380, 0x83 }, + { 0x4381, 0x83 }, + { 0x4382, 0x83 }, + { 0x4383, 0x83 }, + { 0x4384, 0x88 }, + { 0x4385, 0x87 }, + { 0x4386, 0x87 }, + { 0x4387, 0x88 }, + { 0x4388, 0x91 }, + { 0x4389, 0x90 }, + { 0x438A, 0x90 }, + { 0x438B, 0x91 }, + { 0x438C, 0x9E }, + { 0x438D, 0x9E }, + { 0x438E, 0x9E }, + { 0x438F, 0xA0 }, + { 0x4390, 0xA0 }, + { 0x4391, 0xA2 }, + { 0x4392, 0xA2 }, + { 0x4393, 0xA0 }, + { 0x4394, 0x92 }, + { 0x4395, 0x94 }, + { 0x4396, 0x94 }, + { 0x4397, 0x91 }, + { 0x4398, 0x89 }, + { 0x4399, 0x8A }, + { 0x439A, 0x89 }, + { 0x439B, 0x88 }, + { 0x439C, 0x85 }, + { 0x439D, 0x85 }, + { 0x439E, 0x85 }, + { 0x439F, 0x84 }, + { 0x43A0, 0x85 }, + { 0x43A1, 0x84 }, + { 0x43A2, 0x84 }, + { 0x43A3, 0x84 }, + { 0x43A4, 0x88 }, + { 0x43A5, 0x86 }, + { 0x43A6, 0x86 }, + { 0x43A7, 0x87 }, + { 0x43A8, 0x8E }, + { 0x43A9, 0x8B }, + { 0x43AA, 0x8B }, + { 0x43AB, 0x8D }, + { 0x43AC, 0x97 }, + { 0x43AD, 0x96 }, + { 0x43AE, 0x96 }, + { 0x43AF, 0x98 }, + { 0x43B0, 0xA5 }, + { 0x43B1, 0xA5 }, + { 0x43B2, 0xA5 }, + { 0x43B3, 0xA9 }, + { 0x43B4, 0xB7 }, + { 0x43B5, 0xBB }, + { 0x43B6, 0xBB }, + { 0x43B7, 0xB8 }, + { 0x43B8, 0x9C }, + { 0x43B9, 0x9C }, + { 0x43BA, 0x9C }, + { 0x43BB, 0x9A }, + { 0x43BC, 0x92 }, + { 0x43BD, 0x92 }, + { 0x43BE, 0x92 }, + { 0x43BF, 0x8F }, + { 0x43C0, 0x8B }, + { 0x43C1, 0x8B }, + { 0x43C2, 0x8B }, + { 0x43C3, 0x89 }, + { 0x43C4, 0x8A }, + { 0x43C5, 0x89 }, + { 0x43C6, 0x89 }, + { 0x43C7, 0x88 }, + { 0x43C8, 0x8D }, + { 0x43C9, 0x8B }, + { 0x43CA, 0x8B }, + { 0x43CB, 0x8C }, + { 0x43CC, 0x94 }, + { 0x43CD, 0x91 }, + { 0x43CE, 0x92 }, + { 0x43CF, 0x93 }, + { 0x43D0, 0x9E }, + { 0x43D1, 0x9D }, + { 0x43D2, 0x9D }, + { 0x43D3, 0xA1 }, + { 0x43D4, 0xB2 }, + { 0x43D5, 0xB4 }, + { 0x43D6, 0xB5 }, + { 0x43D7, 0xBD }, + { 0x4400, 0x80 }, + { 0x4401, 0x80 }, + { 0x4402, 0x80 }, + { 0x4403, 0x80 }, + { 0x4404, 0x80 }, + { 0x4405, 0x80 }, + { 0x4406, 0x80 }, + { 0x4407, 0x80 }, + { 0x4408, 0x80 }, + { 0x4409, 0x80 }, + { 0x440A, 0x80 }, + { 0x440B, 0x80 }, + { 0x440C, 0x80 }, + { 0x440D, 0x80 }, + { 0x440E, 0x80 }, + { 0x440F, 0x80 }, + { 0x4410, 0x80 }, + { 0x4411, 0x80 }, + { 0x4412, 0x80 }, + { 0x4413, 0x80 }, + { 0x4414, 0x80 }, + { 0x4415, 0x80 }, + { 0x4416, 0x80 }, + { 0x4417, 0x80 }, + { 0x4418, 0x80 }, + { 0x4419, 0x80 }, + { 0x441A, 0x80 }, + { 0x441B, 0x80 }, + { 0x441C, 0x80 }, + { 0x441D, 0x80 }, + { 0x441E, 0x80 }, + { 0x441F, 0x80 }, + { 0x4420, 0x80 }, + { 0x4421, 0x80 }, + { 0x4422, 0x80 }, + { 0x4423, 0x80 }, + { 0x4424, 0x80 }, + { 0x4425, 0x80 }, + { 0x4426, 0x80 }, + { 0x4427, 0x80 }, + { 0x4428, 0x80 }, + { 0x4429, 0x80 }, + { 0x442A, 0x80 }, + { 0x442B, 0x80 }, + { 0x442C, 0x80 }, + { 0x442D, 0x80 }, + { 0x442E, 0x80 }, + { 0x442F, 0x80 }, + { 0x4430, 0x80 }, + { 0x4431, 0x80 }, + { 0x4432, 0x80 }, + { 0x4433, 0x80 }, + { 0x4434, 0x80 }, + { 0x4435, 0x80 }, + { 0x4436, 0x80 }, + { 0x4437, 0x80 }, + { 0x4438, 0x80 }, + { 0x4439, 0x80 }, + { 0x443A, 0x80 }, + { 0x443B, 0x80 }, + { 0x443C, 0x80 }, + { 0x443D, 0x80 }, + { 0x443E, 0x80 }, + { 0x443F, 0x80 }, + { 0x4440, 0x80 }, + { 0x4441, 0x80 }, + { 0x4442, 0x80 }, + { 0x4443, 0x80 }, + { 0x4444, 0x80 }, + { 0x4445, 0x80 }, + { 0x4446, 0x80 }, + { 0x4447, 0x80 }, + { 0x4448, 0x80 }, + { 0x4449, 0x80 }, + { 0x444A, 0x80 }, + { 0x444B, 0x80 }, + { 0x444C, 0x80 }, + { 0x444D, 0x80 }, + { 0x444E, 0x80 }, + { 0x444F, 0x80 }, + { 0x4450, 0x80 }, + { 0x4451, 0x80 }, + { 0x4452, 0x80 }, + { 0x4453, 0x80 }, + { 0x4454, 0x80 }, + { 0x4455, 0x80 }, + { 0x4456, 0x80 }, + { 0x4457, 0x80 }, + { 0x4458, 0x80 }, + { 0x4459, 0x80 }, + { 0x445A, 0x80 }, + { 0x445B, 0x80 }, + { 0x445C, 0x80 }, + { 0x445D, 0x80 }, + { 0x445E, 0x80 }, + { 0x445F, 0x80 }, + { 0x4460, 0x80 }, + { 0x4461, 0x80 }, + { 0x4462, 0x80 }, + { 0x4463, 0x80 }, + { 0x4464, 0x80 }, + { 0x4465, 0x80 }, + { 0x4466, 0x80 }, + { 0x4467, 0x80 }, + { 0x4468, 0x80 }, + { 0x4469, 0x80 }, + { 0x446A, 0x80 }, + { 0x446B, 0x80 }, + { 0x446C, 0x80 }, + { 0x446D, 0x80 }, + { 0x446E, 0x80 }, + { 0x446F, 0x80 }, + { 0x4470, 0x80 }, + { 0x4471, 0x80 }, + { 0x4472, 0x80 }, + { 0x4473, 0x80 }, + { 0x4474, 0x80 }, + { 0x4475, 0x80 }, + { 0x4476, 0x80 }, + { 0x4477, 0x80 }, + { 0x4478, 0x80 }, + { 0x4479, 0x80 }, + { 0x447A, 0x80 }, + { 0x447B, 0x80 }, + { 0x447C, 0x80 }, + { 0x447D, 0x80 }, + { 0x447E, 0x80 }, + { 0x447F, 0x80 }, + { 0x4480, 0x80 }, + { 0x4481, 0x80 }, + { 0x4482, 0x80 }, + { 0x4483, 0x80 }, + { 0x4484, 0x80 }, + { 0x4485, 0x80 }, + { 0x4486, 0x80 }, + { 0x4487, 0x80 }, + { 0x4488, 0x80 }, + { 0x4489, 0x80 }, + { 0x448A, 0x80 }, + { 0x448B, 0x80 }, + { 0x448C, 0x80 }, + { 0x448D, 0x80 }, + { 0x448E, 0x80 }, + { 0x448F, 0x80 }, + { 0x4490, 0x80 }, + { 0x4491, 0x80 }, + { 0x4492, 0x80 }, + { 0x4493, 0x80 }, + { 0x4494, 0x80 }, + { 0x4495, 0x80 }, + { 0x4496, 0x80 }, + { 0x4497, 0x80 }, + { 0x4498, 0x80 }, + { 0x4499, 0x80 }, + { 0x449A, 0x80 }, + { 0x449B, 0x80 }, + { 0x449C, 0x80 }, + { 0x449D, 0x80 }, + { 0x449E, 0x80 }, + { 0x449F, 0x80 }, + { 0x44A0, 0x80 }, + { 0x44A1, 0x80 }, + { 0x44A2, 0x80 }, + { 0x44A3, 0x80 }, + { 0x44A4, 0x80 }, + { 0x44A5, 0x80 }, + { 0x44A6, 0x80 }, + { 0x44A7, 0x80 }, + { 0x44A8, 0x80 }, + { 0x44A9, 0x80 }, + { 0x44AA, 0x80 }, + { 0x44AB, 0x80 }, + { 0x44AC, 0x80 }, + { 0x44AD, 0x80 }, + { 0x44AE, 0x80 }, + { 0x44AF, 0x80 }, + { 0x44B0, 0x80 }, + { 0x44B1, 0x80 }, + { 0x44B2, 0x80 }, + { 0x44B3, 0x80 }, + { 0x44B4, 0x80 }, + { 0x44B5, 0x80 }, + { 0x44B6, 0x80 }, + { 0x44B7, 0x80 }, + { 0x44B8, 0x80 }, + { 0x44B9, 0x80 }, + { 0x44BA, 0x80 }, + { 0x44BB, 0x80 }, + { 0x44BC, 0x80 }, + { 0x44BD, 0x80 }, + { 0x44BE, 0x80 }, + { 0x44BF, 0x80 }, + { 0x44C0, 0x80 }, + { 0x44C1, 0x80 }, + { 0x44C2, 0x80 }, + { 0x44C3, 0x80 }, + { 0x44C4, 0x80 }, + { 0x44C5, 0x80 }, + { 0x44C6, 0x80 }, + { 0x44C7, 0x80 }, + { 0x44C8, 0x80 }, + { 0x44C9, 0x80 }, + { 0x44CA, 0x80 }, + { 0x44CB, 0x80 }, + { 0x44CC, 0x80 }, + { 0x44CD, 0x80 }, + { 0x44CE, 0x80 }, + { 0x44CF, 0x80 }, + { 0x44D0, 0x80 }, + { 0x44D1, 0x80 }, + { 0x44D2, 0x80 }, + { 0x44D3, 0x80 }, + { 0x44D4, 0x80 }, + { 0x44D5, 0x80 }, + { 0x44D6, 0x80 }, + { 0x44D7, 0x80 }, + { 0x4500, 0x80 }, + { 0x4501, 0x80 }, + { 0x4502, 0x80 }, + { 0x4503, 0x80 }, + { 0x4504, 0x80 }, + { 0x4505, 0x80 }, + { 0x4506, 0x80 }, + { 0x4507, 0x80 }, + { 0x4508, 0x80 }, + { 0x4509, 0x80 }, + { 0x450A, 0x80 }, + { 0x450B, 0x80 }, + { 0x450C, 0x80 }, + { 0x450D, 0x80 }, + { 0x450E, 0x80 }, + { 0x450F, 0x80 }, + { 0x4510, 0x80 }, + { 0x4511, 0x80 }, + { 0x4512, 0x80 }, + { 0x4513, 0x80 }, + { 0x4514, 0x80 }, + { 0x4515, 0x80 }, + { 0x4516, 0x80 }, + { 0x4517, 0x80 }, + { 0x4518, 0x80 }, + { 0x4519, 0x80 }, + { 0x451A, 0x80 }, + { 0x451B, 0x80 }, + { 0x451C, 0x80 }, + { 0x451D, 0x80 }, + { 0x451E, 0x80 }, + { 0x451F, 0x80 }, + { 0x4520, 0x80 }, + { 0x4521, 0x80 }, + { 0x4522, 0x80 }, + { 0x4523, 0x80 }, + { 0x4524, 0x80 }, + { 0x4525, 0x80 }, + { 0x4526, 0x80 }, + { 0x4527, 0x80 }, + { 0x4528, 0x80 }, + { 0x4529, 0x80 }, + { 0x452A, 0x80 }, + { 0x452B, 0x80 }, + { 0x452C, 0x80 }, + { 0x452D, 0x80 }, + { 0x452E, 0x80 }, + { 0x452F, 0x80 }, + { 0x4530, 0x80 }, + { 0x4531, 0x80 }, + { 0x4532, 0x80 }, + { 0x4533, 0x80 }, + { 0x4534, 0x80 }, + { 0x4535, 0x80 }, + { 0x4536, 0x80 }, + { 0x4537, 0x80 }, + { 0x4538, 0x80 }, + { 0x4539, 0x80 }, + { 0x453A, 0x80 }, + { 0x453B, 0x80 }, + { 0x453C, 0x80 }, + { 0x453D, 0x80 }, + { 0x453E, 0x80 }, + { 0x453F, 0x80 }, + { 0x4540, 0x80 }, + { 0x4541, 0x80 }, + { 0x4542, 0x80 }, + { 0x4543, 0x80 }, + { 0x4544, 0x80 }, + { 0x4545, 0x80 }, + { 0x4546, 0x80 }, + { 0x4547, 0x80 }, + { 0x4548, 0x80 }, + { 0x4549, 0x80 }, + { 0x454A, 0x80 }, + { 0x454B, 0x80 }, + { 0x454C, 0x80 }, + { 0x454D, 0x80 }, + { 0x454E, 0x80 }, + { 0x454F, 0x80 }, + { 0x4550, 0x80 }, + { 0x4551, 0x80 }, + { 0x4552, 0x80 }, + { 0x4553, 0x80 }, + { 0x4554, 0x80 }, + { 0x4555, 0x80 }, + { 0x4556, 0x80 }, + { 0x4557, 0x80 }, + { 0x4558, 0x80 }, + { 0x4559, 0x80 }, + { 0x455A, 0x80 }, + { 0x455B, 0x80 }, + { 0x455C, 0x80 }, + { 0x455D, 0x80 }, + { 0x455E, 0x80 }, + { 0x455F, 0x80 }, + { 0x4560, 0x80 }, + { 0x4561, 0x80 }, + { 0x4562, 0x80 }, + { 0x4563, 0x80 }, + { 0x4564, 0x80 }, + { 0x4565, 0x80 }, + { 0x4566, 0x80 }, + { 0x4567, 0x80 }, + { 0x4568, 0x80 }, + { 0x4569, 0x80 }, + { 0x456A, 0x80 }, + { 0x456B, 0x80 }, + { 0x456C, 0x80 }, + { 0x456D, 0x80 }, + { 0x456E, 0x80 }, + { 0x456F, 0x80 }, + { 0x4570, 0x80 }, + { 0x4571, 0x80 }, + { 0x4572, 0x80 }, + { 0x4573, 0x80 }, + { 0x4574, 0x80 }, + { 0x4575, 0x80 }, + { 0x4576, 0x80 }, + { 0x4577, 0x80 }, + { 0x4578, 0x80 }, + { 0x4579, 0x80 }, + { 0x457A, 0x80 }, + { 0x457B, 0x80 }, + { 0x457C, 0x80 }, + { 0x457D, 0x80 }, + { 0x457E, 0x80 }, + { 0x457F, 0x80 }, + { 0x4580, 0x80 }, + { 0x4581, 0x80 }, + { 0x4582, 0x80 }, + { 0x4583, 0x80 }, + { 0x4584, 0x80 }, + { 0x4585, 0x80 }, + { 0x4586, 0x80 }, + { 0x4587, 0x80 }, + { 0x4588, 0x80 }, + { 0x4589, 0x80 }, + { 0x458A, 0x80 }, + { 0x458B, 0x80 }, + { 0x458C, 0x80 }, + { 0x458D, 0x80 }, + { 0x458E, 0x80 }, + { 0x458F, 0x80 }, + { 0x4590, 0x80 }, + { 0x4591, 0x80 }, + { 0x4592, 0x80 }, + { 0x4593, 0x80 }, + { 0x4594, 0x80 }, + { 0x4595, 0x80 }, + { 0x4596, 0x80 }, + { 0x4597, 0x80 }, + { 0x4598, 0x80 }, + { 0x4599, 0x80 }, + { 0x459A, 0x80 }, + { 0x459B, 0x80 }, + { 0x459C, 0x80 }, + { 0x459D, 0x80 }, + { 0x459E, 0x80 }, + { 0x459F, 0x80 }, + { 0x45A0, 0x80 }, + { 0x45A1, 0x80 }, + { 0x45A2, 0x80 }, + { 0x45A3, 0x80 }, + { 0x45A4, 0x80 }, + { 0x45A5, 0x80 }, + { 0x45A6, 0x80 }, + { 0x45A7, 0x80 }, + { 0x45A8, 0x80 }, + { 0x45A9, 0x80 }, + { 0x45AA, 0x80 }, + { 0x45AB, 0x80 }, + { 0x45AC, 0x80 }, + { 0x45AD, 0x80 }, + { 0x45AE, 0x80 }, + { 0x45AF, 0x80 }, + { 0x45B0, 0x80 }, + { 0x45B1, 0x80 }, + { 0x45B2, 0x80 }, + { 0x45B3, 0x80 }, + { 0x45B4, 0x80 }, + { 0x45B5, 0x80 }, + { 0x45B6, 0x80 }, + { 0x45B7, 0x80 }, + { 0x45B8, 0x80 }, + { 0x45B9, 0x80 }, + { 0x45BA, 0x80 }, + { 0x45BB, 0x80 }, + { 0x45BC, 0x80 }, + { 0x45BD, 0x80 }, + { 0x45BE, 0x80 }, + { 0x45BF, 0x80 }, + { 0x45C0, 0x80 }, + { 0x45C1, 0x80 }, + { 0x45C2, 0x80 }, + { 0x45C3, 0x80 }, + { 0x45C4, 0x80 }, + { 0x45C5, 0x80 }, + { 0x45C6, 0x80 }, + { 0x45C7, 0x80 }, + { 0x45C8, 0x80 }, + { 0x45C9, 0x80 }, + { 0x45CA, 0x80 }, + { 0x45CB, 0x80 }, + { 0x45CC, 0x80 }, + { 0x45CD, 0x80 }, + { 0x45CE, 0x80 }, + { 0x45CF, 0x80 }, + { 0x45D0, 0x80 }, + { 0x45D1, 0x80 }, + { 0x45D2, 0x80 }, + { 0x45D3, 0x80 }, + { 0x45D4, 0x80 }, + { 0x45D5, 0x80 }, + { 0x45D6, 0x80 }, + { 0x45D7, 0x80 }, + { 0x7000, 0xAB }, + { 0x7001, 0xBA }, + { 0x7002, 0x40 }, + { 0x7003, 0x02 }, + { 0x7004, 0x00 }, + { 0x7005, 0x00 }, + { 0x7006, 0x00 }, + { 0x7007, 0x00 }, + { 0x7008, 0x00 }, + { 0x7009, 0x00 }, + { 0x700A, 0x00 }, + { 0x700B, 0x00 }, + { 0x700C, 0x00 }, + { 0x700D, 0x00 }, + { 0x700E, 0x00 }, + { 0x700F, 0x00 }, + { 0x7010, 0x55 }, + { 0x7011, 0x88 }, + { 0x7012, 0x40 }, + { 0x7013, 0x01 }, + { 0x7014, 0x72 }, + { 0x7015, 0xF1 }, + { 0x7016, 0x02 }, + { 0x7017, 0xF8 }, + { 0x7018, 0x00 }, + { 0x7019, 0x00 }, + { 0x701A, 0x00 }, + { 0x701B, 0x00 }, + { 0x701C, 0x00 }, + { 0x701D, 0x00 }, + { 0x701E, 0x00 }, + { 0x701F, 0x00 }, + { 0x7020, 0x00 }, + { 0x7021, 0x00 }, + { 0x7022, 0x00 }, + { 0x7023, 0x00 }, + { 0x7024, 0x00 }, + { 0x7025, 0x00 }, + { 0x7026, 0x00 }, + { 0x7027, 0x00 }, + { 0x7028, 0x00 }, + { 0x7029, 0x00 }, + { 0x702A, 0x00 }, + { 0x702B, 0x00 }, + { 0x702C, 0x00 }, + { 0x702D, 0x00 }, + { 0x702E, 0x00 }, + { 0x702F, 0x00 }, + { 0x7030, 0x00 }, + { 0x7031, 0x00 }, + { 0x7032, 0x00 }, + { 0x7033, 0x00 }, + { 0x7034, 0x00 }, + { 0x7035, 0x00 }, + { 0x7036, 0x00 }, + { 0x7037, 0x00 }, + { 0x7038, 0x00 }, + { 0x7039, 0x00 }, + { 0x703A, 0x00 }, + { 0x703B, 0x00 }, + { 0x703C, 0x00 }, + { 0x703D, 0x00 }, + { 0x703E, 0x00 }, + { 0x703F, 0x00 }, + { 0x7040, 0x00 }, + { 0x7041, 0x00 }, + { 0x7042, 0x00 }, + { 0x7043, 0x00 }, + { 0x7044, 0x00 }, + { 0x7045, 0x00 }, + { 0x7046, 0x00 }, + { 0x7047, 0x00 }, + { 0x7048, 0x00 }, + { 0x7049, 0x00 }, + { 0x704A, 0x00 }, + { 0x704B, 0x00 }, + { 0x704C, 0x00 }, + { 0x704D, 0x00 }, + { 0x704E, 0x00 }, + { 0x704F, 0x00 }, + { 0x7050, 0x00 }, + { 0x7051, 0x00 }, + { 0x7052, 0x00 }, + { 0x7053, 0x00 }, + { 0x7054, 0x00 }, + { 0x7055, 0x00 }, + { 0x7056, 0x00 }, + { 0x7057, 0x00 }, + { 0x7058, 0x00 }, + { 0x7059, 0x00 }, + { 0x705A, 0x00 }, + { 0x705B, 0x00 }, + { 0x705C, 0x00 }, + { 0x705D, 0x00 }, + { 0x705E, 0x00 }, + { 0x705F, 0x00 }, + { 0x7060, 0x00 }, + { 0x7061, 0x00 }, + { 0x7062, 0x00 }, + { 0x7063, 0x00 }, + { 0x7064, 0x00 }, + { 0x7065, 0x00 }, + { 0x7066, 0x00 }, + { 0x7067, 0x00 }, + { 0x7068, 0x00 }, + { 0x7069, 0x00 }, + { 0x706A, 0x00 }, + { 0x706B, 0x00 }, + { 0x706C, 0x00 }, + { 0x706D, 0x00 }, + { 0x706E, 0x00 }, + { 0x706F, 0x00 }, + { 0x7070, 0x00 }, + { 0x7071, 0x00 }, + { 0x7072, 0x00 }, + { 0x7073, 0x00 }, + { 0x7074, 0x00 }, + { 0x7075, 0x00 }, + { 0x7076, 0x00 }, + { 0x7077, 0x00 }, + { 0x7078, 0x00 }, + { 0x7079, 0x00 }, + { 0x707A, 0x00 }, + { 0x707B, 0x00 }, + { 0x707C, 0x00 }, + { 0x707D, 0x00 }, + { 0x707E, 0x00 }, + { 0x707F, 0x00 }, + { 0x7080, 0x00 }, + { 0x7081, 0x00 }, + { 0x7082, 0x00 }, + { 0x7083, 0x00 }, + { 0x7084, 0x00 }, + { 0x7085, 0x00 }, + { 0x7086, 0x00 }, + { 0x7087, 0x00 }, + { 0x7088, 0x00 }, + { 0x7089, 0x00 }, + { 0x708A, 0x00 }, + { 0x708B, 0x00 }, + { 0x708C, 0x00 }, + { 0x708D, 0x00 }, + { 0x708E, 0x00 }, + { 0x708F, 0x00 }, + { 0x7090, 0x00 }, + { 0x7091, 0xF0 }, + { 0x7092, 0x02 }, + { 0x7093, 0xF8 }, + { 0x7094, 0x8D }, + { 0x7095, 0xF6 }, + { 0x7096, 0xFA }, + { 0x7097, 0xFF }, + { 0x7098, 0xF0 }, + { 0x7099, 0xB5 }, + { 0x709A, 0x04 }, + { 0x709B, 0x46 }, + { 0x709C, 0x8F }, + { 0x709D, 0xB0 }, + { 0x709E, 0x5F }, + { 0x709F, 0x48 }, + { 0x70A0, 0x0C }, + { 0x70A1, 0x90 }, + { 0x70A2, 0x5F }, + { 0x70A3, 0x48 }, + { 0x70A4, 0x06 }, + { 0x70A5, 0x90 }, + { 0x70A6, 0x20 }, + { 0x70A7, 0x46 }, + { 0x70A8, 0x34 }, + { 0x70A9, 0x30 }, + { 0x70AA, 0x0B }, + { 0x70AB, 0x90 }, + { 0x70AC, 0x5B }, + { 0x70AD, 0x48 }, + { 0x70AE, 0x5A }, + { 0x70AF, 0x49 }, + { 0x70B0, 0x26 }, + { 0x70B1, 0x46 }, + { 0x70B2, 0x66 }, + { 0x70B3, 0x30 }, + { 0x70B4, 0x3A }, + { 0x70B5, 0x31 }, + { 0x70B6, 0x3C }, + { 0x70B7, 0x36 }, + { 0x70B8, 0x05 }, + { 0x70B9, 0x90 }, + { 0x70BA, 0x0A }, + { 0x70BB, 0x30 }, + { 0x70BC, 0x04 }, + { 0x70BD, 0x90 }, + { 0x70BE, 0x59 }, + { 0x70BF, 0x48 }, + { 0x70C0, 0x55 }, + { 0x70C1, 0x4A }, + { 0x70C2, 0x40 }, + { 0x70C3, 0x6E }, + { 0x70C4, 0xC0 }, + { 0x70C5, 0x07 }, + { 0x70C6, 0x7D }, + { 0x70C7, 0xD1 }, + { 0x70C8, 0x17 }, + { 0x70C9, 0x88 }, + { 0x70CA, 0x0A }, + { 0x70CB, 0x5E }, + { 0x70CC, 0x0D }, + { 0x70CD, 0x92 }, + { 0x70CE, 0x53 }, + { 0x70CF, 0x49 }, + { 0x70D0, 0x55 }, + { 0x70D1, 0x48 }, + { 0x70D2, 0x94 }, + { 0x70D3, 0x31 }, + { 0x70D4, 0x89 }, + { 0x70D5, 0x6B }, + { 0x70D6, 0x80 }, + { 0x70D7, 0x68 }, + { 0x70D8, 0x09 }, + { 0x70D9, 0x02 }, + { 0x70DA, 0x00 }, + { 0x70DB, 0x03 }, + { 0x70DC, 0x09 }, + { 0x70DD, 0x0E }, + { 0x70DE, 0x00 }, + { 0x70DF, 0x0B }, + { 0x70E0, 0x49 }, + { 0x70E1, 0x1C }, + { 0x70E2, 0x48 }, + { 0x70E3, 0x43 }, + { 0x70E4, 0x4D }, + { 0x70E5, 0x49 }, + { 0x70E6, 0x6C }, + { 0x70E7, 0x39 }, + { 0x70E8, 0x8A }, + { 0x70E9, 0x6A }, + { 0x70EA, 0x07 }, + { 0x70EB, 0x92 }, + { 0x70EC, 0xCA }, + { 0x70ED, 0x6A }, + { 0x70EE, 0x00 }, + { 0x70EF, 0x21 }, + { 0x70F0, 0xC9 }, + { 0x70F1, 0x43 }, + { 0x70F2, 0x03 }, + { 0x70F3, 0x92 }, + { 0x70F4, 0x00 }, + { 0x70F5, 0x22 }, + { 0x70F6, 0x00 }, + { 0x70F7, 0x91 }, + { 0x70F8, 0x01 }, + { 0x70F9, 0x92 }, + { 0x70FA, 0x39 }, + { 0x70FB, 0x46 }, + { 0x70FC, 0x8F }, + { 0x70FD, 0xF6 }, + { 0x70FE, 0xCE }, + { 0x70FF, 0xFB }, + { 0x7100, 0x01 }, + { 0x7101, 0x22 }, + { 0x7102, 0x00 }, + { 0x7103, 0x23 }, + { 0x7104, 0x8C }, + { 0x7105, 0xF6 }, + { 0x7106, 0x02 }, + { 0x7107, 0xFA }, + { 0x7108, 0x00 }, + { 0x7109, 0x21 }, + { 0x710A, 0x05 }, + { 0x710B, 0x46 }, + { 0x710C, 0x01 }, + { 0x710D, 0x91 }, + { 0x710E, 0x00 }, + { 0x710F, 0x90 }, + { 0x7110, 0x39 }, + { 0x7111, 0x46 }, + { 0x7112, 0x07 }, + { 0x7113, 0x98 }, + { 0x7114, 0x8F }, + { 0x7115, 0xF6 }, + { 0x7116, 0xC2 }, + { 0x7117, 0xFB }, + { 0x7118, 0x0D }, + { 0x7119, 0x9A }, + { 0x711A, 0xD3 }, + { 0x711B, 0x17 }, + { 0x711C, 0x80 }, + { 0x711D, 0x18 }, + { 0x711E, 0x59 }, + { 0x711F, 0x41 }, + { 0x7120, 0x01 }, + { 0x7121, 0x22 }, + { 0x7122, 0x00 }, + { 0x7123, 0x23 }, + { 0x7124, 0x8C }, + { 0x7125, 0xF6 }, + { 0x7126, 0xCD }, + { 0x7127, 0xF9 }, + { 0x7128, 0x07 }, + { 0x7129, 0x90 }, + { 0x712A, 0x00 }, + { 0x712B, 0x20 }, + { 0x712C, 0x01 }, + { 0x712D, 0x90 }, + { 0x712E, 0x00 }, + { 0x712F, 0x95 }, + { 0x7130, 0x39 }, + { 0x7131, 0x46 }, + { 0x7132, 0x03 }, + { 0x7133, 0x98 }, + { 0x7134, 0x8F }, + { 0x7135, 0xF6 }, + { 0x7136, 0xB2 }, + { 0x7137, 0xFB }, + { 0x7138, 0x01 }, + { 0x7139, 0x22 }, + { 0x713A, 0x00 }, + { 0x713B, 0x23 }, + { 0x713C, 0x8C }, + { 0x713D, 0xF6 }, + { 0x713E, 0xE6 }, + { 0x713F, 0xF9 }, + { 0x7140, 0x02 }, + { 0x7141, 0x46 }, + { 0x7142, 0x07 }, + { 0x7143, 0x98 }, + { 0x7144, 0x00 }, + { 0x7145, 0x23 }, + { 0x7146, 0x81 }, + { 0x7147, 0x0B }, + { 0x7148, 0x80 }, + { 0x7149, 0x04 }, + { 0x714A, 0x7A }, + { 0x714B, 0xF6 }, + { 0x714C, 0x54 }, + { 0x714D, 0xF8 }, + { 0x714E, 0x37 }, + { 0x714F, 0x4A }, + { 0x7150, 0x00 }, + { 0x7151, 0x23 }, + { 0x7152, 0x00 }, + { 0x7153, 0x92 }, + { 0x7154, 0x01 }, + { 0x7155, 0x93 }, + { 0x7156, 0x01 }, + { 0x7157, 0x22 }, + { 0x7158, 0x8C }, + { 0x7159, 0xF6 }, + { 0x715A, 0xD8 }, + { 0x715B, 0xF9 }, + { 0x715C, 0x05 }, + { 0x715D, 0x46 }, + { 0x715E, 0x60 }, + { 0x715F, 0x68 }, + { 0x7160, 0x00 }, + { 0x7161, 0x23 }, + { 0x7162, 0x01 }, + { 0x7163, 0x0C }, + { 0x7164, 0x00 }, + { 0x7165, 0x04 }, + { 0x7166, 0xE2 }, + { 0x7167, 0x68 }, + { 0x7168, 0x7A }, + { 0x7169, 0xF6 }, + { 0x716A, 0x45 }, + { 0x716B, 0xF8 }, + { 0x716C, 0x00 }, + { 0x716D, 0x22 }, + { 0x716E, 0xD2 }, + { 0x716F, 0x43 }, + { 0x7170, 0x00 }, + { 0x7171, 0x23 }, + { 0x7172, 0x00 }, + { 0x7173, 0x92 }, + { 0x7174, 0x01 }, + { 0x7175, 0x93 }, + { 0x7176, 0x1A }, + { 0x7177, 0x46 }, + { 0x7178, 0x8C }, + { 0x7179, 0xF6 }, + { 0x717A, 0xC8 }, + { 0x717B, 0xF9 }, + { 0x717C, 0x29 }, + { 0x717D, 0x46 }, + { 0x717E, 0x8F }, + { 0x717F, 0xF6 }, + { 0x7180, 0x8D }, + { 0x7181, 0xFB }, + { 0x7182, 0x8A }, + { 0x7183, 0x03 }, + { 0x7184, 0x80 }, + { 0x7185, 0x0C }, + { 0x7186, 0x10 }, + { 0x7187, 0x43 }, + { 0x7188, 0x00 }, + { 0x7189, 0x22 }, + { 0x718A, 0xD2 }, + { 0x718B, 0x43 }, + { 0x718C, 0x00 }, + { 0x718D, 0x23 }, + { 0x718E, 0x00 }, + { 0x718F, 0x92 }, + { 0x7190, 0x89 }, + { 0x7191, 0x0C }, + { 0x7192, 0x01 }, + { 0x7193, 0x93 }, + { 0x7194, 0x1A }, + { 0x7195, 0x46 }, + { 0x7196, 0x8C }, + { 0x7197, 0xF6 }, + { 0x7198, 0xB9 }, + { 0x7199, 0xF9 }, + { 0x719A, 0x00 }, + { 0x719B, 0x24 }, + { 0x719C, 0x03 }, + { 0x719D, 0x90 }, + { 0x719E, 0x0C }, + { 0x719F, 0x98 }, + { 0x71A0, 0x61 }, + { 0x71A1, 0x00 }, + { 0x71A2, 0x45 }, + { 0x71A3, 0x5A }, + { 0x71A4, 0x06 }, + { 0x71A5, 0x98 }, + { 0x71A6, 0x22 }, + { 0x71A7, 0x4A }, + { 0x71A8, 0x40 }, + { 0x71A9, 0x5A }, + { 0x71AA, 0x00 }, + { 0x71AB, 0x21 }, + { 0x71AC, 0x8C }, + { 0x71AD, 0xF6 }, + { 0x71AE, 0xBE }, + { 0x71AF, 0xF9 }, + { 0x71B0, 0x07 }, + { 0x71B1, 0x46 }, + { 0x71B2, 0x28 }, + { 0x71B3, 0x46 }, + { 0x71B4, 0x03 }, + { 0x71B5, 0x99 }, + { 0x71B6, 0x8F }, + { 0x71B7, 0xF6 }, + { 0x71B8, 0x71 }, + { 0x71B9, 0xFB }, + { 0x71BA, 0x3A }, + { 0x71BB, 0x46 }, + { 0x71BC, 0x00 }, + { 0x71BD, 0x23 }, + { 0x71BE, 0x79 }, + { 0x71BF, 0xF6 }, + { 0x71C0, 0xCA }, + { 0x71C1, 0xFF }, + { 0x71C2, 0x00 }, + { 0x71C3, 0xE0 }, + { 0x71C4, 0x0F }, + { 0x71C5, 0xE0 }, + { 0x71C6, 0x8A }, + { 0x71C7, 0x02 }, + { 0x71C8, 0x80 }, + { 0x71C9, 0x0D }, + { 0x71CA, 0x10 }, + { 0x71CB, 0x43 }, + { 0x71CC, 0x19 }, + { 0x71CD, 0x4A }, + { 0x71CE, 0x00 }, + { 0x71CF, 0x23 }, + { 0x71D0, 0x00 }, + { 0x71D1, 0x92 }, + { 0x71D2, 0x89 }, + { 0x71D3, 0x0D }, + { 0x71D4, 0x01 }, + { 0x71D5, 0x93 }, + { 0x71D6, 0x40 }, + { 0x71D7, 0x22 }, + { 0x71D8, 0x8C }, + { 0x71D9, 0xF6 }, + { 0x71DA, 0x98 }, + { 0x71DB, 0xF9 }, + { 0x71DC, 0xA1 }, + { 0x71DD, 0x00 }, + { 0x71DE, 0x64 }, + { 0x71DF, 0x1C }, + { 0x71E0, 0x70 }, + { 0x71E1, 0x50 }, + { 0x71E2, 0x04 }, + { 0x71E3, 0x2C }, + { 0x71E4, 0xDB }, + { 0x71E5, 0xD3 }, + { 0x71E6, 0x14 }, + { 0x71E7, 0x4D }, + { 0x71E8, 0x00 }, + { 0x71E9, 0x24 }, + { 0x71EA, 0x0B }, + { 0x71EB, 0x98 }, + { 0x71EC, 0x67 }, + { 0x71ED, 0x00 }, + { 0x71EE, 0xC0 }, + { 0x71EF, 0x5B }, + { 0x71F0, 0x2A }, + { 0x71F1, 0x46 }, + { 0x71F2, 0x40 }, + { 0x71F3, 0x21 }, + { 0x71F4, 0x8C }, + { 0x71F5, 0xF6 }, + { 0x71F6, 0x9A }, + { 0x71F7, 0xF9 }, + { 0x71F8, 0x05 }, + { 0x71F9, 0x99 }, + { 0x71FA, 0x0E }, + { 0x71FB, 0x4A }, + { 0x71FC, 0xC8 }, + { 0x71FD, 0x53 }, + { 0x71FE, 0xA7 }, + { 0x71FF, 0x00 }, + { 0x7200, 0xF0 }, + { 0x7201, 0x59 }, + { 0x7202, 0x40 }, + { 0x7203, 0x21 }, + { 0x7204, 0x8C }, + { 0x7205, 0xF6 }, + { 0x7206, 0x7B }, + { 0x7207, 0xF9 }, + { 0x7208, 0x04 }, + { 0x7209, 0x99 }, + { 0x720A, 0x64 }, + { 0x720B, 0x1C }, + { 0x720C, 0xC8 }, + { 0x720D, 0x51 }, + { 0x720E, 0x04 }, + { 0x720F, 0x2C }, + { 0x7210, 0xEB }, + { 0x7211, 0xD3 }, + { 0x7212, 0x0F }, + { 0x7213, 0xB0 }, + { 0x7214, 0xF0 }, + { 0x7215, 0xBD }, + { 0x7216, 0x00 }, + { 0x7217, 0x00 }, + { 0x7218, 0x76 }, + { 0x7219, 0x69 }, + { 0x721A, 0x18 }, + { 0x721B, 0x00 }, + { 0x721C, 0xEC }, + { 0x721D, 0x58 }, + { 0x721E, 0x18 }, + { 0x721F, 0x00 }, + { 0x7220, 0x38 }, + { 0x7221, 0x36 }, + { 0x7222, 0x18 }, + { 0x7223, 0x00 }, + { 0x7224, 0x00 }, + { 0x7225, 0x35 }, + { 0x7226, 0x18 }, + { 0x7227, 0x00 }, + { 0x7228, 0x00 }, + { 0x7229, 0x20 }, + { 0x722A, 0x18 }, + { 0x722B, 0x00 }, + { 0x722C, 0xFF }, + { 0x722D, 0xFF }, + { 0x722E, 0xFF }, + { 0x722F, 0x3F }, + { 0x7230, 0xFF }, + { 0x7231, 0x07 }, + { 0x7232, 0x00 }, + { 0x7233, 0x00 }, + { 0x7234, 0xFF }, + { 0x7235, 0xFF }, + { 0x7236, 0x07 }, + { 0x7237, 0x00 }, + { 0x7238, 0xFF }, + { 0x7239, 0x1F }, + { 0x723A, 0x00 }, + { 0x723B, 0x00 }, + { 0x723C, 0x01 }, + { 0x723D, 0xF6 }, + { 0x723E, 0x45 }, + { 0x723F, 0x12 }, +}; + +static const struct reg_sequence imx390_linear_1936x1096[] = { + { 0x000C, 0xF2 }, + { 0x000D, 0x02 }, + { 0x000E, 0x00 }, + { 0x0010, 0xF2 }, + { 0x0011, 0x02 }, + { 0x0012, 0x00 }, + { 0x0018, 0x0F }, + { 0x0019, 0x00 }, + { 0x001A, 0x0C }, + { 0x001B, 0x00 }, + { 0x0038, 0x00 }, + { 0x003C, 0x00 }, /* OBB_CLAMP_R_SP1H */ + { 0x003D, 0x00 }, /* OBB_CLAMP_R_SP1H */ + { 0x003E, 0x00 }, + { 0x0040, 0x00 }, /* OBB_CLAMP_GR_SP1H */ + { 0x0041, 0x00 }, /* OBB_CLAMP_GR_SP1H */ + { 0x0042, 0x00 }, + { 0x0044, 0x00 }, /* OBB_CLAMP_GB_SP1H */ + { 0x0045, 0x00 }, /* OBB_CLAMP_GB_SP1H */ + { 0x0046, 0x00 }, + { 0x0048, 0x00 }, + { 0x0049, 0x00 }, + { 0x004A, 0x00 }, + { 0x004C, 0x00 }, + { 0x004D, 0x00 }, + { 0x004E, 0x00 }, + { 0x0050, 0x00 }, + { 0x0051, 0x00 }, + { 0x0052, 0x00 }, + { 0x0054, 0x00 }, + { 0x0055, 0x00 }, + { 0x0056, 0x00 }, + { 0x0058, 0x00 }, + { 0x0059, 0x00 }, + { 0x005A, 0x00 }, + { 0x005C, 0x00 }, + { 0x005D, 0x00 }, + { 0x005E, 0x00 }, + { 0x0060, 0x00 }, + { 0x0061, 0x00 }, + { 0x0062, 0x00 }, + { 0x0064, 0x00 }, + { 0x0065, 0x00 }, + { 0x0066, 0x00 }, + { 0x0068, 0x00 }, + { 0x0069, 0x00 }, + { 0x006A, 0x00 }, + { 0x0074, 0x00 }, + { 0x0078, 0x00 }, + { 0x007C, 0x00 }, + { 0x007D, 0x00 }, + { 0x0080, 0x00 }, + { 0x0081, 0x00 }, + { 0x00F4, 0x1C }, + { 0x00F5, 0xF8 }, + { 0x00F6, 0x01 }, + { 0x00F8, 0x03 }, + { 0x00F9, 0x00 }, + { 0x00FA, 0x00 }, + { 0x00FB, 0x00 }, + { 0x0114, 0x00 }, + { 0x0115, 0x01 }, + { 0x0118, 0x20 }, + { 0x0119, 0x03 }, + { 0x011A, 0x00 }, + { 0x011B, 0x41 }, + { 0x011C, 0x80 }, + { 0x011D, 0x00 }, + { 0x0120, 0x20 }, + { 0x0121, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x44 }, + { 0x0124, 0x00 }, + { 0x0125, 0x01 }, + { 0x0128, 0xAC }, + { 0x0129, 0x0D }, + { 0x012A, 0x00 }, + { 0x012B, 0xA4 }, + { 0x012C, 0x00 }, + { 0x012D, 0x01 }, + { 0x0130, 0xC4 }, + { 0x0131, 0x09 }, + { 0x0132, 0x00 }, + { 0x0133, 0xDA }, + { 0x013B, 0x01 }, + { 0x01C4, 0x00 }, + { 0x01C5, 0x00 }, + { 0x01CC, 0x01 }, + { 0x01D0, 0x09 }, + { 0x01D4, 0x01 }, + { 0x0232, 0x18 }, + { 0x0233, 0x00 }, + { 0x0390, 0x00 }, + { 0x0391, 0x00 }, + { 0x0392, 0x00 }, + { 0x03C0, 0x04 }, + { 0x2000, 0x55 }, + { 0x2001, 0x55 }, + { 0x2002, 0x55 }, + { 0x2003, 0x05 }, + { 0x2004, 0x02 }, + { 0x2008, 0x65 }, + { 0x2009, 0x04 }, + { 0x200A, 0x00 }, + { 0x200C, 0x30 }, + { 0x200D, 0x11 }, + { 0x2010, 0x04 }, + { 0x2014, 0x01 }, + { 0x2018, 0x02 }, + { 0x2019, 0x04 }, + { 0x201A, 0x00 }, + { 0x201C, 0x21 }, + { 0x201D, 0x11 }, + { 0x201E, 0x00 }, + { 0x201F, 0x00 }, + { 0x2020, 0xBC }, + { 0x2021, 0x00 }, + { 0x2022, 0x7F }, + { 0x2023, 0x00 }, + { 0x2024, 0xBA }, + { 0x2025, 0x00 }, + { 0x2026, 0x81 }, + { 0x2027, 0x00 }, + { 0x2028, 0x7D }, + { 0x2029, 0x90 }, + { 0x202A, 0x05 }, + { 0x202C, 0xFC }, + { 0x202D, 0x02 }, + { 0x202E, 0x25 }, + { 0x202F, 0x03 }, + { 0x2030, 0x05 }, + { 0x2031, 0x02 }, + { 0x2032, 0xCA }, + { 0x2033, 0x02 }, + { 0x2034, 0xFC }, + { 0x2035, 0x02 }, + { 0x2036, 0x25 }, + { 0x2037, 0x03 }, + { 0x2038, 0x25 }, + { 0x2039, 0x97 }, + { 0x203A, 0xEC }, + { 0x203B, 0x01 }, + { 0x203C, 0xF5 }, + { 0x203D, 0x8E }, + { 0x203E, 0x0C }, + { 0x203F, 0x2D }, + { 0x2040, 0x69 }, + { 0x2041, 0x01 }, + { 0x2042, 0x8E }, + { 0x2043, 0x01 }, + { 0x2044, 0x0C }, + { 0x2045, 0x02 }, + { 0x2046, 0x31 }, + { 0x2047, 0x02 }, + { 0x2048, 0x6A }, + { 0x2049, 0x01 }, + { 0x204A, 0x8E }, + { 0x204B, 0x01 }, + { 0x204C, 0x0D }, + { 0x204D, 0x02 }, + { 0x204E, 0x31 }, + { 0x204F, 0x02 }, + { 0x2050, 0x7B }, + { 0x2051, 0x00 }, + { 0x2052, 0x7D }, + { 0x2053, 0x00 }, + { 0x2054, 0x95 }, + { 0x2055, 0x00 }, + { 0x2056, 0x97 }, + { 0x2057, 0x00 }, + { 0x2058, 0xAD }, + { 0x2059, 0x00 }, + { 0x205A, 0xAF }, + { 0x205B, 0x00 }, + { 0x205C, 0x92 }, + { 0x205D, 0x00 }, + { 0x205E, 0x94 }, + { 0x205F, 0x00 }, + { 0x2060, 0x8E }, + { 0x2061, 0x00 }, + { 0x2062, 0x90 }, + { 0x2063, 0x00 }, + { 0x2064, 0xB1 }, + { 0x2065, 0x00 }, + { 0x2066, 0xB3 }, + { 0x2067, 0x00 }, + { 0x2068, 0x08 }, + { 0x2069, 0x00 }, + { 0x206A, 0x04 }, + { 0x206B, 0x00 }, + { 0x206C, 0x84 }, + { 0x206D, 0x00 }, + { 0x206E, 0x80 }, + { 0x206F, 0x00 }, + { 0x2070, 0x04 }, + { 0x2071, 0x00 }, + { 0x2072, 0x46 }, + { 0x2073, 0x00 }, + { 0x2074, 0xE9 }, + { 0x2075, 0x01 }, + { 0x2076, 0x74 }, + { 0x2077, 0x02 }, + { 0x2078, 0x80 }, + { 0x2079, 0x00 }, + { 0x207A, 0xC1 }, + { 0x207B, 0x00 }, + { 0x207C, 0xFF }, + { 0x207D, 0x03 }, + { 0x207E, 0xFF }, + { 0x207F, 0x03 }, + { 0x2080, 0x78 }, + { 0x2081, 0x00 }, + { 0x2082, 0x6A }, + { 0x2083, 0x01 }, + { 0x2084, 0xE4 }, + { 0x2085, 0x01 }, + { 0x2086, 0x2B }, + { 0x2087, 0x03 }, + { 0x2088, 0x00 }, + { 0x2089, 0x00 }, + { 0x208A, 0xFF }, + { 0x208B, 0x03 }, + { 0x208C, 0xFF }, + { 0x208D, 0x03 }, + { 0x208E, 0xFF }, + { 0x208F, 0x03 }, + { 0x2090, 0x7D }, + { 0x2091, 0x00 }, + { 0x2092, 0x62 }, + { 0x2093, 0x01 }, + { 0x2094, 0xE9 }, + { 0x2095, 0x01 }, + { 0x2096, 0x00 }, + { 0x2097, 0x00 }, + { 0x2098, 0x7C }, + { 0x2099, 0x00 }, + { 0x209A, 0x21 }, + { 0x209B, 0x03 }, + { 0x209C, 0xE9 }, + { 0x209D, 0x01 }, + { 0x209E, 0x21 }, + { 0x209F, 0x03 }, + { 0x20A0, 0xFF }, + { 0x20A1, 0x03 }, + { 0x20A2, 0xFF }, + { 0x20A3, 0x03 }, + { 0x20A4, 0xFF }, + { 0x20A5, 0x03 }, + { 0x20A6, 0xFF }, + { 0x20A7, 0x03 }, + { 0x20A8, 0xFF }, + { 0x20A9, 0x03 }, + { 0x20AA, 0xFF }, + { 0x20AB, 0x03 }, + { 0x20AC, 0xFF }, + { 0x20AD, 0x03 }, + { 0x20AE, 0xFF }, + { 0x20AF, 0x03 }, + { 0x20B0, 0xFF }, + { 0x20B1, 0x03 }, + { 0x20B2, 0xFF }, + { 0x20B3, 0x03 }, + { 0x20B4, 0x87 }, + { 0x20B5, 0xCC }, + { 0x20B6, 0x87 }, + { 0x20B7, 0x08 }, + { 0x20B8, 0xF4 }, + { 0x20B9, 0xA5 }, + { 0x20BA, 0x07 }, + { 0x20BC, 0x1F }, + { 0x20BD, 0x01 }, + { 0x20BE, 0xF6 }, + { 0x20BF, 0x00 }, + { 0x20C0, 0x90 }, + { 0x20C1, 0x01 }, + { 0x20C2, 0x67 }, + { 0x20C3, 0x01 }, + { 0x20C4, 0xFF }, + { 0x20C5, 0x03 }, + { 0x20C6, 0xFF }, + { 0x20C7, 0x03 }, + { 0x20C8, 0x33 }, + { 0x20C9, 0x02 }, + { 0x20CA, 0x0A }, + { 0x20CB, 0x02 }, + { 0x20CC, 0x7F }, + { 0x20CD, 0x00 }, + { 0x20CE, 0xD2 }, + { 0x20CF, 0x00 }, + { 0x20D0, 0x81 }, + { 0x20D1, 0x00 }, + { 0x20D2, 0x87 }, + { 0x20D3, 0x00 }, + { 0x20D4, 0x09 }, + { 0x20D5, 0x00 }, + { 0x20D8, 0x7F }, + { 0x20D9, 0x00 }, + { 0x20DA, 0x62 }, + { 0x20DB, 0x01 }, + { 0x20DC, 0x7F }, + { 0x20DD, 0x00 }, + { 0x20DE, 0x62 }, + { 0x20DF, 0x01 }, + { 0x20E0, 0x65 }, + { 0x20E1, 0x00 }, + { 0x20E2, 0x75 }, + { 0x20E3, 0x00 }, + { 0x20E4, 0xE0 }, + { 0x20E5, 0x00 }, + { 0x20E6, 0xF0 }, + { 0x20E7, 0x00 }, + { 0x20E8, 0x4C }, + { 0x20E9, 0x01 }, + { 0x20EA, 0x5C }, + { 0x20EB, 0x01 }, + { 0x20EC, 0xD1 }, + { 0x20ED, 0x01 }, + { 0x20EE, 0xE1 }, + { 0x20EF, 0x01 }, + { 0x20F0, 0x93 }, + { 0x20F1, 0x02 }, + { 0x20F2, 0xA3 }, + { 0x20F3, 0x02 }, + { 0x20F4, 0x0D }, + { 0x20F5, 0x03 }, + { 0x20F6, 0x1D }, + { 0x20F7, 0x03 }, + { 0x20F8, 0x57 }, + { 0x20F9, 0x00 }, + { 0x20FA, 0x7B }, + { 0x20FB, 0x00 }, + { 0x20FC, 0xD2 }, + { 0x20FD, 0x00 }, + { 0x20FE, 0xF6 }, + { 0x20FF, 0x00 }, + { 0x2100, 0x3E }, + { 0x2101, 0x01 }, + { 0x2102, 0x60 }, + { 0x2103, 0x01 }, + { 0x2104, 0xC3 }, + { 0x2105, 0x01 }, + { 0x2106, 0xE5 }, + { 0x2107, 0x01 }, + { 0x2108, 0x85 }, + { 0x2109, 0x02 }, + { 0x210A, 0xA9 }, + { 0x210B, 0x02 }, + { 0x210C, 0xFF }, + { 0x210D, 0x02 }, + { 0x210E, 0x21 }, + { 0x210F, 0x03 }, + { 0x2110, 0xFF }, + { 0x2111, 0x03 }, + { 0x2112, 0x00 }, + { 0x2113, 0x00 }, + { 0x2114, 0xFF }, + { 0x2115, 0x03 }, + { 0x2116, 0xFF }, + { 0x2117, 0x03 }, + { 0x2118, 0xFF }, + { 0x2119, 0x03 }, + { 0x211A, 0xFF }, + { 0x211B, 0x03 }, + { 0x211C, 0xFF }, + { 0x211D, 0x03 }, + { 0x211E, 0xFF }, + { 0x211F, 0x03 }, + { 0x2120, 0xFF }, + { 0x2121, 0x03 }, + { 0x2122, 0xFF }, + { 0x2123, 0x03 }, + { 0x2124, 0xFF }, + { 0x2125, 0x03 }, + { 0x2126, 0xFF }, + { 0x2127, 0x03 }, + { 0x2128, 0x7D }, + { 0x2129, 0x90 }, + { 0x212A, 0xD5 }, + { 0x212B, 0x07 }, + { 0x212C, 0x64 }, + { 0x212D, 0x01 }, + { 0x2130, 0x5F }, + { 0x2131, 0x7D }, + { 0x2132, 0x05 }, + { 0x2134, 0x78 }, + { 0x2135, 0x00 }, + { 0x2136, 0x76 }, + { 0x2137, 0x00 }, + { 0x2138, 0xF3 }, + { 0x2139, 0x00 }, + { 0x213A, 0xF1 }, + { 0x213B, 0x00 }, + { 0x213C, 0xA6 }, + { 0x213D, 0x02 }, + { 0x213E, 0xA4 }, + { 0x213F, 0x02 }, + { 0x2140, 0x7D }, + { 0x2141, 0x00 }, + { 0x2142, 0x8D }, + { 0x2143, 0x00 }, + { 0x2144, 0xA1 }, + { 0x2145, 0x01 }, + { 0x2146, 0xB1 }, + { 0x2147, 0x01 }, + { 0x2148, 0xAB }, + { 0x2149, 0x02 }, + { 0x214A, 0xBB }, + { 0x214B, 0x02 }, + { 0x214C, 0x17 }, + { 0x214D, 0x5C }, + { 0x214E, 0x00 }, + { 0x2150, 0x00 }, + { 0x2151, 0x00 }, + { 0x2152, 0xF8 }, + { 0x2153, 0x00 }, + { 0x2154, 0xBE }, + { 0x2155, 0x00 }, + { 0x2156, 0x7D }, + { 0x2157, 0x00 }, + { 0x2158, 0x25 }, + { 0x2159, 0x00 }, + { 0x215A, 0x7D }, + { 0x215B, 0x00 }, + { 0x215C, 0x62 }, + { 0x215D, 0x01 }, + { 0x215E, 0xFF }, + { 0x215F, 0x03 }, + { 0x2160, 0x26 }, + { 0x2161, 0x00 }, + { 0x2162, 0x7D }, + { 0x2163, 0x00 }, + { 0x2164, 0x63 }, + { 0x2165, 0x01 }, + { 0x2166, 0xFF }, + { 0x2167, 0x03 }, + { 0x2168, 0xCB }, + { 0x2169, 0x02 }, + { 0x216A, 0xCF }, + { 0x216B, 0x02 }, + { 0x216C, 0xFF }, + { 0x216D, 0x03 }, + { 0x216E, 0xFF }, + { 0x216F, 0x03 }, + { 0x2170, 0xFF }, + { 0x2171, 0x03 }, + { 0x2172, 0xFF }, + { 0x2173, 0x03 }, + { 0x2174, 0xFF }, + { 0x2175, 0x03 }, + { 0x2176, 0xFF }, + { 0x2177, 0x03 }, + { 0x2178, 0x7E }, + { 0x2179, 0x00 }, + { 0x217A, 0xBD }, + { 0x217B, 0x00 }, + { 0x217C, 0xEC }, + { 0x217D, 0x01 }, + { 0x217E, 0x7B }, + { 0x217F, 0x02 }, + { 0x2180, 0xD1 }, + { 0x2181, 0x02 }, + { 0x2182, 0x25 }, + { 0x2183, 0x03 }, + { 0x2184, 0x7F }, + { 0x2185, 0x00 }, + { 0x2186, 0xBD }, + { 0x2187, 0x00 }, + { 0x2188, 0xED }, + { 0x2189, 0x01 }, + { 0x218A, 0x7B }, + { 0x218B, 0x02 }, + { 0x218C, 0xD2 }, + { 0x218D, 0x02 }, + { 0x218E, 0x25 }, + { 0x218F, 0x03 }, + { 0x2190, 0xFF }, + { 0x2191, 0x03 }, + { 0x2192, 0xFF }, + { 0x2193, 0x03 }, + { 0x2194, 0xE9 }, + { 0x2195, 0x01 }, + { 0x2196, 0x21 }, + { 0x2197, 0x03 }, + { 0x2198, 0x17 }, + { 0x2199, 0xFC }, + { 0x219A, 0x7F }, + { 0x219B, 0x01 }, + { 0x219C, 0xFF }, + { 0x219D, 0x03 }, + { 0x21A0, 0x1B }, + { 0x21A1, 0x1B }, + { 0x21A2, 0x1B }, + { 0x21A3, 0x1B }, + { 0x21A4, 0x2E }, + { 0x21A5, 0x80 }, + { 0x21A6, 0x00 }, + { 0x21A8, 0x04 }, + { 0x21A9, 0x98 }, + { 0x21AA, 0x60 }, + { 0x21AB, 0x03 }, + { 0x21AC, 0x7F }, + { 0x21AD, 0x80 }, + { 0x21AE, 0x09 }, + { 0x21B0, 0x1C }, + { 0x21B1, 0x00 }, + { 0x21B2, 0xA0 }, + { 0x21B3, 0x00 }, + { 0x21B4, 0x0C }, + { 0x21B5, 0x00 }, + { 0x21B6, 0x2D }, + { 0x21B7, 0x00 }, + { 0x21B8, 0x20 }, + { 0x21B9, 0x00 }, + { 0x21BA, 0x02 }, + { 0x21BB, 0x00 }, + { 0x21BC, 0xCC }, + { 0x21BD, 0x00 }, + { 0x21BE, 0x4A }, + { 0x21BF, 0x00 }, + { 0x21C0, 0xD0 }, + { 0x21C1, 0x00 }, + { 0x21C2, 0x44 }, + { 0x21C3, 0x00 }, + { 0x21C4, 0x00 }, + { 0x21C5, 0xE0 }, + { 0x21C6, 0x00 }, + { 0x21C8, 0x11 }, + { 0x21C9, 0x00 }, + { 0x21CA, 0x02 }, + { 0x21CC, 0x08 }, + { 0x21CD, 0xC0 }, + { 0x21CE, 0x0C }, + { 0x21D0, 0x44 }, + { 0x21D1, 0x00 }, + { 0x21D2, 0x02 }, + { 0x21D4, 0x02 }, + { 0x21D5, 0x20 }, + { 0x21D6, 0x2C }, + { 0x21D8, 0xFE }, + { 0x21D9, 0x9D }, + { 0x21DA, 0xDF }, + { 0x21DB, 0x03 }, + { 0x21DC, 0x62 }, + { 0x21DD, 0x01 }, + { 0x21DE, 0x7F }, + { 0x21DF, 0x00 }, + { 0x21E0, 0xB7 }, + { 0x21E1, 0x01 }, + { 0x21E2, 0xB5 }, + { 0x21E3, 0x01 }, + { 0x21E4, 0xC1 }, + { 0x21E5, 0x02 }, + { 0x21E6, 0xBF }, + { 0x21E7, 0x02 }, + { 0x21E8, 0xB3 }, + { 0x21E9, 0x0D }, + { 0x21EA, 0x00 }, + { 0x21EB, 0x04 }, + { 0x21EC, 0x90 }, + { 0x21ED, 0x07 }, + { 0x21EE, 0x58 }, + { 0x21EF, 0x04 }, + { 0x21F0, 0x54 }, + { 0x21F1, 0x04 }, + { 0x21F4, 0x02 }, + { 0x21F5, 0x00 }, + { 0x21F6, 0x00 }, + { 0x21F8, 0x3C }, + { 0x21F9, 0x00 }, + { 0x21FC, 0x28 }, + { 0x21FD, 0x00 }, + { 0x21FE, 0x3C }, + { 0x21FF, 0x00 }, + { 0x2200, 0x00 }, + { 0x2204, 0x4C }, + { 0x2205, 0x04 }, + { 0x2206, 0x65 }, + { 0x2207, 0x04 }, + { 0x2208, 0x0A }, + { 0x2209, 0x00 }, + { 0x220C, 0x47 }, + { 0x220D, 0x00 }, + { 0x220E, 0x1F }, + { 0x220F, 0x00 }, + { 0x2210, 0x17 }, + { 0x2211, 0x00 }, + { 0x2212, 0x0F }, + { 0x2213, 0x00 }, + { 0x2214, 0x17 }, + { 0x2215, 0x00 }, + { 0x2216, 0x47 }, + { 0x2217, 0x00 }, + { 0x2218, 0x0F }, + { 0x2219, 0x00 }, + { 0x221A, 0x0F }, + { 0x221B, 0x00 }, + { 0x221C, 0x03 }, + { 0x2220, 0x20 }, + { 0x2221, 0x20 }, + { 0x2222, 0x22 }, + { 0x2223, 0x02 }, + { 0x2224, 0xA7 }, + { 0x2225, 0xAA }, + { 0x2226, 0x80 }, + { 0x2227, 0x08 }, + { 0x2228, 0x01 }, + { 0x22B2, 0x92 }, + { 0x22B4, 0x20 }, + { 0x22B5, 0x00 }, + { 0x22B6, 0x20 }, + { 0x22B7, 0x00 }, + { 0x22B8, 0x20 }, + { 0x22B9, 0x00 }, + { 0x22BA, 0x20 }, + { 0x22BB, 0x00 }, + { 0x22BC, 0x20 }, + { 0x22BD, 0x00 }, + { 0x22BE, 0x20 }, + { 0x22BF, 0x00 }, + { 0x22C0, 0x20 }, + { 0x22C1, 0x00 }, + { 0x22C2, 0x20 }, + { 0x22C3, 0x00 }, + { 0x22C4, 0x20 }, + { 0x22C5, 0x00 }, + { 0x22C6, 0x20 }, + { 0x22C7, 0x00 }, + { 0x22C8, 0x20 }, + { 0x22C9, 0x00 }, + { 0x22CA, 0x20 }, + { 0x22CB, 0x00 }, + { 0x22CC, 0x20 }, + { 0x22CD, 0x00 }, + { 0x22CE, 0x20 }, + { 0x22CF, 0x00 }, + { 0x22DA, 0x00 }, + { 0x2308, 0x01 }, + { 0x2311, 0x09 }, + { 0x2318, 0x40 }, + { 0x2319, 0xCD }, + { 0x231A, 0x54 }, + { 0x2324, 0x10 }, + { 0x2325, 0x00 }, + { 0x2328, 0x00 }, + { 0x2354, 0x0C }, + { 0x23C0, 0x5D }, + { 0x244C, 0x00 }, + { 0x244D, 0x02 }, + { 0x244E, 0x54 }, + { 0x244F, 0x02 }, + { 0x24A0, 0x00 }, + { 0x24DA, 0x6F }, + { 0x24DB, 0x00 }, + { 0x24DC, 0x62 }, + { 0x24DD, 0x01 }, + { 0x24EA, 0x32 }, + { 0x24EB, 0x00 }, + { 0x24EC, 0xDC }, + { 0x24ED, 0x00 }, + { 0x24FA, 0x32 }, + { 0x24FB, 0x00 }, + { 0x24FC, 0xDD }, + { 0x24FD, 0x00 }, + { 0x254A, 0x15 }, + { 0x254B, 0x01 }, + { 0x255A, 0x15 }, + { 0x255B, 0x01 }, + { 0x2560, 0x01 }, + { 0x2561, 0x00 }, + { 0x2562, 0x2A }, + { 0x2563, 0x00 }, + { 0x2564, 0xF8 }, + { 0x2565, 0x00 }, + { 0x2566, 0x15 }, + { 0x2567, 0x01 }, + { 0x2568, 0x0C }, + { 0x2569, 0x02 }, + { 0x256A, 0x31 }, + { 0x256B, 0x02 }, + { 0x2578, 0x90 }, + { 0x2579, 0x01 }, + { 0x257A, 0x92 }, + { 0x257B, 0x01 }, + { 0x257C, 0xB8 }, + { 0x257D, 0x02 }, + { 0x257E, 0xBA }, + { 0x257F, 0x02 }, + { 0x2584, 0x90 }, + { 0x2585, 0x01 }, + { 0x2586, 0x92 }, + { 0x2587, 0x01 }, + { 0x2588, 0xB8 }, + { 0x2589, 0x02 }, + { 0x258A, 0xBA }, + { 0x258B, 0x02 }, + { 0x26B8, 0x10 }, + { 0x26B9, 0x00 }, + { 0x26BA, 0x33 }, + { 0x26BB, 0x00 }, + { 0x26BC, 0x89 }, + { 0x26BD, 0x00 }, + { 0x26BE, 0xB0 }, + { 0x26BF, 0x00 }, + { 0x26C4, 0x4E }, + { 0x26C5, 0x00 }, + { 0x26C8, 0xC9 }, + { 0x26C9, 0x00 }, + { 0x26CC, 0x35 }, + { 0x26CD, 0x01 }, + { 0x26D0, 0xBA }, + { 0x26D1, 0x01 }, + { 0x26D4, 0x7C }, + { 0x26D5, 0x02 }, + { 0x26D8, 0xF6 }, + { 0x26D9, 0x02 }, + { 0x26DE, 0x51 }, + { 0x26DF, 0x00 }, + { 0x26E0, 0x7F }, + { 0x26E1, 0x00 }, + { 0x26E2, 0xCC }, + { 0x26E3, 0x00 }, + { 0x26E4, 0xF8 }, + { 0x26E5, 0x00 }, + { 0x26E6, 0x38 }, + { 0x26E7, 0x01 }, + { 0x26E8, 0x65 }, + { 0x26E9, 0x01 }, + { 0x26EA, 0xBD }, + { 0x26EB, 0x01 }, + { 0x26EE, 0x7F }, + { 0x26EF, 0x02 }, + { 0x26F0, 0xAB }, + { 0x26F1, 0x02 }, + { 0x26F2, 0xF9 }, + { 0x26F3, 0x02 }, + { 0x2722, 0x59 }, + { 0x2723, 0x02 }, + { 0x2938, 0x55 }, + { 0x2939, 0x00 }, + { 0x293A, 0x17 }, + { 0x293B, 0x00 }, + { 0x293C, 0xD0 }, + { 0x293D, 0x00 }, + { 0x293E, 0x91 }, + { 0x293F, 0x00 }, + { 0x2940, 0x3C }, + { 0x2941, 0x01 }, + { 0x2942, 0x0C }, + { 0x2943, 0x01 }, + { 0x2944, 0xC1 }, + { 0x2945, 0x01 }, + { 0x2946, 0x76 }, + { 0x2947, 0x01 }, + { 0x2948, 0x83 }, + { 0x2949, 0x02 }, + { 0x294A, 0xFB }, + { 0x294B, 0x01 }, + { 0x294C, 0xFD }, + { 0x294D, 0x02 }, + { 0x294E, 0xBF }, + { 0x294F, 0x02 }, + { 0x2A06, 0xFF }, + { 0x2A07, 0x03 }, + { 0x2A20, 0x00 }, + { 0x2A21, 0x00 }, + { 0x2A22, 0x7D }, + { 0x2A23, 0x00 }, + { 0x2B11, 0x19 }, + { 0x2B13, 0x15 }, + { 0x2B14, 0x14 }, + { 0x2B15, 0x13 }, + { 0x2B16, 0x12 }, + { 0x2B17, 0x11 }, + { 0x2B18, 0x10 }, + { 0x2B19, 0x0F }, + { 0x2B1A, 0x0E }, + { 0x2B1B, 0x0D }, + { 0x2B1C, 0x0C }, + { 0x2B1D, 0x0B }, + { 0x2B1E, 0x0A }, + { 0x2B1F, 0x09 }, + { 0x2B20, 0x08 }, + { 0x2B21, 0x07 }, + { 0x2B22, 0x06 }, + { 0x2B23, 0x05 }, + { 0x2B24, 0x04 }, + { 0x2B25, 0x03 }, + { 0x2B26, 0x03 }, + { 0x2B38, 0x01 }, + { 0x2B45, 0xE3 }, + { 0x2B50, 0x01 }, + { 0x2B51, 0x00 }, + { 0x2B6D, 0x47 }, + { 0x2B70, 0x02 }, + { 0x2B71, 0x02 }, + { 0x2B72, 0x02 }, + { 0x2B7F, 0x7F }, + { 0x2B80, 0x94 }, + { 0x2B81, 0x06 }, + { 0x2B87, 0x1B }, + { 0x2B88, 0x1B }, + { 0x2B89, 0x17 }, + { 0x2B8A, 0x12 }, + { 0x2B8B, 0x12 }, + { 0x2B8D, 0x2B }, + { 0x2B8E, 0x2B }, + { 0x2B8F, 0x2B }, + { 0x2B90, 0x7F }, + { 0x2B91, 0x1F }, + { 0x2B94, 0x7F }, + { 0x2B95, 0x27 }, + { 0x2B98, 0x7F }, + { 0x2B99, 0x57 }, + { 0x2BA8, 0xBC }, + { 0x2BA9, 0x62 }, + { 0x2BC1, 0x70 }, + { 0x2BC5, 0x80 }, + { 0x2BD5, 0x30 }, + { 0x2BD6, 0xF0 }, + { 0x2BD8, 0xDB }, + { 0x2BD9, 0xF6 }, + { 0x2BDA, 0x63 }, + { 0x2BDB, 0x0C }, + { 0x2BDC, 0x5C }, + { 0x2C98, 0xE1 }, + { 0x2C99, 0x2E }, + { 0x2C9B, 0x86 }, + { 0x2CA9, 0x80 }, + { 0x2CAA, 0x01 }, + { 0x2D39, 0x0E }, + { 0x2D54, 0x00 }, + { 0x2D5B, 0x58 }, + { 0x2D64, 0x64 }, + { 0x2D65, 0x80 }, + { 0x3000, 0x00 }, + { 0x3001, 0x00 }, + { 0x3002, 0x23 }, + { 0x3003, 0xA1 }, + { 0x3004, 0x00 }, + { 0x3005, 0x20 }, + { 0x3006, 0x84 }, + { 0x3007, 0x00 }, + { 0x3008, 0x06 }, + { 0x3009, 0xB4 }, + { 0x300A, 0x1F }, + { 0x300B, 0x00 }, + { 0x300C, 0x00 }, + { 0x300D, 0x1B }, + { 0x300E, 0x90 }, + { 0x300F, 0x97 }, + { 0x3010, 0x00 }, + { 0x3011, 0x00 }, + { 0x3012, 0x21 }, + { 0x3013, 0x21 }, + { 0x3014, 0x00 }, + { 0x3015, 0x20 }, + { 0x3016, 0x84 }, + { 0x3017, 0x00 }, + { 0x3018, 0x30 }, + { 0x3019, 0x09 }, + { 0x301A, 0x46 }, + { 0x301B, 0x00 }, + { 0x3070, 0xC1 }, + { 0x3071, 0x81 }, + { 0x3072, 0x29 }, + { 0x3073, 0x81 }, + { 0x3410, (IMX390_OUT_WIDTH & 0xFF) }, + { 0x3411, (IMX390_OUT_WIDTH >> 8) }, + { 0x3418, (IMX390_OUT_HEIGHT & 0xFF) }, + { 0x3419, (IMX390_OUT_HEIGHT >> 8) }, + { 0x34A0, 0x30 }, + { 0x34C0, 0xD3 }, + { 0x34C1, 0x00 }, + { 0x34C2, 0xD3 }, + { 0x34C3, 0x00 }, + { 0x34C4, 0xD3 }, + { 0x34C5, 0x00 }, + { 0x34C6, 0xD3 }, + { 0x34C7, 0x00 }, + { 0x34C8, 0xE2 }, + { 0x34C9, 0x21 }, + { 0x34CA, 0xE0 }, + { 0x34CB, 0x1F }, + { 0x34CC, 0x06 }, + { 0x34CD, 0x20 }, + { 0x34CE, 0x28 }, + { 0x34CF, 0x1F }, + { 0x3584, 0x00 }, + { 0x3586, 0x00 }, + { 0x3587, 0x01 }, + { 0x3588, 0xE6 }, + { 0x3589, 0x00 }, + { 0x3590, 0x00 }, + { 0x3591, 0x00 }, + { 0x3594, 0x40 }, + { 0x3598, 0x03 }, + { 0x3599, 0x00 }, + { 0x359A, 0x80 }, + { 0x359B, 0x00 }, + { 0x359C, 0x00 }, + { 0x359D, 0x01 }, + { 0x359E, 0x00 }, + { 0x359F, 0x02 }, + { 0x35A0, 0x00 }, + { 0x35A1, 0x04 }, + { 0x35A2, 0x20 }, + { 0x35A3, 0x00 }, + { 0x35A4, 0x40 }, + { 0x35A5, 0x00 }, + { 0x35A6, 0x80 }, + { 0x35A7, 0x00 }, + { 0x35A8, 0x00 }, + { 0x35A9, 0x01 }, + { 0x35AA, 0x3A }, + { 0x35AB, 0x00 }, + { 0x35AC, 0x80 }, + { 0x35AD, 0x00 }, + { 0x35AE, 0x00 }, + { 0x35AF, 0x01 }, + { 0x35B0, 0x00 }, + { 0x35B1, 0x02 }, + { 0x35B2, 0x00 }, + { 0x35B3, 0x04 }, + { 0x35B4, 0x02 }, + { 0x35B5, 0x00 }, + { 0x35B6, 0x04 }, + { 0x35B7, 0x00 }, + { 0x35B8, 0x08 }, + { 0x35B9, 0x00 }, + { 0x35BA, 0x10 }, + { 0x35BB, 0x00 }, + { 0x35BC, 0x03 }, + { 0x35BD, 0x00 }, + { 0x35C8, 0x00 }, + { 0x35C9, 0x01 }, + { 0x35CA, 0x00 }, + { 0x35CB, 0x04 }, + { 0x35CC, 0x00 }, + { 0x35CD, 0x10 }, + { 0x35CE, 0x00 }, + { 0x35CF, 0x40 }, + { 0x35D0, 0x00 }, + { 0x35D1, 0x0C }, + { 0x35D2, 0x00 }, + { 0x35D3, 0x0C }, + { 0x35D4, 0x00 }, + { 0x35D5, 0x0C }, + { 0x35D6, 0x00 }, + { 0x35D7, 0x0C }, + { 0x35D8, 0x00 }, + { 0x35D9, 0x00 }, + { 0x35DA, 0x08 }, + { 0x35DB, 0x00 }, + { 0x35DC, 0xD8 }, + { 0x35DD, 0x0E }, + { 0x35F0, 0x00 }, + { 0x35F1, 0x10 }, + { 0x35F2, 0x00 }, + { 0x35F3, 0x10 }, + { 0x35F4, 0x00 }, + { 0x35F5, 0x10 }, + { 0x35F6, 0x00 }, + { 0x35F7, 0x03 }, + { 0x35F8, 0x00 }, + { 0x35F9, 0x01 }, + { 0x35FA, 0x38 }, + { 0x35FB, 0x00 }, + { 0x35FC, 0xB3 }, + { 0x35FD, 0x01 }, + { 0x35FE, 0x00 }, + { 0x35FF, 0x00 }, + { 0x3600, 0x04 }, + { 0x3601, 0x06 }, + { 0x3604, 0x03 }, + { 0x3605, 0x00 }, + { 0x3608, 0x03 }, + { 0x3609, 0x00 }, + { 0x360C, 0x00 }, + { 0x360D, 0x00 }, + { 0x3610, 0x10 }, + { 0x3611, 0x01 }, + { 0x3612, 0x00 }, + { 0x3613, 0x00 }, + { 0x3614, 0x00 }, + { 0x3615, 0x00 }, + { 0x361C, 0x00 }, + { 0x361D, 0x01 }, + { 0x361E, 0x00 }, + { 0x361F, 0x01 }, + { 0x3620, 0x01 }, + { 0x3621, 0x00 }, + { 0x3622, 0xB0 }, + { 0x3623, 0x04 }, + { 0x3624, 0xDC }, + { 0x3625, 0x05 }, + { 0x3626, 0x00 }, + { 0x3627, 0x01 }, + { 0x3628, 0xFF }, + { 0x3629, 0x0F }, + { 0x362A, 0x00 }, + { 0x362B, 0x10 }, + { 0x362C, 0x00 }, + { 0x362D, 0x01 }, + { 0x3630, 0x40 }, + { 0x3631, 0x00 }, + { 0x3632, 0x40 }, + { 0x3633, 0x00 }, + { 0x3634, 0x40 }, + { 0x3635, 0x00 }, + { 0x3636, 0x40 }, + { 0x3637, 0x00 }, + { 0x3638, 0x40 }, + { 0x3639, 0x00 }, + { 0x363A, 0x40 }, + { 0x363B, 0x00 }, + { 0x363C, 0x40 }, + { 0x363D, 0x00 }, + { 0x363E, 0x40 }, + { 0x363F, 0x00 }, + { 0x36C4, 0x99 }, + { 0x36C5, 0x09 }, + { 0x36C6, 0x18 }, + { 0x36C7, 0x07 }, + { 0x36C8, 0x65 }, + { 0x36C9, 0x0E }, + { 0x36CC, 0x99 }, + { 0x36CD, 0x01 }, + { 0x36CE, 0x47 }, + { 0x36CF, 0x00 }, + { 0x36D0, 0x04 }, + { 0x36D1, 0x00 }, + { 0x36D4, 0x65 }, + { 0x36D5, 0x0E }, + { 0x36D6, 0xA4 }, + { 0x36D7, 0x0A }, + { 0x36D8, 0x65 }, + { 0x36D9, 0x0E }, + { 0x36DC, 0x65 }, + { 0x36DD, 0x0E }, + { 0x36DE, 0xA4 }, + { 0x36DF, 0x0A }, + { 0x36E0, 0x65 }, + { 0x36E1, 0x0E }, + { 0x36E4, 0x65 }, + { 0x36E5, 0x0E }, + { 0x36E6, 0xA4 }, + { 0x36E7, 0x0A }, + { 0x36E8, 0x65 }, + { 0x36E9, 0x0E }, + { 0x36EE, 0x00 }, + { 0x36EF, 0x00 }, + { 0x36F0, 0x00 }, + { 0x36F1, 0x80 }, + { 0x36F8, 0x00 }, + { 0x3702, 0x03 }, + { 0x3703, 0x04 }, + { 0x3704, 0x08 }, + { 0x370E, 0x0E }, + { 0x3718, 0x62 }, + { 0x3719, 0x4A }, + { 0x371A, 0x38 }, + { 0x371B, 0x20 }, + { 0x371C, 0x64 }, + { 0x371D, 0x42 }, + { 0x371E, 0x32 }, + { 0x371F, 0x1B }, + { 0x3720, 0x9C }, + { 0x3721, 0xA4 }, + { 0x3722, 0xAC }, + { 0x3723, 0xB4 }, + { 0x3748, 0xAA }, + { 0x3749, 0x96 }, + { 0x374A, 0x7D }, + { 0x374B, 0x69 }, + { 0x37C0, 0x00 }, + { 0x37C1, 0x00 }, + { 0x37C2, 0x00 }, + { 0x37C4, 0x00 }, + { 0x37C5, 0x00 }, + { 0x37C6, 0x00 }, + { 0x37C8, 0x00 }, + { 0x37C9, 0x00 }, + { 0x37CA, 0x00 }, + { 0x37CC, 0x00 }, + { 0x37CD, 0x00 }, + { 0x37CE, 0x00 }, + { 0x37D0, 0x00 }, + { 0x37D1, 0x00 }, + { 0x37D2, 0x00 }, + { 0x37D4, 0x00 }, + { 0x37D5, 0x00 }, + { 0x37D6, 0x00 }, + { 0x37D8, 0x00 }, + { 0x37D9, 0x00 }, + { 0x37DA, 0x00 }, + { 0x37DC, 0x00 }, + { 0x37DD, 0x00 }, + { 0x37DE, 0x00 }, + { 0x37E0, 0x00 }, + { 0x37E1, 0x00 }, + { 0x37E2, 0x00 }, + { 0x37E4, 0x00 }, + { 0x37E5, 0x00 }, + { 0x37E6, 0x00 }, + { 0x37E8, 0x00 }, + { 0x37E9, 0x00 }, + { 0x37EA, 0x00 }, + { 0x37EC, 0x00 }, + { 0x37ED, 0x00 }, + { 0x37EE, 0x00 }, + { 0x37F0, 0x00 }, + { 0x37F4, 0x00 }, + { 0x37F5, 0x1E }, + { 0x37F6, 0x34 }, + { 0x37F7, 0x00 }, + { 0x37F8, 0xFF }, + { 0x37F9, 0xFF }, + { 0x37FA, 0x03 }, + { 0x37FC, 0x00 }, + { 0x37FD, 0x00 }, + { 0x37FE, 0x04 }, + { 0x3800, 0xFF }, + { 0x3801, 0xFF }, + { 0x3802, 0x03 }, + { 0x3804, 0x00 }, + { 0x3805, 0x00 }, + { 0x3806, 0x04 }, + { 0x3808, 0x00 }, + { 0x3809, 0x00 }, + { 0x380A, 0x00 }, + { 0x380C, 0x00 }, + { 0x380D, 0x00 }, + { 0x380E, 0x00 }, + { 0x3810, 0x00 }, + { 0x3811, 0x00 }, + { 0x3812, 0x00 }, + { 0x3814, 0x00 }, + { 0x3815, 0x00 }, + { 0x3816, 0x00 }, + { 0x3818, 0x00 }, + { 0x3819, 0x00 }, + { 0x381A, 0x00 }, + { 0x381C, 0x00 }, + { 0x381D, 0x00 }, + { 0x381E, 0x00 }, + { 0x3820, 0x00 }, + { 0x3821, 0x00 }, + { 0x3822, 0x00 }, + { 0x3824, 0x00 }, + { 0x3825, 0x00 }, + { 0x3826, 0x00 }, + { 0x3828, 0x00 }, + { 0x3829, 0x00 }, + { 0x382A, 0x00 }, + { 0x382C, 0x00 }, + { 0x382D, 0x00 }, + { 0x382E, 0x00 }, + { 0x3830, 0x00 }, + { 0x3831, 0x00 }, + { 0x3832, 0x00 }, + { 0x3834, 0x00 }, + { 0x3835, 0x00 }, + { 0x3836, 0x00 }, + { 0x3838, 0x00 }, + { 0x3839, 0x00 }, + { 0x383A, 0x00 }, + { 0x383B, 0x00 }, + { 0x383C, 0x00 }, + { 0x383D, 0x00 }, + { 0x383E, 0x00 }, + { 0x383F, 0x00 }, + { 0x3840, 0x00 }, + { 0x3841, 0x00 }, + { 0x3842, 0x00 }, + { 0x3843, 0x00 }, + { 0x3844, 0x00 }, + { 0x3845, 0x00 }, + { 0x3846, 0x00 }, + { 0x3847, 0x00 }, + { 0x3848, 0x00 }, + { 0x3849, 0x00 }, + { 0x384A, 0x00 }, + { 0x384B, 0x00 }, + { 0x384C, 0x00 }, + { 0x384D, 0x00 }, + { 0x384E, 0x00 }, + { 0x384F, 0x00 }, + { 0x3850, 0xFF }, + { 0x3851, 0x0F }, + { 0x3852, 0x00 }, + { 0x3853, 0x10 }, + { 0x3854, 0xFF }, + { 0x3855, 0x0F }, + { 0x3856, 0x00 }, + { 0x3857, 0x10 }, + { 0x3858, 0xFF }, + { 0x3859, 0x0F }, + { 0x385A, 0x00 }, + { 0x385B, 0x10 }, + { 0x385C, 0x02 }, + { 0x385D, 0x00 }, + { 0x385E, 0x06 }, + { 0x385F, 0x00 }, + { 0x3860, 0x06 }, + { 0x3861, 0x00 }, + { 0x3862, 0x08 }, + { 0x3863, 0x00 }, + { 0x3864, 0x02 }, + { 0x3865, 0x00 }, + { 0x38A0, 0x01 }, + { 0x38A1, 0x01 }, + { 0x38A2, 0x00 }, + { 0x38A3, 0x01 }, + { 0x38A4, 0x07 }, + { 0x38A5, 0x00 }, + { 0x38A6, 0x04 }, + { 0x38A7, 0x05 }, + { 0x38A8, 0x00 }, + { 0x38A9, 0x00 }, + { 0x38AC, 0x00 }, + { 0x38AD, 0x00 }, + { 0x38AE, 0x01 }, + { 0x38B0, 0x02 }, + { 0x38B2, 0x22 }, + { 0x38B3, 0x00 }, + { 0x38B4, 0x17 }, + { 0x38B5, 0x00 }, + { 0x38B6, 0x11 }, + { 0x38B7, 0x00 }, + { 0x38B8, 0x0E }, + { 0x38B9, 0x00 }, + { 0x38BA, 0x2A }, + { 0x38BB, 0x00 }, + { 0x38BC, 0x1C }, + { 0x38BD, 0x00 }, + { 0x38BE, 0x14 }, + { 0x38BF, 0x00 }, + { 0x38C0, 0x10 }, + { 0x38C1, 0x00 }, + { 0x38C2, 0x31 }, + { 0x38C3, 0x00 }, + { 0x38C4, 0x21 }, + { 0x38C5, 0x00 }, + { 0x38C6, 0x18 }, + { 0x38C7, 0x00 }, + { 0x38C8, 0x12 }, + { 0x38C9, 0x00 }, + { 0x38CA, 0x3C }, + { 0x38CB, 0x00 }, + { 0x38CC, 0x29 }, + { 0x38CD, 0x00 }, + { 0x38CE, 0x1D }, + { 0x38CF, 0x00 }, + { 0x38D0, 0x15 }, + { 0x38D1, 0x00 }, + { 0x38D2, 0x4E }, + { 0x38D3, 0x00 }, + { 0x38D4, 0x35 }, + { 0x38D5, 0x00 }, + { 0x38D6, 0x26 }, + { 0x38D7, 0x00 }, + { 0x38D8, 0x1A }, + { 0x38D9, 0x00 }, + { 0x38DA, 0x69 }, + { 0x38DB, 0x00 }, + { 0x38DC, 0x48 }, + { 0x38DD, 0x00 }, + { 0x38DE, 0x33 }, + { 0x38DF, 0x00 }, + { 0x38E0, 0x22 }, + { 0x38E1, 0x00 }, + { 0x38E2, 0x93 }, + { 0x38E3, 0x00 }, + { 0x38E4, 0x64 }, + { 0x38E5, 0x00 }, + { 0x38E6, 0x48 }, + { 0x38E7, 0x00 }, + { 0x38E8, 0x30 }, + { 0x38E9, 0x00 }, + { 0x38EA, 0xD3 }, + { 0x38EB, 0x00 }, + { 0x38EC, 0x90 }, + { 0x38ED, 0x00 }, + { 0x38EE, 0x69 }, + { 0x38EF, 0x00 }, + { 0x38F0, 0x49 }, + { 0x38F1, 0x00 }, + { 0x38F2, 0x39 }, + { 0x38F3, 0x01 }, + { 0x38F4, 0xD5 }, + { 0x38F5, 0x00 }, + { 0x38F6, 0x9F }, + { 0x38F7, 0x00 }, + { 0x38F8, 0x75 }, + { 0x38F9, 0x00 }, + { 0x38FA, 0x00 }, + { 0x38FB, 0x01 }, + { 0x38FC, 0x00 }, + { 0x38FD, 0x01 }, + { 0x38FE, 0x00 }, + { 0x38FF, 0x01 }, + { 0x3900, 0x00 }, + { 0x3901, 0x01 }, + { 0x3902, 0x70 }, + { 0x3903, 0x00 }, + { 0x3904, 0x30 }, + { 0x3905, 0x00 }, + { 0x3906, 0x25 }, + { 0x3907, 0x00 }, + { 0x3908, 0x20 }, + { 0x3909, 0x00 }, + { 0x390A, 0xB2 }, + { 0x390B, 0x00 }, + { 0x390C, 0x80 }, + { 0x390D, 0x00 }, + { 0x390E, 0x70 }, + { 0x390F, 0x00 }, + { 0x3910, 0x50 }, + { 0x3911, 0x00 }, + { 0x3912, 0xB2 }, + { 0x3913, 0x00 }, + { 0x3914, 0x80 }, + { 0x3915, 0x00 }, + { 0x3916, 0x70 }, + { 0x3917, 0x00 }, + { 0x3918, 0x50 }, + { 0x3919, 0x00 }, + { 0x391A, 0xB2 }, + { 0x391B, 0x00 }, + { 0x391C, 0x80 }, + { 0x391D, 0x00 }, + { 0x391E, 0x70 }, + { 0x391F, 0x00 }, + { 0x3920, 0x50 }, + { 0x3921, 0x00 }, + { 0x3922, 0x40 }, + { 0x3923, 0x00 }, + { 0x3924, 0x40 }, + { 0x3925, 0x00 }, + { 0x3926, 0x40 }, + { 0x3927, 0x00 }, + { 0x3928, 0x40 }, + { 0x3929, 0x00 }, + { 0x392A, 0x80 }, + { 0x392B, 0x00 }, + { 0x392C, 0x80 }, + { 0x392D, 0x00 }, + { 0x392E, 0x80 }, + { 0x392F, 0x00 }, + { 0x3930, 0x80 }, + { 0x3931, 0x00 }, + { 0x3932, 0x80 }, + { 0x3933, 0x80 }, + { 0x3934, 0x80 }, + { 0x3940, 0x01 }, + { 0x3941, 0x01 }, + { 0x3942, 0x00 }, + { 0x3943, 0x01 }, + { 0x3944, 0x07 }, + { 0x3945, 0x00 }, + { 0x3946, 0x04 }, + { 0x3947, 0x05 }, + { 0x3948, 0x00 }, + { 0x3949, 0x00 }, + { 0x394C, 0x00 }, + { 0x394D, 0x00 }, + { 0x394E, 0x01 }, + { 0x3950, 0x03 }, + { 0x3952, 0x14 }, + { 0x3953, 0x00 }, + { 0x3954, 0x0F }, + { 0x3955, 0x00 }, + { 0x3956, 0x0E }, + { 0x3957, 0x00 }, + { 0x3958, 0x0E }, + { 0x3959, 0x00 }, + { 0x395A, 0x19 }, + { 0x395B, 0x00 }, + { 0x395C, 0x11 }, + { 0x395D, 0x00 }, + { 0x395E, 0x0F }, + { 0x395F, 0x00 }, + { 0x3960, 0x0E }, + { 0x3961, 0x00 }, + { 0x3962, 0x1C }, + { 0x3963, 0x00 }, + { 0x3964, 0x13 }, + { 0x3965, 0x00 }, + { 0x3966, 0x0F }, + { 0x3967, 0x00 }, + { 0x3968, 0x0E }, + { 0x3969, 0x00 }, + { 0x396A, 0x23 }, + { 0x396B, 0x00 }, + { 0x396C, 0x15 }, + { 0x396D, 0x00 }, + { 0x396E, 0x11 }, + { 0x396F, 0x00 }, + { 0x3970, 0x0E }, + { 0x3971, 0x00 }, + { 0x3972, 0x2E }, + { 0x3973, 0x00 }, + { 0x3974, 0x1A }, + { 0x3975, 0x00 }, + { 0x3976, 0x14 }, + { 0x3977, 0x00 }, + { 0x3978, 0x0F }, + { 0x3979, 0x00 }, + { 0x397A, 0x3E }, + { 0x397B, 0x00 }, + { 0x397C, 0x23 }, + { 0x397D, 0x00 }, + { 0x397E, 0x1A }, + { 0x397F, 0x00 }, + { 0x3980, 0x12 }, + { 0x3981, 0x00 }, + { 0x3982, 0x56 }, + { 0x3983, 0x00 }, + { 0x3984, 0x31 }, + { 0x3985, 0x00 }, + { 0x3986, 0x25 }, + { 0x3987, 0x00 }, + { 0x3988, 0x1A }, + { 0x3989, 0x00 }, + { 0x398A, 0x7B }, + { 0x398B, 0x00 }, + { 0x398C, 0x49 }, + { 0x398D, 0x00 }, + { 0x398E, 0x39 }, + { 0x398F, 0x00 }, + { 0x3990, 0x2C }, + { 0x3991, 0x00 }, + { 0x3992, 0xB4 }, + { 0x3993, 0x00 }, + { 0x3994, 0x75 }, + { 0x3995, 0x00 }, + { 0x3996, 0x61 }, + { 0x3997, 0x00 }, + { 0x3998, 0x53 }, + { 0x3999, 0x00 }, + { 0x399A, 0x00 }, + { 0x399B, 0x01 }, + { 0x399C, 0x00 }, + { 0x399D, 0x01 }, + { 0x399E, 0x00 }, + { 0x399F, 0x01 }, + { 0x39A0, 0x00 }, + { 0x39A1, 0x01 }, + { 0x39A2, 0x70 }, + { 0x39A3, 0x00 }, + { 0x39A4, 0x30 }, + { 0x39A5, 0x00 }, + { 0x39A6, 0x25 }, + { 0x39A7, 0x00 }, + { 0x39A8, 0x20 }, + { 0x39A9, 0x00 }, + { 0x39AA, 0xB2 }, + { 0x39AB, 0x00 }, + { 0x39AC, 0x80 }, + { 0x39AD, 0x00 }, + { 0x39AE, 0x70 }, + { 0x39AF, 0x00 }, + { 0x39B0, 0x80 }, + { 0x39B1, 0x00 }, + { 0x39B2, 0xB2 }, + { 0x39B3, 0x00 }, + { 0x39B4, 0x80 }, + { 0x39B5, 0x00 }, + { 0x39B6, 0x70 }, + { 0x39B7, 0x00 }, + { 0x39B8, 0x80 }, + { 0x39B9, 0x00 }, + { 0x39BA, 0xB2 }, + { 0x39BB, 0x00 }, + { 0x39BC, 0x80 }, + { 0x39BD, 0x00 }, + { 0x39BE, 0x70 }, + { 0x39BF, 0x00 }, + { 0x39C0, 0x80 }, + { 0x39C1, 0x00 }, + { 0x39C2, 0x40 }, + { 0x39C3, 0x00 }, + { 0x39C4, 0x40 }, + { 0x39C5, 0x00 }, + { 0x39C6, 0x40 }, + { 0x39C7, 0x00 }, + { 0x39C8, 0x40 }, + { 0x39C9, 0x00 }, + { 0x39CA, 0x80 }, + { 0x39CB, 0x00 }, + { 0x39CC, 0x80 }, + { 0x39CD, 0x00 }, + { 0x39CE, 0x80 }, + { 0x39CF, 0x00 }, + { 0x39D0, 0x80 }, + { 0x39D1, 0x00 }, + { 0x39D2, 0x80 }, + { 0x39D3, 0x80 }, + { 0x39D4, 0x80 }, + { 0x39E0, 0x01 }, + { 0x39E1, 0x00 }, + { 0x39E4, 0x40 }, + { 0x39E5, 0x01 }, + { 0x39E6, 0x01 }, + { 0x39E8, 0x00 }, + { 0x39E9, 0x01 }, + { 0x39EA, 0x00 }, + { 0x39EB, 0x00 }, + { 0x39EC, 0x01 }, + { 0x39ED, 0x00 }, + { 0x39EE, 0x01 }, + { 0x39F0, 0x03 }, + { 0x39F1, 0x04 }, + { 0x39F2, 0x0E }, + { 0x39F4, 0x1C }, + { 0x39F5, 0x00 }, + { 0x39F6, 0x13 }, + { 0x39F7, 0x00 }, + { 0x39F8, 0x0D }, + { 0x39F9, 0x00 }, + { 0x39FA, 0x07 }, + { 0x39FB, 0x00 }, + { 0x39FC, 0x38 }, + { 0x39FD, 0x00 }, + { 0x39FE, 0x1C }, + { 0x39FF, 0x00 }, + { 0x3A00, 0x11 }, + { 0x3A01, 0x00 }, + { 0x3A02, 0x08 }, + { 0x3A03, 0x00 }, + { 0x3A04, 0x4A }, + { 0x3A05, 0x00 }, + { 0x3A06, 0x23 }, + { 0x3A07, 0x00 }, + { 0x3A08, 0x15 }, + { 0x3A09, 0x00 }, + { 0x3A0A, 0x09 }, + { 0x3A0B, 0x00 }, + { 0x3A0C, 0x65 }, + { 0x3A0D, 0x00 }, + { 0x3A0E, 0x2D }, + { 0x3A0F, 0x00 }, + { 0x3A10, 0x1A }, + { 0x3A11, 0x00 }, + { 0x3A12, 0x0B }, + { 0x3A13, 0x00 }, + { 0x3A14, 0x8D }, + { 0x3A15, 0x00 }, + { 0x3A16, 0x3D }, + { 0x3A17, 0x00 }, + { 0x3A18, 0x23 }, + { 0x3A19, 0x00 }, + { 0x3A1A, 0x0E }, + { 0x3A1B, 0x00 }, + { 0x3A1C, 0xC5 }, + { 0x3A1D, 0x00 }, + { 0x3A1E, 0x55 }, + { 0x3A1F, 0x00 }, + { 0x3A20, 0x30 }, + { 0x3A21, 0x00 }, + { 0x3A22, 0x13 }, + { 0x3A23, 0x00 }, + { 0x3A24, 0x16 }, + { 0x3A25, 0x01 }, + { 0x3A26, 0x76 }, + { 0x3A27, 0x00 }, + { 0x3A28, 0x42 }, + { 0x3A29, 0x00 }, + { 0x3A2A, 0x1A }, + { 0x3A2B, 0x00 }, + { 0x3A2C, 0x88 }, + { 0x3A2D, 0x01 }, + { 0x3A2E, 0xA7 }, + { 0x3A2F, 0x00 }, + { 0x3A30, 0x5D }, + { 0x3A31, 0x00 }, + { 0x3A32, 0x24 }, + { 0x3A33, 0x00 }, + { 0x3A34, 0x2A }, + { 0x3A35, 0x02 }, + { 0x3A36, 0xEB }, + { 0x3A37, 0x00 }, + { 0x3A38, 0x83 }, + { 0x3A39, 0x00 }, + { 0x3A3A, 0x32 }, + { 0x3A3B, 0x00 }, + { 0x3A3C, 0x00 }, + { 0x3A3D, 0x01 }, + { 0x3A3E, 0x00 }, + { 0x3A3F, 0x01 }, + { 0x3A40, 0x00 }, + { 0x3A41, 0x01 }, + { 0x3A42, 0x00 }, + { 0x3A43, 0x01 }, + { 0x3A44, 0x80 }, + { 0x3A45, 0x00 }, + { 0x3A46, 0x50 }, + { 0x3A47, 0x00 }, + { 0x3A48, 0x30 }, + { 0x3A49, 0x00 }, + { 0x3A4A, 0x20 }, + { 0x3A4B, 0x00 }, + { 0x3A4C, 0x99 }, + { 0x3A4D, 0x00 }, + { 0x3A4E, 0x80 }, + { 0x3A4F, 0x00 }, + { 0x3A50, 0x80 }, + { 0x3A51, 0x00 }, + { 0x3A52, 0x80 }, + { 0x3A53, 0x00 }, + { 0x3A54, 0x99 }, + { 0x3A55, 0x00 }, + { 0x3A56, 0x80 }, + { 0x3A57, 0x00 }, + { 0x3A58, 0x80 }, + { 0x3A59, 0x00 }, + { 0x3A5A, 0x80 }, + { 0x3A5B, 0x00 }, + { 0x3A5C, 0x99 }, + { 0x3A5D, 0x00 }, + { 0x3A5E, 0x80 }, + { 0x3A5F, 0x00 }, + { 0x3A60, 0x80 }, + { 0x3A61, 0x00 }, + { 0x3A62, 0x80 }, + { 0x3A63, 0x00 }, + { 0x3A64, 0x1C }, + { 0x3A65, 0x00 }, + { 0x3A66, 0x13 }, + { 0x3A67, 0x00 }, + { 0x3A68, 0x0D }, + { 0x3A69, 0x00 }, + { 0x3A6A, 0x07 }, + { 0x3A6B, 0x00 }, + { 0x3A6C, 0x0C }, + { 0x3A6D, 0x00 }, + { 0x3A6E, 0x09 }, + { 0x3A6F, 0x00 }, + { 0x3A70, 0x06 }, + { 0x3A71, 0x00 }, + { 0x3A72, 0x03 }, + { 0x3A73, 0x00 }, + { 0x3A74, 0x1F }, + { 0x3A75, 0x00 }, + { 0x3A76, 0x1B }, + { 0x3A77, 0x00 }, + { 0x3A78, 0x0F }, + { 0x3A79, 0x00 }, + { 0x3A7A, 0x08 }, + { 0x3A7B, 0x00 }, + { 0x3A7C, 0x80 }, + { 0x3A7D, 0x00 }, + { 0x3A7E, 0x80 }, + { 0x3A7F, 0x00 }, + { 0x3A80, 0x80 }, + { 0x3A81, 0x00 }, + { 0x3A82, 0x80 }, + { 0x3A83, 0x00 }, + { 0x3A84, 0x09 }, + { 0x3A85, 0x00 }, + { 0x3A86, 0x04 }, + { 0x3A87, 0x00 }, + { 0x3A88, 0x03 }, + { 0x3A89, 0x00 }, + { 0x3A8A, 0x01 }, + { 0x3A8B, 0x00 }, + { 0x3A8C, 0x19 }, + { 0x3A8D, 0x01 }, + { 0x3A8E, 0xD2 }, + { 0x3A8F, 0x00 }, + { 0x3A90, 0x8C }, + { 0x3A91, 0x00 }, + { 0x3A92, 0x64 }, + { 0x3A93, 0x00 }, + { 0x3A94, 0xFF }, + { 0x3A95, 0x00 }, + { 0x3A96, 0xD2 }, + { 0x3A97, 0x00 }, + { 0x3A98, 0x8C }, + { 0x3A99, 0x00 }, + { 0x3A9A, 0x64 }, + { 0x3A9B, 0x00 }, + { 0x3A9C, 0x08 }, + { 0x3A9D, 0x10 }, + { 0x3A9E, 0x80 }, + { 0x3A9F, 0x80 }, + { 0x3AA0, 0x80 }, + { 0x3AA1, 0x04 }, + { 0x3AA2, 0x05 }, + { 0x3AC0, 0x01 }, + { 0x3AC4, 0x81 }, + { 0x3AC5, 0x00 }, + { 0x3AC6, 0x00 }, + { 0x3AC7, 0x00 }, + { 0x3AC8, 0x00 }, + { 0x3AC9, 0x00 }, + { 0x3ACA, 0x00 }, + { 0x3ACB, 0x00 }, + { 0x3ACC, 0x02 }, + { 0x3ACD, 0x00 }, + { 0x3ACE, 0x81 }, + { 0x3ACF, 0x00 }, + { 0x3AD0, 0x00 }, + { 0x3AD1, 0x00 }, + { 0x3AD2, 0xFD }, + { 0x3AD3, 0x03 }, + { 0x3AD4, 0x02 }, + { 0x3AD5, 0x00 }, + { 0x3AD6, 0x00 }, + { 0x3AD7, 0x00 }, + { 0x3AD8, 0x81 }, + { 0x3AD9, 0x00 }, + { 0x3ADA, 0xFD }, + { 0x3ADB, 0x03 }, + { 0x3ADC, 0xFF }, + { 0x3ADD, 0x03 }, + { 0x3ADE, 0x01 }, + { 0x3ADF, 0x00 }, + { 0x3AE0, 0x01 }, + { 0x3AE1, 0x00 }, + { 0x3AE2, 0x7E }, + { 0x3AE3, 0x00 }, + { 0x3AF4, 0x00 }, + { 0x3AF6, 0x40 }, + { 0x3AF7, 0x1E }, + { 0x3AF8, 0x01 }, + { 0x3AFA, 0x63 }, + { 0x3AFB, 0x09 }, + { 0x3AFC, 0x11 }, + { 0x3AFD, 0x09 }, + { 0x3AFE, 0x00 }, + { 0x3AFF, 0x00 }, + { 0x3B00, 0x00 }, + { 0x3B01, 0x00 }, + { 0x3B02, 0x84 }, + { 0x3B03, 0x06 }, + { 0x3B04, 0x30 }, + { 0x3B05, 0x06 }, + { 0x3B06, 0x00 }, + { 0x3B07, 0x00 }, + { 0x3B08, 0x00 }, + { 0x3B09, 0x00 }, + { 0x3B0A, 0x00 }, + { 0x3B0B, 0x00 }, + { 0x3B0C, 0x00 }, + { 0x3B0D, 0x00 }, + { 0x3B0E, 0x00 }, + { 0x3B0F, 0x00 }, + { 0x3B10, 0x00 }, + { 0x3B11, 0x00 }, + { 0x3B12, 0x00 }, + { 0x3B13, 0x00 }, + { 0x3B14, 0x00 }, + { 0x3B15, 0x00 }, + { 0x3B16, 0x00 }, + { 0x3B17, 0x00 }, + { 0x3B18, 0x00 }, + { 0x3B19, 0x00 }, + { 0x3B1A, 0x00 }, + { 0x3B1B, 0x00 }, + { 0x3B1C, 0x00 }, + { 0x3B1D, 0x00 }, + { 0x3B1E, 0x00 }, + { 0x3B1F, 0x00 }, + { 0x3B20, 0x00 }, + { 0x3B21, 0x00 }, + { 0x3B22, 0x00 }, + { 0x3B23, 0x00 }, + { 0x3B24, 0x00 }, + { 0x3B25, 0x00 }, + { 0x3B26, 0x00 }, + { 0x3B27, 0x00 }, + { 0x3B28, 0x00 }, + { 0x3B29, 0x00 }, + { 0x3B2A, 0x00 }, + { 0x3B2C, 0x00 }, + { 0x3B2E, 0x00 }, + { 0x3B30, 0x00 }, + { 0x3B32, 0x0C }, + { 0x4000, 0xD1 }, + { 0x4001, 0xC0 }, + { 0x4002, 0xC0 }, + { 0x4003, 0xB8 }, + { 0x4004, 0xC0 }, + { 0x4005, 0xB8 }, + { 0x4006, 0xB9 }, + { 0x4007, 0xB7 }, + { 0x4008, 0xB0 }, + { 0x4009, 0xAB }, + { 0x400A, 0xAC }, + { 0x400B, 0xAB }, + { 0x400C, 0xA8 }, + { 0x400D, 0xA6 }, + { 0x400E, 0xA6 }, + { 0x400F, 0xA5 }, + { 0x4010, 0xA2 }, + { 0x4011, 0xA0 }, + { 0x4012, 0xA0 }, + { 0x4013, 0x9F }, + { 0x4014, 0xA4 }, + { 0x4015, 0xA2 }, + { 0x4016, 0xA2 }, + { 0x4017, 0x9C }, + { 0x4018, 0xA8 }, + { 0x4019, 0xA6 }, + { 0x401A, 0xA8 }, + { 0x401B, 0xAA }, + { 0x401C, 0xB0 }, + { 0x401D, 0xAE }, + { 0x401E, 0xAE }, + { 0x401F, 0xAE }, + { 0x4020, 0xBA }, + { 0x4021, 0xAE }, + { 0x4022, 0xAF }, + { 0x4023, 0xAE }, + { 0x4024, 0xC6 }, + { 0x4025, 0xBD }, + { 0x4026, 0xBD }, + { 0x4027, 0xBA }, + { 0x4028, 0xB0 }, + { 0x4029, 0xA9 }, + { 0x402A, 0xAA }, + { 0x402B, 0xA8 }, + { 0x402C, 0x9F }, + { 0x402D, 0x9C }, + { 0x402E, 0x9C }, + { 0x402F, 0x9B }, + { 0x4030, 0x93 }, + { 0x4031, 0x91 }, + { 0x4032, 0x92 }, + { 0x4033, 0x91 }, + { 0x4034, 0x8D }, + { 0x4035, 0x8C }, + { 0x4036, 0x8C }, + { 0x4037, 0x8C }, + { 0x4038, 0x8F }, + { 0x4039, 0x8E }, + { 0x403A, 0x8E }, + { 0x403B, 0x8E }, + { 0x403C, 0x98 }, + { 0x403D, 0x96 }, + { 0x403E, 0x96 }, + { 0x403F, 0x95 }, + { 0x4040, 0xA4 }, + { 0x4041, 0xA0 }, + { 0x4042, 0xA0 }, + { 0x4043, 0x9E }, + { 0x4044, 0xB3 }, + { 0x4045, 0xAE }, + { 0x4046, 0xAF }, + { 0x4047, 0xAB }, + { 0x4048, 0xC2 }, + { 0x4049, 0xB7 }, + { 0x404A, 0xB8 }, + { 0x404B, 0xB5 }, + { 0x404C, 0xAB }, + { 0x404D, 0xA4 }, + { 0x404E, 0xA5 }, + { 0x404F, 0xA3 }, + { 0x4050, 0x99 }, + { 0x4051, 0x96 }, + { 0x4052, 0x96 }, + { 0x4053, 0x96 }, + { 0x4054, 0x8B }, + { 0x4055, 0x8A }, + { 0x4056, 0x8A }, + { 0x4057, 0x8A }, + { 0x4058, 0x82 }, + { 0x4059, 0x81 }, + { 0x405A, 0x81 }, + { 0x405B, 0x81 }, + { 0x405C, 0x85 }, + { 0x405D, 0x86 }, + { 0x405E, 0x85 }, + { 0x405F, 0x85 }, + { 0x4060, 0x90 }, + { 0x4061, 0x90 }, + { 0x4062, 0x8F }, + { 0x4063, 0x8F }, + { 0x4064, 0x9D }, + { 0x4065, 0x9B }, + { 0x4066, 0x9B }, + { 0x4067, 0x9A }, + { 0x4068, 0xAF }, + { 0x4069, 0xAA }, + { 0x406A, 0xAC }, + { 0x406B, 0xAA }, + { 0x406C, 0xC2 }, + { 0x406D, 0xB7 }, + { 0x406E, 0xB8 }, + { 0x406F, 0xB5 }, + { 0x4070, 0xAB }, + { 0x4071, 0xA4 }, + { 0x4072, 0xA4 }, + { 0x4073, 0xA3 }, + { 0x4074, 0x99 }, + { 0x4075, 0x96 }, + { 0x4076, 0x96 }, + { 0x4077, 0x96 }, + { 0x4078, 0x8B }, + { 0x4079, 0x8A }, + { 0x407A, 0x8A }, + { 0x407B, 0x8A }, + { 0x407C, 0x82 }, + { 0x407D, 0x82 }, + { 0x407E, 0x82 }, + { 0x407F, 0x82 }, + { 0x4080, 0x85 }, + { 0x4081, 0x86 }, + { 0x4082, 0x86 }, + { 0x4083, 0x86 }, + { 0x4084, 0x90 }, + { 0x4085, 0x90 }, + { 0x4086, 0x8F }, + { 0x4087, 0x8F }, + { 0x4088, 0x9D }, + { 0x4089, 0x9B }, + { 0x408A, 0x9B }, + { 0x408B, 0x99 }, + { 0x408C, 0xAE }, + { 0x408D, 0xAA }, + { 0x408E, 0xAA }, + { 0x408F, 0xA7 }, + { 0x4090, 0xC7 }, + { 0x4091, 0xBA }, + { 0x4092, 0xBC }, + { 0x4093, 0xB9 }, + { 0x4094, 0xB1 }, + { 0x4095, 0xA8 }, + { 0x4096, 0xA8 }, + { 0x4097, 0xA7 }, + { 0x4098, 0x9F }, + { 0x4099, 0x9B }, + { 0x409A, 0x9B }, + { 0x409B, 0x9B }, + { 0x409C, 0x93 }, + { 0x409D, 0x91 }, + { 0x409E, 0x91 }, + { 0x409F, 0x91 }, + { 0x40A0, 0x8D }, + { 0x40A1, 0x8C }, + { 0x40A2, 0x8C }, + { 0x40A3, 0x8C }, + { 0x40A4, 0x8E }, + { 0x40A5, 0x8E }, + { 0x40A6, 0x8D }, + { 0x40A7, 0x8D }, + { 0x40A8, 0x96 }, + { 0x40A9, 0x95 }, + { 0x40AA, 0x95 }, + { 0x40AB, 0x94 }, + { 0x40AC, 0xA2 }, + { 0x40AD, 0x9F }, + { 0x40AE, 0x9F }, + { 0x40AF, 0x9D }, + { 0x40B0, 0xB1 }, + { 0x40B1, 0xAC }, + { 0x40B2, 0xAB }, + { 0x40B3, 0xAA }, + { 0x40B4, 0xD3 }, + { 0x40B5, 0xBC }, + { 0x40B6, 0xBD }, + { 0x40B7, 0xBC }, + { 0x40B8, 0xC1 }, + { 0x40B9, 0xB7 }, + { 0x40BA, 0xB7 }, + { 0x40BB, 0xB5 }, + { 0x40BC, 0xB0 }, + { 0x40BD, 0xAA }, + { 0x40BE, 0xAA }, + { 0x40BF, 0xAA }, + { 0x40C0, 0xA8 }, + { 0x40C1, 0xA4 }, + { 0x40C2, 0xA4 }, + { 0x40C3, 0xA4 }, + { 0x40C4, 0xA2 }, + { 0x40C5, 0x9F }, + { 0x40C6, 0x9F }, + { 0x40C7, 0x9F }, + { 0x40C8, 0xA3 }, + { 0x40C9, 0xA0 }, + { 0x40CA, 0xA0 }, + { 0x40CB, 0xA0 }, + { 0x40CC, 0xA6 }, + { 0x40CD, 0xA3 }, + { 0x40CE, 0xA3 }, + { 0x40CF, 0xA2 }, + { 0x40D0, 0xAF }, + { 0x40D1, 0xAB }, + { 0x40D2, 0xAA }, + { 0x40D3, 0xA8 }, + { 0x40D4, 0xBA }, + { 0x40D5, 0xAE }, + { 0x40D6, 0xAE }, + { 0x40D7, 0xAB }, + { 0x4100, 0xBD }, + { 0x4101, 0xBA }, + { 0x4102, 0xBD }, + { 0x4103, 0xB7 }, + { 0x4104, 0xB7 }, + { 0x4105, 0xB7 }, + { 0x4106, 0xB8 }, + { 0x4107, 0xB5 }, + { 0x4108, 0xAB }, + { 0x4109, 0xAA }, + { 0x410A, 0xAC }, + { 0x410B, 0xAB }, + { 0x410C, 0xA4 }, + { 0x410D, 0xA5 }, + { 0x410E, 0xA5 }, + { 0x410F, 0xA4 }, + { 0x4110, 0x9F }, + { 0x4111, 0xA0 }, + { 0x4112, 0xA0 }, + { 0x4113, 0x9F }, + { 0x4114, 0xA0 }, + { 0x4115, 0xA0 }, + { 0x4116, 0xA0 }, + { 0x4117, 0x9F }, + { 0x4118, 0xA1 }, + { 0x4119, 0xA1 }, + { 0x411A, 0xA1 }, + { 0x411B, 0xA0 }, + { 0x411C, 0xA7 }, + { 0x411D, 0xA6 }, + { 0x411E, 0xA6 }, + { 0x411F, 0xA6 }, + { 0x4120, 0xA7 }, + { 0x4121, 0xA6 }, + { 0x4122, 0xA6 }, + { 0x4123, 0xA3 }, + { 0x4124, 0xB9 }, + { 0x4125, 0xB9 }, + { 0x4126, 0xBA }, + { 0x4127, 0xB8 }, + { 0x4128, 0xA6 }, + { 0x4129, 0xA7 }, + { 0x412A, 0xA7 }, + { 0x412B, 0xA6 }, + { 0x412C, 0x9B }, + { 0x412D, 0x9B }, + { 0x412E, 0x9B }, + { 0x412F, 0x9B }, + { 0x4130, 0x91 }, + { 0x4131, 0x92 }, + { 0x4132, 0x92 }, + { 0x4133, 0x91 }, + { 0x4134, 0x8C }, + { 0x4135, 0x8C }, + { 0x4136, 0x8C }, + { 0x4137, 0x8C }, + { 0x4138, 0x8D }, + { 0x4139, 0x8D }, + { 0x413A, 0x8D }, + { 0x413B, 0x8D }, + { 0x413C, 0x93 }, + { 0x413D, 0x93 }, + { 0x413E, 0x93 }, + { 0x413F, 0x92 }, + { 0x4140, 0x9A }, + { 0x4141, 0x9A }, + { 0x4142, 0x9A }, + { 0x4143, 0x99 }, + { 0x4144, 0xA7 }, + { 0x4145, 0xA5 }, + { 0x4146, 0xA6 }, + { 0x4147, 0xA6 }, + { 0x4148, 0xB8 }, + { 0x4149, 0xB4 }, + { 0x414A, 0xB4 }, + { 0x414B, 0xB3 }, + { 0x414C, 0xA3 }, + { 0x414D, 0xA2 }, + { 0x414E, 0xA3 }, + { 0x414F, 0xA2 }, + { 0x4150, 0x96 }, + { 0x4151, 0x96 }, + { 0x4152, 0x96 }, + { 0x4153, 0x96 }, + { 0x4154, 0x8A }, + { 0x4155, 0x8A }, + { 0x4156, 0x8A }, + { 0x4157, 0x8A }, + { 0x4158, 0x82 }, + { 0x4159, 0x82 }, + { 0x415A, 0x82 }, + { 0x415B, 0x82 }, + { 0x415C, 0x84 }, + { 0x415D, 0x85 }, + { 0x415E, 0x84 }, + { 0x415F, 0x84 }, + { 0x4160, 0x8D }, + { 0x4161, 0x8D }, + { 0x4162, 0x8D }, + { 0x4163, 0x8D }, + { 0x4164, 0x96 }, + { 0x4165, 0x96 }, + { 0x4166, 0x96 }, + { 0x4167, 0x95 }, + { 0x4168, 0xA5 }, + { 0x4169, 0xA2 }, + { 0x416A, 0xA3 }, + { 0x416B, 0xA2 }, + { 0x416C, 0xB7 }, + { 0x416D, 0xB3 }, + { 0x416E, 0xB5 }, + { 0x416F, 0xB4 }, + { 0x4170, 0xA4 }, + { 0x4171, 0xA2 }, + { 0x4172, 0xA3 }, + { 0x4173, 0xA2 }, + { 0x4174, 0x97 }, + { 0x4175, 0x96 }, + { 0x4176, 0x96 }, + { 0x4177, 0x96 }, + { 0x4178, 0x8B }, + { 0x4179, 0x8A }, + { 0x417A, 0x8A }, + { 0x417B, 0x8A }, + { 0x417C, 0x81 }, + { 0x417D, 0x81 }, + { 0x417E, 0x81 }, + { 0x417F, 0x81 }, + { 0x4180, 0x84 }, + { 0x4181, 0x84 }, + { 0x4182, 0x84 }, + { 0x4183, 0x84 }, + { 0x4184, 0x8C }, + { 0x4185, 0x8D }, + { 0x4186, 0x8D }, + { 0x4187, 0x8D }, + { 0x4188, 0x95 }, + { 0x4189, 0x96 }, + { 0x418A, 0x96 }, + { 0x418B, 0x95 }, + { 0x418C, 0xA1 }, + { 0x418D, 0xA1 }, + { 0x418E, 0xA1 }, + { 0x418F, 0xA0 }, + { 0x4190, 0xBC }, + { 0x4191, 0xB8 }, + { 0x4192, 0xB8 }, + { 0x4193, 0xB9 }, + { 0x4194, 0xA8 }, + { 0x4195, 0xA5 }, + { 0x4196, 0xA6 }, + { 0x4197, 0xA5 }, + { 0x4198, 0x9C }, + { 0x4199, 0x9A }, + { 0x419A, 0x9A }, + { 0x419B, 0x9A }, + { 0x419C, 0x91 }, + { 0x419D, 0x91 }, + { 0x419E, 0x91 }, + { 0x419F, 0x91 }, + { 0x41A0, 0x8B }, + { 0x41A1, 0x8B }, + { 0x41A2, 0x8B }, + { 0x41A3, 0x8B }, + { 0x41A4, 0x8C }, + { 0x41A5, 0x8C }, + { 0x41A6, 0x8C }, + { 0x41A7, 0x8C }, + { 0x41A8, 0x91 }, + { 0x41A9, 0x92 }, + { 0x41AA, 0x91 }, + { 0x41AB, 0x91 }, + { 0x41AC, 0x98 }, + { 0x41AD, 0x99 }, + { 0x41AE, 0x99 }, + { 0x41AF, 0x98 }, + { 0x41B0, 0xA3 }, + { 0x41B1, 0xA3 }, + { 0x41B2, 0xA3 }, + { 0x41B3, 0xA2 }, + { 0x41B4, 0xC1 }, + { 0x41B5, 0xB8 }, + { 0x41B6, 0xB9 }, + { 0x41B7, 0xBA }, + { 0x41B8, 0xB8 }, + { 0x41B9, 0xB4 }, + { 0x41BA, 0xB4 }, + { 0x41BB, 0xB4 }, + { 0x41BC, 0xAA }, + { 0x41BD, 0xA7 }, + { 0x41BE, 0xA7 }, + { 0x41BF, 0xA8 }, + { 0x41C0, 0xA4 }, + { 0x41C1, 0xA2 }, + { 0x41C2, 0xA2 }, + { 0x41C3, 0xA3 }, + { 0x41C4, 0x9E }, + { 0x41C5, 0x9D }, + { 0x41C6, 0x9D }, + { 0x41C7, 0x9D }, + { 0x41C8, 0x9E }, + { 0x41C9, 0x9D }, + { 0x41CA, 0x9D }, + { 0x41CB, 0x9D }, + { 0x41CC, 0x9E }, + { 0x41CD, 0x9E }, + { 0x41CE, 0x9E }, + { 0x41CF, 0x9E }, + { 0x41D0, 0xA3 }, + { 0x41D1, 0xA3 }, + { 0x41D2, 0xA2 }, + { 0x41D3, 0xA1 }, + { 0x41D4, 0xA7 }, + { 0x41D5, 0xA7 }, + { 0x41D6, 0xA7 }, + { 0x41D7, 0xA3 }, + { 0x4200, 0xCE }, + { 0x4201, 0xC0 }, + { 0x4202, 0xC1 }, + { 0x4203, 0xB9 }, + { 0x4204, 0xC3 }, + { 0x4205, 0xB9 }, + { 0x4206, 0xBC }, + { 0x4207, 0xBD }, + { 0x4208, 0xB3 }, + { 0x4209, 0xAE }, + { 0x420A, 0xAF }, + { 0x420B, 0xAE }, + { 0x420C, 0xAA }, + { 0x420D, 0xA8 }, + { 0x420E, 0xA8 }, + { 0x420F, 0xA6 }, + { 0x4210, 0xA4 }, + { 0x4211, 0xA2 }, + { 0x4212, 0xA2 }, + { 0x4213, 0xA0 }, + { 0x4214, 0xA4 }, + { 0x4215, 0xA3 }, + { 0x4216, 0xA2 }, + { 0x4217, 0xA0 }, + { 0x4218, 0xA7 }, + { 0x4219, 0xA5 }, + { 0x421A, 0xA3 }, + { 0x421B, 0xA1 }, + { 0x421C, 0xB0 }, + { 0x421D, 0xA8 }, + { 0x421E, 0xA8 }, + { 0x421F, 0xA6 }, + { 0x4220, 0xB4 }, + { 0x4221, 0xAA }, + { 0x4222, 0xA5 }, + { 0x4223, 0xA3 }, + { 0x4224, 0xC7 }, + { 0x4225, 0xBC }, + { 0x4226, 0xBE }, + { 0x4227, 0xBC }, + { 0x4228, 0xB0 }, + { 0x4229, 0xA9 }, + { 0x422A, 0xA9 }, + { 0x422B, 0xA8 }, + { 0x422C, 0xA0 }, + { 0x422D, 0x9D }, + { 0x422E, 0x9D }, + { 0x422F, 0x9C }, + { 0x4230, 0x94 }, + { 0x4231, 0x93 }, + { 0x4232, 0x93 }, + { 0x4233, 0x92 }, + { 0x4234, 0x8E }, + { 0x4235, 0x8D }, + { 0x4236, 0x8D }, + { 0x4237, 0x8C }, + { 0x4238, 0x8F }, + { 0x4239, 0x8E }, + { 0x423A, 0x8E }, + { 0x423B, 0x8D }, + { 0x423C, 0x96 }, + { 0x423D, 0x94 }, + { 0x423E, 0x94 }, + { 0x423F, 0x92 }, + { 0x4240, 0xA1 }, + { 0x4241, 0x9C }, + { 0x4242, 0x9C }, + { 0x4243, 0x99 }, + { 0x4244, 0xB0 }, + { 0x4245, 0xA8 }, + { 0x4246, 0xAB }, + { 0x4247, 0xA7 }, + { 0x4248, 0xC3 }, + { 0x4249, 0xB7 }, + { 0x424A, 0xB7 }, + { 0x424B, 0xBC }, + { 0x424C, 0xAB }, + { 0x424D, 0xA4 }, + { 0x424E, 0xA5 }, + { 0x424F, 0xA5 }, + { 0x4250, 0x9A }, + { 0x4251, 0x97 }, + { 0x4252, 0x97 }, + { 0x4253, 0x98 }, + { 0x4254, 0x8C }, + { 0x4255, 0x8B }, + { 0x4256, 0x8B }, + { 0x4257, 0x8B }, + { 0x4258, 0x82 }, + { 0x4259, 0x82 }, + { 0x425A, 0x82 }, + { 0x425B, 0x82 }, + { 0x425C, 0x85 }, + { 0x425D, 0x85 }, + { 0x425E, 0x85 }, + { 0x425F, 0x84 }, + { 0x4260, 0x8F }, + { 0x4261, 0x8E }, + { 0x4262, 0x8E }, + { 0x4263, 0x8D }, + { 0x4264, 0x9B }, + { 0x4265, 0x98 }, + { 0x4266, 0x98 }, + { 0x4267, 0x95 }, + { 0x4268, 0xAE }, + { 0x4269, 0xA5 }, + { 0x426A, 0xA7 }, + { 0x426B, 0xA2 }, + { 0x426C, 0xC2 }, + { 0x426D, 0xB7 }, + { 0x426E, 0xB8 }, + { 0x426F, 0xB9 }, + { 0x4270, 0xAA }, + { 0x4271, 0xA4 }, + { 0x4272, 0xA4 }, + { 0x4273, 0xA5 }, + { 0x4274, 0x99 }, + { 0x4275, 0x96 }, + { 0x4276, 0x97 }, + { 0x4277, 0x98 }, + { 0x4278, 0x8B }, + { 0x4279, 0x8A }, + { 0x427A, 0x8A }, + { 0x427B, 0x8B }, + { 0x427C, 0x81 }, + { 0x427D, 0x81 }, + { 0x427E, 0x81 }, + { 0x427F, 0x82 }, + { 0x4280, 0x84 }, + { 0x4281, 0x84 }, + { 0x4282, 0x84 }, + { 0x4283, 0x84 }, + { 0x4284, 0x8E }, + { 0x4285, 0x8E }, + { 0x4286, 0x8D }, + { 0x4287, 0x8C }, + { 0x4288, 0x9A }, + { 0x4289, 0x97 }, + { 0x428A, 0x97 }, + { 0x428B, 0x95 }, + { 0x428C, 0xAA }, + { 0x428D, 0xA3 }, + { 0x428E, 0xA3 }, + { 0x428F, 0xA2 }, + { 0x4290, 0xC7 }, + { 0x4291, 0xBA }, + { 0x4292, 0xC0 }, + { 0x4293, 0xC3 }, + { 0x4294, 0xB0 }, + { 0x4295, 0xA7 }, + { 0x4296, 0xA7 }, + { 0x4297, 0xA9 }, + { 0x4298, 0x9F }, + { 0x4299, 0x9B }, + { 0x429A, 0x9B }, + { 0x429B, 0x9D }, + { 0x429C, 0x93 }, + { 0x429D, 0x91 }, + { 0x429E, 0x91 }, + { 0x429F, 0x92 }, + { 0x42A0, 0x8C }, + { 0x42A1, 0x8B }, + { 0x42A2, 0x8B }, + { 0x42A3, 0x8C }, + { 0x42A4, 0x8D }, + { 0x42A5, 0x8C }, + { 0x42A6, 0x8C }, + { 0x42A7, 0x8C }, + { 0x42A8, 0x94 }, + { 0x42A9, 0x93 }, + { 0x42AA, 0x92 }, + { 0x42AB, 0x91 }, + { 0x42AC, 0x9E }, + { 0x42AD, 0x9B }, + { 0x42AE, 0x9B }, + { 0x42AF, 0x98 }, + { 0x42B0, 0xAC }, + { 0x42B1, 0xA6 }, + { 0x42B2, 0xA6 }, + { 0x42B3, 0xA2 }, + { 0x42B4, 0xCE }, + { 0x42B5, 0xBA }, + { 0x42B6, 0xBC }, + { 0x42B7, 0xB7 }, + { 0x42B8, 0xC5 }, + { 0x42B9, 0xB5 }, + { 0x42BA, 0xBA }, + { 0x42BB, 0xC0 }, + { 0x42BC, 0xB1 }, + { 0x42BD, 0xA8 }, + { 0x42BE, 0xAE }, + { 0x42BF, 0xAF }, + { 0x42C0, 0xA7 }, + { 0x42C1, 0xA3 }, + { 0x42C2, 0xA3 }, + { 0x42C3, 0xA5 }, + { 0x42C4, 0xA0 }, + { 0x42C5, 0x9D }, + { 0x42C6, 0x9D }, + { 0x42C7, 0x9F }, + { 0x42C8, 0xA0 }, + { 0x42C9, 0x9E }, + { 0x42CA, 0x9E }, + { 0x42CB, 0x9F }, + { 0x42CC, 0xA2 }, + { 0x42CD, 0xA0 }, + { 0x42CE, 0xA0 }, + { 0x42CF, 0xA0 }, + { 0x42D0, 0xA8 }, + { 0x42D1, 0xA5 }, + { 0x42D2, 0xA5 }, + { 0x42D3, 0xA2 }, + { 0x42D4, 0xB3 }, + { 0x42D5, 0xAA }, + { 0x42D6, 0xAB }, + { 0x42D7, 0xA3 }, + { 0x42D8, 0x00 }, + { 0x42D9, 0x00 }, + { 0x4300, 0xA2 }, + { 0x4301, 0xAE }, + { 0x4302, 0xAD }, + { 0x4303, 0xB5 }, + { 0x4304, 0x95 }, + { 0x4305, 0x9A }, + { 0x4306, 0x98 }, + { 0x4307, 0x9B }, + { 0x4308, 0x8D }, + { 0x4309, 0x90 }, + { 0x430A, 0x8F }, + { 0x430B, 0x91 }, + { 0x430C, 0x86 }, + { 0x430D, 0x88 }, + { 0x430E, 0x87 }, + { 0x430F, 0x89 }, + { 0x4310, 0x86 }, + { 0x4311, 0x87 }, + { 0x4312, 0x86 }, + { 0x4313, 0x88 }, + { 0x4314, 0x89 }, + { 0x4315, 0x88 }, + { 0x4316, 0x88 }, + { 0x4317, 0x8E }, + { 0x4318, 0x90 }, + { 0x4319, 0x8F }, + { 0x431A, 0x8C }, + { 0x431B, 0x8C }, + { 0x431C, 0x9C }, + { 0x431D, 0x99 }, + { 0x431E, 0x98 }, + { 0x431F, 0x99 }, + { 0x4320, 0xAB }, + { 0x4321, 0xB0 }, + { 0x4322, 0xAD }, + { 0x4323, 0xAF }, + { 0x4324, 0x9B }, + { 0x4325, 0x9F }, + { 0x4326, 0x9E }, + { 0x4327, 0xA1 }, + { 0x4328, 0x8E }, + { 0x4329, 0x91 }, + { 0x432A, 0x90 }, + { 0x432B, 0x93 }, + { 0x432C, 0x86 }, + { 0x432D, 0x88 }, + { 0x432E, 0x87 }, + { 0x432F, 0x89 }, + { 0x4330, 0x82 }, + { 0x4331, 0x84 }, + { 0x4332, 0x83 }, + { 0x4333, 0x84 }, + { 0x4334, 0x82 }, + { 0x4335, 0x82 }, + { 0x4336, 0x82 }, + { 0x4337, 0x83 }, + { 0x4338, 0x85 }, + { 0x4339, 0x84 }, + { 0x433A, 0x84 }, + { 0x433B, 0x85 }, + { 0x433C, 0x8A }, + { 0x433D, 0x89 }, + { 0x433E, 0x88 }, + { 0x433F, 0x89 }, + { 0x4340, 0x93 }, + { 0x4341, 0x91 }, + { 0x4342, 0x91 }, + { 0x4343, 0x93 }, + { 0x4344, 0xA0 }, + { 0x4345, 0x9E }, + { 0x4346, 0x9D }, + { 0x4347, 0xA1 }, + { 0x4348, 0x95 }, + { 0x4349, 0x9B }, + { 0x434A, 0x9A }, + { 0x434B, 0x9C }, + { 0x434C, 0x8A }, + { 0x434D, 0x8D }, + { 0x434E, 0x8C }, + { 0x434F, 0x8D }, + { 0x4350, 0x83 }, + { 0x4351, 0x85 }, + { 0x4352, 0x84 }, + { 0x4353, 0x85 }, + { 0x4354, 0x80 }, + { 0x4355, 0x81 }, + { 0x4356, 0x81 }, + { 0x4357, 0x81 }, + { 0x4358, 0x80 }, + { 0x4359, 0x80 }, + { 0x435A, 0x80 }, + { 0x435B, 0x80 }, + { 0x435C, 0x82 }, + { 0x435D, 0x81 }, + { 0x435E, 0x81 }, + { 0x435F, 0x81 }, + { 0x4360, 0x85 }, + { 0x4361, 0x84 }, + { 0x4362, 0x84 }, + { 0x4363, 0x85 }, + { 0x4364, 0x8D }, + { 0x4365, 0x8B }, + { 0x4366, 0x8B }, + { 0x4367, 0x8D }, + { 0x4368, 0x98 }, + { 0x4369, 0x98 }, + { 0x436A, 0x95 }, + { 0x436B, 0x98 }, + { 0x436C, 0x95 }, + { 0x436D, 0x9A }, + { 0x436E, 0x99 }, + { 0x436F, 0x9A }, + { 0x4370, 0x8A }, + { 0x4371, 0x8D }, + { 0x4372, 0x8C }, + { 0x4373, 0x8C }, + { 0x4374, 0x83 }, + { 0x4375, 0x85 }, + { 0x4376, 0x84 }, + { 0x4377, 0x84 }, + { 0x4378, 0x80 }, + { 0x4379, 0x80 }, + { 0x437A, 0x80 }, + { 0x437B, 0x80 }, + { 0x437C, 0x7F }, + { 0x437D, 0x7F }, + { 0x437E, 0x7F }, + { 0x437F, 0x7F }, + { 0x4380, 0x81 }, + { 0x4381, 0x80 }, + { 0x4382, 0x80 }, + { 0x4383, 0x81 }, + { 0x4384, 0x84 }, + { 0x4385, 0x83 }, + { 0x4386, 0x83 }, + { 0x4387, 0x84 }, + { 0x4388, 0x8B }, + { 0x4389, 0x8A }, + { 0x438A, 0x8A }, + { 0x438B, 0x8C }, + { 0x438C, 0x97 }, + { 0x438D, 0x96 }, + { 0x438E, 0x96 }, + { 0x438F, 0x99 }, + { 0x4390, 0x99 }, + { 0x4391, 0x9F }, + { 0x4392, 0x9E }, + { 0x4393, 0x9D }, + { 0x4394, 0x8D }, + { 0x4395, 0x90 }, + { 0x4396, 0x90 }, + { 0x4397, 0x8F }, + { 0x4398, 0x85 }, + { 0x4399, 0x87 }, + { 0x439A, 0x87 }, + { 0x439B, 0x86 }, + { 0x439C, 0x81 }, + { 0x439D, 0x83 }, + { 0x439E, 0x82 }, + { 0x439F, 0x82 }, + { 0x43A0, 0x80 }, + { 0x43A1, 0x81 }, + { 0x43A2, 0x81 }, + { 0x43A3, 0x81 }, + { 0x43A4, 0x82 }, + { 0x43A5, 0x82 }, + { 0x43A6, 0x82 }, + { 0x43A7, 0x82 }, + { 0x43A8, 0x86 }, + { 0x43A9, 0x85 }, + { 0x43AA, 0x85 }, + { 0x43AB, 0x87 }, + { 0x43AC, 0x8D }, + { 0x43AD, 0x8D }, + { 0x43AE, 0x8D }, + { 0x43AF, 0x90 }, + { 0x43B0, 0x9A }, + { 0x43B1, 0x9A }, + { 0x43B2, 0x9B }, + { 0x43B3, 0x9D }, + { 0x43B4, 0xA0 }, + { 0x43B5, 0xAD }, + { 0x43B6, 0xAC }, + { 0x43B7, 0xAA }, + { 0x43B8, 0x93 }, + { 0x43B9, 0x97 }, + { 0x43BA, 0x97 }, + { 0x43BB, 0x96 }, + { 0x43BC, 0x8B }, + { 0x43BD, 0x8E }, + { 0x43BE, 0x8E }, + { 0x43BF, 0x8C }, + { 0x43C0, 0x83 }, + { 0x43C1, 0x85 }, + { 0x43C2, 0x85 }, + { 0x43C3, 0x84 }, + { 0x43C4, 0x82 }, + { 0x43C5, 0x84 }, + { 0x43C6, 0x83 }, + { 0x43C7, 0x83 }, + { 0x43C8, 0x83 }, + { 0x43C9, 0x84 }, + { 0x43CA, 0x84 }, + { 0x43CB, 0x85 }, + { 0x43CC, 0x8A }, + { 0x43CD, 0x8A }, + { 0x43CE, 0x8A }, + { 0x43CF, 0x8C }, + { 0x43D0, 0x92 }, + { 0x43D1, 0x93 }, + { 0x43D2, 0x93 }, + { 0x43D3, 0x96 }, + { 0x43D4, 0x9F }, + { 0x43D5, 0xA6 }, + { 0x43D6, 0xA5 }, + { 0x43D7, 0xAA }, + { 0x4400, 0xA1 }, + { 0x4401, 0xAB }, + { 0x4402, 0xA7 }, + { 0x4403, 0xB0 }, + { 0x4404, 0x91 }, + { 0x4405, 0x96 }, + { 0x4406, 0x94 }, + { 0x4407, 0x99 }, + { 0x4408, 0x8A }, + { 0x4409, 0x8E }, + { 0x440A, 0x8C }, + { 0x440B, 0x8F }, + { 0x440C, 0x85 }, + { 0x440D, 0x86 }, + { 0x440E, 0x86 }, + { 0x440F, 0x88 }, + { 0x4410, 0x85 }, + { 0x4411, 0x86 }, + { 0x4412, 0x85 }, + { 0x4413, 0x87 }, + { 0x4414, 0x88 }, + { 0x4415, 0x87 }, + { 0x4416, 0x87 }, + { 0x4417, 0x89 }, + { 0x4418, 0x91 }, + { 0x4419, 0x8F }, + { 0x441A, 0x8F }, + { 0x441B, 0x90 }, + { 0x441C, 0x9C }, + { 0x441D, 0x9B }, + { 0x441E, 0x9A }, + { 0x441F, 0x9A }, + { 0x4420, 0xB3 }, + { 0x4421, 0xB1 }, + { 0x4422, 0xB0 }, + { 0x4423, 0xB2 }, + { 0x4424, 0x96 }, + { 0x4425, 0x9C }, + { 0x4426, 0x9A }, + { 0x4427, 0x9E }, + { 0x4428, 0x8B }, + { 0x4429, 0x8F }, + { 0x442A, 0x8E }, + { 0x442B, 0x91 }, + { 0x442C, 0x84 }, + { 0x442D, 0x87 }, + { 0x442E, 0x86 }, + { 0x442F, 0x88 }, + { 0x4430, 0x82 }, + { 0x4431, 0x83 }, + { 0x4432, 0x82 }, + { 0x4433, 0x84 }, + { 0x4434, 0x82 }, + { 0x4435, 0x82 }, + { 0x4436, 0x82 }, + { 0x4437, 0x83 }, + { 0x4438, 0x84 }, + { 0x4439, 0x84 }, + { 0x443A, 0x84 }, + { 0x443B, 0x84 }, + { 0x443C, 0x8B }, + { 0x443D, 0x89 }, + { 0x443E, 0x89 }, + { 0x443F, 0x89 }, + { 0x4440, 0x95 }, + { 0x4441, 0x93 }, + { 0x4442, 0x93 }, + { 0x4443, 0x93 }, + { 0x4444, 0xA2 }, + { 0x4445, 0xA2 }, + { 0x4446, 0xA1 }, + { 0x4447, 0xA0 }, + { 0x4448, 0x8F }, + { 0x4449, 0x97 }, + { 0x444A, 0x97 }, + { 0x444B, 0x98 }, + { 0x444C, 0x87 }, + { 0x444D, 0x8B }, + { 0x444E, 0x8A }, + { 0x444F, 0x8B }, + { 0x4450, 0x81 }, + { 0x4451, 0x83 }, + { 0x4452, 0x83 }, + { 0x4453, 0x84 }, + { 0x4454, 0x7F }, + { 0x4455, 0x80 }, + { 0x4456, 0x80 }, + { 0x4457, 0x81 }, + { 0x4458, 0x80 }, + { 0x4459, 0x80 }, + { 0x445A, 0x80 }, + { 0x445B, 0x80 }, + { 0x445C, 0x82 }, + { 0x445D, 0x81 }, + { 0x445E, 0x81 }, + { 0x445F, 0x81 }, + { 0x4460, 0x87 }, + { 0x4461, 0x85 }, + { 0x4462, 0x85 }, + { 0x4463, 0x86 }, + { 0x4464, 0x90 }, + { 0x4465, 0x8E }, + { 0x4466, 0x8E }, + { 0x4467, 0x8E }, + { 0x4468, 0x9B }, + { 0x4469, 0x9C }, + { 0x446A, 0x9A }, + { 0x446B, 0x9A }, + { 0x446C, 0x91 }, + { 0x446D, 0x97 }, + { 0x446E, 0x95 }, + { 0x446F, 0x95 }, + { 0x4470, 0x87 }, + { 0x4471, 0x8A }, + { 0x4472, 0x8A }, + { 0x4473, 0x89 }, + { 0x4474, 0x81 }, + { 0x4475, 0x83 }, + { 0x4476, 0x83 }, + { 0x4477, 0x83 }, + { 0x4478, 0x7F }, + { 0x4479, 0x80 }, + { 0x447A, 0x80 }, + { 0x447B, 0x80 }, + { 0x447C, 0x80 }, + { 0x447D, 0x80 }, + { 0x447E, 0x80 }, + { 0x447F, 0x7F }, + { 0x4480, 0x81 }, + { 0x4481, 0x81 }, + { 0x4482, 0x81 }, + { 0x4483, 0x81 }, + { 0x4484, 0x85 }, + { 0x4485, 0x85 }, + { 0x4486, 0x85 }, + { 0x4487, 0x85 }, + { 0x4488, 0x8E }, + { 0x4489, 0x8D }, + { 0x448A, 0x8D }, + { 0x448B, 0x8E }, + { 0x448C, 0x9D }, + { 0x448D, 0x9C }, + { 0x448E, 0x9C }, + { 0x448F, 0x9C }, + { 0x4490, 0x94 }, + { 0x4491, 0x9B }, + { 0x4492, 0x9A }, + { 0x4493, 0x97 }, + { 0x4494, 0x8A }, + { 0x4495, 0x8E }, + { 0x4496, 0x8E }, + { 0x4497, 0x8C }, + { 0x4498, 0x84 }, + { 0x4499, 0x86 }, + { 0x449A, 0x86 }, + { 0x449B, 0x84 }, + { 0x449C, 0x81 }, + { 0x449D, 0x83 }, + { 0x449E, 0x83 }, + { 0x449F, 0x81 }, + { 0x44A0, 0x81 }, + { 0x44A1, 0x82 }, + { 0x44A2, 0x82 }, + { 0x44A3, 0x81 }, + { 0x44A4, 0x83 }, + { 0x44A5, 0x83 }, + { 0x44A6, 0x83 }, + { 0x44A7, 0x83 }, + { 0x44A8, 0x88 }, + { 0x44A9, 0x88 }, + { 0x44AA, 0x88 }, + { 0x44AB, 0x88 }, + { 0x44AC, 0x91 }, + { 0x44AD, 0x91 }, + { 0x44AE, 0x91 }, + { 0x44AF, 0x92 }, + { 0x44B0, 0xA0 }, + { 0x44B1, 0xA0 }, + { 0x44B2, 0xA0 }, + { 0x44B3, 0xA0 }, + { 0x44B4, 0x9E }, + { 0x44B5, 0xA9 }, + { 0x44B6, 0xA8 }, + { 0x44B7, 0xA3 }, + { 0x44B8, 0x90 }, + { 0x44B9, 0x95 }, + { 0x44BA, 0x95 }, + { 0x44BB, 0x92 }, + { 0x44BC, 0x8A }, + { 0x44BD, 0x8E }, + { 0x44BE, 0x8E }, + { 0x44BF, 0x8B }, + { 0x44C0, 0x84 }, + { 0x44C1, 0x86 }, + { 0x44C2, 0x86 }, + { 0x44C3, 0x84 }, + { 0x44C4, 0x84 }, + { 0x44C5, 0x85 }, + { 0x44C6, 0x85 }, + { 0x44C7, 0x84 }, + { 0x44C8, 0x86 }, + { 0x44C9, 0x87 }, + { 0x44CA, 0x87 }, + { 0x44CB, 0x86 }, + { 0x44CC, 0x8D }, + { 0x44CD, 0x8E }, + { 0x44CE, 0x8E }, + { 0x44CF, 0x8D }, + { 0x44D0, 0x98 }, + { 0x44D1, 0x98 }, + { 0x44D2, 0x99 }, + { 0x44D3, 0x9A }, + { 0x44D4, 0xA9 }, + { 0x44D5, 0xAA }, + { 0x44D6, 0xAA }, + { 0x44D7, 0xAD }, + { 0x4500, 0x9F }, + { 0x4501, 0xA8 }, + { 0x4502, 0xA5 }, + { 0x4503, 0xAF }, + { 0x4504, 0x8F }, + { 0x4505, 0x96 }, + { 0x4506, 0x92 }, + { 0x4507, 0x94 }, + { 0x4508, 0x89 }, + { 0x4509, 0x8D }, + { 0x450A, 0x8A }, + { 0x450B, 0x8E }, + { 0x450C, 0x84 }, + { 0x450D, 0x85 }, + { 0x450E, 0x84 }, + { 0x450F, 0x87 }, + { 0x4510, 0x84 }, + { 0x4511, 0x85 }, + { 0x4512, 0x84 }, + { 0x4513, 0x86 }, + { 0x4514, 0x87 }, + { 0x4515, 0x86 }, + { 0x4516, 0x86 }, + { 0x4517, 0x88 }, + { 0x4518, 0x8F }, + { 0x4519, 0x8D }, + { 0x451A, 0x8D }, + { 0x451B, 0x8F }, + { 0x451C, 0x9A }, + { 0x451D, 0x9A }, + { 0x451E, 0x98 }, + { 0x451F, 0x9A }, + { 0x4520, 0xAF }, + { 0x4521, 0xAF }, + { 0x4522, 0xB2 }, + { 0x4523, 0xB1 }, + { 0x4524, 0x95 }, + { 0x4525, 0x9B }, + { 0x4526, 0x97 }, + { 0x4527, 0x9C }, + { 0x4528, 0x8A }, + { 0x4529, 0x8E }, + { 0x452A, 0x8D }, + { 0x452B, 0x90 }, + { 0x452C, 0x84 }, + { 0x452D, 0x86 }, + { 0x452E, 0x85 }, + { 0x452F, 0x87 }, + { 0x4530, 0x81 }, + { 0x4531, 0x82 }, + { 0x4532, 0x82 }, + { 0x4533, 0x83 }, + { 0x4534, 0x81 }, + { 0x4535, 0x81 }, + { 0x4536, 0x81 }, + { 0x4537, 0x82 }, + { 0x4538, 0x84 }, + { 0x4539, 0x83 }, + { 0x453A, 0x83 }, + { 0x453B, 0x84 }, + { 0x453C, 0x8A }, + { 0x453D, 0x88 }, + { 0x453E, 0x88 }, + { 0x453F, 0x89 }, + { 0x4540, 0x94 }, + { 0x4541, 0x92 }, + { 0x4542, 0x91 }, + { 0x4543, 0x92 }, + { 0x4544, 0xA1 }, + { 0x4545, 0xA0 }, + { 0x4546, 0x9C }, + { 0x4547, 0x9D }, + { 0x4548, 0x8F }, + { 0x4549, 0x96 }, + { 0x454A, 0x95 }, + { 0x454B, 0x92 }, + { 0x454C, 0x87 }, + { 0x454D, 0x8A }, + { 0x454E, 0x89 }, + { 0x454F, 0x8A }, + { 0x4550, 0x81 }, + { 0x4551, 0x83 }, + { 0x4552, 0x82 }, + { 0x4553, 0x83 }, + { 0x4554, 0x7F }, + { 0x4555, 0x80 }, + { 0x4556, 0x80 }, + { 0x4557, 0x81 }, + { 0x4558, 0x7F }, + { 0x4559, 0x80 }, + { 0x455A, 0x7F }, + { 0x455B, 0x80 }, + { 0x455C, 0x81 }, + { 0x455D, 0x81 }, + { 0x455E, 0x81 }, + { 0x455F, 0x81 }, + { 0x4560, 0x86 }, + { 0x4561, 0x85 }, + { 0x4562, 0x85 }, + { 0x4563, 0x85 }, + { 0x4564, 0x8F }, + { 0x4565, 0x8D }, + { 0x4566, 0x8D }, + { 0x4567, 0x8D }, + { 0x4568, 0x99 }, + { 0x4569, 0x9A }, + { 0x456A, 0x97 }, + { 0x456B, 0x99 }, + { 0x456C, 0x90 }, + { 0x456D, 0x95 }, + { 0x456E, 0x93 }, + { 0x456F, 0x92 }, + { 0x4570, 0x87 }, + { 0x4571, 0x8A }, + { 0x4572, 0x88 }, + { 0x4573, 0x87 }, + { 0x4574, 0x81 }, + { 0x4575, 0x83 }, + { 0x4576, 0x82 }, + { 0x4577, 0x82 }, + { 0x4578, 0x7F }, + { 0x4579, 0x80 }, + { 0x457A, 0x80 }, + { 0x457B, 0x80 }, + { 0x457C, 0x80 }, + { 0x457D, 0x80 }, + { 0x457E, 0x80 }, + { 0x457F, 0x80 }, + { 0x4580, 0x81 }, + { 0x4581, 0x81 }, + { 0x4582, 0x81 }, + { 0x4583, 0x81 }, + { 0x4584, 0x85 }, + { 0x4585, 0x85 }, + { 0x4586, 0x84 }, + { 0x4587, 0x85 }, + { 0x4588, 0x8E }, + { 0x4589, 0x8D }, + { 0x458A, 0x8C }, + { 0x458B, 0x8D }, + { 0x458C, 0x9B }, + { 0x458D, 0x9B }, + { 0x458E, 0x9A }, + { 0x458F, 0x98 }, + { 0x4590, 0x94 }, + { 0x4591, 0x9A }, + { 0x4592, 0x94 }, + { 0x4593, 0x90 }, + { 0x4594, 0x8A }, + { 0x4595, 0x8D }, + { 0x4596, 0x8C }, + { 0x4597, 0x89 }, + { 0x4598, 0x84 }, + { 0x4599, 0x86 }, + { 0x459A, 0x85 }, + { 0x459B, 0x83 }, + { 0x459C, 0x82 }, + { 0x459D, 0x83 }, + { 0x459E, 0x82 }, + { 0x459F, 0x80 }, + { 0x45A0, 0x81 }, + { 0x45A1, 0x82 }, + { 0x45A2, 0x81 }, + { 0x45A3, 0x80 }, + { 0x45A4, 0x83 }, + { 0x45A5, 0x83 }, + { 0x45A6, 0x83 }, + { 0x45A7, 0x83 }, + { 0x45A8, 0x88 }, + { 0x45A9, 0x87 }, + { 0x45AA, 0x87 }, + { 0x45AB, 0x88 }, + { 0x45AC, 0x91 }, + { 0x45AD, 0x90 }, + { 0x45AE, 0x90 }, + { 0x45AF, 0x91 }, + { 0x45B0, 0x9F }, + { 0x45B1, 0x9F }, + { 0x45B2, 0x9E }, + { 0x45B3, 0x9F }, + { 0x45B4, 0x9F }, + { 0x45B5, 0xA8 }, + { 0x45B6, 0xA6 }, + { 0x45B7, 0xA7 }, + { 0x45B8, 0x8D }, + { 0x45B9, 0x95 }, + { 0x45BA, 0x90 }, + { 0x45BB, 0x8A }, + { 0x45BC, 0x89 }, + { 0x45BD, 0x8D }, + { 0x45BE, 0x88 }, + { 0x45BF, 0x86 }, + { 0x45C0, 0x84 }, + { 0x45C1, 0x86 }, + { 0x45C2, 0x85 }, + { 0x45C3, 0x82 }, + { 0x45C4, 0x84 }, + { 0x45C5, 0x85 }, + { 0x45C6, 0x85 }, + { 0x45C7, 0x83 }, + { 0x45C8, 0x86 }, + { 0x45C9, 0x86 }, + { 0x45CA, 0x86 }, + { 0x45CB, 0x85 }, + { 0x45CC, 0x8E }, + { 0x45CD, 0x8D }, + { 0x45CE, 0x8D }, + { 0x45CF, 0x8C }, + { 0x45D0, 0x99 }, + { 0x45D1, 0x98 }, + { 0x45D2, 0x98 }, + { 0x45D3, 0x98 }, + { 0x45D4, 0xA6 }, + { 0x45D5, 0xA9 }, + { 0x45D6, 0xA7 }, + { 0x45D7, 0xAC }, +}; diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index 155898d3d7ad..2f66395989ff 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -120,6 +120,54 @@ static const struct csi2rx_fmt formats[] = { .code = MEDIA_BUS_FMT_VYUY8_2X8, .bpp = 16, }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 8, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .bpp = 12, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .bpp = 12, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .bpp = 12, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .bpp = 12, + }, }; static u8 csi2rx_get_bpp(u32 code) @@ -231,7 +279,6 @@ static int csi2rx_configure_external_dphy(struct csi2rx_priv *csi2rx) struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; s64 link_freq; int ret; - bool got_pm = true; link_freq = csi2rx_get_link_freq(csi2rx); if (link_freq < 0) @@ -245,9 +292,7 @@ static int csi2rx_configure_external_dphy(struct csi2rx_priv *csi2rx) cfg->lanes = csi2rx->num_lanes; ret = phy_pm_runtime_get_sync(csi2rx->dphy); - if (ret == -ENOTSUPP) - got_pm = false; - else if (ret) + if (ret < 0 && ret != -ENOTSUPP) return ret; ret = phy_set_mode_ext(csi2rx->dphy, PHY_MODE_MIPI_DPHY, @@ -267,9 +312,7 @@ static int csi2rx_configure_external_dphy(struct csi2rx_priv *csi2rx) } out: - if (got_pm) - phy_pm_runtime_put(csi2rx->dphy); - + phy_pm_runtime_put(csi2rx->dphy); return ret; } @@ -307,10 +350,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); - ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); - if (ret) - goto err_disable_pclk; - /* Enable DPHY clk and data lanes. */ if (csi2rx->dphy) { reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; @@ -320,6 +359,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) } writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + ret = csi2rx_configure_external_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_pclk; + } } /* @@ -352,14 +398,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; - if (csi2rx->dphy) { - ret = csi2rx_configure_external_dphy(csi2rx); - if (ret) { - dev_err(csi2rx->dev, - "Failed to configure external DPHY: %d\n", ret); - goto err_disable_sysclk; - } - } + ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); + if (ret) + goto err_disable_sysclk; clk_disable_unprepare(csi2rx->p_clk); @@ -371,6 +412,10 @@ err_disable_pixclk: for (; i > 0; i--) clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); + if (csi2rx->dphy) { + phy_power_off(csi2rx->dphy); + phy_pm_runtime_put(csi2rx->dphy); + } err_disable_pclk: clk_disable_unprepare(csi2rx->p_clk); @@ -411,6 +456,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) if (phy_power_off(csi2rx->dphy)) dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + + phy_pm_runtime_put(csi2rx->dphy); } } diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 021c003026c7..32563cffbf05 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -251,6 +251,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, { struct v4l2_subdev *sd = vin_to_source(vin); struct v4l2_subdev_state *sd_state; + static struct lock_class_key key; struct v4l2_subdev_format format = { .which = which, .pad = vin->parallel->source_pad, @@ -259,9 +260,9 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, u32 width, height; int ret; - sd_state = v4l2_subdev_alloc_state(sd); - if (sd_state == NULL) - return -ENOMEM; + sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key); + if (IS_ERR(sd_state)) + return PTR_ERR(sd_state); if (!rvin_format_from_pixel(vin, pix->pixelformat)) pix->pixelformat = RVIN_DEFAULT_FORMAT; @@ -295,7 +296,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, rvin_format_align(vin, pix); done: - v4l2_subdev_free_state(sd_state); + __v4l2_subdev_state_free(sd_state); return ret; } diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index ae8ae046a512..3ccd06d37924 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -49,34 +49,48 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) { struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; u32 num_lanes = mipi_csi2->num_data_lanes; + struct v4l2_subdev_state *state; u32 bpp; s64 freq; /* - * With multistream input we don't have bpp, and cannot use - * V4L2_CID_PIXEL_RATE. Passing 0 as bpp causes v4l2_get_link_freq() - * to return an error if it falls back to V4L2_CID_PIXEL_RATE. + * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back + * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available. + * + * With multistream input there is no single pixel rate, and thus we + * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which + * causes v4l2_get_link_freq() to return an error if it falls back to + * V4L2_CID_PIXEL_RATE. */ - if (phy->stream_configs.num_configs == 0) + state = v4l2_subdev_lock_active_state(&phy->subdev); + + if (state->routing.num_routes == 0) { + v4l2_subdev_unlock_state(state); return -EINVAL; + } - if (phy->stream_configs.num_configs > 2) { + if (state->routing.num_routes > 1) { bpp = 0; } else { const struct cal_format_info *fmtinfo; + struct v4l2_subdev_route *route = &state->routing.routes[0]; struct v4l2_mbus_framefmt *fmt; - /* The first format is for the sink */ - fmt = &phy->stream_configs.configs[0].fmt; + fmt = v4l2_state_get_stream_format(state, route->sink_pad, + route->sink_stream); fmtinfo = cal_format_by_code(fmt->code); - if (!fmtinfo) + if (!fmtinfo) { + v4l2_subdev_unlock_state(state); return -EINVAL; + } bpp = fmtinfo->bpp; } + v4l2_subdev_unlock_state(state); + freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes); if (freq < 0) { phy_err(phy, "failed to get link freq for subdev '%s'\n", @@ -602,25 +616,26 @@ done: } int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, - struct v4l2_mbus_frame_desc *fd) + struct v4l2_mbus_frame_desc *desc) { struct media_pad *pad; int ret; if (!phy->source) - return -ENODEV; + return -EPIPE; pad = media_entity_remote_pad(&phy->pads[CAL_CAMERARX_PAD_SINK]); if (!pad) - return -ENODEV; + return -EPIPE; ret = v4l2_subdev_call(phy->source, pad, get_frame_desc, pad->index, - fd); + desc); if (ret) return ret; - if (fd->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { - dev_err(phy->cal->dev, "Frame desc do not describe CSI-2 link"); + if (desc->type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { + dev_err(phy->cal->dev, + "Frame descriptor does not describe CSI-2 link"); return -EINVAL; } @@ -649,142 +664,52 @@ cal_camerarx_get_phy_from_entity(struct media_entity *entity) return to_cal_camerarx(sd); } -static struct v4l2_subdev_krouting * -cal_camerarx_get_routing_table(struct cal_camerarx *phy, - struct v4l2_subdev_state *cfg, u32 which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &phy->routing; - else - return &cfg->routing; -} - -static struct v4l2_subdev_stream_configs * -cal_camerarx_get_stream_configs(struct cal_camerarx *phy, - struct v4l2_subdev_state *cfg, u32 which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &phy->stream_configs; - else - return &cfg->stream_configs; -} - -struct v4l2_mbus_framefmt * -cal_camerarx_get_stream_format(struct cal_camerarx *phy, - struct v4l2_subdev_state *cfg, - unsigned int pad, u32 stream, u32 which) -{ - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - stream_configs = cal_camerarx_get_stream_configs(phy, cfg, which); - - for (i = 0; i < stream_configs->num_configs; ++i) { - if (stream_configs->configs[i].pad == pad && - stream_configs->configs[i].stream == stream) - return &stream_configs->configs[i].fmt; - } - - return NULL; -} - -static int cal_camerarx_find_opposite_end(struct v4l2_subdev_krouting *routing, - u32 pad, u32 stream, u32 *other_pad, - u32 *other_stream) -{ - unsigned int i; - - for (i = 0; i < routing->num_routes; ++i) { - struct v4l2_subdev_route *route = &routing->routes[i]; - - if (cal_rx_pad_is_source(pad)) { - if (route->source_pad == pad && - route->source_stream == stream) { - *other_pad = route->sink_pad; - *other_stream = route->sink_stream; - return 0; - } - } else { - if (route->sink_pad == pad && - route->sink_stream == stream) { - *other_pad = route->source_pad; - *other_stream = route->source_stream; - return 0; - } - } - } - - return -EINVAL; -} - -static struct v4l2_mbus_framefmt * -cal_camerarx_get_opposite_stream_format(struct cal_camerarx *phy, - struct v4l2_subdev_state *cfg, - u32 pad, u32 stream, u32 which) -{ - struct v4l2_subdev_krouting *routing; - u32 other_pad, other_stream; - int ret; - - routing = cal_camerarx_get_routing_table(phy, cfg, which); - - ret = cal_camerarx_find_opposite_end(routing, pad, stream, &other_pad, - &other_stream); - if (ret) - return NULL; - - return cal_camerarx_get_stream_format(phy, cfg, other_pad, - other_stream, which); -} - static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) { struct cal_camerarx *phy = to_cal_camerarx(sd); - int r = 0; + int ret = 0; mutex_lock(&phy->mutex); if (enable) - r = cal_camerarx_start(phy); + ret = cal_camerarx_start(phy); else cal_camerarx_stop(phy); mutex_unlock(&phy->mutex); - return r; + return ret; } static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - int r = 0; + int ret = 0; - mutex_lock(&phy->mutex); + v4l2_subdev_lock_state(state); /* No transcoding, source and sink codes must match. */ if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; if (code->index > 0) { - r = -EINVAL; + ret = -EINVAL; goto out; } - fmt = cal_camerarx_get_opposite_stream_format(phy, sd_state, - code->pad, code->stream, - code->which); + fmt = v4l2_state_get_opposite_stream_format(state, code->pad, + code->stream); if (!fmt) { - r = -EINVAL; + ret = -EINVAL; goto out; } code->code = fmt->code; } else { if (code->index >= cal_num_formats) { - r = -EINVAL; + ret = -EINVAL; goto out; } @@ -792,38 +717,37 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, } out: - mutex_unlock(&phy->mutex); + v4l2_subdev_unlock_state(state); - return r; + return ret; } static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_frame_size_enum *fse) { - struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; - int r = 0; + int ret = 0; if (fse->index > 0) return -EINVAL; - mutex_lock(&phy->mutex); + v4l2_subdev_lock_state(state); /* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; - fmt = cal_camerarx_get_opposite_stream_format( - phy, sd_state, fse->pad, fse->stream, fse->which); + fmt = v4l2_state_get_opposite_stream_format(state, fse->pad, + fse->stream); if (!fmt) { - r = -EINVAL; + ret = -EINVAL; goto out; } if (fse->code != fmt->code) { - r = -EINVAL; + ret = -EINVAL; goto out; } @@ -834,53 +758,26 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, } else { fmtinfo = cal_format_by_code(fse->code); if (!fmtinfo) { - r = -EINVAL; + ret = -EINVAL; goto out; } - fse->min_width = - CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); - fse->max_width = - CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); fse->min_height = CAL_MIN_HEIGHT_LINES; fse->max_height = CAL_MAX_HEIGHT_LINES; } out: - mutex_unlock(&phy->mutex); - - return r; -} + v4l2_subdev_unlock_state(state); -static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct cal_camerarx *phy = to_cal_camerarx(sd); - struct v4l2_mbus_framefmt *fmt; - - mutex_lock(&phy->mutex); - - fmt = cal_camerarx_get_stream_format(phy, sd_state, format->pad, - format->stream, format->which); - - if (!fmt) { - mutex_unlock(&phy->mutex); - return -EINVAL; - } - - format->format = *fmt; - - mutex_unlock(&phy->mutex); - - return 0; + return ret; } static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { - struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; struct v4l2_mbus_framefmt *fmt; unsigned int bpp; @@ -888,7 +785,7 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* No transcoding, source and sink formats must match. */ if (cal_rx_pad_is_source(format->pad)) - return cal_camerarx_sd_get_fmt(sd, sd_state, format); + return v4l2_subdev_get_fmt(sd, state, format); /* * Default to the first format if the requested media bus code isn't @@ -912,10 +809,10 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, /* Store the format and propagate it to the source pad. */ - mutex_lock(&phy->mutex); + v4l2_subdev_lock_state(state); - fmt = cal_camerarx_get_stream_format(phy, sd_state, format->pad, - format->stream, format->which); + fmt = v4l2_state_get_stream_format(state, format->pad, + format->stream); if (!fmt) { ret = -EINVAL; goto out; @@ -923,9 +820,8 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, *fmt = format->format; - fmt = cal_camerarx_get_opposite_stream_format(phy, sd_state, format->pad, - format->stream, - format->which); + fmt = v4l2_state_get_opposite_stream_format(state, format->pad, + format->stream); if (!fmt) { ret = -EINVAL; goto out; @@ -934,29 +830,15 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, *fmt = format->format; out: - mutex_unlock(&phy->mutex); + v4l2_subdev_unlock_state(state); return ret; } -static int cal_camerarx_sd_get_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *cfg, +static int _cal_camerarx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, struct v4l2_subdev_krouting *routing) { - struct cal_camerarx *phy = to_cal_camerarx(sd); - struct v4l2_subdev_krouting *src; - - src = cal_camerarx_get_routing_table(phy, cfg, routing->which); - - return v4l2_subdev_cpy_routing(routing, src); -} - -static void cal_camerarx_init_formats(struct v4l2_subdev *sd, - struct v4l2_subdev_state *cfg, - u32 which) -{ - struct cal_camerarx *phy = to_cal_camerarx(sd); - static const struct v4l2_mbus_framefmt format = { .width = 640, .height = 480, @@ -967,48 +849,37 @@ static void cal_camerarx_init_formats(struct v4l2_subdev *sd, .quantization = V4L2_QUANTIZATION_LIM_RANGE, .xfer_func = V4L2_XFER_FUNC_SRGB, }; + int ret; - struct v4l2_subdev_stream_configs *stream_configs; - unsigned int i; - - stream_configs = cal_camerarx_get_stream_configs(phy, cfg, which); + ret = v4l2_routing_simple_verify(routing); + if (ret) + return ret; - for (i = 0; i < stream_configs->num_configs; ++i) - stream_configs->configs[i].fmt = format; -} + /* TODO: verify that all streams from a single RX port go to a single TX port */ -static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *cfg, - struct v4l2_subdev_krouting *routing) -{ - struct cal_camerarx *phy = to_cal_camerarx(sd); - int ret; - struct v4l2_subdev_krouting *dst; - struct v4l2_subdev_stream_configs *stream_configs; + v4l2_subdev_lock_state(state); - dst = cal_camerarx_get_routing_table(phy, cfg, routing->which); - stream_configs = - cal_camerarx_get_stream_configs(phy, cfg, routing->which); + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); - ret = v4l2_subdev_dup_routing(dst, routing); - if (ret) - return ret; + v4l2_subdev_unlock_state(state); - ret = v4l2_init_stream_configs(stream_configs, dst); if (ret) return ret; - /* Initialize stream formats */ - cal_camerarx_init_formats(sd, cfg, routing->which); - return 0; } -static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) +static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) { - u32 which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + return _cal_camerarx_sd_set_routing(sd, state, routing); +} +static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ struct v4l2_subdev_route routes[] = { { .sink_pad = 0, .sink_stream = 0, @@ -1018,13 +889,12 @@ static int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd, } }; struct v4l2_subdev_krouting routing = { - .which = which, .num_routes = 1, .routes = routes, }; /* Initialize routing to single route to the fist source pad */ - return cal_camerarx_sd_set_routing(sd, sd_state, &routing); + return _cal_camerarx_sd_set_routing(sd, state, &routing); } static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = { @@ -1035,9 +905,8 @@ static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = { .init_cfg = cal_camerarx_sd_init_cfg, .enum_mbus_code = cal_camerarx_sd_enum_mbus_code, .enum_frame_size = cal_camerarx_sd_enum_frame_size, - .get_fmt = cal_camerarx_sd_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = cal_camerarx_sd_set_fmt, - .get_routing = cal_camerarx_sd_get_routing, .set_routing = cal_camerarx_sd_set_routing, }; @@ -1046,18 +915,9 @@ static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = { .pad = &cal_camerarx_pad_ops, }; -static bool cal_camerarx_has_route(struct media_entity *entity, unsigned int pad0, - unsigned int pad1) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); - struct cal_camerarx *phy = to_cal_camerarx(sd); - - return v4l2_subdev_has_route(&phy->routing, pad0, pad1); -} - static struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, - .has_route = cal_camerarx_has_route, + .has_route = v4l2_subdev_has_route, }; /* ------------------------------------------------------------------ @@ -1071,8 +931,8 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; struct v4l2_subdev *sd; - int ret; unsigned int i; + int ret; phy = kzalloc(sizeof(*phy), GFP_KERNEL); if (!phy) @@ -1081,6 +941,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + spin_lock_init(&phy->vc_lock); mutex_init(&phy->mutex); phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -1091,7 +952,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, if (IS_ERR(phy->base)) { cal_err(cal, "failed to ioremap\n"); ret = PTR_ERR(phy->base); - goto error; + goto err_free_phy; } cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", @@ -1099,11 +960,11 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = cal_camerarx_regmap_init(cal, phy); if (ret) - goto error; + goto err_free_phy; ret = cal_camerarx_parse_dt(phy); if (ret) - goto error; + goto err_free_phy; /* Initialize the V4L2 subdev and media entity. */ sd = &phy->subdev; @@ -1121,22 +982,23 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), phy->pads); if (ret) - goto error; + goto err_free_phy; - ret = cal_camerarx_sd_init_cfg(sd, NULL); + ret = v4l2_subdev_init_finalize(sd); if (ret) - goto error; + goto err_entity_cleanup; ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); if (ret) - goto error; + goto err_free_state; return phy; -error: - v4l2_subdev_free_routing(&phy->routing); - v4l2_uninit_stream_configs(&phy->stream_configs); +err_free_state: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: media_entity_cleanup(&phy->subdev.entity); +err_free_phy: kfree(phy); return ERR_PTR(ret); } @@ -1147,10 +1009,12 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) return; v4l2_device_unregister_subdev(&phy->subdev); - v4l2_subdev_free_routing(&phy->routing); - v4l2_uninit_stream_configs(&phy->stream_configs); + + v4l2_subdev_cleanup(&phy->subdev); + media_entity_cleanup(&phy->subdev.entity); of_node_put(phy->source_ep_node); of_node_put(phy->source_node); + mutex_destroy(&phy->mutex); kfree(phy); } diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c index 3b0e5c421904..963e0bf3436d 100644 --- a/drivers/media/platform/ti/cal/cal-video.c +++ b/drivers/media/platform/ti/cal/cal-video.c @@ -123,12 +123,13 @@ static int __subdev_get_format(struct cal_ctx *ctx, { struct v4l2_subdev_format sd_fmt; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + struct v4l2_subdev *sd = ctx->phy->source; int ret; sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -145,13 +146,14 @@ static int __subdev_set_format(struct cal_ctx *ctx, { struct v4l2_subdev_format sd_fmt; struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + struct v4l2_subdev *sd = ctx->phy->source; int ret; sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; sd_fmt.pad = 0; *mbus_fmt = *fmt; - ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt); + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt); if (ret) return ret; @@ -193,9 +195,10 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; - int ret, found; + int found; fmtinfo = find_format_by_pix(ctx, f->fmt.pix.pixelformat); if (!fmtinfo) { @@ -210,13 +213,14 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; /* check for/find a valid width/height */ - ret = 0; found = false; fse.pad = 0; fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, + int ret; + + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); if (ret) break; @@ -253,6 +257,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = &ctx->phy->subdev; struct vb2_queue *q = &ctx->vb_vidq; struct v4l2_subdev_format sd_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -292,7 +297,8 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv, ctx->v_fmt.fmt.pix.field = sd_fmt.format.field; cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt); - v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt); + v4l2_subdev_call(sd, pad, set_fmt, v4l2_subdev_get_active_state(sd), + &sd_fmt); ctx->fmtinfo = fmtinfo; *f = ctx->v_fmt; @@ -304,6 +310,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_size_enum fse; int ret; @@ -321,8 +328,8 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh, fse.code = fmtinfo->code; fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL, - &fse); + ret = v4l2_subdev_call(sd, pad, enum_frame_size, + v4l2_subdev_get_active_state(sd), &fse); if (ret) return ret; @@ -364,6 +371,7 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { struct cal_ctx *ctx = video_drvdata(file); + struct v4l2_subdev *sd = ctx->phy->source; const struct cal_format_info *fmtinfo; struct v4l2_subdev_frame_interval_enum fie = { .index = fival->index, @@ -378,8 +386,10 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fie.code = fmtinfo->code; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval, - NULL, &fie); + + ret = v4l2_subdev_call(sd, pad, enum_frame_interval, + v4l2_subdev_get_active_state(sd), &fie); + if (ret) return ret; fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; @@ -688,24 +698,34 @@ static int cal_video_check_format(struct cal_ctx *ctx) { const struct v4l2_mbus_framefmt *format; struct media_pad *remote_pad; + struct v4l2_subdev_state *state; + int ret = 0; remote_pad = media_entity_remote_pad(&ctx->pad); if (!remote_pad) return -ENODEV; - format = cal_camerarx_get_stream_format(ctx->phy, NULL, - remote_pad->index, 0, - V4L2_SUBDEV_FORMAT_ACTIVE); - if (!format) - return -EINVAL; + state = v4l2_subdev_lock_active_state(&ctx->phy->subdev); + + format = v4l2_state_get_stream_format(state, + remote_pad->index, 0); + if (!format) { + ret = -EINVAL; + goto out; + } if (ctx->fmtinfo->code != format->code || ctx->v_fmt.fmt.pix.height != format->height || ctx->v_fmt.fmt.pix.width != format->width || - ctx->v_fmt.fmt.pix.field != format->field) - return -EPIPE; + ctx->v_fmt.fmt.pix.field != format->field) { + ret = -EPIPE; + goto out; + } - return 0; +out: + v4l2_subdev_unlock_state(state); + + return ret; } static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -719,6 +739,7 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) struct v4l2_subdev_route *route = NULL; struct media_pad *remote_pad; unsigned int i; + struct v4l2_subdev_state *state; /* Find the PHY connected to this video device */ @@ -731,11 +752,13 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) ctx->phy = cal_camerarx_get_phy_from_entity(remote_pad->entity); + state = v4l2_subdev_lock_active_state(&ctx->phy->subdev); + /* Find the stream */ - for (i = 0; i < ctx->phy->routing.num_routes; ++i) { + for (i = 0; i < state->routing.num_routes; ++i) { struct v4l2_subdev_route *r = - &ctx->phy->routing.routes[i]; + &state->routing.routes[i]; if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) continue; @@ -749,12 +772,15 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) } if (!route) { + v4l2_subdev_unlock_state(state); ctx_err(ctx, "Failed to find route\n"); ret = -ENODEV; goto error_release_buffers; } ctx->stream = route->sink_stream; + + v4l2_subdev_unlock_state(state); } ret = media_pipeline_start(ctx->vdev.entity.pads, &ctx->phy->pipe); @@ -868,6 +894,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) const struct cal_format_info *fmtinfo; unsigned int i, j, k; int ret = 0; + struct v4l2_subdev *sd = ctx->phy->source; /* Enumerate sub device formats and enable all matching local formats */ ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats, @@ -879,20 +906,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) memset(&mbus_code, 0, sizeof(mbus_code)); mbus_code.index = j; mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code, + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &mbus_code); if (ret == -EINVAL) break; if (ret) { ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n", - ctx->phy->source->name, ret); + sd->name, ret); return ret; } ctx_dbg(2, ctx, "subdev %s: code: %04x idx: %u\n", - ctx->phy->source->name, mbus_code.code, j); + sd->name, mbus_code.code, j); for (k = 0; k < cal_num_formats; k++) { fmtinfo = &cal_formats[k]; @@ -910,7 +937,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) if (i == 0) { ctx_err(ctx, "No suitable format reported by subdev %s\n", - ctx->phy->source->name); + sd->name); return -EINVAL; } @@ -1002,11 +1029,13 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx) /* Create links from all video nodes to all PHYs */ - for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy; ++phy_idx) { - for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS; ++pad_idx) { + for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy; + ++phy_idx) { + for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS; + ++pad_idx) { /* - * Enable only links from video0 to PHY0 pad 1, and - * video1 to PHY1 pad 1. + * Enable only links from video0 to PHY0 pad 1, + * and video1 to PHY1 pad 1. */ bool enable = (ctx->dma_ctx == 0 && phy_idx == 0 && pad_idx == 1) || diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 69a7ed9667c4..0399ac9f57d6 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -492,6 +492,10 @@ cal_get_remote_frame_desc_entry(struct cal_camerarx *phy, u32 stream, } } + dev_err(phy->cal->dev, + "Failed to find stream %u from remote frame descriptor\n", + stream); + return -ENODEV; } @@ -507,15 +511,12 @@ int cal_ctx_prepare(struct cal_ctx *ctx) ctx->datatype = CAL_CSI2_CTX_DT_ANY; } else if (!ret) { ctx_dbg(2, ctx, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", - entry.stream, - entry.length, - entry.bus.csi2.vc, - entry.bus.csi2.dt); + entry.stream, entry.length, entry.bus.csi2.vc, + entry.bus.csi2.dt); ctx->vc = entry.bus.csi2.vc; ctx->datatype = entry.bus.csi2.dt; } else { - ctx_err(ctx, "Failed to get remote frame desc: %d\n", ret); return ret; } @@ -542,6 +543,22 @@ void cal_ctx_unprepare(struct cal_ctx *ctx) void cal_ctx_start(struct cal_ctx *ctx) { + struct cal_camerarx *phy = ctx->phy; + + /* + * Reset the frame number & sequence number, but only if the + * virtual channel is not already in use. + */ + + spin_lock(&phy->vc_lock); + + if (phy->vc_enable_count[ctx->vc]++ == 0) { + phy->vc_frame_number[ctx->vc] = 0; + phy->vc_sequence[ctx->vc] = 0; + } + + spin_unlock(&phy->vc_lock); + ctx->dma.state = CAL_DMA_RUNNING; /* Configure the CSI-2, pixel processing and write DMA contexts. */ @@ -561,8 +578,15 @@ void cal_ctx_start(struct cal_ctx *ctx) void cal_ctx_stop(struct cal_ctx *ctx) { + struct cal_camerarx *phy = ctx->phy; long timeout; + WARN_ON(phy->vc_enable_count[ctx->vc] == 0); + + spin_lock(&phy->vc_lock); + phy->vc_enable_count[ctx->vc]--; + spin_unlock(&phy->vc_lock); + /* * Request DMA stop and wait until it completes. If completion times * out, forcefully disable the DMA. @@ -599,6 +623,32 @@ void cal_ctx_stop(struct cal_ctx *ctx) * ------------------------------------------------------------------ */ +/* + * Track a sequence number for each virtual channel, which is shared by + * all contexts using the same virtual channel. This is done using the + * CSI-2 frame number as a base. + */ +static void cal_update_seq_number(struct cal_ctx *ctx) +{ + struct cal_dev *cal = ctx->cal; + struct cal_camerarx *phy = ctx->phy; + u32 prev_frame_num, frame_num; + u8 vc = ctx->vc; + + frame_num = cal_read(cal, CAL_CSI2_STATUS(phy->instance, ctx->csi2_ctx)); + frame_num &= 0xffff; + + if (phy->vc_frame_number[vc] != frame_num) { + prev_frame_num = phy->vc_frame_number[vc]; + + if (prev_frame_num > frame_num) + prev_frame_num = 0; + + phy->vc_sequence[vc] += frame_num - prev_frame_num; + phy->vc_frame_number[vc] = frame_num; + } +} + static inline void cal_irq_wdma_start(struct cal_ctx *ctx) { spin_lock(&ctx->dma.lock); @@ -629,15 +679,13 @@ static inline void cal_irq_wdma_start(struct cal_ctx *ctx) } spin_unlock(&ctx->dma.lock); + + cal_update_seq_number(ctx); } static inline void cal_irq_wdma_end(struct cal_ctx *ctx) { struct cal_buffer *buf = NULL; - u32 frame_num; - - frame_num = cal_read(ctx->cal, CAL_CSI2_STATUS(ctx->phy->instance, - ctx->csi2_ctx)) & 0xffff; spin_lock(&ctx->dma.lock); @@ -659,27 +707,62 @@ static inline void cal_irq_wdma_end(struct cal_ctx *ctx) if (buf) { buf->vb.vb2_buf.timestamp = ktime_get_ns(); buf->vb.field = ctx->v_fmt.fmt.pix.field; - buf->vb.sequence = frame_num; + buf->vb.sequence = ctx->phy->vc_sequence[ctx->vc]; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } } +static void cal_irq_handle_wdma(struct cal_ctx *ctx, bool start, bool end) +{ + /* + * CAL HW interrupts are inherently racy. If we get both start and end + * interrupts, we don't know what has happened: did the DMA for a single + * frame start and end, or did one frame end and a new frame start? + * + * Usually for normal pixel frames we get the interrupts separately. If + * we do get both, we have to guess. The assumption in the code below is + * that the active vertical area is larger than the blanking vertical + * area, and thus it is more likely that we get the end of the old frame + * and the start of a new frame. + * + * However, for embedded data, which is only a few lines high, we always + * get both interrupts. Here the assumption is that we get both for the + * same frame. + */ + if (ctx->v_fmt.fmt.pix.height < 10) { + if (start) + cal_irq_wdma_start(ctx); + + if (end) + cal_irq_wdma_end(ctx); + } else { + if (end) + cal_irq_wdma_end(ctx); + + if (start) + cal_irq_wdma_start(ctx); + } +} + static irqreturn_t cal_irq(int irq_cal, void *data) { struct cal_dev *cal = data; - u32 status; - - status = cal_read(cal, CAL_HL_IRQSTATUS(0)); - if (status) { - unsigned int i; + u32 status[3]; + unsigned int i; - cal_write(cal, CAL_HL_IRQSTATUS(0), status); + for (i = 0; i < 3; ++i) { + status[i] = cal_read(cal, CAL_HL_IRQSTATUS(i)); + if (status[i]) + cal_write(cal, CAL_HL_IRQSTATUS(i), status[i]); + } - if (status & CAL_HL_IRQ_OCPO_ERR_MASK) + if (status[0]) { + if (status[0] & CAL_HL_IRQ_OCPO_ERR_MASK) dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); for (i = 0; i < cal->data->num_csi2_phy; ++i) { - if (status & CAL_HL_IRQ_CIO_MASK(i)) { + if (status[0] & CAL_HL_IRQ_CIO_MASK(i)) { u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); @@ -690,7 +773,7 @@ static irqreturn_t cal_irq(int irq_cal, void *data) cio_stat); } - if (status & CAL_HL_IRQ_VC_MASK(i)) { + if (status[0] & CAL_HL_IRQ_VC_MASK(i)) { u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i)); dev_err_ratelimited(cal->dev, @@ -702,32 +785,12 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } } - /* Check which DMA just finished */ - status = cal_read(cal, CAL_HL_IRQSTATUS(1)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(1), status); - - for (i = 0; i < cal->num_contexts; ++i) { - if (status & CAL_HL_IRQ_WDMA_END_MASK(i)) - cal_irq_wdma_end(cal->ctx[i]); - } - } - - /* Check which DMA just started */ - status = cal_read(cal, CAL_HL_IRQSTATUS(2)); - if (status) { - unsigned int i; - - /* Clear Interrupt status */ - cal_write(cal, CAL_HL_IRQSTATUS(2), status); + for (i = 0; i < cal->num_contexts; ++i) { + bool end = !!(status[1] & CAL_HL_IRQ_WDMA_END_MASK(i)); + bool start = !!(status[2] & CAL_HL_IRQ_WDMA_START_MASK(i)); - for (i = 0; i < cal->num_contexts; ++i) { - if (status & CAL_HL_IRQ_WDMA_START_MASK(i)) - cal_irq_wdma_start(cal->ctx[i]); - } + if (start || end) + cal_irq_handle_wdma(cal->ctx[i], start, end); } return IRQ_HANDLED; @@ -792,16 +855,30 @@ static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); unsigned int i; - int ret = 0; + int ret; for (i = 0; i < cal->num_contexts; ++i) { ret = cal_ctx_v4l2_register(cal->ctx[i]); if (ret) - return ret; + goto err_ctx_unreg; } - if (cal_mc_api) - ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); + if (!cal_mc_api) + return 0; + + ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); + if (ret) + goto err_ctx_unreg; + + return 0; + +err_ctx_unreg: + for (; i > 0; --i) { + if (!cal->ctx[i - 1]) + continue; + + cal_ctx_v4l2_unregister(cal->ctx[i - 1]); + } return ret; } @@ -1184,14 +1261,14 @@ static int cal_probe(struct platform_device *pdev) if (!cal->phy[i]->source_node) continue; - cal->ctx[i] = cal_ctx_create(cal, i); - if (!cal->ctx[i]) { - cal_err(cal, "Failed to create context %u\n", i); + cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i); + if (!cal->ctx[cal->num_contexts]) { + cal_err(cal, "Failed to create context %u\n", cal->num_contexts); ret = -ENODEV; goto error_context; } - cal->ctx[i]->phy = cal->phy[i]; + cal->ctx[cal->num_contexts]->phy = cal->phy[i]; cal->num_contexts++; } diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h index 6626c2a59fc2..444f0de591ac 100644 --- a/drivers/media/platform/ti/cal/cal.h +++ b/drivers/media/platform/ti/cal/cal.h @@ -179,13 +179,21 @@ struct cal_camerarx { struct v4l2_subdev subdev; struct media_pad pads[CAL_CAMERARX_NUM_PADS]; - /* mutex for camerarx ops */ + /* protects the vc_* fields below */ + spinlock_t vc_lock; + u8 vc_enable_count[4]; + u8 vc_frame_number[4]; + u32 vc_sequence[4]; + + /* + * Lock for camerarx ops. Protects: + * - routing + * - stream_configs + * - enable_count + */ struct mutex mutex; - unsigned int enable_count; - - struct v4l2_subdev_krouting routing; - struct v4l2_subdev_stream_configs stream_configs; + unsigned int enable_count; }; struct cal_dev { @@ -322,7 +330,7 @@ const struct cal_format_info *cal_format_by_code(u32 code); void cal_quickdump_regs(struct cal_dev *cal); int cal_camerarx_get_remote_frame_desc(struct cal_camerarx *phy, - struct v4l2_mbus_frame_desc *fd); + struct v4l2_mbus_frame_desc *desc); struct cal_camerarx *cal_camerarx_get_phy_from_entity(struct media_entity *entity); void cal_camerarx_disable(struct cal_camerarx *phy); void cal_camerarx_i913_errata(struct cal_camerarx *phy); @@ -341,9 +349,4 @@ void cal_ctx_v4l2_unregister(struct cal_ctx *ctx); int cal_ctx_v4l2_init(struct cal_ctx *ctx); void cal_ctx_v4l2_cleanup(struct cal_ctx *ctx); -struct v4l2_mbus_framefmt * -cal_camerarx_get_stream_format(struct cal_camerarx *phy, - struct v4l2_subdev_state *state, - unsigned int pad, u32 stream, u32 which); - #endif /* __TI_CAL_H__ */ diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c index 71282b85b784..6e791e152755 100644 --- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c +++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c @@ -25,12 +25,16 @@ #define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20)) #define SHIM_DMACNTX_EN BIT(31) #define SHIM_DMACNTX_YUV422 GENMASK(27, 26) +#define SHIM_DMACNTX_SIZE GENMASK(21, 20) #define SHIM_DMACNTX_VC GENMASK(9, 6) #define SHIM_DMACNTX_FMT GENMASK(5, 0) #define SHIM_DMACNTX_UYVY 0 #define SHIM_DMACNTX_VYUY 1 #define SHIM_DMACNTX_YUYV 2 #define SHIM_DMACNTX_YVYU 3 +#define SHIM_DMACNTX_SIZE_8 0 +#define SHIM_DMACNTX_SIZE_16 1 +#define SHIM_DMACNTX_SIZE_32 2 #define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20)) #define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0) @@ -40,6 +44,9 @@ #define CSI_DF_YUV422 0x1e #define CSI_DF_RGB444 0x20 #define CSI_DF_RGB888 0x24 +#define CSI_DF_RAW8 0x2a +#define CSI_DF_RAW10 0x2b +#define CSI_DF_RAW12 0x2c #define PSIL_WORD_SIZE_BYTES 16 #define TI_CSI2RX_MAX_CTX 32 @@ -66,6 +73,7 @@ struct ti_csi2rx_fmt { enum v4l2_colorspace colorspace; u32 csi_df; /* CSI Data format. */ u8 bpp; /* Bits per pixel. */ + u8 size; /* Data size shift when unpacking. */ }; struct ti_csi2rx_buffer { @@ -125,8 +133,6 @@ struct ti_csi2rx_dev { struct v4l2_device v4l2_dev; struct v4l2_subdev *source; struct v4l2_subdev subdev; - struct v4l2_subdev_krouting routing; - struct v4l2_subdev_stream_configs stream_configs; struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX]; }; @@ -137,24 +143,112 @@ static const struct ti_csi2rx_fmt formats[] = { .colorspace = V4L2_COLORSPACE_SRGB, .csi_df = CSI_DF_YUV422, .bpp = 16, + .size = SHIM_DMACNTX_SIZE_8, }, { .fourcc = V4L2_PIX_FMT_UYVY, .code = MEDIA_BUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_SRGB, .csi_df = CSI_DF_YUV422, .bpp = 16, + .size = SHIM_DMACNTX_SIZE_8, }, { .fourcc = V4L2_PIX_FMT_YVYU, .code = MEDIA_BUS_FMT_YVYU8_2X8, .colorspace = V4L2_COLORSPACE_SRGB, .csi_df = CSI_DF_YUV422, .bpp = 16, + .size = SHIM_DMACNTX_SIZE_8, }, { .fourcc = V4L2_PIX_FMT_VYUY, .code = MEDIA_BUS_FMT_VYUY8_2X8, .colorspace = V4L2_COLORSPACE_SRGB, .csi_df = CSI_DF_YUV422, .bpp = 16, + .size = SHIM_DMACNTX_SIZE_8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW8, + .bpp = 8, + .size = SHIM_DMACNTX_SIZE_8, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW8, + .bpp = 8, + .size = SHIM_DMACNTX_SIZE_8, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW8, + .bpp = 8, + .size = SHIM_DMACNTX_SIZE_8, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW8, + .bpp = 8, + .size = SHIM_DMACNTX_SIZE_8, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW10, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW10, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW10, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW10, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW12, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW12, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW12, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .colorspace = V4L2_COLORSPACE_SRGB, + .csi_df = CSI_DF_RAW12, + .bpp = 16, + .size = SHIM_DMACNTX_SIZE_16, }, /* More formats can be supported but they are not listed for now. */ @@ -482,6 +576,7 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx) break; } + reg |= FIELD_PREP(SHIM_DMACNTX_SIZE, fmt->size); reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc); writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx)); @@ -733,10 +828,12 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) struct ti_csi2rx_dev *csi = ctx->csi; struct ti_csi2rx_dma *dma = &ctx->dma; struct ti_csi2rx_buffer *buf, *tmp; + struct v4l2_subdev_krouting *routing; struct v4l2_subdev_route *route = NULL; struct media_pad *remote_pad; unsigned long flags = 0; int ret = 0, i; + struct v4l2_subdev_state *state; spin_lock_irqsave(&dma->lock, flags); if (list_empty(&dma->queue)) @@ -755,9 +852,13 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) goto err_pipeline; } + state = v4l2_subdev_lock_active_state(&csi->subdev); + + routing = &state->routing; + /* Find the stream to process. */ - for (i = 0; i < csi->routing.num_routes; i++) { - struct v4l2_subdev_route *r = &csi->routing.routes[i]; + for (i = 0; i < routing->num_routes; i++) { + struct v4l2_subdev_route *r = &routing->routes[i]; if (!(r->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) continue; @@ -771,11 +872,14 @@ static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count) if (!route) { ret = -ENODEV; + v4l2_subdev_unlock_state(state); goto err_pipeline; } ctx->stream = route->sink_stream; + v4l2_subdev_unlock_state(state); + ret = ti_csi2rx_get_vc(ctx); if (ret == -ENOIOCTLCMD) ctx->vc = 0; @@ -891,67 +995,43 @@ static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd) return container_of(sd, struct ti_csi2rx_dev, subdev); } -static struct v4l2_subdev_krouting * -ti_csi2rx_get_routing_table(struct ti_csi2rx_dev *csi, - struct v4l2_subdev_state *state, u32 which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &csi->routing; - else - return &state->routing; -} - -static struct v4l2_subdev_stream_configs * -ti_csi2rx_get_stream_configs(struct ti_csi2rx_dev *csi, - struct v4l2_subdev_state *state, u32 which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &csi->stream_configs; - else - return &state->stream_configs; -} - -static int ti_csi2rx_sd_get_routing(struct v4l2_subdev *sd, +static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_krouting *routing) { - struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); - struct v4l2_subdev_krouting *src; + int ret; + + const struct v4l2_mbus_framefmt format = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + + v4l2_subdev_lock_state(state); + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); - src = ti_csi2rx_get_routing_table(csi, state, routing->which); + v4l2_subdev_unlock_state(state); - return v4l2_subdev_cpy_routing(routing, src); + return ret; } static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, struct v4l2_subdev_krouting *routing) { - struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd); - struct v4l2_subdev_krouting *dst; - struct v4l2_subdev_stream_configs *stream_configs; - int ret; - - dst = ti_csi2rx_get_routing_table(csi, state, routing->which); - stream_configs = ti_csi2rx_get_stream_configs(csi, state, - routing->which); - - ret = v4l2_subdev_dup_routing(dst, routing); - if (ret) - return ret; - - ret = v4l2_init_stream_configs(stream_configs, dst); - if (ret) - return ret; - - return 0; + return _ti_csi2rx_sd_set_routing(sd, state, routing); } static int ti_csi2rx_sd_init_cfg(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) + struct v4l2_subdev_state *state) { - u32 which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - struct v4l2_subdev_route routes[] = { { .sink_pad = 0, .sink_stream = 0, @@ -961,13 +1041,12 @@ static int ti_csi2rx_sd_init_cfg(struct v4l2_subdev *sd, } }; struct v4l2_subdev_krouting routing = { - .which = which, .num_routes = 1, .routes = routes, }; /* Initialize routing to single route to the fist source pad */ - return ti_csi2rx_sd_set_routing(sd, sd_state, &routing); + return _ti_csi2rx_sd_set_routing(sd, state, &routing); } static int ti_csi2rx_sd_s_stream(struct v4l2_subdev *sd, int enable) @@ -1011,7 +1090,6 @@ static const struct v4l2_subdev_video_ops ti_csi2rx_subdev_video_ops = { static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = { .init_cfg = ti_csi2rx_sd_init_cfg, - .get_routing = ti_csi2rx_sd_get_routing, .set_routing = ti_csi2rx_sd_set_routing, }; @@ -1130,7 +1208,7 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops); sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_MULTIPLEXED; strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name)); sd->dev = csi->dev; @@ -1146,16 +1224,18 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi) if (ret) goto unregister_media; - ret = ti_csi2rx_sd_init_cfg(sd, NULL); + ret = v4l2_subdev_init_finalize(sd); if (ret) goto unregister_media; ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd); if (ret) - goto unregister_media; + goto cleanup_subdev; return 0; +cleanup_subdev: + v4l2_subdev_cleanup(sd); unregister_media: media_device_unregister(mdev); unregister_v4l2: diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 7d92262f3378..dded3ff57830 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -613,6 +613,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, const char *name, unsigned int num_pads, const struct v4l2_subdev_ops *ops, u32 function) { + static struct lock_class_key key; struct v4l2_subdev *subdev; unsigned int i; int ret; @@ -675,10 +676,11 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, * Allocate the pad configuration to store formats and selection * rectangles. */ - entity->config = v4l2_subdev_alloc_state(&entity->subdev); - if (entity->config == NULL) { + entity->config = __v4l2_subdev_state_alloc(&entity->subdev, + "vsp1:config->lock", &key); + if (IS_ERR(entity->config)) { media_entity_cleanup(&entity->subdev.entity); - return -ENOMEM; + return PTR_ERR(entity->config); } return 0; @@ -690,6 +692,6 @@ void vsp1_entity_destroy(struct vsp1_entity *entity) entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); - v4l2_subdev_free_state(entity->config); + __v4l2_subdev_state_free(entity->config); media_entity_cleanup(&entity->subdev.entity); } diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 4f03bfa300a1..0b97c9083607 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -28,8 +28,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { struct v4l2_subdev_state *state; + static struct lock_class_key key; - state = v4l2_subdev_alloc_state(sd); + state = __v4l2_subdev_state_alloc(sd, "fh->state->lock", &key); if (IS_ERR(state)) return PTR_ERR(state); @@ -40,7 +41,7 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { - v4l2_subdev_free_state(fh->state); + __v4l2_subdev_state_free(fh->state); fh->state = NULL; } @@ -149,9 +150,40 @@ static inline int check_pad(struct v4l2_subdev *sd, u32 pad) return 0; } -static int check_cfg(u32 which, struct v4l2_subdev_state *state) +static int check_state_pads(struct v4l2_subdev *sd, u32 which, + struct v4l2_subdev_state *state) { - if (which == V4L2_SUBDEV_FORMAT_TRY && !state) + if (sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED) + return 0; + + if (which == V4L2_SUBDEV_FORMAT_TRY && (!state || !state->pads)) + return -EINVAL; + + return 0; +} + +static int check_state_pad_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u32 stream) +{ + struct v4l2_mbus_framefmt *fmt; + + if (!(sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED)) + return 0; + + /* + * We need to take the state lock to access the format, but as we then + * have to unlock, nothing prevents someone changing the state before + * this call thread enters the driver's op and the driver has the + * change to lock the state. + */ + v4l2_subdev_lock_state(state); + + fmt = v4l2_state_get_stream_format(state, pad, stream); + + v4l2_subdev_unlock_state(state); + + if (!fmt) return -EINVAL; return 0; @@ -165,7 +197,8 @@ static inline int check_format(struct v4l2_subdev *sd, return -EINVAL; return check_which(format->which) ? : check_pad(sd, format->pad) ? : - check_cfg(format->which, state); + check_state_pads(sd, format->which, state) ? : + check_state_pad_stream(sd, state, format->pad, format->stream); } static int call_get_fmt(struct v4l2_subdev *sd, @@ -192,7 +225,8 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd, return -EINVAL; return check_which(code->which) ? : check_pad(sd, code->pad) ? : - check_cfg(code->which, state) ? : + check_state_pads(sd, code->which, state) ? : + check_state_pad_stream(sd, state, code->pad, code->stream) ? : sd->ops->pad->enum_mbus_code(sd, state, code); } @@ -204,7 +238,8 @@ static int call_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; return check_which(fse->which) ? : check_pad(sd, fse->pad) ? : - check_cfg(fse->which, state) ? : + check_state_pads(sd, fse->which, state) ? : + check_state_pad_stream(sd, state, fse->pad, fse->stream) ? : sd->ops->pad->enum_frame_size(sd, state, fse); } @@ -239,7 +274,8 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd, return -EINVAL; return check_which(fie->which) ? : check_pad(sd, fie->pad) ? : - check_cfg(fie->which, state) ? : + check_state_pads(sd, fie->which, state) ? : + check_state_pad_stream(sd, state, fie->pad, fie->stream) ? : sd->ops->pad->enum_frame_interval(sd, state, fie); } @@ -251,7 +287,8 @@ static inline int check_selection(struct v4l2_subdev *sd, return -EINVAL; return check_which(sel->which) ? : check_pad(sd, sel->pad) ? : - check_cfg(sel->which, state); + check_state_pads(sd, sel->which, state) ? : + check_state_pad_stream(sd, state, sel->pad, sel->stream); } static int call_get_selection(struct v4l2_subdev *sd, @@ -354,6 +391,59 @@ const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = { EXPORT_SYMBOL(v4l2_subdev_call_wrappers); #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + +static struct v4l2_subdev_state * +subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh, + unsigned int cmd, void *arg) +{ + u32 which; + + switch (cmd) { + default: + return NULL; + + case VIDIOC_SUBDEV_G_FMT: + case VIDIOC_SUBDEV_S_FMT: { + which = ((struct v4l2_subdev_format *)arg)->which; + break; + } + case VIDIOC_SUBDEV_G_CROP: + case VIDIOC_SUBDEV_S_CROP: { + which = ((struct v4l2_subdev_crop *)arg)->which; + break; + } + case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { + which = ((struct v4l2_subdev_mbus_code_enum *)arg)->which; + break; + } + case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { + which = ((struct v4l2_subdev_frame_size_enum *)arg)->which; + break; + } + + case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { + which = ((struct v4l2_subdev_frame_interval_enum *)arg)->which; + break; + } + + case VIDIOC_SUBDEV_G_SELECTION: + case VIDIOC_SUBDEV_S_SELECTION: { + which = ((struct v4l2_subdev_selection *)arg)->which; + break; + } + + case VIDIOC_SUBDEV_G_ROUTING: + case VIDIOC_SUBDEV_S_ROUTING: { + which = ((struct v4l2_subdev_routing *)arg)->which; + break; + } + } + + return which == V4L2_SUBDEV_FORMAT_TRY ? + subdev_fh->state : + v4l2_subdev_get_active_state(sd); +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); @@ -361,15 +451,20 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_fh *vfh = file->private_data; struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); + struct v4l2_subdev_state *state; int rval; + state = subdev_ioctl_get_state(sd, subdev_fh, cmd, arg); + switch (cmd) { case VIDIOC_SUBDEV_QUERYCAP: { struct v4l2_subdev_capability *cap = arg; memset(cap->reserved, 0, sizeof(cap->reserved)); cap->version = LINUX_VERSION_CODE; - cap->capabilities = ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0; + cap->capabilities = + (ro_subdev ? V4L2_SUBDEV_CAP_RO_SUBDEV : 0) | + ((sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED) ? V4L2_SUBDEV_CAP_MPLEXED : 0); return 0; } @@ -485,7 +580,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); - return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->state, format); + return v4l2_subdev_call(sd, pad, get_fmt, state, format); } case VIDIOC_SUBDEV_S_FMT: { @@ -496,7 +591,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(format->reserved, 0, sizeof(format->reserved)); memset(format->format.reserved, 0, sizeof(format->format.reserved)); - return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->state, format); + return v4l2_subdev_call(sd, pad, set_fmt, state, format); } case VIDIOC_SUBDEV_G_CROP: { @@ -510,7 +605,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) sel.target = V4L2_SEL_TGT_CROP; rval = v4l2_subdev_call( - sd, pad, get_selection, subdev_fh->state, &sel); + sd, pad, get_selection, state, &sel); crop->rect = sel.r; @@ -532,7 +627,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) sel.r = crop->rect; rval = v4l2_subdev_call( - sd, pad, set_selection, subdev_fh->state, &sel); + sd, pad, set_selection, state, &sel); crop->rect = sel.r; @@ -543,7 +638,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_mbus_code_enum *code = arg; memset(code->reserved, 0, sizeof(code->reserved)); - return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->state, + return v4l2_subdev_call(sd, pad, enum_mbus_code, state, code); } @@ -551,7 +646,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_frame_size_enum *fse = arg; memset(fse->reserved, 0, sizeof(fse->reserved)); - return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->state, + return v4l2_subdev_call(sd, pad, enum_frame_size, state, fse); } @@ -576,7 +671,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_frame_interval_enum *fie = arg; memset(fie->reserved, 0, sizeof(fie->reserved)); - return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->state, + return v4l2_subdev_call(sd, pad, enum_frame_interval, state, fie); } @@ -585,7 +680,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( - sd, pad, get_selection, subdev_fh->state, sel); + sd, pad, get_selection, state, sel); } case VIDIOC_SUBDEV_S_SELECTION: { @@ -596,7 +691,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(sel->reserved, 0, sizeof(sel->reserved)); return v4l2_subdev_call( - sd, pad, set_selection, subdev_fh->state, sel); + sd, pad, set_selection, state, sel); } case VIDIOC_G_EDID: { @@ -662,19 +757,26 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_ROUTING: { struct v4l2_subdev_routing *routing = arg; - struct v4l2_subdev_krouting krouting = { - .which = routing->which, - .num_routes = routing->num_routes, - .routes = (struct v4l2_subdev_route *)(uintptr_t) - routing->routes, - }; - int ret; + struct v4l2_subdev_krouting *krouting; - ret = v4l2_subdev_call(sd, pad, get_routing, subdev_fh->state, &krouting); + if (!(sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED)) + return -ENOIOCTLCMD; - routing->num_routes = krouting.num_routes; + memset(routing->reserved, 0, sizeof(routing->reserved)); - return ret; + krouting = &state->routing; + + if (routing->num_routes < krouting->num_routes) { + routing->num_routes = krouting->num_routes; + return -ENOSPC; + } + + memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes, + krouting->routes, + krouting->num_routes * sizeof(*krouting->routes)); + routing->num_routes = krouting->num_routes; + + return 0; } case VIDIOC_SUBDEV_S_ROUTING: { @@ -684,26 +786,41 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_subdev_krouting krouting = {}; unsigned int i; + if (!(sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED)) + return -ENOIOCTLCMD; + if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev) return -EPERM; + memset(routing->reserved, 0, sizeof(routing->reserved)); + for (i = 0; i < routing->num_routes; ++i) { - if (routes[i].sink_pad >= sd->entity.num_pads || - routes[i].source_pad >= sd->entity.num_pads) + const struct v4l2_subdev_route *route = &routes[i]; + const struct media_pad *pads = sd->entity.pads; + + /* Do not check sink pad for source routes */ + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE)) { + if (route->sink_pad >= sd->entity.num_pads) + return -EINVAL; + + if (!(pads[route->sink_pad].flags & + MEDIA_PAD_FL_SINK)) + return -EINVAL; + } + + if (route->source_pad >= sd->entity.num_pads) return -EINVAL; - if (!(sd->entity.pads[routes[i].sink_pad].flags & - MEDIA_PAD_FL_SINK) || - !(sd->entity.pads[routes[i].source_pad].flags & + if (!(pads[route->source_pad].flags & MEDIA_PAD_FL_SOURCE)) return -EINVAL; } - krouting.which = routing->which; krouting.num_routes = routing->num_routes; krouting.routes = routes; - return v4l2_subdev_call(sd, pad, set_routing, subdev_fh->state, &krouting); + return v4l2_subdev_call(sd, pad, set_routing, state, + routing->which, &krouting); } default: @@ -789,6 +906,71 @@ const struct v4l2_file_operations v4l2_subdev_fops = { .poll = subdev_poll, }; +static int +v4l2_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, + const struct v4l2_subdev_krouting *routing) +{ + u32 num_configs = 0; + unsigned int i; + u32 format_idx = 0; + + kvfree(stream_configs->configs); + stream_configs->configs = NULL; + stream_configs->num_configs = 0; + + /* Count number of formats needed */ + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + /* + * Each route needs a format on both ends of the route, except + * for source streams which only need one format. + */ + num_configs += + (route->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE) ? 1 : 2; + } + + if (num_configs) { + stream_configs->configs = + kvcalloc(num_configs, sizeof(*stream_configs->configs), + GFP_KERNEL); + + if (!stream_configs->configs) + return -ENOMEM; + + stream_configs->num_configs = num_configs; + } + + /* + * Fill in the 'pad' and stream' value for each item in the array from + * the routing table + */ + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + u32 idx; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_SOURCE)) { + idx = format_idx++; + + stream_configs->configs[idx].pad = route->sink_pad; + stream_configs->configs[idx].stream = route->sink_stream; + } + + idx = format_idx++; + + stream_configs->configs[idx].pad = route->source_pad; + stream_configs->configs[idx].stream = route->source_stream; + } + + return 0; +} + #ifdef CONFIG_MEDIA_CONTROLLER int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity, @@ -843,11 +1025,14 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, if (is_media_entity_v4l2_subdev(pad->entity)) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(pad->entity); + struct v4l2_subdev_state *state; + + state = v4l2_subdev_get_active_state(sd); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt->pad = pad->index; fmt->stream = stream; - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + return v4l2_subdev_call(sd, pad, get_fmt, state, fmt); } WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, @@ -857,234 +1042,133 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, return -EINVAL; } -int v4l2_subdev_get_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing) +static int cmp_u32(const void *a, const void *b) { - int ret; - - routing->which = state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - routing->routes = NULL; - routing->num_routes = 0; - - ret = v4l2_subdev_call(sd, pad, get_routing, state, routing); - if (ret == 0) - return 0; - if (ret != -ENOSPC) - return ret; - - routing->routes = kvmalloc_array(routing->num_routes, - sizeof(*routing->routes), GFP_KERNEL); - if (!routing->routes) - return -ENOMEM; - - ret = v4l2_subdev_call(sd, pad, get_routing, state, routing); - if (ret) { - kvfree(routing->routes); - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_get_routing); + u32 a32 = *(u32 *)a; + u32 b32 = *(u32 *)b; -void v4l2_subdev_free_routing(struct v4l2_subdev_krouting *routing) -{ - kvfree(routing->routes); - routing->routes = NULL; - routing->num_routes = 0; + return a32 > b32 ? 1 : (a32 < b32 ? -1 : 0); } -EXPORT_SYMBOL_GPL(v4l2_subdev_free_routing); -int v4l2_subdev_cpy_routing(struct v4l2_subdev_krouting *dst, - const struct v4l2_subdev_krouting *src) +static int v4l2_link_validate_get_streams(struct media_link *link, + bool is_source, u32 *out_num_streams, + const u32 **out_streams, + bool *allocated) { - if (dst->num_routes < src->num_routes) { - dst->num_routes = src->num_routes; - return -ENOSPC; - } - - memcpy(dst->routes, src->routes, - src->num_routes * sizeof(*src->routes)); - dst->num_routes = src->num_routes; - dst->which = src->which; - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_cpy_routing); + static const u32 default_streams[] = { 0 }; + struct v4l2_subdev_krouting *routing; + struct v4l2_subdev *subdev; + u32 num_streams; + u32 *streams; + unsigned int i; + struct v4l2_subdev_state *state; -int v4l2_subdev_dup_routing(struct v4l2_subdev_krouting *dst, - const struct v4l2_subdev_krouting *src) -{ - v4l2_subdev_free_routing(dst); + if (is_source) + subdev = media_entity_to_v4l2_subdev(link->source->entity); + else + subdev = media_entity_to_v4l2_subdev(link->sink->entity); - if (src->num_routes == 0) { - dst->which = src->which; + if (!(subdev->flags & V4L2_SUBDEV_FL_MULTIPLEXED)) { + *out_num_streams = 1; + *out_streams = default_streams; + *allocated = false; return 0; } - dst->routes = kvmalloc_array(src->num_routes, sizeof(*src->routes), - GFP_KERNEL); - if (!dst->routes) - return -ENOMEM; + state = v4l2_subdev_lock_active_state(subdev); - memcpy(dst->routes, src->routes, - src->num_routes * sizeof(*src->routes)); - dst->num_routes = src->num_routes; - dst->which = src->which; + routing = &state->routing; - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_dup_routing); + streams = kmalloc_array(routing->num_routes, sizeof(u32), GFP_KERNEL); -bool v4l2_subdev_has_route(struct v4l2_subdev_krouting *routing, - unsigned int pad0, unsigned int pad1) -{ - unsigned int i; + if (!streams) { + v4l2_subdev_unlock_state(state); + return -ENOMEM; + } + + num_streams = 0; for (i = 0; i < routing->num_routes; ++i) { struct v4l2_subdev_route *route = &routing->routes[i]; + int j; + u32 route_pad; + u32 route_stream; + u32 link_pad; + + if (is_source) { + route_pad = route->source_pad; + route_stream = route->source_stream; + link_pad = link->source->index; + } else { + route_pad = route->sink_pad; + route_stream = route->sink_stream; + link_pad = link->sink->index; + } if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) continue; - if ((route->sink_pad == pad0 && route->source_pad == pad1) || - (route->source_pad == pad0 && route->sink_pad == pad1)) - return true; + if (route_pad != link_pad) + continue; + + /* look for duplicates... */ + for (j = 0; j < num_streams; ++j) { + if (streams[j] == route_stream) + break; + } + + /* ...and drop the stream if we already have it */ + if (j != num_streams) + continue; + + streams[num_streams++] = route_stream; } - return false; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_has_route); + v4l2_subdev_unlock_state(state); -static int cmp_u32(const void *a, const void *b) -{ - u32 a32 = *(u32 *)a; - u32 b32 = *(u32 *)b; + sort(streams, num_streams, sizeof(u32), &cmp_u32, NULL); - return a32 > b32 ? 1 : (a32 < b32 ? -1 : 0); + *out_num_streams = num_streams; + *out_streams = streams; + *allocated = true; + + return 0; } int v4l2_subdev_link_validate(struct media_link *link) { - int ret; - unsigned int i; - - struct v4l2_subdev *source_subdev = - media_entity_to_v4l2_subdev(link->source->entity); struct v4l2_subdev *sink_subdev = media_entity_to_v4l2_subdev(link->sink->entity); struct device *dev = sink_subdev->entity.graph_obj.mdev->dev; - - struct v4l2_subdev_krouting routing; - - static const u32 default_streams[] = { 0 }; - - u32 num_source_streams = 0; - const u32 *source_streams = NULL; - u32 num_sink_streams = 0; - const u32 *sink_streams = NULL; + u32 num_source_streams; + const u32 *source_streams; + bool source_allocated; + u32 num_sink_streams; + const u32 *sink_streams; + bool sink_allocated; + unsigned int sink_idx; + unsigned int source_idx; + int ret; dev_dbg(dev, "validating link \"%s\":%u -> \"%s\":%u\n", link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); - /* Get source streams */ - - memset(&routing, 0, sizeof(routing)); - - ret = v4l2_subdev_get_routing(source_subdev, NULL, &routing); - - if (ret && ret != -ENOIOCTLCMD) + ret = v4l2_link_validate_get_streams(link, true, &num_source_streams, + &source_streams, + &source_allocated); + if (ret) return ret; - if (ret == -ENOIOCTLCMD) { - num_source_streams = 1; - source_streams = default_streams; - } else { - u32 *streams; - - streams = kmalloc_array(routing.num_routes, sizeof(u32), - GFP_KERNEL); - - for (i = 0; i < routing.num_routes; ++i) { - int j; - struct v4l2_subdev_route *route = &routing.routes[i]; - - if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) - continue; - - if (route->source_pad != link->source->index) - continue; - - for (j = 0; j < num_source_streams; ++j) { - if (streams[j] == route->source_stream) - break; - } - - if (j != num_source_streams) - continue; - - streams[num_source_streams++] = route->source_stream; - - } - - sort(streams, num_source_streams, sizeof(u32), &cmp_u32, NULL); - - source_streams = streams; - - v4l2_subdev_free_routing(&routing); - } - - /* Get sink streams */ - - memset(&routing, 0, sizeof(routing)); - - ret = v4l2_subdev_get_routing(sink_subdev, NULL, &routing); + ret = v4l2_link_validate_get_streams(link, false, &num_sink_streams, + &sink_streams, &sink_allocated); + if (ret) + goto free_source; - if (ret && ret != -ENOIOCTLCMD) - goto out; - - if (ret == -ENOIOCTLCMD) { - num_sink_streams = 1; - sink_streams = default_streams; - } else { - u32 *streams; - - streams = kmalloc_array(routing.num_routes, sizeof(u32), - GFP_KERNEL); - - for (i = 0; i < routing.num_routes; ++i) { - struct v4l2_subdev_route *route = &routing.routes[i]; - int j; - - if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) - continue; - - if (route->sink_pad != link->sink->index) - continue; - - for (j = 0; j < num_sink_streams; ++j) { - if (streams[j] == route->sink_stream) - break; - } - - if (j != num_sink_streams) - continue; - - streams[num_sink_streams++] = route->sink_stream; - } - - sort(streams, num_sink_streams, sizeof(u32), &cmp_u32, NULL); - - sink_streams = streams; - - v4l2_subdev_free_routing(&routing); - } - - if (num_source_streams != num_sink_streams) { + /* It is ok to have more source streams than sink streams */ + if (num_source_streams < num_sink_streams) { dev_err(dev, - "Sink and source stream count mismatch: %d vs %d\n", + "Not enough source streams: %d < %d\n", num_source_streams, num_sink_streams); ret = -EINVAL; goto out; @@ -1092,18 +1176,26 @@ int v4l2_subdev_link_validate(struct media_link *link) /* Validate source and sink stream formats */ - for (i = 0; i < num_source_streams; ++i) { + source_idx = 0; + + for (sink_idx = 0; sink_idx < num_sink_streams; ++sink_idx) { struct v4l2_subdev_format sink_fmt, source_fmt; u32 stream; - if (source_streams[i] != sink_streams[i]) { - dev_err(dev, "Sink and source streams do not match\n"); + stream = sink_streams[sink_idx]; + + for (; source_idx < num_source_streams; ++source_idx) { + if (source_streams[source_idx] == stream) + break; + } + + if (source_idx == num_source_streams) { + dev_err(dev, "No source stream for sink stream %u\n", + stream); ret = -EINVAL; goto out; } - stream = source_streams[i]; - dev_dbg(dev, "validating stream \"%s\":%u:%u -> \"%s\":%u:%u\n", link->source->entity->name, link->source->index, stream, link->sink->entity->name, link->sink->index, stream); @@ -1145,26 +1237,60 @@ int v4l2_subdev_link_validate(struct media_link *link) } out: - if (source_streams != default_streams) - kfree(source_streams); - - if (sink_streams != default_streams) + if (sink_allocated) kfree(sink_streams); +free_source: + if (source_allocated) + kfree(source_streams); + return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); -struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd) +bool v4l2_subdev_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct v4l2_subdev_krouting *routing; + unsigned int i; + struct v4l2_subdev_state *state; + + state = v4l2_subdev_lock_active_state(sd); + + routing = &state->routing; + + for (i = 0; i < routing->num_routes; ++i) { + struct v4l2_subdev_route *route = &routing->routes[i]; + + if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) + continue; + + if ((route->sink_pad == pad0 && route->source_pad == pad1) || + (route->source_pad == pad0 && route->sink_pad == pad1)) { + v4l2_subdev_unlock_state(state); + return true; + } + } + + v4l2_subdev_unlock_state(state); + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_has_route); + +struct v4l2_subdev_state * +__v4l2_subdev_state_alloc(struct v4l2_subdev *sd, const char *lock_name, + struct lock_class_key *lock_key) { struct v4l2_subdev_state *state; int ret; state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) { - ret = -ENOMEM; - goto err; - } + if (!state) + return ERR_PTR(-ENOMEM); + + __mutex_init(&state->lock, lock_name, lock_key); /* Drivers that support streams do not need the legacy pad config */ if (!(sd->flags & V4L2_SUBDEV_FL_MULTIPLEXED) && sd->entity.num_pads) { @@ -1191,17 +1317,21 @@ err: return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(v4l2_subdev_alloc_state); +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_alloc); -void v4l2_subdev_free_state(struct v4l2_subdev_state *state) +void __v4l2_subdev_state_free(struct v4l2_subdev_state *state) { - v4l2_subdev_free_routing(&state->routing); - v4l2_uninit_stream_configs(&state->stream_configs); + if (!state) + return; + + mutex_destroy(&state->lock); + kvfree(state->routing.routes); + kvfree(state->stream_configs.configs); kvfree(state->pads); - kvfree(state); + kfree(state); } -EXPORT_SYMBOL_GPL(v4l2_subdev_free_state); +EXPORT_SYMBOL_GPL(__v4l2_subdev_state_free); #endif /* CONFIG_MEDIA_CONTROLLER */ @@ -1232,64 +1362,202 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); -int v4l2_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, - const struct v4l2_subdev_krouting *routing) +int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, + struct lock_class_key *key) { - u32 num_configs = 0; - unsigned int i; - u32 format_idx = 0; + struct v4l2_subdev_state *state; - v4l2_uninit_stream_configs(stream_configs); + state = __v4l2_subdev_state_alloc(sd, name, key); + if (IS_ERR(state)) + return PTR_ERR(state); - /* Count number of formats needed */ - for (i = 0; i < routing->num_routes; ++i) { - struct v4l2_subdev_route *route = &routing->routes[i]; + sd->state = state; - if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) - continue; + return 0; +} +EXPORT_SYMBOL_GPL(__v4l2_subdev_init_finalize); - /* Each route needs a format on both ends of the route */ - num_configs += 2; - } +void v4l2_subdev_cleanup(struct v4l2_subdev *sd) +{ + __v4l2_subdev_state_free(sd->state); + sd->state = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_cleanup); - if (num_configs) { - stream_configs->configs = - kvcalloc(num_configs, sizeof(*stream_configs->configs), - GFP_KERNEL); +struct v4l2_subdev_state *v4l2_subdev_lock_active_state(struct v4l2_subdev *sd) +{ + mutex_lock(&sd->state->lock); - if (!stream_configs->configs) + return sd->state; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_lock_active_state); + +void v4l2_subdev_lock_state(struct v4l2_subdev_state *state) +{ + mutex_lock(&state->lock); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_lock_state); + +void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state) +{ + mutex_unlock(&state->lock); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_unlock_state); + +int v4l2_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct v4l2_subdev_krouting *dst = &state->routing; + const struct v4l2_subdev_krouting *src = routing; + + lockdep_assert_held(&state->lock); + + kvfree(dst->routes); + dst->routes = NULL; + dst->num_routes = 0; + + if (src->num_routes > 0) { + dst->routes = kvmalloc_array(src->num_routes, + sizeof(*src->routes), GFP_KERNEL); + if (!dst->routes) return -ENOMEM; - stream_configs->num_configs = num_configs; + memcpy(dst->routes, src->routes, + src->num_routes * sizeof(*src->routes)); + dst->num_routes = src->num_routes; + } + + return v4l2_init_stream_configs(&state->stream_configs, dst); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing); + +int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing, + const struct v4l2_mbus_framefmt *fmt) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + int ret; + + ret = v4l2_subdev_set_routing(sd, state, routing); + if (ret) + return ret; + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) + stream_configs->configs[i].fmt = *fmt; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_set_routing_with_fmt); + +struct v4l2_mbus_framefmt * +v4l2_state_get_stream_format(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream) +{ + struct v4l2_subdev_stream_configs *stream_configs; + unsigned int i; + + lockdep_assert_held(&state->lock); + + stream_configs = &state->stream_configs; + + for (i = 0; i < stream_configs->num_configs; ++i) { + if (stream_configs->configs[i].pad == pad && + stream_configs->configs[i].stream == stream) + return &stream_configs->configs[i].fmt; } - /* Fill in the 'pad' and stream' value for each item in the array from the routing table */ + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_state_get_stream_format); + +int v4l2_state_find_opposite_end(struct v4l2_subdev_krouting *routing, u32 pad, + u32 stream, u32 *other_pad, u32 *other_stream) +{ + unsigned int i; + for (i = 0; i < routing->num_routes; ++i) { struct v4l2_subdev_route *route = &routing->routes[i]; - u32 idx; - if (!(route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)) - continue; + if (route->source_pad == pad && + route->source_stream == stream) { + *other_pad = route->sink_pad; + *other_stream = route->sink_stream; + return 0; + } - idx = format_idx++; + if (route->sink_pad == pad && route->sink_stream == stream) { + *other_pad = route->source_pad; + *other_stream = route->source_stream; + return 0; + } + } - stream_configs->configs[idx].pad = route->sink_pad; - stream_configs->configs[idx].stream = route->sink_stream; + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_state_find_opposite_end); - idx = format_idx++; +struct v4l2_mbus_framefmt * +v4l2_state_get_opposite_stream_format(struct v4l2_subdev_state *state, u32 pad, + u32 stream) +{ + u32 other_pad, other_stream; + int ret; - stream_configs->configs[idx].pad = route->source_pad; - stream_configs->configs[idx].stream = route->source_stream; + ret = v4l2_state_find_opposite_end(&state->routing, pad, stream, + &other_pad, &other_stream); + if (ret) + return NULL; + + return v4l2_state_get_stream_format(state, other_pad, other_stream); +} +EXPORT_SYMBOL_GPL(v4l2_state_get_opposite_stream_format); + +int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt; + + v4l2_subdev_lock_state(state); + + fmt = v4l2_state_get_stream_format(state, format->pad, format->stream); + if (!fmt) { + v4l2_subdev_unlock_state(state); + return -EINVAL; } + format->format = *fmt; + + v4l2_subdev_unlock_state(state); + return 0; } -EXPORT_SYMBOL_GPL(v4l2_init_stream_configs); +EXPORT_SYMBOL_GPL(v4l2_subdev_get_fmt); -void v4l2_uninit_stream_configs(struct v4l2_subdev_stream_configs *stream_configs) +int v4l2_routing_simple_verify(const struct v4l2_subdev_krouting *routing) { - kvfree(stream_configs->configs); - stream_configs->configs = NULL; - stream_configs->num_configs = 0; + unsigned int i, j; + + for (i = 0; i < routing->num_routes; ++i) { + const struct v4l2_subdev_route *route = &routing->routes[i]; + + for (j = i + 1; j < routing->num_routes; ++j) { + const struct v4l2_subdev_route *r = &routing->routes[j]; + + if (route->sink_pad == r->sink_pad && + route->sink_stream == r->sink_stream) + return -EINVAL; + + if (route->source_pad == r->source_pad && + route->source_stream == r->source_stream) + return -EINVAL; + } + } + + return 0; } -EXPORT_SYMBOL_GPL(v4l2_uninit_stream_configs); +EXPORT_SYMBOL_GPL(v4l2_routing_simple_verify); diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h index 3a45c1fe4957..edd8edda0647 100644 --- a/drivers/staging/media/ipu3/include/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -418,7 +418,7 @@ struct ipu3_uapi_af_config_s { IPU3_UAPI_AWB_FR_SPARE_FOR_BUBBLES) * IPU3_UAPI_MAX_STRIPES) /** - * struct ipu3_uapi_awb_fr_meta_data - AWB filter response meta data + * struct ipu3_uapi_awb_fr_raw_buffer - AWB filter response meta data * * @meta_data: Statistics output on the grid after convolving with 1D filter. */ @@ -1506,7 +1506,7 @@ struct ipu3_uapi_sharp_cfg { } __packed; /** - * struct struct ipu3_uapi_far_w - Sharpening config for far sub-group + * struct ipu3_uapi_far_w - Sharpening config for far sub-group * * @dir_shrp: Weight of wide direct sharpening, u1.6, range [0, 64], default 64. * @reserved0: reserved @@ -1526,7 +1526,7 @@ struct ipu3_uapi_far_w { } __packed; /** - * struct struct ipu3_uapi_unsharp_cfg - Unsharp config + * struct ipu3_uapi_unsharp_cfg - Unsharp config * * @unsharp_weight: Unsharp mask blending weight. * u1.6, range [0, 64], default 16. @@ -1772,7 +1772,7 @@ struct ipu3_uapi_vss_lut_y { } __packed; /** - * struct ipu3_uapi_yuvp1_iefd_vssnlm_cf - IEFd Vssnlm Lookup table + * struct ipu3_uapi_yuvp1_iefd_vssnlm_cfg - IEFd Vssnlm Lookup table * * @vss_lut_x: vss lookup table. See &ipu3_uapi_vss_lut_x description * @vss_lut_y: vss lookup table. See &ipu3_uapi_vss_lut_y description diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index ce4bb7cd890b..b95dd336e95d 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -490,6 +490,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, struct v4l2_pix_format *pix) { const struct tegra_video_format *fmtinfo; + static struct lock_class_key key; struct v4l2_subdev *subdev; struct v4l2_subdev_format fmt; struct v4l2_subdev_state *sd_state; @@ -506,9 +507,10 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, if (!subdev) return -ENODEV; - sd_state = v4l2_subdev_alloc_state(subdev); - if (!sd_state) - return -ENOMEM; + sd_state = __v4l2_subdev_state_alloc(subdev, "tegra:state->lock", + &key); + if (IS_ERR(sd_state)) + return PTR_ERR(sd_state); /* * Retrieve the format information and if requested format isn't * supported, keep the current format. @@ -550,7 +552,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, v4l2_fill_pix_format(pix, &fmt.format); tegra_channel_fmt_align(chan, pix, fmtinfo->bpp); - v4l2_subdev_free_state(sd_state); + __v4l2_subdev_state_free(sd_state); return 0; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 43d43972901e..1580d51aea4f 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -23,7 +23,6 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/of.h> -#include <linux/of_graph.h> #include <linux/acpi.h> #include <linux/pinctrl/consumer.h> #include <linux/reset.h> @@ -85,7 +84,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) * mode. If the controller supports DRD but the dr_mode is not * specified or set to OTG, then set the mode to peripheral. */ - if (mode == USB_DR_MODE_OTG && !dwc->edev && + if (mode == USB_DR_MODE_OTG && (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) || !device_property_read_bool(dwc->dev, "usb-role-switch")) && !DWC3_VER_IS_PRIOR(DWC3, 330A)) @@ -1471,51 +1470,6 @@ static void dwc3_check_params(struct dwc3 *dwc) } } -static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) -{ - struct device *dev = dwc->dev; - struct device_node *np_phy; - struct extcon_dev *edev = NULL; - const char *name; - - if (device_property_read_bool(dev, "extcon")) - return extcon_get_edev_by_phandle(dev, 0); - - /* - * Device tree platforms should get extcon via phandle. - * On ACPI platforms, we get the name from a device property. - * This device property is for kernel internal use only and - * is expected to be set by the glue code. - */ - if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { - edev = extcon_get_extcon_dev(name); - if (!edev) - return ERR_PTR(-EPROBE_DEFER); - - return edev; - } - - /* - * Try to get an extcon device from the USB PHY controller's "port" - * node. Check if it has the "port" node first, to avoid printing the - * error message from underlying code, as it's a valid case: extcon - * device (and "port" node) may be missing in case of "usb-role-switch" - * or OTG mode. - */ - np_phy = of_parse_phandle(dev->of_node, "phys", 0); - if (of_graph_is_present(np_phy)) { - struct device_node *np_conn; - - np_conn = of_graph_get_remote_node(np_phy, -1, -1); - if (np_conn) - edev = extcon_find_edev_by_node(np_conn); - of_node_put(np_conn); - } - of_node_put(np_phy); - - return edev; -} - static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1616,14 +1570,6 @@ static int dwc3_probe(struct platform_device *pdev) goto err2; } - dwc->edev = dwc3_get_extcon(dwc); - if (IS_ERR(dwc->edev)) { - ret = PTR_ERR(dwc->edev); - dev_err_probe(dwc->dev, ret, "failed to get extcon"); - - goto err3; - } - ret = dwc3_get_dr_mode(dwc); if (ret) goto err3; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 9a414edc439a..3e1c1aacf002 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -8,6 +8,7 @@ */ #include <linux/extcon.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/property.h> @@ -437,6 +438,44 @@ static int dwc3_drd_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + struct device_node *np_phy, *np_conn; + struct extcon_dev *edev; + const char *name; + + if (device_property_read_bool(dev, "extcon")) + return extcon_get_edev_by_phandle(dev, 0); + + /* + * Device tree platforms should get extcon via phandle. + * On ACPI platforms, we get the name from a device property. + * This device property is for kernel internal use only and + * is expected to be set by the glue code. + */ + if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { + edev = extcon_get_extcon_dev(name); + if (!edev) + return ERR_PTR(-EPROBE_DEFER); + + return edev; + } + + np_phy = of_parse_phandle(dev->of_node, "phys", 0); + np_conn = of_graph_get_remote_node(np_phy, -1, -1); + + if (np_conn) + edev = extcon_find_edev_by_node(np_conn); + else + edev = NULL; + + of_node_put(np_conn); + of_node_put(np_phy); + + return edev; +} + #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) #define ROLE_SWITCH 1 static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, @@ -529,6 +568,10 @@ int dwc3_drd_init(struct dwc3 *dwc) { int ret, irq; + dwc->edev = dwc3_get_extcon(dwc); + if (IS_ERR(dwc->edev)) + return PTR_ERR(dwc->edev); + if (ROLE_SWITCH && device_property_read_bool(dwc->dev, "usb-role-switch")) { ret = dwc3_setup_role_switch(dwc); diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h new file mode 100644 index 000000000000..e015db668f44 --- /dev/null +++ b/include/linux/i2c-atr.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * drivers/i2c/i2c-atr.h -- I2C Address Translator + * + * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> + * + * Based on i2c-mux.h + */ + +#ifndef _LINUX_I2C_ATR_H +#define _LINUX_I2C_ATR_H + +#ifdef __KERNEL__ + +#include <linux/i2c.h> +#include <linux/mutex.h> + +struct i2c_atr; + +/** + * struct i2c_atr_ops - Callbacks from ATR to the device driver. + * @select: Ask the driver to select a child bus (optional) + * @deselect: Ask the driver to deselect a child bus (optional) + * @attach_client: Notify the driver of a new device connected on a child + * bus. The driver must choose an I2C alias, configure the + * hardware to use it and return it in `alias_id`. + * @detach_client: Notify the driver of a device getting disconnected. The + * driver must configure the hardware to stop using the + * alias. + * + * All these functions return 0 on success, a negative error code otherwise. + */ +struct i2c_atr_ops { + int (*select)(struct i2c_atr *atr, u32 chan_id); + int (*deselect)(struct i2c_atr *atr, u32 chan_id); + int (*attach_client)(struct i2c_atr *atr, u32 chan_id, + const struct i2c_board_info *info, + const struct i2c_client *client, + u16 *alias_id); + void (*detach_client)(struct i2c_atr *atr, u32 chan_id, + const struct i2c_client *client); +}; + +/* + * Helper to add I2C ATR features to a device driver. + */ +struct i2c_atr { + /* private: internal use only */ + + struct i2c_adapter *parent; + struct device *dev; + const struct i2c_atr_ops *ops; + + void *priv; + + struct i2c_algorithm algo; + struct mutex lock; + int max_adapters; + + struct i2c_adapter *adapter[0]; +}; + +struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, + const struct i2c_atr_ops *ops, int max_adapters); +void i2c_atr_delete(struct i2c_atr *atr); + +static inline void i2c_atr_set_clientdata(struct i2c_atr *atr, void *data) +{ + atr->priv = data; +} + +static inline void *i2c_atr_get_clientdata(struct i2c_atr *atr) +{ + return atr->priv; +} + +int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id); +void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_I2C_ATR_H */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index a670ae129f4b..752f049faae9 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -561,6 +561,21 @@ struct i2c_lock_operations { }; /** + * struct i2c_attach_operations - callbacks to notify client attach/detach + * @attach_client: Notify of new client being attached + * @detach_client: Notify of new client being detached + * + * Both ops are optional. + */ +struct i2c_attach_operations { + int (*attach_client)(struct i2c_adapter *adapter, + const struct i2c_board_info *info, + const struct i2c_client *client); + void (*detach_client)(struct i2c_adapter *adapter, + const struct i2c_client *client); +}; + +/** * struct i2c_timings - I2C timing information * @bus_freq_hz: the bus frequency in Hz * @scl_rise_ns: time SCL signal takes to rise in ns; t(r) in the I2C specification @@ -702,6 +717,7 @@ struct i2c_adapter { /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; + const struct i2c_attach_operations *attach_ops; struct rt_mutex bus_lock; struct rt_mutex mux_lock; diff --git a/include/media/cec.h b/include/media/cec.h index cd35ae6b7560..208c9613c07e 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -28,8 +28,8 @@ * @minor: device node minor number * @registered: the device was correctly registered * @unregistered: the device was unregistered - * @fhs_lock: lock to control access to the filehandle list * @fhs: the list of open filehandles (cec_fh) + * @lock: lock to control access to this structure * * This structure represents a cec-related device node. * diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index e547cbeee431..b04a38be5183 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -321,7 +321,7 @@ int dvb_create_media_graph(struct dvb_adapter *adap, int dvb_generic_open(struct inode *inode, struct file *file); /** - * dvb_generic_close - Digital TV close function, used by DVB devices + * dvb_generic_release - Digital TV close function, used by DVB devices * * @inode: pointer to &struct inode. * @file: pointer to &struct file. diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 9ecbb98908f0..de0a0820cfb2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -1294,7 +1294,7 @@ static inline void v4l2_ctrl_request_hdl_put(struct v4l2_ctrl_handler *hdl) } /** - * v4l2_ctrl_request_ctrl_find() - Find a control with the given ID. + * v4l2_ctrl_request_hdl_ctrl_find() - Find a control with the given ID. * * @hdl: The control handler from the request. * @id: The ID of the control to find. diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index ad2d41952442..6a4afd4a7df2 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -43,8 +43,8 @@ enum vfl_devnode_type { }; /** - * enum vfl_direction - Identifies if a &struct video_device corresponds - * to a receiver, a transmitter or a mem-to-mem device. + * enum vfl_devnode_direction - Identifies if a &struct video_device + * corresponds to a receiver, a transmitter or a mem-to-mem device. * * @VFL_DIR_RX: device is a receiver. * @VFL_DIR_TX: device is a transmitter. diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 64ec4de948e9..8a8977a33ec1 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -174,7 +174,7 @@ int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); /** - * __v4l2_device_register_ro_subdev_nodes - Registers device nodes for + * __v4l2_device_register_subdev_nodes - Registers device nodes for * all subdevs of the v4l2 device that are marked with the * %V4L2_SUBDEV_FL_HAS_DEVNODE flag. * diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 2cc0cabc124f..8fa963326bf6 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -224,7 +224,7 @@ static inline bool can_reduce_fps(struct v4l2_bt_timings *bt) } /** - * struct v4l2_hdmi_rx_colorimetry - describes the HDMI colorimetry information + * struct v4l2_hdmi_colorimetry - describes the HDMI colorimetry information * @colorspace: enum v4l2_colorspace, the colorspace * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding * @quantization: enum v4l2_quantization, colorspace quantization diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index ed0840f3d5df..c542fa53e209 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -520,7 +520,7 @@ v4l2_async_notifier_parse_fwnode_endpoints_by_port(struct device *dev, parse_endpoint_func parse_endpoint); /** - * v4l2_fwnode_reference_parse_sensor_common - parse common references on + * v4l2_async_notifier_parse_fwnode_sensor_common - parse common references on * sensors for async sub-devices * @dev: the device node the properties of which are parsed for references * @notifier: the async notifier where the async subdevs will be added diff --git a/include/media/v4l2-h264.h b/include/media/v4l2-h264.h index f08ba181263d..1cc89d2e693a 100644 --- a/include/media/v4l2-h264.h +++ b/include/media/v4l2-h264.h @@ -66,11 +66,11 @@ v4l2_h264_build_b_ref_lists(const struct v4l2_h264_reflist_builder *builder, u8 *b0_reflist, u8 *b1_reflist); /** - * v4l2_h264_build_b_ref_lists() - Build the P reference list + * v4l2_h264_build_p_ref_list() - Build the P reference list * * @builder: reference list builder context - * @p_reflist: 16-bytes array used to store the P reference list. Each entry - * is an index in the DPB + * @reflist: 16-bytes array used to store the P reference list. Each entry + * is an index in the DPB * * This functions builds the P reference lists. This procedure is describe in * section '8.2.4 Decoding process for reference picture lists construction' diff --git a/include/media/v4l2-jpeg.h b/include/media/v4l2-jpeg.h index ddba2a56c321..3a3344a97678 100644 --- a/include/media/v4l2-jpeg.h +++ b/include/media/v4l2-jpeg.h @@ -91,7 +91,9 @@ struct v4l2_jpeg_scan_header { * struct v4l2_jpeg_header - parsed JPEG header * @sof: pointer to frame header and size * @sos: pointer to scan header and size + * @num_dht: number of entries in @dht * @dht: pointers to huffman tables and sizes + * @num_dqt: number of entries in @dqt * @dqt: pointers to quantization tables and sizes * @frame: parsed frame header * @scan: pointer to parsed scan header, optional diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index c20e2dc6d432..841e190aedd9 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -147,7 +147,7 @@ v4l2_fill_pix_format(struct v4l2_pix_format *pix_fmt, } /** - * v4l2_fill_pix_format - Ancillary routine that fills a &struct + * v4l2_fill_mbus_format - Ancillary routine that fills a &struct * v4l2_mbus_framefmt from a &struct v4l2_pix_format and a * data format code. * @@ -170,7 +170,7 @@ static inline void v4l2_fill_mbus_format(struct v4l2_mbus_framefmt *mbus_fmt, } /** - * v4l2_fill_pix_format - Ancillary routine that fills a &struct + * v4l2_fill_pix_format_mplane - Ancillary routine that fills a &struct * v4l2_pix_format_mplane fields from a media bus structure. * * @pix_mp_fmt: pointer to &struct v4l2_pix_format_mplane to be filled @@ -190,7 +190,7 @@ v4l2_fill_pix_format_mplane(struct v4l2_pix_format_mplane *pix_mp_fmt, } /** - * v4l2_fill_pix_format - Ancillary routine that fills a &struct + * v4l2_fill_mbus_format_mplane - Ancillary routine that fills a &struct * v4l2_mbus_framefmt from a &struct v4l2_pix_format_mplane. * * @mbus_fmt: pointer to &struct v4l2_mbus_framefmt to be filled diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e25a304cd0c8..fdeaae7f4e44 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -693,7 +693,7 @@ struct v4l2_subdev_stream_config { * struct v4l2_subdev_stream_configs - A collection of stream configs. * * @num_configs: number of entries in @config. - * @config: an array of &struct v4l2_subdev_stream_configs. + * @configs: an array of &struct v4l2_subdev_stream_configs. */ struct v4l2_subdev_stream_configs { u32 num_configs; @@ -703,22 +703,20 @@ struct v4l2_subdev_stream_configs { /** * struct v4l2_subdev_krouting - subdev routing table * - * @which: format type (from enum v4l2_subdev_format_whence) - * @routes: &struct v4l2_subdev_route * @num_routes: number of routes + * @routes: &struct v4l2_subdev_route * - * This structure is used to translate arguments received from - * VIDIOC_SUBDEV_G/S_ROUTING() ioctl to subdev device drivers operations. + * This structure contains the routing table for a subdev. */ struct v4l2_subdev_krouting { - u32 which; - struct v4l2_subdev_route *routes; unsigned int num_routes; + struct v4l2_subdev_route *routes; }; /** - * struct v4l2_subdev_state - Used for storing subdev information. + * struct v4l2_subdev_state - Used for storing subdev state information. * + * @lock: mutex for the state * @pads: &struct v4l2_subdev_pad_config array * @routing: routing table for the subdev * @stream_configs: stream configurations (only for V4L2_SUBDEV_FL_MULTIPLEXED) @@ -728,6 +726,7 @@ struct v4l2_subdev_krouting { * %V4L2_SUBDEV_FORMAT_ACTIVE it is safe to pass %NULL. */ struct v4l2_subdev_state { + struct mutex lock; struct v4l2_subdev_pad_config *pads; struct v4l2_subdev_krouting routing; struct v4l2_subdev_stream_configs stream_configs; @@ -795,7 +794,6 @@ struct v4l2_subdev_state { * pad index it has been called on is not valid or in case of * unrecoverable failures. * - * @get_routing: get the subdevice routing table. * @set_routing: enable or disable data connection routes described in the * subdevice routing table. */ @@ -842,11 +840,9 @@ struct v4l2_subdev_pad_ops { struct v4l2_mbus_config *config); int (*set_mbus_config)(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *config); - int (*get_routing)(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *route); int (*set_routing)(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, struct v4l2_subdev_krouting *route); }; @@ -925,7 +921,12 @@ struct v4l2_subdev_internal_ops { /* * Set this flag if this subdev supports multiplexed streams. This means * that the driver supports routing and handles the stream parameter in its - * v4l2_subdev_pad_ops handlers. + * v4l2_subdev_pad_ops handlers. More specifically, this means: + * + * - Centrally managed active state is enabled + * - Legacy pad config is _not_ supported (state->pads) + * - Routing ioctls are available + * - Multiple streams per pad are supported */ #define V4L2_SUBDEV_FL_MULTIPLEXED (1U << 4) @@ -982,6 +983,8 @@ struct v4l2_subdev_platform_data { * @subdev_notifier: A sub-device notifier implicitly registered for the sub- * device using v4l2_device_register_sensor_subdev(). * @pdata: common part of subdevice platform data + * @state: active state for the subdev (NULL for subdevs tracking the state + * internally) * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. @@ -1013,6 +1016,19 @@ struct v4l2_subdev { struct v4l2_async_notifier *notifier; struct v4l2_async_notifier *subdev_notifier; struct v4l2_subdev_platform_data *pdata; + + /* + * The fields below are private, and should only be accessed via + * appropriate functions. + */ + + /* + * TODO: state should most likely be changed from a pointer to an + * embedded field. For the time being it's kept as a pointer to more + * easily catch uses of state in the cases where the driver doesn't + * support it. + */ + struct v4l2_subdev_state *state; }; @@ -1071,8 +1087,8 @@ struct v4l2_subdev_fh { * &struct v4l2_subdev_pad_config->try_fmt * * @sd: pointer to &struct v4l2_subdev - * @state: pointer to &struct v4l2_subdev_state. - * @pad: index of the pad in the @state array. + * @state: pointer to &struct v4l2_subdev_state + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array */ static inline struct v4l2_mbus_framefmt * v4l2_subdev_get_try_format(struct v4l2_subdev *sd, @@ -1090,7 +1106,7 @@ v4l2_subdev_get_try_format(struct v4l2_subdev *sd, * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. - * @pad: index of the pad in the @state array. + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. */ static inline struct v4l2_rect * v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, @@ -1108,7 +1124,7 @@ v4l2_subdev_get_try_crop(struct v4l2_subdev *sd, * * @sd: pointer to &struct v4l2_subdev * @state: pointer to &struct v4l2_subdev_state. - * @pad: index of the pad in the @state array. + * @pad: index of the pad in the &struct v4l2_subdev_state->pads array. */ static inline struct v4l2_rect * v4l2_subdev_get_try_compose(struct v4l2_subdev *sd, @@ -1219,16 +1235,40 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, int v4l2_subdev_link_validate(struct media_link *link); /** - * v4l2_subdev_alloc_state - allocate v4l2_subdev_state + * v4l2_subdev_has_route - MC has_route implementation for subdevs * - * Must call v4l2_subdev_free_state() when state is no longer needed. + * @entity: pointer to &struct media_entity + * @pad0: pad number for the first pad + * @pad1: pad number for the second pad + * + * This function looks at the routing in subdev's active state and returns if + * there is a route connecting pad0 and pad1. + * + * This function can be used as implementation for + * media_entity_operations.has_route. */ -struct v4l2_subdev_state *v4l2_subdev_alloc_state(struct v4l2_subdev *sd); +bool v4l2_subdev_has_route(struct media_entity *entity, unsigned int pad0, + unsigned int pad1); /** - * v4l2_subdev_free_state - uninitialize v4l2_subdev_state + * __v4l2_subdev_state_alloc - allocate v4l2_subdev_state + * + * @sd: pointer to &struct v4l2_subdev for which the state is being allocated. + * @lock_name: name of the state lock + * @key: lock_class_key for the lock + * + * Must call __v4l2_subdev_state_free() when state is no longer needed. */ -void v4l2_subdev_free_state(struct v4l2_subdev_state *state); +struct v4l2_subdev_state *__v4l2_subdev_state_alloc(struct v4l2_subdev *sd, + const char *lock_name, + struct lock_class_key *key); + +/** + * __v4l2_subdev_state_free - free a v4l2_subdev_state + * + * @state: v4l2_subdev_state to be freed. + */ +void __v4l2_subdev_state_free(struct v4l2_subdev_state *state); #endif /* CONFIG_MEDIA_CONTROLLER */ @@ -1298,96 +1338,220 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, const struct v4l2_event *ev); /** - * v4l2_subdev_get_routing() - Get routing from a subdevice + * v4l2_subdev_init_finalize() - Finalize the initialization of the subdevice + * @sd: The subdev * - * @sd: The subdev from which to get the routing - * @state: Pointer to &struct v4l2_subdev_state - * @routing: Pointer to the target &struct v4l2_subdev_krouting + * This finalizes the initialization of the subdev, including allocation of + * the active state for the subdev. * - * Get a copy of the subdevice's routing table. + * This must be called by the subdev drivers that use the centralized active + * state, after the subdev struct has been initialized and + * media_entity_pads_init() has been called. * - * Must be freed with v4l2_subdev_free_routing after use. + * Must call v4l2_subdev_cleanup() when the subdev is being removed. */ -int v4l2_subdev_get_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing); +#define v4l2_subdev_init_finalize(sd) \ + ({ \ + static struct lock_class_key __key; \ + const char *name = KBUILD_BASENAME \ + ":" __stringify(__LINE__) ":subdev->state->lock"; \ + __v4l2_subdev_init_finalize(sd, name, &__key); \ + }) + +int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, + struct lock_class_key *key); /** - * v4l2_subdev_free_routing() - Free the routing + * v4l2_subdev_cleanup() - Release the resources needed by the subdevice + * @sd: The subdevice * - * @routing: The routing to be freed - * - * Frees the routing data in @routing. + * This will release the resources allocated in v4l2_subdev_init_finalize. */ -void v4l2_subdev_free_routing(struct v4l2_subdev_krouting *routing); +void v4l2_subdev_cleanup(struct v4l2_subdev *sd); /** - * v4l2_subdev_cpy_routing() - Copy the routing + * v4l2_subdev_get_active_state() - Return the active subdev state for subdevice + * @sd: The subdevice * - * @dst: The destination routing - * @src: The source routing + * Return the active state for the subdevice, or NULL if the subdev does not + * support active state. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_get_active_state(struct v4l2_subdev *sd) +{ + return sd->state; +} + +/** + * v4l2_subdev_lock_active_state() - Lock and return the active subdev state for + * subdevice + * @sd: The subdevice * - * Copies routing from @src to @dst without allocating space. If @dst does not - * have enough space, set dst->num_routes to the required number of routes, and - * return -ENOSPC. + * Return the locked active state for the subdevice, or NULL if the subdev + * does not support active state. * - * Can be used in subdevice's v4l2_subdev_pad_ops.get_routing() callback. + * Must be unlocked with v4l2_subdev_unlock_state() after use. */ -int v4l2_subdev_cpy_routing(struct v4l2_subdev_krouting *dst, - const struct v4l2_subdev_krouting *src); +struct v4l2_subdev_state *v4l2_subdev_lock_active_state(struct v4l2_subdev *sd); /** - * v4l2_subdev_dup_routing() - Duplicate the routing + * v4l2_subdev_lock_state() - Lock the subdev state + * @state: The subdevice state * - * @dst: The destination routing - * @src: The source routing + * Lock the given subdev state. * - * Makes a duplicate of the routing from @src to @dst by allocating enough - * memory and making a copy of the routing. + * Must be unlocked with v4l2_subdev_unlock_state() after use. + */ +void v4l2_subdev_lock_state(struct v4l2_subdev_state *state); + +/** + * v4l2_subdev_unlock_state() - Unlock the subdev state + * @state: The subdevice state + * + * Unlock the given subdev state. + */ +void v4l2_subdev_unlock_state(struct v4l2_subdev_state *state); + +/** + * v4l2_subdev_validate_and_lock_state() - Gets locked TRY or ACTIVE subdev + * state + * @sd: subdevice + * @state: subdevice state as passed to the subdev op + * + * Due to legacy reasons, when subdev drivers call ops in other subdevs they use + * NULL as the state parameter, as subdevs always used to have their active + * state stored privately. + * + * However, newer state-aware subdev drivers, which store their active state in + * a common place, subdev->state, expect to always get a proper state as a + * parameter. + * + * These state-aware drivers can use v4l2_subdev_validate_and_lock_state() + * instead of v4l2_subdev_lock_state(). v4l2_subdev_validate_and_lock_state() + * solves the issue by using subdev->state in case the passed state is + * NULL. + * + * This is a temporary helper function, and should be removed when we can ensure + * that all drivers pass proper state when calling other subdevs. + */ +static inline struct v4l2_subdev_state * +v4l2_subdev_validate_and_lock_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + state = state ? state : sd->state; + + v4l2_subdev_lock_state(state); + + return state; +} + +/** + * v4l2_subdev_set_routing() - Set given routing to subdev state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state * - * Can be used in subdevice's v4l2_subdev_pad_ops.set_routing() callback - * to store the given routing. + * This will release old routing table (if any) from the state, allocate + * enough space for the given routing, and copy the routing. * - * Must be freed with v4l2_subdev_free_routing after use. + * This can be used from the subdev driver's set_routing op, after validating + * the routing. */ -int v4l2_subdev_dup_routing(struct v4l2_subdev_krouting *dst, - const struct v4l2_subdev_krouting *src); +int v4l2_subdev_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing); /** - * v4l2_subdev_has_route() - Check if there is a route between two pads + * v4l2_subdev_set_routing_with_fmt() - Set given routing and format to subdev + * state + * @sd: The subdevice + * @state: The subdevice state + * @routing: Routing that will be copied to subdev state + * @fmt: Format used to initialize all the streams + * + * This is the same as v4l2_subdev_set_routing, but additionally initializes + * all the streams using the given format. + */ +int v4l2_subdev_set_routing_with_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing, + const struct v4l2_mbus_framefmt *fmt); + +/** + * v4l2_state_get_stream_format() - Get pointer to a stream format + * @state: subdevice state + * @pad: pad id + * @stream: stream id * - * @routing: The subdevice's routing - * @pad0: First pad - * @pad1: Second pad + * This returns a pointer to &struct v4l2_mbus_framefmt for the given pad + + * stream in the subdev state. * - * Returns whether there is a route between @pad0 and @pad1 of the same - * subdevice according to the given routing. + * If the state does not contain the given pad + stream, NULL is returned. */ -bool v4l2_subdev_has_route(struct v4l2_subdev_krouting *routing, - unsigned int pad0, unsigned int pad1); +struct v4l2_mbus_framefmt * +v4l2_state_get_stream_format(struct v4l2_subdev_state *state, unsigned int pad, + u32 stream); +/** + * v4l2_state_find_opposite_end() - Find the opposite stream + * @routing: routing used to find the opposite side + * @pad: pad id + * @stream: stream id + * @other_pad: pointer used to return the opposite pad + * @other_stream: pointer used to return the opposite stream + * + * This function uses the routing table to find the pad + stream which is + * opposite the given pad + stream. + * + * Returns 0 on success, or -EINVAL if no matching route is found. + */ +int v4l2_state_find_opposite_end(struct v4l2_subdev_krouting *routing, u32 pad, + u32 stream, u32 *other_pad, u32 *other_stream); /** - * v4l2_init_stream_configs() - Initialize stream configs according to routing + * v4l2_state_get_opposite_stream_format() - Get pointer to opposite stream + * format + * @state: subdevice state + * @pad: pad id + * @stream: stream id + * + * This returns a pointer to &struct v4l2_mbus_framefmt for the pad + stream + * that is opposite the given pad + stream in the subdev state. + * + * If the state does not contain the given pad + stream, NULL is returned. + */ +struct v4l2_mbus_framefmt * +v4l2_state_get_opposite_stream_format(struct v4l2_subdev_state *state, u32 pad, + u32 stream); +/** + * v4l2_subdev_get_fmt() - Fill format based on state + * @sd: subdevice + * @state: subdevice state + * @format: pointer to &struct v4l2_subdev_format * - * @stream_configs: The stream configs to initialize - * @routing: The routing used for the stream configs + * Fill @format based on the pad and stream given in the @format struct. * - * Initializes @stream_configs according to @routing, allocating enough - * space to hold configuration for each route endpoint. + * This function can be used by the subdev drivers to implement + * v4l2_subdev_pad_ops.get_fmt if the subdev driver does not need to do + * anything special in their get_fmt op. * - * Must be freed with v4l2_uninit_stream_configs(). + * Returns 0 on success, error value otherwise. */ -int v4l2_init_stream_configs(struct v4l2_subdev_stream_configs *stream_configs, - const struct v4l2_subdev_krouting *routing); +int v4l2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format); /** - * v4l2_uninit_stream_configs() - Uninitialize stream configs + * v4l2_routing_simple_verify() - Verify that all streams are non-overlapping + * 1-to-1 streams + * @routing: routing to verify * - * @stream_configs: The stream configs to uninitialize + * This verifies that the given routing contains only non-overlapping 1-to-1 + * streams. In other words, no two streams have the same source or sink + * stream ID on a single pad. This is the most common case of routing + * supported by devices. * - * Frees any allocated memory in @stream_configs. + * Returns 0 on success, error value otherwise. */ -void v4l2_uninit_stream_configs(struct v4l2_subdev_stream_configs *stream_configs); +int v4l2_routing_simple_verify(const struct v4l2_subdev_krouting *routing); #endif diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index ee359c5bc74a..a92505493c47 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -1043,7 +1043,7 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file, size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblock); /** - * vb2_read() - implements write() syscall logic. + * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue. * @data: pointed to target userspace buffer * @count: number of bytes to write diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 7d1a06c52469..dc8879d179fd 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -396,6 +396,7 @@ struct cec_drm_connector_info { * associated with the CEC adapter. * @type: connector type (if any) * @drm: drm connector info + * @raw: array to pad the union */ struct cec_connector_info { __u32 type; @@ -453,7 +454,7 @@ struct cec_event_lost_msgs { * struct cec_event - CEC event structure * @ts: the timestamp of when the event was sent. * @event: the event. - * array. + * @flags: event flags. * @state_change: the event payload for CEC_EVENT_STATE_CHANGE. * @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS. * @raw: array to pad the union. diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index 41dc6c265ef1..75ef6129264d 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -44,6 +44,8 @@ enum v4l2_subdev_format_whence { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @format: media bus format (format code and frame size) + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_format { __u32 which; @@ -58,6 +60,8 @@ struct v4l2_subdev_format { * @which: format type (from enum v4l2_subdev_format_whence) * @pad: pad number, as reported by the media API * @rect: pad crop rectangle boundaries + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_crop { __u32 which; @@ -80,6 +84,8 @@ struct v4l2_subdev_crop { * @code: format code (MEDIA_BUS_FMT_ definitions) * @which: format type (from enum v4l2_subdev_format_whence) * @flags: flags set by the driver, (V4L2_SUBDEV_MBUS_CODE_*) + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_mbus_code_enum { __u32 pad; @@ -93,10 +99,16 @@ struct v4l2_subdev_mbus_code_enum { /** * struct v4l2_subdev_frame_size_enum - Media bus format enumeration - * @pad: pad number, as reported by the media API * @index: format index during enumeration + * @pad: pad number, as reported by the media API * @code: format code (MEDIA_BUS_FMT_ definitions) + * @min_width: minimum frame width, in pixels + * @max_width: maximum frame width, in pixels + * @min_height: minimum frame height, in pixels + * @max_height: maximum frame height, in pixels * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_size_enum { __u32 index; @@ -115,6 +127,8 @@ struct v4l2_subdev_frame_size_enum { * struct v4l2_subdev_frame_interval - Pad-level frame rate * @pad: pad number, as reported by the media API * @interval: frame interval in seconds + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_interval { __u32 pad; @@ -132,6 +146,8 @@ struct v4l2_subdev_frame_interval { * @height: frame height in pixels * @interval: frame interval in seconds * @which: format type (from enum v4l2_subdev_format_whence) + * @stream: stream number, defined in subdev routing + * @reserved: drivers and applications must zero this array */ struct v4l2_subdev_frame_interval_enum { __u32 index; @@ -154,6 +170,7 @@ struct v4l2_subdev_frame_interval_enum { * defined in v4l2-common.h; V4L2_SEL_TGT_* . * @flags: constraint flags, defined in v4l2-common.h; V4L2_SEL_FLAG_*. * @r: coordinates of the selection window + * @stream: stream number, defined in subdev routing * @reserved: for future use, set to zero for now * * Hardware may use multiple helper windows to process a video stream. @@ -185,24 +202,29 @@ struct v4l2_subdev_capability { /* The v4l2 sub-device video device node is registered in read-only mode. */ #define V4L2_SUBDEV_CAP_RO_SUBDEV 0x00000001 -/** +/* The v4l2 sub-device supports multiplexed streams. */ +#define V4L2_SUBDEV_CAP_MPLEXED 0x00000002 + +/* * Is the route active? An active route will start when streaming is enabled * on a video node. */ -#define V4L2_SUBDEV_ROUTE_FL_ACTIVE BIT(0) +#define V4L2_SUBDEV_ROUTE_FL_ACTIVE _BITUL(0) -/** +/* * Is the route immutable, i.e. can it be activated and inactivated? * Set by the driver. */ -#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE BIT(1) +#define V4L2_SUBDEV_ROUTE_FL_IMMUTABLE _BITUL(1) -/** - * Is the route a source endpoint? A source endpoint route doesn't come - * from "anywhere", and the sink_pad and sink_stream fields are unused. +/* + * Is the route a source endpoint? A source endpoint route refers to a stream + * generated internally by the subdevice (usually a sensor), and thus there + * is no sink-side endpoint for the route. The sink_pad and sink_stream + * fields are unused. * Set by the driver. */ -#define V4L2_SUBDEV_ROUTE_FL_SOURCE BIT(2) +#define V4L2_SUBDEV_ROUTE_FL_SOURCE _BITUL(2) /** * struct v4l2_subdev_route - A route inside a subdev diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index c637b697af2e..50d7bc4fa6ad 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -978,8 +978,10 @@ struct v4l2_requestbuffers { * pointing to this plane * @fd: when memory is V4L2_MEMORY_DMABUF, a userspace file * descriptor associated with this plane + * @m: union of @mem_offset, @userptr and @fd * @data_offset: offset in the plane to the start of data; usually 0, * unless there is a header in front of the data + * @reserved: drivers and applications must zero this array * * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer * with two planes can have one plane for Y, and another for interleaved CbCr @@ -1021,10 +1023,14 @@ struct v4l2_plane { * a userspace file descriptor associated with this buffer * @planes: for multiplanar buffers; userspace pointer to the array of plane * info structs for this buffer + * @m: union of @offset, @userptr, @planes and @fd * @length: size in bytes of the buffer (NOT its payload) for single-plane * buffers (when type != *_MPLANE); number of elements in the * planes array for multi-plane buffers + * @reserved2: drivers and applications must zero this field * @request_fd: fd of the request that this buffer should use + * @reserved: for backwards compatibility with applications that do not know + * about @request_fd * * Contains data exchanged by application and driver using one of the Streaming * I/O methods. @@ -1062,7 +1068,7 @@ struct v4l2_buffer { #ifndef __KERNEL__ /** * v4l2_timeval_to_ns - Convert timeval to nanoseconds - * @ts: pointer to the timeval variable to be converted + * @tv: pointer to the timeval variable to be converted * * Returns the scalar nanosecond representation of the timeval * parameter. @@ -1123,6 +1129,7 @@ static inline __u64 v4l2_timeval_to_ns(const struct timeval *tv) * @flags: flags for newly created file, currently only O_CLOEXEC is * supported, refer to manual of open syscall for more details * @fd: file descriptor associated with DMABUF (set by driver) + * @reserved: drivers and applications must zero this array * * Contains data used for exporting a video buffer as DMABUF file descriptor. * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF @@ -2216,6 +2223,7 @@ struct v4l2_mpeg_vbi_fmt_ivtv { * this plane will be used * @bytesperline: distance in bytes between the leftmost pixels in two * adjacent lines + * @reserved: drivers and applications must zero this array */ struct v4l2_plane_pix_format { __u32 sizeimage; @@ -2234,8 +2242,10 @@ struct v4l2_plane_pix_format { * @num_planes: number of planes for this format * @flags: format flags (V4L2_PIX_FMT_FLAG_*) * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @hsv_enc: enum v4l2_hsv_encoding, HSV encoding * @quantization: enum v4l2_quantization, colorspace quantization * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * @reserved: drivers and applications must zero this array */ struct v4l2_pix_format_mplane { __u32 width; @@ -2260,6 +2270,7 @@ struct v4l2_pix_format_mplane { * struct v4l2_sdr_format - SDR format definition * @pixelformat: little endian four character code (fourcc) * @buffersize: maximum size in bytes required for data + * @reserved: drivers and applications must zero this array */ struct v4l2_sdr_format { __u32 pixelformat; @@ -2286,6 +2297,8 @@ struct v4l2_meta_format { * @vbi: raw VBI capture or output parameters * @sliced: sliced VBI capture or output parameters * @raw_data: placeholder for future extensions and custom formats + * @fmt: union of @pix, @pix_mp, @win, @vbi, @sliced, @sdr, @meta + * and @raw_data */ struct v4l2_format { __u32 type; diff --git a/ti_config_fragments/audio_display.cfg b/ti_config_fragments/audio_display.cfg index 28635cfb35f8..32799e681905 100644 --- a/ti_config_fragments/audio_display.cfg +++ b/ti_config_fragments/audio_display.cfg @@ -137,6 +137,10 @@ CONFIG_VIDEO_OV1063X=m CONFIG_VIDEO_MT9T11X=m CONFIG_VIDEO_OV490=m CONFIG_VIDEO_OV5640=m +CONFIG_VIDEO_IMX390=m + +CONFIG_VIDEO_DS90UB960=m +CONFIG_VIDEO_DS90UB953=m CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m |
