diff options
886 files changed, 42452 insertions, 18431 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-sgi_uv b/Documentation/ABI/testing/sysfs-firmware-sgi_uv index 66800baab096..637c668cbe45 100644 --- a/Documentation/ABI/testing/sysfs-firmware-sgi_uv +++ b/Documentation/ABI/testing/sysfs-firmware-sgi_uv @@ -1,27 +1,159 @@ What: /sys/firmware/sgi_uv/ -Date: August 2008 -Contact: Russ Anderson <rja@sgi.com> +Date: September 2020 +Contact: Justin Ernst <justin.ernst@hpe.com> Description: The /sys/firmware/sgi_uv directory contains information - about the SGI UV platform. + about the UV platform. - Under that directory are a number of files:: + Under that directory are a number of read-only attributes:: + archtype + hub_type + hubless partition_id coherence_id + uv_type + + The archtype entry contains the UV architecture type that + is used to select arch-dependent addresses and features. + It can be set via the OEM_ID in the ACPI MADT table or by + UVsystab entry both passed from UV BIOS. + + The hub_type entry is used to select the type of hub which is + similar to uv_type but encoded in a binary format. Include + the file uv_hub.h to get the definitions. + + The hubless entry basically is present and set only if there + is no hub. In this case the hub_type entry is not present. The partition_id entry contains the partition id. - SGI UV systems can be partitioned into multiple physical + UV systems can be partitioned into multiple physical machines, which each partition running a unique copy - of the operating system. Each partition will have a unique - partition id. To display the partition id, use the command:: - - cat /sys/firmware/sgi_uv/partition_id + of the operating system. Each partition will have a unique + partition id. The coherence_id entry contains the coherence id. - A partitioned SGI UV system can have one or more coherence - domain. The coherence id indicates which coherence domain - this partition is in. To display the coherence id, use the - command:: + A partitioned UV system can have one or more coherence + domains. The coherence id indicates which coherence domain + this partition is in. + + The uv_type entry contains the hub revision number. + This value can be used to identify the UV system version:: + "0.*" = Hubless UV ('*' is subtype) + + "3.0" = UV2 + "5.0" = UV3 + "7.0" = UV4 + "7.1" = UV4a + "9.0" = UV5 + + The /sys/firmware/sgi_uv directory also contains two directories:: + + hubs/ + pcibuses/ + + The hubs directory contains a number of hub objects, each representing + a UV Hub visible to the BIOS. Each hub object's name is appended by a + unique ordinal value (ex. /sys/firmware/sgi_uv/hubs/hub_5) + + Each hub object directory contains a number of read-only attributes:: + + cnode + location + name + nasid + shared + this_partition + + The cnode entry contains the cnode number of the corresponding hub. + If a cnode value is not applicable, the value returned will be -1. + + The location entry contains the location string of the corresponding hub. + This value is used to physically identify a hub within a system. + + The name entry contains the name of the corresponding hub. This name can + be two variants:: + + "UVHub x.x" = A 'node' ASIC, connecting a CPU to the interconnect + fabric. The 'x.x' value represents the ASIC revision. + (ex. 'UVHub 5.0') + + "NLxRouter" = A 'router ASIC, only connecting other ASICs to + the interconnect fabric. The 'x' value representing + the fabric technology version. (ex. 'NL8Router') + + The nasid entry contains the nasid number of the corresponding hub. + If a nasid value is not applicable, the value returned will be -1. + + The shared entry contains a boolean value describing whether the + corresponding hub is shared between system partitions. + + The this_partition entry contains a boolean value describing whether + the corresponding hub is local to the current partition. + + Each hub object directory also contains a number of port objects, + each representing a fabric port on the corresponding hub. + A port object's name is appended by a unique ordinal value + (ex. /sys/firmware/sgi_uv/hubs/hub_5/port_3) + + Each port object directory contains a number of read-only attributes:: + + conn_hub + conn_port + + The conn_hub entry contains a value representing the unique + oridinal value of the hub on the other end of the fabric + cable plugged into the port. If the port is disconnected, + the value returned will be -1. + + The conn_port entry contains a value representing the unique + oridinal value of the port on the other end of the fabric cable + plugged into the port. If the port is disconnected, the value + returned will be -1. + + Ex: + A value of '3' is read from: + /sys/firmware/sgi_uv/hubs/hub_5/port_3/conn_hub + + and a value of '6' is read from: + /sys/firmware/sgi_uv/hubs/hub_5/port_3/conn_port + + representing that this port is connected to: + /sys/firmware/sgi_uv/hubs/hub_3/port_6 + + The pcibuses directory contains a number of PCI bus objects. + Each PCI bus object's name is appended by its PCI bus address. + (ex. pcibus_0003:80) + + Each pcibus object has a number of possible read-only attributes:: + + type + location + slot + ppb_addr + iio_stack + + The type entry contains a value describing the type of IO at + the corresponding PCI bus address. Known possible values + across all UV versions are:: + + BASE IO + PCIe IO + PCIe SLOT + NODE IO + Riser + PPB + + The location entry contains the location string of the UV Hub + of the CPU physically connected to the corresponding PCI bus. + + The slot entry contains the physical slot number of the + corresponding PCI bus. This value is used to physically locate + PCI cards within a system. + + The ppb_addr entry contains the PCI address string of the + bridged PCI bus. This entry is only present when the PCI bus + object type is 'PPB'. - cat /sys/firmware/sgi_uv/coherence_id + The iio_stack entry contains a value describing the IIO stack + number that the corresponding PCI bus object is connected to. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 44fde25bb221..a7d60d847584 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3375,6 +3375,8 @@ nosep [BUGS=X86-32] Disables x86 SYSENTER/SYSEXIT support. + nosgx [X86-64,SGX] Disables Intel SGX kernel support. + nosmp [SMP] Tells an SMP kernel to act as a UP kernel, and disable the IO APIC. legacy for "maxcpus=0". diff --git a/Documentation/admin-guide/media/rkisp1.rst b/Documentation/admin-guide/media/rkisp1.rst index 42e37ed255f6..2267e4fb475e 100644 --- a/Documentation/admin-guide/media/rkisp1.rst +++ b/Documentation/admin-guide/media/rkisp1.rst @@ -86,7 +86,7 @@ the driver through the rkisp_params node to improve image quality during a video stream. The buffer format is defined by struct :c:type:`rkisp1_stat_buffer`, and userspace should set -:ref:`V4L2_META_FMT_RK_ISP1_STAT_3A <v4l2-meta-fmt-stat-rkisp1>` as the +:ref:`V4L2_META_FMT_RK_ISP1_STAT_3A <v4l2-meta-fmt-rk-isp1-stat-3a>` as the dataformat. .. _rkisp1_params: @@ -100,7 +100,7 @@ and others. The buffer format is defined by struct :c:type:`rkisp1_params_cfg`, and userspace should set -:ref:`V4L2_META_FMT_RK_ISP1_PARAMS <v4l2-meta-fmt-params-rkisp1>` as the +:ref:`V4L2_META_FMT_RK_ISP1_PARAMS <v4l2-meta-fmt-rk-isp1-params>` as the dataformat. diff --git a/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml new file mode 100644 index 000000000000..a1d55a2634a5 --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/auxdisplay/modtronix,lcd2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Modtronix engineering LCD2S Character LCD Display + +maintainers: + - Lars Poeschel <poeschel@lemonage.de> + +description: + The LCD2S is a Character LCD Display manufactured by Modtronix Engineering. + The display supports a serial I2C and SPI interface. The driver currently + only supports the I2C interface. + +properties: + compatible: + const: modtronix,lcd2s + + reg: + maxItems: 1 + description: + I2C bus address of the display. + + display-height-chars: + description: Height of the display, in character cells. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 4 + + display-width-chars: + description: Width of the display, in character cells. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 16 + maximum: 20 + +required: + - compatible + - reg + - display-height-chars + - display-width-chars + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + lcd2s: auxdisplay@28 { + compatible = "modtronix,lcd2s"; + reg = <0x28>; + display-height-chars = <4>; + display-width-chars = <20>; + }; + }; diff --git a/Documentation/devicetree/bindings/crypto/intel,keembay-ocs-aes.yaml b/Documentation/devicetree/bindings/crypto/intel,keembay-ocs-aes.yaml new file mode 100644 index 000000000000..ee2c099981b2 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/intel,keembay-ocs-aes.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/crypto/intel,keembay-ocs-aes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel Keem Bay OCS AES Device Tree Bindings + +maintainers: + - Daniele Alessandrelli <daniele.alessandrelli@intel.com> + +description: + The Intel Keem Bay Offload and Crypto Subsystem (OCS) AES engine provides + hardware-accelerated AES/SM4 encryption/decryption. + +properties: + compatible: + const: intel,keembay-ocs-aes + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + crypto@30008000 { + compatible = "intel,keembay-ocs-aes"; + reg = <0x30008000 0x1000>; + interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&scmi_clk 95>; + }; diff --git a/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt b/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt index 6a0f3d90d682..8ca9e0a049d8 100644 --- a/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt +++ b/Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt @@ -1,6 +1,6 @@ -Aspeed AST2500 SoC EDAC node +Aspeed BMC SoC EDAC node -The Aspeed AST2500 SoC supports DDR3 and DDR4 memory with and without ECC (error +The Aspeed BMC SoC supports DDR3 and DDR4 memory with and without ECC (error correction check). The memory controller supports SECDED (single bit error correction, double bit @@ -11,7 +11,10 @@ Note, the bootloader must configure ECC mode in the memory controller. Required properties: -- compatible: should be "aspeed,ast2500-sdram-edac" +- compatible: should be one of + - "aspeed,ast2400-sdram-edac" + - "aspeed,ast2500-sdram-edac" + - "aspeed,ast2600-sdram-edac" - reg: sdram controller register set should be <0x1e6e0000 0x174> - interrupts: should be AVIC interrupt #0 diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml index 4cc1a670c986..2f7058f7760c 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml @@ -18,6 +18,8 @@ properties: - allwinner,sun7i-a20-video-engine - allwinner,sun8i-a33-video-engine - allwinner,sun8i-h3-video-engine + - allwinner,sun8i-v3s-video-engine + - allwinner,sun8i-r40-video-engine - allwinner,sun50i-a64-video-engine - allwinner,sun50i-h5-video-engine - allwinner,sun50i-h6-video-engine diff --git a/Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml b/Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml new file mode 100644 index 000000000000..bee93bd84771 --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020 BayLibre, SAS +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/amlogic,axg-ge2d.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic GE2D Acceleration Unit + +maintainers: + - Neil Armstrong <narmstrong@baylibre.com> + +properties: + compatible: + enum: + - amlogic,axg-ge2d + + interrupts: + minItems: 1 + + reg: + minItems: 1 + + resets: + maxItems: 1 + + clocks: + minItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - resets + +additionalProperties: false + +examples: + - | + ge2d: ge2d@ff940000 { + compatible = "amlogic,axg-ge2d"; + reg = <0xff940000 0x10000>; + interrupts = <150>; + clocks = <&clk_ge2d>; + resets = <&reset_ge2d>; + }; diff --git a/Documentation/devicetree/bindings/media/coda.txt b/Documentation/devicetree/bindings/media/coda.txt deleted file mode 100644 index 90eb74cc1993..000000000000 --- a/Documentation/devicetree/bindings/media/coda.txt +++ /dev/null @@ -1,31 +0,0 @@ -Chips&Media Coda multi-standard codec IP -======================================== - -Coda codec IPs are present in i.MX SoCs in various versions, -called VPU (Video Processing Unit). - -Required properties: -- compatible : should be "fsl,<chip>-src" for i.MX SoCs: - (a) "fsl,imx27-vpu" for CodaDx6 present in i.MX27 - (b) "fsl,imx51-vpu" for CodaHx4 present in i.MX51 - (c) "fsl,imx53-vpu" for CODA7541 present in i.MX53 - (d) "fsl,imx6q-vpu" for CODA960 present in i.MX6q -- reg: should be register base and length as documented in the - SoC reference manual -- interrupts : Should contain the VPU interrupt. For CODA960, - a second interrupt is needed for the MJPEG unit. -- clocks : Should contain the ahb and per clocks, in the order - determined by the clock-names property. -- clock-names : Should be "ahb", "per" -- iram : phandle pointing to the SRAM device node - -Example: - -vpu: vpu@63ff4000 { - compatible = "fsl,imx53-vpu"; - reg = <0x63ff4000 0x1000>; - interrupts = <9>; - clocks = <&clks 63>, <&clks 63>; - clock-names = "ahb", "per"; - iram = <&ocram>; -}; diff --git a/Documentation/devicetree/bindings/media/coda.yaml b/Documentation/devicetree/bindings/media/coda.yaml new file mode 100644 index 000000000000..7bac0057faf7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/coda.yaml @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/coda.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Chips&Media Coda multi-standard codec IP + +maintainers: + - Philipp Zabel <p.zabel@pengutronix.de> + +description: |- + Coda codec IPs are present in i.MX SoCs in various versions, + called VPU (Video Processing Unit). + +properties: + compatible: + oneOf: + - items: + - const: fsl,imx27-vpu + - const: cnm,codadx6 + - items: + - const: fsl,imx51-vpu + - const: cnm,codahx4 + - items: + - const: fsl,imx53-vpu + - const: cnm,coda7541 + - items: + - enum: + - fsl,imx6dl-vpu + - fsl,imx6q-vpu + - const: cnm,coda960 + + reg: + maxItems: 1 + + clocks: + items: + - description: PER clock + - description: AHB interface clock + + clock-names: + items: + - const: per + - const: ahb + + resets: + maxItems: 1 + + iram: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle pointing to the SRAM device node + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + contains: + const: cnm,coda960 + then: + properties: + interrupts: + items: + - description: BIT processor interrupt + - description: JPEG unit interrupt + + interrupt-names: + items: + - const: bit + - const: jpeg + else: + properties: + interrupts: + items: + - description: BIT processor interrupt + + - if: + properties: + compatible: + contains: + enum: + - fsl,imx6dl-vpu + - fsl,imx6q-vpu + then: + properties: + power-domains: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle pointing to the PU power domain + maxItems: 1 + +examples: + - | + vpu: video-codec@63ff4000 { + compatible = "fsl,imx53-vpu", "cnm,coda7541"; + reg = <0x63ff4000 0x1000>; + interrupts = <9>; + clocks = <&clks 63>, <&clks 63>; + clock-names = "per", "ahb"; + iram = <&ocram>; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.txt b/Documentation/devicetree/bindings/media/i2c/adv7604.txt deleted file mode 100644 index b3e688b77a38..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/adv7604.txt +++ /dev/null @@ -1,88 +0,0 @@ -* Analog Devices ADV7604/11/12 video decoder with HDMI receiver - -The ADV7604 and ADV7611/12 are multiformat video decoders with an integrated -HDMI receiver. The ADV7604 has four multiplexed HDMI inputs and one analog -input, and the ADV7611 has one HDMI input and no analog input. The 7612 is -similar to the 7611 but has 2 HDMI inputs. - -These device tree bindings support the ADV7611/12 only at the moment. - -Required Properties: - - - compatible: Must contain one of the following - - "adi,adv7611" for the ADV7611 - - "adi,adv7612" for the ADV7612 - - - reg: I2C slave addresses - The ADV76xx has up to thirteen 256-byte maps that can be accessed via the - main I2C ports. Each map has it own I2C address and acts as a standard - slave device on the I2C bus. The main address is mandatory, others are - optional and revert to defaults if not specified. - - - hpd-gpios: References to the GPIOs that control the HDMI hot-plug - detection pins, one per HDMI input. The active flag indicates the GPIO - level that enables hot-plug detection. - -The device node must contain one 'port' child node per device input and output -port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes -are numbered as follows. - - Port ADV7611 ADV7612 ------------------------------------------------------------- - HDMI 0 0, 1 - Digital output 1 2 - -The digital output port node must contain at least one endpoint. - -Optional Properties: - - - reset-gpios: Reference to the GPIO connected to the device's reset pin. - - default-input: Select which input is selected after reset. - - reg-names : Names of maps with programmable addresses. - It can contain any map needing a non-default address. - Possible maps names are : - "main", "avlink", "cec", "infoframe", "esdp", "dpp", "afe", - "rep", "edid", "hdmi", "test", "cp", "vdp" - -Optional Endpoint Properties: - - The following three properties are defined in video-interfaces.txt and are - valid for source endpoints only. - - - hsync-active: Horizontal synchronization polarity. Defaults to active low. - - vsync-active: Vertical synchronization polarity. Defaults to active low. - - pclk-sample: Pixel clock polarity. Defaults to output on the falling edge. - - If none of hsync-active, vsync-active and pclk-sample is specified the - endpoint will use embedded BT.656 synchronization. - -Example: - - hdmi_receiver@4c { - compatible = "adi,adv7611"; - /* - * The edid page will be accessible @ 0x66 on the I2C bus. All - * other maps will retain their default addresses. - */ - reg = <0x4c>, <0x66>; - reg-names = "main", "edid"; - - reset-gpios = <&ioexp 0 GPIO_ACTIVE_LOW>; - hpd-gpios = <&ioexp 2 GPIO_ACTIVE_HIGH>; - - #address-cells = <1>; - #size-cells = <0>; - - default-input = <0>; - - port@0 { - reg = <0>; - }; - port@1 { - reg = <1>; - hdmi_in: endpoint { - remote-endpoint = <&ccdc_in>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml new file mode 100644 index 000000000000..407baddfaa1d --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/adv7604.yaml @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/adv7604.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADV7604/11/12 video decoder with HDMI receiver + +maintainers: + - Hans Verkuil <hverkuil-cisco@xs4all.nl> + +description: + The ADV7604 and ADV7611/12 are multiformat video decoders with an integrated + HDMI receiver. The ADV7604 has four multiplexed HDMI inputs and one analog + input, and the ADV7611 has one HDMI input and no analog input. The 7612 is + similar to the 7611 but has 2 HDMI inputs. + + These device tree bindings support the ADV7611/12 only at the moment. + +properties: + compatible: + items: + - enum: + - adi,adv7611 + - adi,adv7612 + + reg: + minItems: 1 + maxItems: 13 + + reg-names: + minItems: 1 + maxItems: 13 + items: + - const: main + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + - enum: [ avlink, cec, infoframe, esdp, dpp, afe, rep, edid, hdmi, test, cp, vdp ] + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + hpd-gpios: + minItems: 1 + description: + References to the GPIOs that control the HDMI hot-plug detection pins, + one per HDMI input. The active flag indicates the GPIO level that + enables hot-plug detection. + + default-input: + maxItems: 1 + description: + Select which input is selected after reset. + + ports: + type: object + description: + A node containing input and output port nodes with endpoint definitions + as documented in + Documentation/devicetree/bindings/media/video-interfaces.txt + +required: + - compatible + - reg + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + const: adi,adv7611 + then: + properties: + ports: + properties: + '#address-cells': + const: 1 + '#size-cells': + const: 0 + port@0: + type: object + description: Input port + port@1: + type: object + description: Output port + + required: + - port@1 + + additionalProperties: false + + required: + - ports + + - if: + properties: + compatible: + contains: + const: adi,adv7612 + then: + properties: + ports: + properties: + '#address-cells': + const: 1 + '#size-cells': + const: 0 + port@2: + type: object + description: Output port + + patternProperties: + "^port@[0-1]$": + type: object + description: Input port + + required: + - port@2 + + additionalProperties: false + + required: + - ports + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hdmi_receiver@4c { + compatible = "adi,adv7611"; + /* + * The edid page will be accessible @ 0x66 on the I2C bus. All + * other maps will retain their default addresses. + */ + reg = <0x4c>, <0x66>; + reg-names = "main", "edid"; + + reset-gpios = <&ioexp 0 GPIO_ACTIVE_LOW>; + hpd-gpios = <&ioexp 2 GPIO_ACTIVE_HIGH>; + default-input = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + }; + + port@1 { + reg = <1>; + hdmi_in: endpoint { + remote-endpoint = <&ccdc_in>; + }; + }; + }; + + + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt deleted file mode 100644 index bd896e9f67d1..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt +++ /dev/null @@ -1,46 +0,0 @@ -* Aptina MT9V111 CMOS sensor ----------------------------- - -The Aptina MT9V111 is a 1/4-Inch VGA-format digital image sensor with a core -based on Aptina MT9V011 sensor and an integrated Image Flow Processor (IFP). - -The sensor has an active pixel array of 640x480 pixels and can output a number -of image resolution and formats controllable through a simple two-wires -interface. - -Required properties: --------------------- - -- compatible: shall be "aptina,mt9v111". -- clocks: reference to the system clock input provider. - -Optional properties: --------------------- - -- enable-gpios: output enable signal, pin name "OE#". Active low. -- standby-gpios: low power state control signal, pin name "STANDBY". - Active high. -- reset-gpios: chip reset signal, pin name "RESET#". Active low. - -The device node must contain one 'port' child node with one 'endpoint' child -sub-node for its digital output video port, in accordance with the video -interface bindings defined in: -Documentation/devicetree/bindings/media/video-interfaces.txt - -Example: --------- - - &i2c1 { - camera@48 { - compatible = "aptina,mt9v111"; - reg = <0x48>; - - clocks = <&camera_clk>; - - port { - mt9v111_out: endpoint { - remote-endpoint = <&ceu_in>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml new file mode 100644 index 000000000000..ff9546e95d05 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/aptina,mt9v111.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Aptina MT9V111 CMOS sensor + +maintainers: + - Jacopo Mondi <jacopo@jmondi.org> + +description: | + The Aptina MT9V111 is a 1/4-Inch VGA-format digital image sensor with a core + based on Aptina MT9V011 sensor and an integrated Image Flow Processor (IFP). + + The sensor has an active pixel array of 640x480 pixels and can output a number + of image resolutions and formats controllable through a simple two-wires + interface. + +properties: + compatible: + const: aptina,mt9v111 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + enable-gpios: + description: Enable signal, pin name "OE#". Active low. + maxItems: 1 + + standby-gpios: + description: | + Low power state control signal, pin name "STANDBY". Active high. + maxItems: 1 + + reset-gpios: + description: Chip reset signal, pin name "RESET#". Active low. + maxItems: 1 + + port: + type: object + description: | + Output video port. See ../video-interfaces.txt. + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + camera@48 { + compatible = "aptina,mt9v111"; + reg = <0x48>; + clocks = <&camera_clk>; + + port { + mt9v111_out: endpoint { + remote-endpoint = <&ceu_in>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml new file mode 100644 index 000000000000..d94bd67ccea1 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2014--2020 Intel Corporation + +$id: http://devicetree.org/schemas/media/i2c/mipi-ccs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MIPI CCS, SMIA++ and SMIA compliant camera sensors + +maintainers: + - Sakari Ailus <sakari.ailus@linux.intel.com> + +description: + + CCS (Camera Command Set) is a raw Bayer camera sensor standard defined by the + MIPI Alliance; see + <URL:https://www.mipi.org/specifications/camera-command-set>. + + SMIA (Standard Mobile Imaging Architecture) is an image sensor standard + defined jointly by Nokia and ST. SMIA++, defined by Nokia, is an extension of + that. + + More detailed documentation can be found in + Documentation/devicetree/bindings/media/video-interfaces.txt . + +properties: + compatible: + oneOf: + - items: + - const: mipi-ccs-1.1 + - const: mipi-ccs + - items: + - const: mipi-ccs-1.0 + - const: mipi-ccs + - const: nokia,smia + + reg: + maxItems: 1 + + vana-supply: + description: Analogue voltage supply (VANA), sensor dependent. + maxItems: 1 + + vcore-supply: + description: Core voltage supply (VCore), sensor dependent. + maxItems: 1 + + vio-supply: + description: I/O voltage supply (VIO), sensor dependent. + maxItems: 1 + + clocks: + description: External clock to the sensor. + maxItems: 1 + + clock-frequency: + description: Frequency of the external clock to the sensor in Hz. + + reset-gpios: + description: Reset GPIO. Also commonly called XSHUTDOWN in hardware + documentation. + maxItems: 1 + + flash-leds: + description: Flash LED phandles. See ../video-interfaces.txt for details. + + lens-focus: + description: Lens focus controller phandles. See ../video-interfaces.txt + for details. + + rotation: + description: Rotation of the sensor. See ../video-interfaces.txt for + details. + enum: [ 0, 180 ] + + port: + type: object + properties: + endpoint: + type: object + properties: + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: List of allowed data link frequencies. + data-lanes: + minItems: 1 + maxItems: 8 + bus-type: + description: The type of the data bus. + oneOf: + - const: 1 # CSI-2 C-PHY + - const: 3 # CCP2 + - const: 4 # CSI-2 D-PHY + + required: + - link-frequencies + - data-lanes + - bus-type + +required: + - compatible + - reg + - clock-frequency + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c2 { + #address-cells = <1>; + #size-cells = <0>; + + clock-frequency = <400000>; + + camera-sensor@10 { + compatible = "mipi-ccs-1.0", "mipi-ccs"; + reg = <0x10>; + reset-gpios = <&gpio3 20 GPIO_ACTIVE_LOW>; + vana-supply = <&vaux3>; + clocks = <&omap3_isp 0>; + clock-frequency = <9600000>; + port { + ccs_ep: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&csi2a_ep>; + link-frequencies = /bits/ 64 <199200000 210000000 + 499200000>; + bus-type = <4>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt b/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt deleted file mode 100644 index 10ece8108081..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt +++ /dev/null @@ -1,66 +0,0 @@ -SMIA/SMIA++ sensor - -SMIA (Standard Mobile Imaging Architecture) is an image sensor standard -defined jointly by Nokia and ST. SMIA++, defined by Nokia, is an extension -of that. These definitions are valid for both types of sensors. - -More detailed documentation can be found in -Documentation/devicetree/bindings/media/video-interfaces.txt . - -The device node should contain a "port" node which may contain one or more -endpoint nodes, in accordance with video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt . - -Mandatory properties --------------------- - -- compatible: "nokia,smia" -- reg: I2C address (0x10, or an alternative address) -- vana-supply: Analogue voltage supply (VANA), typically 2,8 volts (sensor - dependent). -- clocks: External clock to the sensor -- clock-frequency: Frequency of the external clock to the sensor -- link-frequencies: List of allowed data link frequencies. An array of - 64-bit elements. - - -Optional properties -------------------- - -- reset-gpios: XSHUTDOWN GPIO -- flash-leds: See ../video-interfaces.txt -- lens-focus: See ../video-interfaces.txt -- rotation: Integer property; valid values are 0 (sensor mounted upright) - and 180 (sensor mounted upside down). See - ../video-interfaces.txt . - - -Endpoint node mandatory properties ----------------------------------- - -- data-lanes: <1..n> - - -Example -------- - -&i2c2 { - clock-frequency = <400000>; - - camera-sensor@10 { - compatible = "nokia,smia"; - reg = <0x10>; - reset-gpios = <&gpio3 20 0>; - vana-supply = <&vaux3>; - clocks = <&omap3_isp 0>; - clock-frequency = <9600000>; - nokia,nvm-size = <512>; /* 8 * 64 */ - link-frequencies = /bits/ 64 <199200000 210000000 499200000>; - port { - smiapp_ep: endpoint { - data-lanes = <1 2>; - remote-endpoint = <&csi2a_ep>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/ov2680.txt b/Documentation/devicetree/bindings/media/i2c/ov2680.txt deleted file mode 100644 index 11e925ed9dad..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov2680.txt +++ /dev/null @@ -1,46 +0,0 @@ -* Omnivision OV2680 MIPI CSI-2 sensor - -Required Properties: -- compatible: should be "ovti,ov2680". -- clocks: reference to the xvclk input clock. -- clock-names: should be "xvclk". -- DOVDD-supply: Digital I/O voltage supply. -- DVDD-supply: Digital core voltage supply. -- AVDD-supply: Analog voltage supply. - -Optional Properties: -- reset-gpios: reference to the GPIO connected to the powerdown/reset pin, - if any. This is an active low signal to the OV2680. - -The device node must contain one 'port' child node for its digital output -video port, and this port must have a single endpoint in accordance with - the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Endpoint node required properties for CSI-2 connection are: -- remote-endpoint: a phandle to the bus receiver's endpoint node. -- clock-lanes: should be set to <0> (clock lane on hardware lane 0). -- data-lanes: should be set to <1> (one CSI-2 lane supported). - -Example: - -&i2c2 { - ov2680: camera-sensor@36 { - compatible = "ovti,ov2680"; - reg = <0x36>; - clocks = <&osc>; - clock-names = "xvclk"; - reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; - DOVDD-supply = <&sw2_reg>; - DVDD-supply = <&sw2_reg>; - AVDD-supply = <®_peri_3p15v>; - - port { - ov2680_to_mipi: endpoint { - remote-endpoint = <&mipi_from_sensor>; - clock-lanes = <0>; - data-lanes = <1>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/ov772x.txt b/Documentation/devicetree/bindings/media/i2c/ov772x.txt deleted file mode 100644 index 0b3ede5b8e6a..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov772x.txt +++ /dev/null @@ -1,40 +0,0 @@ -* Omnivision OV7720/OV7725 CMOS sensor - -The Omnivision OV7720/OV7725 sensor supports multiple resolutions output, -such as VGA, QVGA, and any size scaling down from CIF to 40x30. It also can -support the YUV422, RGB565/555/444, GRB422 or raw RGB output formats. - -Required Properties: -- compatible: shall be one of - "ovti,ov7720" - "ovti,ov7725" -- clocks: reference to the xclk input clock. - -Optional Properties: -- reset-gpios: reference to the GPIO connected to the RSTB pin which is - active low, if any. -- powerdown-gpios: reference to the GPIO connected to the PWDN pin which is - active high, if any. - -The device node shall contain one 'port' child node with one child 'endpoint' -subnode for its digital output video port, in accordance with the video -interface bindings defined in Documentation/devicetree/bindings/media/ -video-interfaces.txt. - -Example: - -&i2c0 { - ov772x: camera@21 { - compatible = "ovti,ov7725"; - reg = <0x21>; - reset-gpios = <&axi_gpio_0 0 GPIO_ACTIVE_LOW>; - powerdown-gpios = <&axi_gpio_0 1 GPIO_ACTIVE_LOW>; - clocks = <&xclk>; - - port { - ov772x_0: endpoint { - remote-endpoint = <&vcap1_in0>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml new file mode 100644 index 000000000000..1c3879ec4122 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2020 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov02a10.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV02A10 CMOS Sensor Device Tree Bindings + +maintainers: + - Dongchun Zhu <dongchun.zhu@mediatek.com> + +description: |- + The Omnivision OV02A10 is a low-cost, high performance, 1/5-inch, 2 megapixel + image sensor, which is the latest production derived from Omnivision's CMOS + image sensor technology. Ihis chip supports high frame rate speeds up to 30fps + @ 1600x1200 (UXGA) resolution transferred over a 1-lane MIPI interface. The + sensor output is available via CSI-2 serial data output. + +properties: + compatible: + const: ovti,ov02a10 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + description: + External clock for the sensor. + items: + - const: eclk + + clock-frequency: + description: + Frequency of the eclk clock in Hz. + + dovdd-supply: + description: + Definition of the regulator used as Digital I/O voltage supply. + + avdd-supply: + description: + Definition of the regulator used as Analog voltage supply. + + dvdd-supply: + description: + Definition of the regulator used as Digital core voltage supply. + + powerdown-gpios: + description: + Must be the device tree identifier of the GPIO connected to the + PD_PAD pin. This pin is used to place the OV02A10 into standby mode + or shutdown mode. As the line needs to be high for the powerdown mode + to be active, it should be marked GPIO_ACTIVE_HIGH. + maxItems: 1 + + reset-gpios: + description: + Must be the device tree identifier of the GPIO connected to the + RST_PD pin. If specified, it will be asserted during driver probe. + As the line needs to be low for the reset to be active, it should be + marked GPIO_ACTIVE_LOW. + maxItems: 1 + + rotation: + description: + Definition of the sensor's placement. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: + - 0 # Sensor Mounted Upright + - 180 # Sensor Mounted Upside Down + default: 0 + + # See ../video-interfaces.txt for details + port: + type: object + additionalProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + type: object + additionalProperties: false + + properties: + link-frequencies: true + ovti,mipi-clock-voltage: + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + description: + Definition of MIPI clock voltage unit. This entry corresponds to + the link speed defined by the 'link-frequencies' property. + If present, the value shall be in the range of 0-4. + default: 4 + remote-endpoint: true + + required: + - link-frequencies + - remote-endpoint + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - clock-names + - clock-frequency + - dovdd-supply + - avdd-supply + - dvdd-supply + - powerdown-gpios + - reset-gpios + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov02a10: camera-sensor@3d { + compatible = "ovti,ov02a10"; + reg = <0x3d>; + + powerdown-gpios = <&pio 107 GPIO_ACTIVE_HIGH>; + reset-gpios = <&pio 109 GPIO_ACTIVE_LOW>; + + clocks = <&ov02a10_clk>; + clock-names = "eclk"; + clock-frequency = <24000000>; + + rotation = <180>; + + dovdd-supply = <&ov02a10_dovdd>; + avdd-supply = <&ov02a10_avdd>; + dvdd-supply = <&ov02a10_dvdd>; + + port { + wcam_out: endpoint { + link-frequencies = /bits/ 64 <390000000>; + ovti,mipi-clock-voltage = <3>; + remote-endpoint = <&mipi_in_wcam>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml new file mode 100644 index 000000000000..43bf749807e1 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov2680.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov2680.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV2680 CMOS Sensor + +maintainers: + - Rui Miguel Silva <rmfrfs@gmail.com> + +description: |- + The OV2680 color sensor is a low voltage, high performance 1/5 inch UXGA (2 + megapixel) CMOS image sensor that provides a single-chip UXGA (1600 x 1200) + camera. It provides full-frame, sub-sampled, or windowed 10-bit images in + various formats via the control of the Serial Camera Control Bus (SCCB) + interface. The OV2680 has an image array capable of operating at up to 30 + frames per second (fps) in UXGA resolution. + +properties: + compatible: + const: ovti,ov2680 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: xvclk + + reset-gpios: + description: + The phandle and specifier for the GPIO that controls sensor reset. + This corresponds to the hardware pin XSHUTDOWN which is physically + active low. + maxItems: 1 + + dovdd-supply: + description: + Definition of the regulator used as interface power supply. + + avdd-supply: + description: + Definition of the regulator used as analog power supply. + + dvdd-supply: + description: + Definition of the regulator used as digital power supply. + + port: + type: object + description: + A node containing an output port node with an endpoint definition + as documented in + Documentation/devicetree/bindings/media/video-interfaces.txt + +required: + - compatible + - reg + - clocks + - clock-names + - dovdd-supply + - avdd-supply + - dvdd-supply + - reset-gpios + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ov2680: camera-sensor@36 { + compatible = "ovti,ov2680"; + reg = <0x36>; + clocks = <&osc>; + clock-names = "xvclk"; + reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + + dovdd-supply = <&sw2_reg>; + dvdd-supply = <&sw2_reg>; + avdd-supply = <®_peri_3p15v>; + + port { + ov2680_to_mipi: endpoint { + remote-endpoint = <&mipi_from_sensor>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml new file mode 100644 index 000000000000..6866c2cdac50 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov772x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Omnivision OV7720/OV7725 CMOS sensor + +maintainers: + - Jacopo Mondi <jacopo@jmondi.org> + +description: | + The Omnivision OV7720/OV7725 sensor supports multiple resolutions output, + such as VGA, QVGA, and any size scaling down from CIF to 40x30. It also can + support the YUV422, RGB565/555/444, GRB422 or raw RGB output formats. + +properties: + compatible: + enum: + - ovti,ov7720 + - ovti,ov7725 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + reset-gpios: + description: | + Reference to the GPIO connected to the RSTB pin which is active low. + maxItems: 1 + + powerdown-gpios: + description: | + Reference to the GPIO connected to the PWDN pin which is active high. + maxItems: 1 + + port: + type: object + description: | + Video output port. See ../video-interfaces.txt. + + properties: + endpoint: + type: object + + properties: + bus-type: + enum: [5, 6] + + bus-width: + enum: [8, 10] + default: 10 + + data-shift: + enum: [0, 2] + default: 0 + + hsync-active: + enum: [0, 1] + default: 1 + + vsync-active: + enum: [0, 1] + default: 1 + + pclk-sample: + enum: [0, 1] + default: 1 + + allOf: + - if: + properties: + bus-type: + const: 6 + then: + properties: + hsync-active: false + vsync-active: false + + - if: + properties: + bus-width: + const: 10 + then: + properties: + data-shift: + const: 0 + + required: + - bus-type + + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + ov772x: camera@21 { + compatible = "ovti,ov7725"; + reg = <0x21>; + reset-gpios = <&axi_gpio_0 0 GPIO_ACTIVE_LOW>; + powerdown-gpios = <&axi_gpio_0 1 GPIO_ACTIVE_LOW>; + clocks = <&xclk>; + + port { + ov772x_0: endpoint { + bus-type = <5>; + vsync-active = <0>; + hsync-active = <0>; + pclk-sample = <0>; + bus-width = <8>; + data-shift = <0>; + remote-endpoint = <&vcap1_in0>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx214.txt b/Documentation/devicetree/bindings/media/i2c/sony,imx214.txt deleted file mode 100644 index f11f28a5fda4..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx214.txt +++ /dev/null @@ -1,53 +0,0 @@ -* Sony 1/3.06-Inch 13.13Mp CMOS Digital Image Sensor - -The Sony imx214 is a 1/3.06-inch CMOS active pixel digital image sensor with -an active array size of 4224H x 3200V. It is programmable through an I2C -interface. -Image data is sent through MIPI CSI-2, through 2 or 4 lanes at a maximum -throughput of 1.2Gbps/lane. - - -Required Properties: -- compatible: Shall be "sony,imx214". -- reg: I2C bus address of the device. Depending on how the sensor is wired, - it shall be <0x10> or <0x1a>; -- enable-gpios: GPIO descriptor for the enable pin. -- vdddo-supply: Chip digital IO regulator (1.8V). -- vdda-supply: Chip analog regulator (2.7V). -- vddd-supply: Chip digital core regulator (1.12V). -- clocks: Reference to the xclk clock. -- clock-frequency: Frequency of the xclk clock. - -Optional Properties: -- flash-leds: See ../video-interfaces.txt -- lens-focus: See ../video-interfaces.txt - -The imx214 device node shall contain one 'port' child node with -an 'endpoint' subnode. For further reading on port node refer to -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Required Properties on endpoint: -- data-lanes: check ../video-interfaces.txt -- link-frequencies: check ../video-interfaces.txt -- remote-endpoint: check ../video-interfaces.txt - -Example: - - camera-sensor@1a { - compatible = "sony,imx214"; - reg = <0x1a>; - vdddo-supply = <&pm8994_lvs1>; - vddd-supply = <&camera_vddd_1v12>; - vdda-supply = <&pm8994_l17>; - lens-focus = <&ad5820>; - enable-gpios = <&msmgpio 25 GPIO_ACTIVE_HIGH>; - clocks = <&mmcc CAMSS_MCLK0_CLK>; - clock-frequency = <24000000>; - port { - imx214_ep: endpoint { - data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <480000000>; - remote-endpoint = <&csiphy0_ep>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml new file mode 100644 index 000000000000..1a3590dd0e98 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx214.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony 1/3.06-Inch 13.13MP CMOS Digital Image Sensor + +maintainers: + - Ricardo Ribalda <ribalda@kernel.org> + +description: | + The Sony IMX214 is a 1/3.06-inch CMOS active pixel digital image sensor with + an active array size of 4224H x 3200V. It is programmable through an I2C + interface. Image data is sent through MIPI CSI-2, through 2 or 4 lanes at a + maximum throughput of 1.2Gbps/lane. + +properties: + compatible: + const: sony,imx214 + + reg: + enum: + - 0x10 + - 0x1a + + clocks: + description: Reference to the xclk clock. + maxItems: 1 + + clock-frequency: + description: Frequency of the xclk clock in Hz. + + enable-gpios: + description: GPIO descriptor for the enable pin. + maxItems: 1 + + vdddo-supply: + description: Chip digital IO regulator (1.8V). + maxItems: 1 + + vdda-supply: + description: Chip analog regulator (2.7V). + maxItems: 1 + + vddd-supply: + description: Chip digital core regulator (1.12V). + maxItems: 1 + + flash-leds: + description: See ../video-interfaces.txt + + lens-focus: + description: See ../video-interfaces.txt + + port: + type: object + description: | + Video output port. See ../video-interfaces.txt. + + properties: + endpoint: + type: object + + properties: + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: See ../video-interfaces.txt + anyOf: + - items: + - const: 1 + - const: 2 + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + link-frequencies: + $ref: /schemas/types.yaml#/definitions/uint64-array + description: See ../video-interfaces.txt + + required: + - data-lanes + - link-frequencies + + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-frequency + - enable-gpios + - vdddo-supply + - vdda-supply + - vddd-supply + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + camera-sensor@1a { + compatible = "sony,imx214"; + reg = <0x1a>; + vdddo-supply = <&pm8994_lvs1>; + vddd-supply = <&camera_vddd_1v12>; + vdda-supply = <&pm8994_l17>; + lens-focus = <&ad5820>; + enable-gpios = <&msmgpio 25 GPIO_ACTIVE_HIGH>; + clocks = <&camera_clk>; + clock-frequency = <24000000>; + + port { + imx214_ep: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <480000000>; + remote-endpoint = <&csiphy0_ep>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/imx7-csi.txt b/Documentation/devicetree/bindings/media/imx7-csi.txt deleted file mode 100644 index d80ceefa0c00..000000000000 --- a/Documentation/devicetree/bindings/media/imx7-csi.txt +++ /dev/null @@ -1,42 +0,0 @@ -Freescale i.MX7 CMOS Sensor Interface -===================================== - -csi node --------- - -This is device node for the CMOS Sensor Interface (CSI) which enables the chip -to connect directly to external CMOS image sensors. - -Required properties: - -- compatible : "fsl,imx7-csi" or "fsl,imx6ul-csi"; -- reg : base address and length of the register set for the device; -- interrupts : should contain CSI interrupt; -- clocks : list of clock specifiers, see - Documentation/devicetree/bindings/clock/clock-bindings.txt for details; -- clock-names : must contain "mclk"; - -The device node shall contain one 'port' child node with one child 'endpoint' -node, according to the bindings defined in: -Documentation/devicetree/bindings/media/video-interfaces.txt. - -In the following example a remote endpoint is a video multiplexer. - -example: - - csi: csi@30710000 { - #address-cells = <1>; - #size-cells = <0>; - - compatible = "fsl,imx7-csi"; - reg = <0x30710000 0x10000>; - interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks IMX7D_CSI_MCLK_ROOT_CLK>; - clock-names = "mclk"; - - port { - csi_from_csi_mux: endpoint { - remote-endpoint = <&csi_mux_to_csi>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt b/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt deleted file mode 100644 index 71fd74ed3ec8..000000000000 --- a/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt +++ /dev/null @@ -1,90 +0,0 @@ -Freescale i.MX7 Mipi CSI2 -========================= - -mipi_csi2 node --------------- - -This is the device node for the MIPI CSI-2 receiver core in i.MX7 SoC. It is -compatible with previous version of Samsung D-phy. - -Required properties: - -- compatible : "fsl,imx7-mipi-csi2"; -- reg : base address and length of the register set for the device; -- interrupts : should contain MIPI CSIS interrupt; -- clocks : list of clock specifiers, see - Documentation/devicetree/bindings/clock/clock-bindings.txt for details; -- clock-names : must contain "pclk", "wrap" and "phy" entries, matching - entries in the clock property; -- power-domains : a phandle to the power domain, see - Documentation/devicetree/bindings/power/power_domain.txt for details. -- reset-names : should include following entry "mrst"; -- resets : a list of phandle, should contain reset entry of - reset-names; -- phy-supply : from the generic phy bindings, a phandle to a regulator that - provides power to MIPI CSIS core; - -Optional properties: - -- clock-frequency : The IP's main (system bus) clock frequency in Hz, default - value when this property is not specified is 166 MHz; -- fsl,csis-hs-settle : differential receiver (HS-RX) settle time; - -The device node should contain two 'port' child nodes with one child 'endpoint' -node, according to the bindings defined in: - Documentation/devicetree/bindings/ media/video-interfaces.txt. - The following are properties specific to those nodes. - -port node ---------- - -- reg : (required) can take the values 0 or 1, where 0 shall be - related to the sink port and port 1 shall be the source - one; - -endpoint node -------------- - -- data-lanes : (required) an array specifying active physical MIPI-CSI2 - data input lanes and their mapping to logical lanes; this - shall only be applied to port 0 (sink port), the array's - content is unused only its length is meaningful, - in this case the maximum length supported is 2; - -example: - - mipi_csi: mipi-csi@30750000 { - #address-cells = <1>; - #size-cells = <0>; - - compatible = "fsl,imx7-mipi-csi2"; - reg = <0x30750000 0x10000>; - interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clks IMX7D_IPG_ROOT_CLK>, - <&clks IMX7D_MIPI_CSI_ROOT_CLK>, - <&clks IMX7D_MIPI_DPHY_ROOT_CLK>; - clock-names = "pclk", "wrap", "phy"; - clock-frequency = <166000000>; - power-domains = <&pgc_mipi_phy>; - phy-supply = <®_1p0d>; - resets = <&src IMX7_RESET_MIPI_PHY_MRST>; - reset-names = "mrst"; - fsl,csis-hs-settle = <3>; - - port@0 { - reg = <0>; - - mipi_from_sensor: endpoint { - remote-endpoint = <&ov2680_to_mipi>; - data-lanes = <1>; - }; - }; - - port@1 { - reg = <1>; - - mipi_vc0_to_csi_mux: endpoint { - remote-endpoint = <&csi_mux_from_mipi_vc0>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml new file mode 100644 index 000000000000..4e81a47e60ac --- /dev/null +++ b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/nxp,imx7-csi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX7 CMOS Sensor Interface + +maintainers: + - Rui Miguel Silva <rmfrfs@gmail.com> + +description: | + This is device node for the CMOS Sensor Interface (CSI) which enables the + chip to connect directly to external CMOS image sensors. + +properties: + compatible: + enum: + - fsl,imx7-csi + - fsl,imx6ul-csi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: mclk + + port: + type: object + description: + A node containing input port nodes with endpoint definitions as documented + in Documentation/devicetree/bindings/media/video-interfaces.txt + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx7d-clock.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + csi: csi@30710000 { + compatible = "fsl,imx7-csi"; + reg = <0x30710000 0x10000>; + interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clks IMX7D_CSI_MCLK_ROOT_CLK>; + clock-names = "mclk"; + + port { + csi_from_csi_mux: endpoint { + remote-endpoint = <&csi_mux_to_csi>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml new file mode 100644 index 000000000000..0668332959e7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml @@ -0,0 +1,173 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/nxp,imx7-mipi-csi2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP i.MX7 Mipi CSI2 + +maintainers: + - Rui Miguel Silva <rmfrfs@gmail.com> + +description: | + This is the device node for the MIPI CSI-2 receiver core in i.MX7 soc. It is + compatible with previous version of samsung d-phy. + +properties: + compatible: + const: fsl,imx7-mipi-csi2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 3 + maxItems: 3 + + clock-names: + items: + - const: pclk + - const: wrap + - const: phy + + power-domains: + maxItems: 1 + + phy-supply: + description: + Phandle to a regulator that provides power to the PHY. This + regulator will be managed during the PHY power on/off sequence. + + resets: + maxItems: 1 + + reset-names: + const: mrst + + clock-frequency: + description: + The IP main (system bus) clock frequency in Hertz + default: 166000000 + + fsl,csis-hs-settle: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Differential receiver (HS-RX) settle time + + ports: + type: object + description: + A node containing input and output port nodes with endpoint definitions + as documented in + Documentation/devicetree/bindings/media/video-interfaces.txt + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + port@0: + type: object + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + reg: + const: 0 + + endpoint: + type: object + + properties: + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: See ../video-interfaces.txt + oneOf: + - items: + - const: 1 + - items: + - const: 1 + - const: 2 + + remote-endpoint: true + + required: + - data-lanes + - remote-endpoint + + additionalProperties: false + + additionalProperties: false + + port@1: + type: object + description: + Output port node + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - power-domains + - phy-supply + - resets + - reset-names + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/imx7d-clock.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/imx7-reset.h> + + mipi_csi: mipi-csi@30750000 { + compatible = "fsl,imx7-mipi-csi2"; + reg = <0x30750000 0x10000>; + interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&clks IMX7D_IPG_ROOT_CLK>, + <&clks IMX7D_MIPI_CSI_ROOT_CLK>, + <&clks IMX7D_MIPI_DPHY_ROOT_CLK>; + clock-names = "pclk", "wrap", "phy"; + clock-frequency = <166000000>; + + power-domains = <&pgc_mipi_phy>; + phy-supply = <®_1p0d>; + resets = <&src IMX7_RESET_MIPI_PHY_MRST>; + reset-names = "mrst"; + fsl,csis-hs-settle = <3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + mipi_from_sensor: endpoint { + remote-endpoint = <&ov2680_to_mipi>; + data-lanes = <1>; + }; + }; + + port@1 { + reg = <1>; + + mipi_vc0_to_csi_mux: endpoint { + remote-endpoint = <&csi_mux_from_mipi_vc0>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt index 09eb6ed99114..498234629e21 100644 --- a/Documentation/devicetree/bindings/media/qcom,camss.txt +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt @@ -8,6 +8,7 @@ Qualcomm Camera Subsystem Definition: Should contain one of: - "qcom,msm8916-camss" - "qcom,msm8996-camss" + - "qcom,sdm660-camss" - reg: Usage: required Value type: <prop-encoded-array> @@ -64,30 +65,36 @@ Qualcomm Camera Subsystem Value type: <stringlist> Definition: Should contain the following entries: - "top_ahb" + - "throttle_axi" (660 only) - "ispif_ahb" - "csiphy0_timer" - "csiphy1_timer" - "csiphy2_timer" (8996 only) + - "csiphy_ahb2crif" (660 only) - "csi0_ahb" - "csi0" - "csi0_phy" - "csi0_pix" - "csi0_rdi" + - "cphy_csid0" (660 only) - "csi1_ahb" - "csi1" - "csi1_phy" - "csi1_pix" - "csi1_rdi" + - "cphy_csid1" (660 only) - "csi2_ahb" (8996 only) - "csi2" (8996 only) - "csi2_phy" (8996 only) - "csi2_pix" (8996 only) - "csi2_rdi" (8996 only) + - "cphy_csid2" (660 only) - "csi3_ahb" (8996 only) - "csi3" (8996 only) - "csi3_phy" (8996 only) - "csi3_pix" (8996 only) - "csi3_rdi" (8996 only) + - "cphy_csid3" (660 only) - "ahb" - "vfe0" - "csi_vfe0" diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index 8ad2cba5f61f..946441b4e1a5 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -83,6 +83,7 @@ properties: - rc-it913x-v2 - rc-kaiomy - rc-khadas + - rc-khamsin - rc-kworld-315u - rc-kworld-pc150u - rc-kworld-plus-tv-analog @@ -102,6 +103,7 @@ properties: - rc-npgtech - rc-odroid - rc-pctv-sedna + - rc-pine64 - rc-pinnacle-color - rc-pinnacle-grey - rc-pinnacle-pctv-hd diff --git a/drivers/staging/media/rkisp1/Documentation/devicetree/bindings/media/rockchip-isp1.yaml b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml index af246b71eac6..2004c054ed1a 100644 --- a/drivers/staging/media/rkisp1/Documentation/devicetree/bindings/media/rockchip-isp1.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml @@ -23,10 +23,27 @@ properties: interrupts: maxItems: 1 - iommus: - maxItems: 1 + clocks: + minItems: 3 + items: + # isp0 and isp1 + - description: ISP clock + - description: ISP AXI clock + - description: ISP AHB clock + # only for isp1 + - description: ISP Pixel clock - power-domains: + clock-names: + minItems: 3 + items: + # isp0 and isp1 + - const: isp + - const: aclk + - const: hclk + # only for isp1 + - const: pclk_isp + + iommus: maxItems: 1 phys: @@ -36,21 +53,8 @@ properties: phy-names: const: dphy - clocks: - items: - - description: ISP clock - - description: ISP AXI clock clock - - description: ISP AXI clock wrapper clock - - description: ISP AHB clock clock - - description: ISP AHB wrapper clock - - clock-names: - items: - - const: clk_isp - - const: aclk_isp - - const: aclk_isp_wrap - - const: hclk_isp - - const: hclk_isp_wrap + power-domains: + maxItems: 1 # See ./video-interfaces.txt for details ports: @@ -94,20 +98,42 @@ properties: remote-endpoint: true + required: + - reg + - "#address-cells" + - "#size-cells" + required: + - "#address-cells" + - "#size-cells" - port@0 required: - compatible + - reg - interrupts - clocks - clock-names - - power-domains - iommus - phys - phy-names + - power-domains - ports +if: + properties: + compatible: + contains: + const: rockchip,rk3399-cif-isp +then: + properties: + clocks: + minItems: 3 + maxItems: 4 + clock-names: + minItems: 3 + maxItems: 4 + additionalProperties: false examples: @@ -117,7 +143,7 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/rk3399-power.h> - parent0: parent@0 { + parent0: parent { #address-cells = <2>; #size-cells = <2>; @@ -126,24 +152,22 @@ examples: reg = <0x0 0xff910000 0x0 0x4000>; interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>; clocks = <&cru SCLK_ISP0>, - <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>, - <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>; - clock-names = "clk_isp", - "aclk_isp", "aclk_isp_wrap", - "hclk_isp", "hclk_isp_wrap"; - power-domains = <&power RK3399_PD_ISP0>; + <&cru ACLK_ISP0_WRAPPER>, + <&cru HCLK_ISP0_WRAPPER>; + clock-names = "isp", "aclk", "hclk"; iommus = <&isp0_mmu>; phys = <&dphy>; phy-names = "dphy"; + power-domains = <&power RK3399_PD_ISP0>; ports { #address-cells = <1>; #size-cells = <0>; port@0 { + reg = <0>; #address-cells = <1>; #size-cells = <0>; - reg = <0>; mipi_in_wcam: endpoint@0 { reg = <0>; @@ -160,8 +184,7 @@ examples: }; }; - i2c7: i2c@ff160000 { - clock-frequency = <400000>; + i2c7: i2c { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml index 3fe778cb5cc3..c18574bb3e81 100644 --- a/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml +++ b/Documentation/devicetree/bindings/media/st,stm32-dcmi.yaml @@ -44,6 +44,43 @@ properties: bindings defined in Documentation/devicetree/bindings/media/video-interfaces.txt. + properties: + endpoint: + type: object + + properties: + bus-type: + enum: [5, 6] + default: 5 + + bus-width: + enum: [8, 10, 12, 14] + default: 8 + + remote-endpoint: true + + allOf: + - if: + properties: + bus-type: + const: 6 + + then: + properties: + hsync-active: false + vsync-active: false + bus-width: + enum: [8] + + required: + - remote-endpoint + - bus-type + - pclk-sample + + unevaluatedProperties: false + + additionalProperties: false + required: - compatible - reg @@ -75,6 +112,7 @@ examples: port { dcmi_0: endpoint { remote-endpoint = <&ov5640_0>; + bus-type = <5>; bus-width = <8>; hsync-active = <0>; vsync-active = <0>; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index e40ee369f808..772b148795f4 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -683,6 +683,8 @@ patternProperties: description: MiraMEMS Sensing Technology Co., Ltd. "^mitsubishi,.*": description: Mitsubishi Electric Corporation + "^modtronix,.*": + description: Modtronix Engineering "^mosaixtech,.*": description: Mosaix Technologies, Inc. "^motorola,.*": diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst index 4d1ae12b9b4d..ffb0cad8137a 100644 --- a/Documentation/driver-api/media/camera-sensor.rst +++ b/Documentation/driver-api/media/camera-sensor.rst @@ -132,3 +132,16 @@ used to obtain device's power state after the power state transition: The function returns a non-zero value if it succeeded getting the power count or runtime PM was disabled, in either of which cases the driver may proceed to access the device. + +Controls +-------- + +For camera sensors that are connected to a bus where transmitter and receiver +require common configuration set by drivers, such as CSI-2 or parallel (BT.601 +or BT.656) bus, the ``V4L2_CID_LINK_FREQ`` control is mandatory on transmitter +drivers. Receiver drivers can use the ``V4L2_CID_LINK_FREQ`` to query the +frequency used on the bus. + +The transmitter drivers should also implement ``V4L2_CID_PIXEL_RATE`` control in +order to tell the maximum pixel rate to the receiver. This is required on raw +camera sensors. diff --git a/Documentation/driver-api/media/cec-core.rst b/Documentation/driver-api/media/cec-core.rst index bc42982ac21e..a26dc87eee8f 100644 --- a/Documentation/driver-api/media/cec-core.rst +++ b/Documentation/driver-api/media/cec-core.rst @@ -143,7 +143,7 @@ To enable/disable the 'monitor all' mode:: int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable); If enabled, then the adapter should be put in a mode to also monitor messages -that not for us. Not all hardware supports this and this function is only +that are not for us. Not all hardware supports this and this function is only called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional (some hardware may always be in 'monitor all' mode). @@ -335,7 +335,7 @@ So this must work: $ cat einj.txt >error-inj The first callback is called when this file is read and it should show the -the current error injection state:: +current error injection state:: int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf); diff --git a/Documentation/driver-api/media/csi2.rst b/Documentation/driver-api/media/csi2.rst index e1b838014906..e3bbc6baf0f0 100644 --- a/Documentation/driver-api/media/csi2.rst +++ b/Documentation/driver-api/media/csi2.rst @@ -28,10 +28,9 @@ interface elements must be present on the sub-device represents the CSI-2 transmitter. The V4L2_CID_LINK_FREQ control is used to tell the receiver driver the -frequency (and not the symbol rate) of the link. The -V4L2_CID_PIXEL_RATE is may be used by the receiver to obtain the pixel -rate the transmitter uses. The -:c:type:`v4l2_subdev_video_ops`->s_stream() callback provides an +frequency (and not the symbol rate) of the link. The V4L2_CID_PIXEL_RATE +control may be used by the receiver to obtain the pixel rate the transmitter +uses. The :c:type:`v4l2_subdev_video_ops`->s_stream() callback provides an ability to start and stop the stream. The value of the V4L2_CID_PIXEL_RATE is calculated as follows:: diff --git a/Documentation/driver-api/media/drivers/ccs/ccs-regs.asc b/Documentation/driver-api/media/drivers/ccs/ccs-regs.asc new file mode 100644 index 000000000000..f2042acc8a45 --- /dev/null +++ b/Documentation/driver-api/media/drivers/ccs/ccs-regs.asc @@ -0,0 +1,1041 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause +# Copyright (C) 2019--2020 Intel Corporation + +# register rflags +# - f field LSB MSB rflags +# - e enum value # after a field +# - e enum value [LSB MSB] +# - b bool bit +# - l arg name min max elsize [discontig...] +# +# rflags +# 8, 16, 32 register bits (default is 8) +# v1.1 defined in version 1.1 +# f formula +# float_ireal iReal or IEEE 754; 32 bits +# ireal unsigned iReal + +# general status registers +module_model_id 0x0000 16 +module_revision_number_major 0x0002 8 +frame_count 0x0005 8 +pixel_order 0x0006 8 +- e GRBG 0 +- e RGGB 1 +- e BGGR 2 +- e GBRG 3 +MIPI_CCS_version 0x0007 8 +- e v1_0 0x10 +- e v1_1 0x11 +- f major 4 7 +- f minor 0 3 +data_pedestal 0x0008 16 +module_manufacturer_id 0x000e 16 +module_revision_number_minor 0x0010 8 +module_date_year 0x0012 8 +module_date_month 0x0013 8 +module_date_day 0x0014 8 +module_date_phase 0x0015 8 +- f 0 2 +- e ts 0 +- e es 1 +- e cs 2 +- e mp 3 +sensor_model_id 0x0016 16 +sensor_revision_number 0x0018 8 +sensor_firmware_version 0x001a 8 +serial_number 0x001c 32 +sensor_manufacturer_id 0x0020 16 +sensor_revision_number_16 0x0022 16 + +# frame format description registers +frame_format_model_type 0x0040 8 +- e 2-byte 1 +- e 4-byte 2 +frame_format_model_subtype 0x0041 8 +- f rows 0 3 +- f columns 4 7 +frame_format_descriptor(n) 0x0042 16 f +- l n 0 14 2 +- f pixels 0 11 +- f pcode 12 15 +- e embedded 1 +- e dummy_pixel 2 +- e black_pixel 3 +- e dark_pixel 4 +- e visible_pixel 5 +- e manuf_specific_0 8 +- e manuf_specific_1 9 +- e manuf_specific_2 10 +- e manuf_specific_3 11 +- e manuf_specific_4 12 +- e manuf_specific_5 13 +- e manuf_specific_6 14 +frame_format_descriptor_4(n) 0x0060 32 f +- l n 0 7 4 +- f pixels 0 15 +- f pcode 28 31 +- e embedded 1 +- e dummy_pixel 2 +- e black_pixel 3 +- e dark_pixel 4 +- e visible_pixel 5 +- e manuf_specific_0 8 +- e manuf_specific_1 9 +- e manuf_specific_2 10 +- e manuf_specific_3 11 +- e manuf_specific_4 12 +- e manuf_specific_5 13 +- e manuf_specific_6 14 + +# analog gain description registers +analog_gain_capability 0x0080 16 +- e global 0 +- e alternate_global 2 +analog_gain_code_min 0x0084 16 +analog_gain_code_max 0x0086 16 +analog_gain_code_step 0x0088 16 +analog_gain_type 0x008a 16 +analog_gain_m0 0x008c 16 +analog_gain_c0 0x008e 16 +analog_gain_m1 0x0090 16 +analog_gain_c1 0x0092 16 +analog_linear_gain_min 0x0094 16 v1.1 +analog_linear_gain_max 0x0096 16 v1.1 +analog_linear_gain_step_size 0x0098 16 v1.1 +analog_exponential_gain_min 0x009a 16 v1.1 +analog_exponential_gain_max 0x009c 16 v1.1 +analog_exponential_gain_step_size 0x009e 16 v1.1 + +# data format description registers +data_format_model_type 0x00c0 8 +- e normal 1 +- e extended 2 +data_format_model_subtype 0x00c1 8 +- f rows 0 3 +- f columns 4 7 +data_format_descriptor(n) 0x00c2 16 f +- l n 0 15 2 +- f compressed 0 7 +- f uncompressed 8 15 + +# general set-up registers +mode_select 0x0100 8 +- e software_standby 0 +- e streaming 1 +image_orientation 0x0101 8 +- b horizontal_mirror 0 +- b vertical_flip 1 +software_reset 0x0103 8 +- e off 0 +- e on 1 +grouped_parameter_hold 0x0104 8 +mask_corrupted_frames 0x0105 8 +- e allow 0 +- e mask 1 +fast_standby_ctrl 0x0106 8 +- e complete_frames 0 +- e frame_truncation 1 +CCI_address_ctrl 0x0107 8 +2nd_CCI_if_ctrl 0x0108 8 +- b enable 0 +- b ack 1 +2nd_CCI_address_ctrl 0x0109 8 +CSI_channel_identifier 0x0110 8 +CSI_signaling_mode 0x0111 8 +- e csi_2_dphy 2 +- e csi_2_cphy 3 +CSI_data_format 0x0112 16 +CSI_lane_mode 0x0114 8 +DPCM_Frame_DT 0x011d 8 +Bottom_embedded_data_DT 0x011e 8 +Bottom_embedded_data_VC 0x011f 8 + +gain_mode 0x0120 8 +- e global 0 +- e alternate 1 +ADC_bit_depth 0x0121 8 +emb_data_ctrl 0x0122 v1.1 +- b raw8_packing_for_raw16 0 +- b raw10_packing_for_raw20 1 +- b raw12_packing_for_raw24 2 + +GPIO_TRIG_mode 0x0130 8 +extclk_frequency_mhz 0x0136 16 ireal +temp_sensor_ctrl 0x0138 8 +- b enable 0 +temp_sensor_mode 0x0139 8 +temp_sensor_output 0x013a 8 + +# integration time registers +fine_integration_time 0x0200 16 +coarse_integration_time 0x0202 16 + +# analog gain registers +analog_gain_code_global 0x0204 16 +analog_linear_gain_global 0x0206 16 v1.1 +analog_exponential_gain_global 0x0208 16 v1.1 + +# digital gain registers +digital_gain_global 0x020e 16 + +# hdr control registers +Short_analog_gain_global 0x0216 16 +Short_digital_gain_global 0x0218 16 + +HDR_mode 0x0220 8 +- b enabled 0 +- b separate_analog_gain 1 +- b upscaling 2 +- b reset_sync 3 +- b timing_mode 4 +- b exposure_ctrl_direct 5 +- b separate_digital_gain 6 +HDR_resolution_reduction 0x0221 8 +- f row 0 3 +- f column 4 7 +Exposure_ratio 0x0222 8 +HDR_internal_bit_depth 0x0223 8 +Direct_short_integration_time 0x0224 16 +Short_analog_linear_gain_global 0x0226 16 v1.1 +Short_analog_exponential_gain_global 0x0228 16 v1.1 + +# clock set-up registers +vt_pix_clk_div 0x0300 16 +vt_sys_clk_div 0x0302 16 +pre_pll_clk_div 0x0304 16 +#vt_pre_pll_clk_div 0x0304 16 +pll_multiplier 0x0306 16 +#vt_pll_multiplier 0x0306 16 +op_pix_clk_div 0x0308 16 +op_sys_clk_div 0x030a 16 +op_pre_pll_clk_div 0x030c 16 +op_pll_multiplier 0x031e 16 +pll_mode 0x0310 8 +- f 0 0 +- e single 0 +- e dual 1 +op_pix_clk_div_rev 0x0312 16 v1.1 +op_sys_clk_div_rev 0x0314 16 v1.1 + +# frame timing registers +frame_length_lines 0x0340 16 +line_length_pck 0x0342 16 + +# image size registers +x_addr_start 0x0344 16 +y_addr_start 0x0346 16 +x_addr_end 0x0348 16 +y_addr_end 0x034a 16 +x_output_size 0x034c 16 +y_output_size 0x034e 16 + +# timing mode registers +Frame_length_ctrl 0x0350 8 +- b automatic 0 +Timing_mode_ctrl 0x0352 8 +- b manual_readout 0 +- b delayed_exposure 1 +Start_readout_rs 0x0353 8 +- b manual_readout_start 0 +Frame_margin 0x0354 16 + +# sub-sampling registers +x_even_inc 0x0380 16 +x_odd_inc 0x0382 16 +y_even_inc 0x0384 16 +y_odd_inc 0x0386 16 + +# monochrome readout registers +monochrome_en 0x0390 v1.1 +- e enabled 0 + +# image scaling registers +Scaling_mode 0x0400 16 +- e no_scaling 0 +- e horizontal 1 +scale_m 0x0404 16 +scale_n 0x0406 16 +digital_crop_x_offset 0x0408 16 +digital_crop_y_offset 0x040a 16 +digital_crop_image_width 0x040c 16 +digital_crop_image_height 0x040e 16 + +# image compression registers +compression_mode 0x0500 16 +- e none 0 +- e dpcm_pcm_simple 1 + +# test pattern registers +test_pattern_mode 0x0600 16 +- e none 0 +- e solid_color 1 +- e color_bars 2 +- e fade_to_grey 3 +- e pn9 4 +- e color_tile 5 +test_data_red 0x0602 16 +test_data_greenR 0x0604 16 +test_data_blue 0x0606 16 +test_data_greenB 0x0608 16 +value_step_size_smooth 0x060a 8 +value_step_size_quantised 0x060b 8 + +# phy configuration registers +tclk_post 0x0800 8 +ths_prepare 0x0801 8 +ths_zero_min 0x0802 8 +ths_trail 0x0803 8 +tclk_trail_min 0x0804 8 +tclk_prepare 0x0805 8 +tclk_zero 0x0806 8 +tlpx 0x0807 8 +phy_ctrl 0x0808 8 +- e auto 0 +- e UI 1 +- e manual 2 +tclk_post_ex 0x080a 16 +ths_prepare_ex 0x080c 16 +ths_zero_min_ex 0x080e 16 +ths_trail_ex 0x0810 16 +tclk_trail_min_ex 0x0812 16 +tclk_prepare_ex 0x0814 16 +tclk_zero_ex 0x0816 16 +tlpx_ex 0x0818 16 + +# link rate register +requested_link_rate 0x0820 32 u16.16 + +# equalization control registers +DPHY_equalization_mode 0x0824 8 v1.1 +- b eq2 0 +PHY_equalization_ctrl 0x0825 8 v1.1 +- b enable 0 + +# d-phy preamble control registers +DPHY_preamble_ctrl 0x0826 8 v1.1 +- b enable 0 +DPHY_preamble_length 0x0826 8 v1.1 + +# d-phy spread spectrum control registers +PHY_SSC_ctrl 0x0828 8 v1.1 +- b enable 0 + +# manual lp control register +manual_LP_ctrl 0x0829 8 v1.1 +- b enable 0 + +# additional phy configuration registers +twakeup 0x082a v1.1 +tinit 0x082b v1.1 +ths_exit 0x082c v1.1 +ths_exit_ex 0x082e 16 v1.1 + +# phy calibration configuration registers +PHY_periodic_calibration_ctrl 0x0830 8 +- b frame_blanking 0 +PHY_periodic_calibration_interval 0x0831 8 +PHY_init_calibration_ctrl 0x0832 8 +- b stream_start 0 +DPHY_calibration_mode 0x0833 8 v1.1 +- b also_alternate 0 +CPHY_calibration_mode 0x0834 8 v1.1 +- e format_1 0 +- e format_2 1 +- e format_3 2 +t3_calpreamble_length 0x0835 8 v1.1 +t3_calpreamble_length_per 0x0836 8 v1.1 +t3_calaltseq_length 0x0837 8 v1.1 +t3_calaltseq_length_per 0x0838 8 v1.1 +FM2_init_seed 0x083a 16 v1.1 +t3_caludefseq_length 0x083c 16 v1.1 +t3_caludefseq_length_per 0x083e 16 v1.1 + +# c-phy manual control registers +TGR_Preamble_Length 0x0841 8 +- b preamable_prog_seq 7 +- f begin_preamble_length 0 5 +TGR_Post_Length 0x0842 8 +- f post_length 0 4 +TGR_Preamble_Prog_Sequence(n2) 0x0843 +- l n2 0 6 1 +- f symbol_n_1 3 5 +- f symbol_n 0 2 +t3_prepare 0x084e 16 +t3_lpx 0x0850 16 + +# alps control register +ALPS_ctrl 0x085a 8 +- b lvlp_dphy 0 +- b lvlp_cphy 1 +- b alp_cphy 2 + +# lrte control registers +TX_REG_CSI_EPD_EN_SSP_cphy 0x0860 16 +TX_REG_CSI_EPD_OP_SLP_cphy 0x0862 16 +TX_REG_CSI_EPD_EN_SSP_dphy 0x0864 16 +TX_REG_CSI_EPD_OP_SLP_dphy 0x0866 16 +TX_REG_CSI_EPD_MISC_OPTION_cphy 0x0868 v1.1 +TX_REG_CSI_EPD_MISC_OPTION_dphy 0x0869 v1.1 + +# scrambling control registers +Scrambling_ctrl 0x0870 +- b enabled 0 +- f 2 3 +- e 1_seed_cphy 0 +- e 4_seed_cphy 3 +lane_seed_value(seed, lane) 0x0872 16 +- l seed 0 3 0x10 +- l lane 0 7 0x2 + +# usl control registers +TX_USL_REV_ENTRY 0x08c0 16 v1.1 +TX_USL_REV_Clock_Counter 0x08c2 16 v1.1 +TX_USL_REV_LP_Counter 0x08c4 16 v1.1 +TX_USL_REV_Frame_Counter 0x08c6 16 v1.1 +TX_USL_REV_Chronological_Timer 0x08c8 16 v1.1 +TX_USL_FWD_ENTRY 0x08ca 16 v1.1 +TX_USL_GPIO 0x08cc 16 v1.1 +TX_USL_Operation 0x08ce 16 v1.1 +- b reset 0 +TX_USL_ALP_ctrl 0x08d0 16 v1.1 +- b clock_pause 0 +TX_USL_APP_BTA_ACK_TIMEOUT 0x08d2 16 v1.1 +TX_USL_SNS_BTA_ACK_TIMEOUT 0x08d2 16 v1.1 +USL_Clock_Mode_d_ctrl 0x08d2 v1.1 +- b cont_clock_standby 0 +- b cont_clock_vblank 1 +- b cont_clock_hblank 2 + +# binning configuration registers +binning_mode 0x0900 8 +binning_type 0x0901 8 +binning_weighting 0x0902 8 + +# data transfer interface registers +data_transfer_if_1_ctrl 0x0a00 8 +- b enable 0 +- b write 1 +- b clear_error 2 +data_transfer_if_1_status 0x0a01 8 +- b read_if_ready 0 +- b write_if_ready 1 +- b data_corrupted 2 +- b improper_if_usage 3 +data_transfer_if_1_page_select 0x0a02 8 +data_transfer_if_1_data(p) 0x0a04 8 f +- l p 0 63 1 + +# image processing and sensor correction configuration registers +shading_correction_en 0x0b00 8 +- b enable 0 +luminance_correction_level 0x0b01 8 +green_imbalance_filter_en 0x0b02 8 +- b enable 0 +mapped_defect_correct_en 0x0b05 8 +- b enable 0 +single_defect_correct_en 0x0b06 8 +- b enable 0 +dynamic_couplet_correct_en 0x0b08 8 +- b enable 0 +combined_defect_correct_en 0x0b0a 8 +- b enable 0 +module_specific_correction_en 0x0b0c 8 +- b enable 0 +dynamic_triplet_defect_correct_en 0x0b13 8 +- b enable 0 +NF_ctrl 0x0b15 8 +- b luma 0 +- b chroma 1 +- b combined 2 + +# optical black pixel readout registers +OB_readout_control 0x0b30 8 +- b enable 0 +- b interleaving 1 +OB_virtual_channel 0x0b31 8 +OB_DT 0x0b32 8 +OB_data_format 0x0b33 8 + +# color temperature feedback registers +color_temperature 0x0b8c 16 +absolute_gain_greenr 0x0b8e 16 +absolute_gain_red 0x0b90 16 +absolute_gain_blue 0x0b92 16 +absolute_gain_greenb 0x0b94 16 + +# cfa conversion registers +CFA_conversion_ctrl 0x0ba0 v1.1 +- b bayer_conversion_enable 0 + +# flash strobe and sa strobe control registers +flash_strobe_adjustment 0x0c12 8 +flash_strobe_start_point 0x0c14 16 +tflash_strobe_delay_rs_ctrl 0x0c16 16 +tflash_strobe_width_high_rs_ctrl 0x0c18 16 +flash_mode_rs 0x0c1a 8 +- b continuous 0 +- b truncate 1 +- b async 3 +flash_trigger_rs 0x0c1b 8 +flash_status 0x0c1c 8 +- b retimed 0 +sa_strobe_mode 0x0c1d 8 +- b continuous 0 +- b truncate 1 +- b async 3 +- b adjust_edge 4 +sa_strobe_start_point 0x0c1e 16 +tsa_strobe_delay_ctrl 0x0c20 16 +tsa_strobe_width_ctrl 0x0c22 16 +sa_strobe_trigger 0x0c24 8 +sa_strobe_status 0x0c25 8 +- b retimed 0 +tSA_strobe_re_delay_ctrl 0x0c30 16 +tSA_strobe_fe_delay_ctrl 0x0c32 16 + +# pdaf control registers +PDAF_ctrl 0x0d00 16 +- b enable 0 +- b processed 1 +- b interleaved 2 +- b visible_pdaf_correction 3 +PDAF_VC 0x0d02 8 +PDAF_DT 0x0d03 8 +pd_x_addr_start 0x0d04 16 +pd_y_addr_start 0x0d06 16 +pd_x_addr_end 0x0d08 16 +pd_y_addr_end 0x0d0a 16 + +# bracketing interface configuration registers +bracketing_LUT_ctrl 0x0e00 8 +bracketing_LUT_mode 0x0e01 8 +- b continue_streaming 0 +- b loop_mode 1 +bracketing_LUT_entry_ctrl 0x0e02 8 +bracketing_LUT_frame(n) 0x0e10 v1.1 f +- l n 0 0xef 1 + +# integration time and gain parameter limit registers +integration_time_capability 0x1000 16 +- b fine 0 +coarse_integration_time_min 0x1004 16 +coarse_integration_time_max_margin 0x1006 16 +fine_integration_time_min 0x1008 16 +fine_integration_time_max_margin 0x100a 16 + +# digital gain parameter limit registers +digital_gain_capability 0x1081 +- e none 0 +- e global 2 +digital_gain_min 0x1084 16 +digital_gain_max 0x1086 16 +digital_gain_step_size 0x1088 16 + +# data pedestal capability registers +Pedestal_capability 0x10e0 8 v1.1 + +# adc capability registers +ADC_capability 0x10f0 8 +- b bit_depth_ctrl 0 +ADC_bit_depth_capability 0x10f4 32 v1.1 + +# video timing parameter limit registers +min_ext_clk_freq_mhz 0x1100 32 float_ireal +max_ext_clk_freq_mhz 0x1104 32 float_ireal +min_pre_pll_clk_div 0x1108 16 +# min_vt_pre_pll_clk_div 0x1108 16 +max_pre_pll_clk_div 0x110a 16 +# max_vt_pre_pll_clk_div 0x110a 16 +min_pll_ip_clk_freq_mhz 0x110c 32 float_ireal +# min_vt_pll_ip_clk_freq_mhz 0x110c 32 float_ireal +max_pll_ip_clk_freq_mhz 0x1110 32 float_ireal +# max_vt_pll_ip_clk_freq_mhz 0x1110 32 float_ireal +min_pll_multiplier 0x1114 16 +# min_vt_pll_multiplier 0x1114 16 +max_pll_multiplier 0x1116 16 +# max_vt_pll_multiplier 0x1116 16 +min_pll_op_clk_freq_mhz 0x1118 32 float_ireal +max_pll_op_clk_freq_mhz 0x111c 32 float_ireal + +# video timing set-up capability registers +min_vt_sys_clk_div 0x1120 16 +max_vt_sys_clk_div 0x1122 16 +min_vt_sys_clk_freq_mhz 0x1124 32 float_ireal +max_vt_sys_clk_freq_mhz 0x1128 32 float_ireal +min_vt_pix_clk_freq_mhz 0x112c 32 float_ireal +max_vt_pix_clk_freq_mhz 0x1130 32 float_ireal +min_vt_pix_clk_div 0x1134 16 +max_vt_pix_clk_div 0x1136 16 +clock_calculation 0x1138 +- b lane_speed 0 +- b link_decoupled 1 +- b dual_pll_op_sys_ddr 2 +- b dual_pll_op_pix_ddr 3 +num_of_vt_lanes 0x1139 +num_of_op_lanes 0x113a +op_bits_per_lane 0x113b 8 v1.1 + +# frame timing parameter limits +min_frame_length_lines 0x1140 16 +max_frame_length_lines 0x1142 16 +min_line_length_pck 0x1144 16 +max_line_length_pck 0x1146 16 +min_line_blanking_pck 0x1148 16 +min_frame_blanking_lines 0x114a 16 +min_line_length_pck_step_size 0x114c +timing_mode_capability 0x114d +- b auto_frame_length 0 +- b rolling_shutter_manual_readout 2 +- b delayed_exposure_start 3 +- b manual_exposure_embedded_data 4 +frame_margin_max_value 0x114e 16 +frame_margin_min_value 0x1150 +gain_delay_type 0x1151 +- e fixed 0 +- e variable 1 + +# output clock set-up capability registers +min_op_sys_clk_div 0x1160 16 +max_op_sys_clk_div 0x1162 16 +min_op_sys_clk_freq_mhz 0x1164 32 float_ireal +max_op_sys_clk_freq_mhz 0x1168 32 float_ireal +min_op_pix_clk_div 0x116c 16 +max_op_pix_clk_div 0x116e 16 +min_op_pix_clk_freq_mhz 0x1170 32 float_ireal +max_op_pix_clk_freq_mhz 0x1174 32 float_ireal + +# image size parameter limit registers +x_addr_min 0x1180 16 +y_addr_min 0x1182 16 +x_addr_max 0x1184 16 +y_addr_max 0x1186 16 +min_x_output_size 0x1188 16 +min_y_output_size 0x118a 16 +max_x_output_size 0x118c 16 +max_y_output_size 0x118e 16 + +x_addr_start_div_constant 0x1190 v1.1 +y_addr_start_div_constant 0x1191 v1.1 +x_addr_end_div_constant 0x1192 v1.1 +y_addr_end_div_constant 0x1193 v1.1 +x_size_div 0x1194 v1.1 +y_size_div 0x1195 v1.1 +x_output_div 0x1196 v1.1 +y_output_div 0x1197 v1.1 +non_flexible_resolution_support 0x1198 v1.1 +- b new_pix_addr 0 +- b new_output_res 1 +- b output_crop_no_pad 2 +- b output_size_lane_dep 3 + +min_op_pre_pll_clk_div 0x11a0 16 +max_op_pre_pll_clk_div 0x11a2 16 +min_op_pll_ip_clk_freq_mhz 0x11a4 32 float_ireal +max_op_pll_ip_clk_freq_mhz 0x11a8 32 float_ireal +min_op_pll_multiplier 0x11ac 16 +max_op_pll_multiplier 0x11ae 16 +min_op_pll_op_clk_freq_mhz 0x11b0 32 float_ireal +max_op_pll_op_clk_freq_mhz 0x11b4 32 float_ireal +clock_tree_pll_capability 0x11b8 8 +- b dual_pll 0 +- b single_pll 1 +- b ext_divider 2 +- b flexible_op_pix_clk_div 3 +clock_capa_type_capability 0x11b9 v1.1 +- b ireal 0 + +# sub-sampling parameters limit registers +min_even_inc 0x11c0 16 +min_odd_inc 0x11c2 16 +max_even_inc 0x11c4 16 +max_odd_inc 0x11c6 16 +aux_subsamp_capability 0x11c8 v1.1 +- b factor_power_of_2 1 +aux_subsamp_mono_capability 0x11c9 v1.1 +- b factor_power_of_2 1 +monochrome_capability 0x11ca v1.1 +- e inc_odd 0 +- e inc_even 1 +pixel_readout_capability 0x11cb v1.1 +- e bayer 0 +- e monochrome 1 +- e bayer_and_mono 2 +min_even_inc_mono 0x11cc 16 v1.1 +max_even_inc_mono 0x11ce 16 v1.1 +min_odd_inc_mono 0x11d0 16 v1.1 +max_odd_inc_mono 0x11d2 16 v1.1 +min_even_inc_bc2 0x11d4 16 v1.1 +max_even_inc_bc2 0x11d6 16 v1.1 +min_odd_inc_bc2 0x11d8 16 v1.1 +max_odd_inc_bc2 0x11da 16 v1.1 +min_even_inc_mono_bc2 0x11dc 16 v1.1 +max_even_inc_mono_bc2 0x11de 16 v1.1 +min_odd_inc_mono_bc2 0x11f0 16 v1.1 +max_odd_inc_mono_bc2 0x11f2 16 v1.1 + +# image scaling limit parameters +scaling_capability 0x1200 16 +- e none 0 +- e horizontal 1 +- e reserved 2 +scaler_m_min 0x1204 16 +scaler_m_max 0x1206 16 +scaler_n_min 0x1208 16 +scaler_n_max 0x120a 16 +digital_crop_capability 0x120e +- e none 0 +- e input_crop 1 + +# hdr limit registers +hdr_capability_1 0x1210 +- b 2x2_binning 0 +- b combined_analog_gain 1 +- b separate_analog_gain 2 +- b upscaling 3 +- b reset_sync 4 +- b direct_short_exp_timing 5 +- b direct_short_exp_synthesis 6 +min_hdr_bit_depth 0x1211 +hdr_resolution_sub_types 0x1212 +hdr_resolution_sub_type(n) 0x1213 +- l n 0 1 1 +- f row 0 3 +- f column 4 7 +hdr_capability_2 0x121b +- b combined_digital_gain 0 +- b separate_digital_gain 1 +- b timing_mode 3 +- b synthesis_mode 4 +max_hdr_bit_depth 0x121c + +# usl capability register +usl_support_capability 0x1230 v1.1 +- b clock_tree 0 +- b rev_clock_tree 1 +- b rev_clock_calc 2 +usl_clock_mode_d_capability 0x1231 v1.1 +- b cont_clock_standby 0 +- b cont_clock_vblank 1 +- b cont_clock_hblank 2 +- b noncont_clock_standby 3 +- b noncont_clock_vblank 4 +- b noncont_clock_hblank 5 +min_op_sys_clk_div_rev 0x1234 v1.1 +max_op_sys_clk_div_rev 0x1236 v1.1 +min_op_pix_clk_div_rev 0x1238 v1.1 +max_op_pix_clk_div_rev 0x123a v1.1 +min_op_sys_clk_freq_rev_mhz 0x123c 32 v1.1 float_ireal +max_op_sys_clk_freq_rev_mhz 0x1240 32 v1.1 float_ireal +min_op_pix_clk_freq_rev_mhz 0x1244 32 v1.1 float_ireal +max_op_pix_clk_freq_rev_mhz 0x1248 32 v1.1 float_ireal +max_bitrate_rev_d_mode_mbps 0x124c 32 v1.1 ireal +max_symrate_rev_c_mode_msps 0x1250 32 v1.1 ireal + +# image compression capability registers +compression_capability 0x1300 +- b dpcm_pcm_simple 0 + +# test mode capability registers +test_mode_capability 0x1310 16 +- b solid_color 0 +- b color_bars 1 +- b fade_to_grey 2 +- b pn9 3 +- b color_tile 5 +pn9_data_format1 0x1312 +pn9_data_format2 0x1313 +pn9_data_format3 0x1314 +pn9_data_format4 0x1315 +pn9_misc_capability 0x1316 +- f num_pixels 0 2 +- b compression 3 +test_pattern_capability 0x1317 v1.1 +- b no_repeat 1 +pattern_size_div_m1 0x1318 v1.1 + +# fifo capability registers +fifo_support_capability 0x1502 +- e none 0 +- e derating 1 +- e derating_overrating 2 + +# csi-2 capability registers +phy_ctrl_capability 0x1600 +- b auto_phy_ctl 0 +- b ui_phy_ctl 1 +- b dphy_time_ui_reg_1_ctl 2 +- b dphy_time_ui_reg_2_ctl 3 +- b dphy_time_ctl 4 +- b dphy_ext_time_ui_reg_1_ctl 5 +- b dphy_ext_time_ui_reg_2_ctl 6 +- b dphy_ext_time_ctl 7 +csi_dphy_lane_mode_capability 0x1601 +- b 1_lane 0 +- b 2_lane 1 +- b 3_lane 2 +- b 4_lane 3 +- b 5_lane 4 +- b 6_lane 5 +- b 7_lane 6 +- b 8_lane 7 +csi_signaling_mode_capability 0x1602 +- b csi_dphy 2 +- b csi_cphy 3 +fast_standby_capability 0x1603 +- e no_frame_truncation 0 +- e frame_truncation 1 +csi_address_control_capability 0x1604 +- b cci_addr_change 0 +- b 2nd_cci_addr 1 +- b sw_changeable_2nd_cci_addr 2 +data_type_capability 0x1605 +- b dpcm_programmable 0 +- b bottom_embedded_dt_programmable 1 +- b bottom_embedded_vc_programmable 2 +- b ext_vc_range 3 +csi_cphy_lane_mode_capability 0x1606 +- b 1_lane 0 +- b 2_lane 1 +- b 3_lane 2 +- b 4_lane 3 +- b 5_lane 4 +- b 6_lane 5 +- b 7_lane 6 +- b 8_lane 7 +emb_data_capability 0x1607 v1.1 +- b two_bytes_per_raw16 0 +- b two_bytes_per_raw20 1 +- b two_bytes_per_raw24 2 +- b no_one_byte_per_raw16 3 +- b no_one_byte_per_raw20 4 +- b no_one_byte_per_raw24 5 +max_per_lane_bitrate_lane_d_mode_mbps(n) 0x1608 32 ireal +- l n 0 7 4 4,0x32 +temp_sensor_capability 0x1618 +- b supported 0 +- b CCS_format 1 +- b reset_0x80 2 +max_per_lane_bitrate_lane_c_mode_mbps(n) 0x161a 32 ireal +- l n 0 7 4 4,0x30 +dphy_equalization_capability 0x162b +- b equalization_ctrl 0 +- b eq1 1 +- b eq2 2 +cphy_equalization_capability 0x162c +- b equalization_ctrl 0 +dphy_preamble_capability 0x162d +- b preamble_seq_ctrl 0 +dphy_ssc_capability 0x162e +- b supported 0 +cphy_calibration_capability 0x162f +- b manual 0 +- b manual_streaming 1 +- b format_1_ctrl 2 +- b format_2_ctrl 3 +- b format_3_ctrl 4 +dphy_calibration_capability 0x1630 +- b manual 0 +- b manual_streaming 1 +- b alternate_seq 2 +phy_ctrl_capability_2 0x1631 +- b tgr_length 0 +- b tgr_preamble_prog_seq 1 +- b extra_cphy_manual_timing 2 +- b clock_based_manual_cdphy 3 +- b clock_based_manual_dphy 4 +- b clock_based_manual_cphy 5 +- b manual_lp_dphy 6 +- b manual_lp_cphy 7 +lrte_cphy_capability 0x1632 +- b pdq_short 0 +- b spacer_short 1 +- b pdq_long 2 +- b spacer_long 3 +- b spacer_no_pdq 4 +lrte_dphy_capability 0x1633 +- b pdq_short_opt1 0 +- b spacer_short_opt1 1 +- b pdq_long_opt1 2 +- b spacer_long_opt1 3 +- b spacer_short_opt2 4 +- b spacer_long_opt2 5 +- b spacer_no_pdq_opt1 6 +- b spacer_variable_opt2 7 +alps_capability_dphy 0x1634 +- e lvlp_not_supported 0 0x3 +- e lvlp_supported 1 0x3 +- e controllable_lvlp 2 0x3 +alps_capability_cphy 0x1635 +- e lvlp_not_supported 0 0x3 +- e lvlp_supported 1 0x3 +- e controllable_lvlp 2 0x3 +- e alp_not_supported 0xc 0xc +- e alp_supported 0xd 0xc +- e controllable_alp 0xe 0xc +scrambling_capability 0x1636 +- b scrambling_supported 0 +- f max_seeds_per_lane_c 1 2 +- e 1 0 +- e 4 3 +- f num_seed_regs 3 5 +- e 0 0 +- e 1 1 +- e 4 4 +- b num_seed_per_lane 6 +dphy_manual_constant 0x1637 +cphy_manual_constant 0x1638 +CSI2_interface_capability_misc 0x1639 v1.1 +- b eotp_short_pkt_opt2 0 +PHY_ctrl_capability_3 0x165c v1.1 +- b dphy_timing_not_multiple 0 +- b dphy_min_timing_value_1 1 +- b twakeup_supported 2 +- b tinit_supported 3 +- b ths_exit_supported 4 +- b cphy_timing_not_multiple 5 +- b cphy_min_timing_value_1 6 +dphy_sf 0x165d v1.1 +cphy_sf 0x165e v1.1 +- f twakeup 0 3 +- f tinit 4 7 +dphy_limits_1 0x165f v1.1 +- f ths_prepare 0 3 +- f ths_zero 4 7 +dphy_limits_2 0x1660 v1.1 +- f ths_trail 0 3 +- f tclk_trail_min 4 7 +dphy_limits_3 0x1661 v1.1 +- f tclk_prepare 0 3 +- f tclk_zero 4 7 +dphy_limits_4 0x1662 v1.1 +- f tclk_post 0 3 +- f tlpx 4 7 +dphy_limits_5 0x1663 v1.1 +- f ths_exit 0 3 +- f twakeup 4 7 +dphy_limits_6 0x1664 v1.1 +- f tinit 0 3 +cphy_limits_1 0x1665 v1.1 +- f t3_prepare_max 0 3 +- f t3_lpx_max 4 7 +cphy_limits_2 0x1666 v1.1 +- f ths_exit_max 0 3 +- f twakeup_max 4 7 +cphy_limits_3 0x1667 v1.1 +- f tinit_max 0 3 + +# binning capability registers +min_frame_length_lines_bin 0x1700 16 +max_frame_length_lines_bin 0x1702 16 +min_line_length_pck_bin 0x1704 16 +max_line_length_pck_bin 0x1706 16 +min_line_blanking_pck_bin 0x1708 16 +fine_integration_time_min_bin 0x170a 16 +fine_integration_time_max_margin_bin 0x170c 16 +binning_capability 0x1710 +- e unsupported 0 +- e binning_then_subsampling 1 +- e subsampling_then_binning 2 +binning_weighting_capability 0x1711 +- b averaged 0 +- b summed 1 +- b bayer_corrected 2 +- b module_specific_weight 3 +binning_sub_types 0x1712 +binning_sub_type(n) 0x1713 +- l n 0 63 1 +- f row 0 3 +- f column 4 7 +binning_weighting_mono_capability 0x1771 v1.1 +- b averaged 0 +- b summed 1 +- b bayer_corrected 2 +- b module_specific_weight 3 +binning_sub_types_mono 0x1772 v1.1 +binning_sub_type_mono(n) 0x1773 v1.1 f +- l n 0 63 1 + +# data transfer interface capability registers +data_transfer_if_capability 0x1800 +- b supported 0 +- b polling 2 + +# sensor correction capability registers +shading_correction_capability 0x1900 +- b color_shading 0 +- b luminance_correction 1 +green_imbalance_capability 0x1901 +- b supported 0 +module_specific_correction_capability 0x1903 +defect_correction_capability 0x1904 16 +- b mapped_defect 0 +- b dynamic_couplet 2 +- b dynamic_single 5 +- b combined_dynamic 8 +defect_correction_capability_2 0x1906 16 +- b dynamic_triplet 3 +nf_capability 0x1908 +- b luma 0 +- b chroma 1 +- b combined 2 + +# optical black readout capability registers +ob_readout_capability 0x1980 +- b controllable_readout 0 +- b visible_pixel_readout 1 +- b different_vc_readout 2 +- b different_dt_readout 3 +- b prog_data_format 4 + +# color feedback capability registers +color_feedback_capability 0x1987 +- b kelvin 0 +- b awb_gain 1 + +# cfa pattern capability registers +CFA_pattern_capability 0x1990 v1.1 +- e bayer 0 +- e monochrome 1 +- e 4x4_quad_bayer 2 +- e vendor_specific 3 +CFA_pattern_conversion_capability 0x1991 v1.1 +- b bayer 0 + +# timer capability registers +flash_mode_capability 0x1a02 +- b single_strobe 0 +sa_strobe_mode_capability 0x1a03 +- b fixed_width 0 +- b edge_ctrl 1 + +# soft reset capability registers +reset_max_delay 0x1a10 v1.1 +reset_min_time 0x1a11 v1.1 + +# pdaf capability registers +pdaf_capability_1 0x1b80 +- b supported 0 +- b processed_bottom_embedded 1 +- b processed_interleaved 2 +- b raw_bottom_embedded 3 +- b raw_interleaved 4 +- b visible_pdaf_correction 5 +- b vc_interleaving 6 +- b dt_interleaving 7 +pdaf_capability_2 0x1b81 +- b ROI 0 +- b after_digital_crop 1 +- b ctrl_retimed 2 + +# bracketing interface capability registers +bracketing_lut_capability_1 0x1c00 +- b coarse_integration 0 +- b global_analog_gain 1 +- b flash 4 +- b global_digital_gain 5 +- b alternate_global_analog_gain 6 +bracketing_lut_capability_2 0x1c01 +- b single_bracketing_mode 0 +- b looped_bracketing_mode 1 +bracketing_lut_size 0x1c02 diff --git a/Documentation/driver-api/media/drivers/ccs/ccs.rst b/Documentation/driver-api/media/drivers/ccs/ccs.rst new file mode 100644 index 000000000000..f49e971f2d92 --- /dev/null +++ b/Documentation/driver-api/media/drivers/ccs/ccs.rst @@ -0,0 +1,82 @@ +.. SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause + +.. include:: <isonum.txt> + +MIPI CCS camera sensor driver +============================= + +The MIPI CCS camera sensor driver is a generic driver for `MIPI CCS +<https://www.mipi.org/specifications/camera-command-set>`_ compliant +camera sensors. It exposes three sub-devices representing the pixel array, +the binner and the scaler. + +As the capabilities of individual devices vary, the driver exposes +interfaces based on the capabilities that exist in hardware. + +Pixel Array sub-device +---------------------- + +The pixel array sub-device represents the camera sensor's pixel matrix, as well +as analogue crop functionality present in many compliant devices. The analogue +crop is configured using the ``V4L2_SEL_TGT_CROP`` on the source pad (0) of the +entity. The size of the pixel matrix can be obtained by getting the +``V4L2_SEL_TGT_NATIVE_SIZE`` target. + +Binner +------ + +The binner sub-device represents the binning functionality on the sensor. For +that purpose, selection target ``V4L2_SEL_TGT_COMPOSE`` is supported on the +sink pad (0). + +Additionally, if a device has no scaler or digital crop functionality, the +source pad (1) expses another digital crop selection rectangle that can only +crop at the end of the lines and frames. + +Scaler +------ + +The scaler sub-device represents the digital crop and scaling functionality of +the sensor. The V4L2 selection target ``V4L2_SEL_TGT_CROP`` is used to +configure the digital crop on the sink pad (0) when digital crop is supported. +Scaling is configured using selection target ``V4L2_SEL_TGT_COMPOSE`` on the +sink pad (0) as well. + +Additionally, if the scaler sub-device exists, its source pad (1) exposes +another digital crop selection rectangle that can only crop at the end of the +lines and frames. + +Digital and analogue crop +------------------------- + +Digital crop functionality is referred to as cropping that effectively works by +dropping some data on the floor. Analogue crop, on the other hand, means that +the cropped information is never retrieved. In case of camera sensors, the +analogue data is never read from the pixel matrix that are outside the +configured selection rectangle that designates crop. The difference has an +effect in device timing and likely also in power consumption. + +Register definition generator +----------------------------- + +The ccs-regs.asc file contains MIPI CCS register definitions that are used +to produce C source code files for definitions that can be better used by +programs written in C language. As there are many dependencies between the +produced files, please do not modify them manually as it's error-prone and +in vain, but instead change the script producing them. + +Usage +~~~~~ + +Conventionally the script is called this way to update the CCS driver +definitions: + +.. code-block:: none + + $ Documentation/driver-api/media/drivers/ccs/mk-ccs-regs -k \ + -e drivers/media/i2c/ccs/ccs-regs.h \ + -L drivers/media/i2c/ccs/ccs-limits.h \ + -l drivers/media/i2c/ccs/ccs-limits.c \ + -c Documentation/driver-api/media/drivers/ccs/ccs-regs.asc + +**Copyright** |copy| 2020 Intel Corporation diff --git a/Documentation/driver-api/media/drivers/ccs/mk-ccs-regs b/Documentation/driver-api/media/drivers/ccs/mk-ccs-regs new file mode 100755 index 000000000000..6668deaf2f19 --- /dev/null +++ b/Documentation/driver-api/media/drivers/ccs/mk-ccs-regs @@ -0,0 +1,433 @@ +#!/usr/bin/perl -w +# SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause +# Copyright (C) 2019--2020 Intel Corporation + +use Getopt::Long qw(:config no_ignore_case); +use File::Basename; + +my $ccsregs = "ccs-regs.asc"; +my $header; +my $regarray; +my $limitc; +my $limith; +my $kernel; +my $help; + +GetOptions("ccsregs|c=s" => \$ccsregs, + "header|e=s" => \$header, + "regarray|r=s" => \$regarray, + "limitc|l=s" => \$limitc, + "limith|L=s" => \$limith, + "kernel|k" => \$kernel, + "help|h" => \$help) or die "can't parse options"; + +$help = 1 if ! defined $header || ! defined $limitc || ! defined $limith; + +if (defined $help) { + print <<EOH +$0 - Create CCS register definitions for C + +usage: $0 -c ccs-regs.asc -e header -r regarray -l limit-c -L limit-header [-k] + + -c ccs register file + -e header file name + -r register description array file name + -l limit and capability array file name + -L limit and capability header file name + -k generate files for kernel space consumption +EOH + ; + exit 0; +} + +my $lh_hdr = ! defined $kernel + ? '#include "ccs-os.h"' . "\n" + : "#include <linux/bits.h>\n#include <linux/types.h>\n"; +my $uint32_t = ! defined $kernel ? 'uint32_t' : 'u32'; +my $uint16_t = ! defined $kernel ? 'uint16_t' : 'u16'; + +open(my $R, "< $ccsregs") or die "can't open $ccsregs"; + +open(my $H, "> $header") or die "can't open $header"; +my $A; +if (defined $regarray) { + open($A, "> $regarray") or die "can't open $regarray"; +} +open(my $LC, "> $limitc") or die "can't open $limitc"; +open(my $LH, "> $limith") or die "can't open $limith"; + +my %this; + +sub is_limit_reg($) { + my $addr = hex $_[0]; + + return 0 if $addr < 0x40; # weed out status registers + return 0 if $addr >= 0x100 && $addr < 0xfff; # weed out configuration registers + + return 1; +} + +my $uc_header = basename uc $header; +$uc_header =~ s/[^A-Z0-9]/_/g; + +my $copyright = "/* Copyright (C) 2019--2020 Intel Corporation */\n"; +my $license = "SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause"; + +for my $fh ($A, $LC) { + print $fh "// $license\n$copyright\n" if defined $fh; +} + +for my $fh ($H, $LH) { + print $fh "/* $license */\n$copyright\n"; +} + +sub bit_def($) { + my $bit = shift @_; + + return "BIT($bit)" if defined $kernel; + return "(1U << $bit)" if $bit =~ /^[a-zA-Z0-9_]+$/; + return "(1U << ($bit))"; +} + +print $H <<EOF +#ifndef __${uc_header}__ +#define __${uc_header}__ + +EOF + ; + +print $H "#include <linux/bits.h>\n\n" if defined $kernel; + +print $H <<EOF +#define CCS_FL_BASE 16 +EOF + ; + +print $H "#define CCS_FL_16BIT " . bit_def("CCS_FL_BASE") . "\n"; +print $H "#define CCS_FL_32BIT " . bit_def("CCS_FL_BASE + 1") . "\n"; +print $H "#define CCS_FL_FLOAT_IREAL " . bit_def("CCS_FL_BASE + 2") . "\n"; +print $H "#define CCS_FL_IREAL " . bit_def("CCS_FL_BASE + 3") . "\n"; + +print $H <<EOF +#define CCS_R_ADDR(r) ((r) & 0xffff) + +EOF + ; + +print $A <<EOF +#include <stdint.h> +#include <stdio.h> +#include "ccs-extra.h" +#include "ccs-regs.h" + +EOF + if defined $A; + +my $uc_limith = basename uc $limith; +$uc_limith =~ s/[^A-Z0-9]/_/g; + +print $LH <<EOF +#ifndef __${uc_limith}__ +#define __${uc_limith}__ + +$lh_hdr +struct ccs_limit { + $uint32_t reg; + $uint16_t size; + $uint16_t flags; + const char *name; +}; + +EOF + ; +print $LH "#define CCS_L_FL_SAME_REG " . bit_def(0) . "\n\n"; + +print $LH <<EOF +extern const struct ccs_limit ccs_limits[]; + +EOF + ; + +print $LC <<EOF +#include "ccs-limits.h" +#include "ccs-regs.h" + +const struct ccs_limit ccs_limits[] = { +EOF + ; + +my $limitcount = 0; +my $argdescs; +my $reglist = "const struct ccs_reg_desc ccs_reg_desc[] = {\n"; + +sub name_split($$) { + my ($name, $addr) = @_; + my $args; + + $name =~ /([^\(]+?)(\(.*)/; + ($name, $args) = ($1, $2); + $args = [split /,\s*/, $args]; + foreach my $t (@$args) { + $t =~ s/[\(\)]//g; + $t =~ s/\//\\\//g; + } + + return ($name, $addr, $args); +} + +sub tabconv($) { + $_ = shift; + + my @l = split "\n", $_; + + map { + s/ {8,8}/\t/g; + s/\t\K +//; + } @l; + + return (join "\n", @l) . "\n"; +} + +sub elem_size(@) { + my @flags = @_; + + return 2 if grep /^16$/, @flags; + return 4 if grep /^32$/, @flags; + return 1; +} + +sub arr_size($) { + my $this = $_[0]; + my $size = $this->{elsize}; + my $h = $this->{argparams}; + + foreach my $arg (@{$this->{args}}) { + my $apref = $h->{$arg}; + + $size *= $apref->{max} - $apref->{min} + 1; + } + + return $size; +} + +sub print_args($$$) { + my ($this, $postfix, $is_same_reg) = @_; + my ($args, $argparams, $name) = + ($this->{args}, $this->{argparams}, $this->{name}); + my $varname = "ccs_reg_arg_" . (lc $name) . $postfix; + my @mins; + my @sorted_args = @{$this->{sorted_args}}; + my $lim_arg; + my $size = arr_size($this); + + $argdescs .= "static const struct ccs_reg_arg " . $varname . "[] = {\n"; + + foreach my $sorted_arg (@sorted_args) { + push @mins, $argparams->{$sorted_arg}->{min}; + } + + foreach my $sorted_arg (@sorted_args) { + my $h = $argparams->{$sorted_arg}; + + $argdescs .= "\t{ \"$sorted_arg\", $h->{min}, $h->{max}, $h->{elsize} },\n"; + + $lim_arg .= defined $lim_arg ? ", $h->{min}" : "$h->{min}"; + } + + $argdescs .= "};\n\n"; + + $reglist .= "\t{ CCS_R_" . (uc $name) . "(" . (join ",", (@mins)) . + "), $size, sizeof($varname) / sizeof(*$varname)," . + " \"" . (lc $name) . "\", $varname },\n"; + + print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . "($lim_arg), " . + $size . ", " . ($is_same_reg ? "CCS_L_FL_SAME_REG" : "0") . + ", \"$name" . (defined $this->{discontig} ? " $lim_arg" : "") . "\" },\n" + if is_limit_reg $this->{base_addr}; +} + +my $hdr_data; + +while (<$R>) { + chop; + s/^\s*//; + next if /^[#;]/ || /^$/; + if (s/^-\s*//) { + if (s/^b\s*//) { + my ($bit, $addr) = split /\t+/; + $bit = uc $bit; + $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) ."_$bit", bit_def($addr) . "\n"; + } elsif (s/^f\s*//) { + s/[,\.-]/_/g; + my @a = split /\s+/; + my ($msb, $lsb, $this_field) = reverse @a; + @a = ( { "name" => "SHIFT", "addr" => $lsb, "fmt" => "%uU", }, + { "name" => "MASK", "addr" => (1 << ($msb + 1)) - 1 - ((1 << $lsb) - 1), "fmt" => "0x%" . join(".", ($this{"elsize"} >> 2) x 2) . "x" } ); + $this{"field"} = $this_field; + foreach my $ar (@a) { + #print $ar->{fmt}."\n"; + $hdr_data .= sprintf "#define %-62s " . $ar->{"fmt"} . "\n", "CCS_" . (uc $this{"name"}) . (defined $this_field ? "_" . uc $this_field : "") . "_" . $ar->{"name"}, $ar->{"addr"} . "\n"; + } + } elsif (s/^e\s*//) { + s/[,\.-]/_/g; + my ($enum, $addr) = split /\s+/; + $enum = uc $enum; + $hdr_data .= sprintf "#define %-62s %s", "CCS_" . (uc ${this{name}}) . (defined $this{"field"} ? "_" . uc $this{"field"} : "") ."_$enum", $addr . ($addr =~ /0x/i ? "" : "U") . "\n"; + } elsif (s/^l\s*//) { + my ($arg, $min, $max, $elsize, @discontig) = split /\s+/; + my $size; + + foreach my $num ($min, $max) { + $num = hex $num if $num =~ /0x/i; + } + + $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MIN_$arg"), $min . ($min =~ /0x/i ? "" : "U") . "\n"; + $hdr_data .= sprintf "#define %-62s %s", "CCS_LIM_" . (uc ${this{name}} . "_MAX_$arg"), $max . ($max =~ /0x/i ? "" : "U") . "\n"; + + my $h = $this{argparams}; + + $h->{$arg} = { "min" => $min, + "max" => $max, + "elsize" => $elsize =~ /^0x/ ? hex $elsize : $elsize, + "discontig" => \@discontig }; + + $this{discontig} = $arg if @discontig; + + next if $#{$this{args}} + 1 != scalar keys %{$this{argparams}}; + + my $reg_formula = "($this{addr}"; + my $lim_formula; + + foreach my $arg (@{$this{args}}) { + my $d = $h->{$arg}->{discontig}; + my $times = $h->{$arg}->{elsize} != 1 ? + " * " . $h->{$arg}->{elsize} : ""; + + if (@$d) { + my ($lim, $offset) = split /,/, $d->[0]; + + $reg_formula .= " + (($arg) < $lim ? ($arg)$times : $offset + (($arg) - $lim)$times)"; + } else { + $reg_formula .= " + ($arg)$times"; + } + + $lim_formula .= (defined $lim_formula ? " + " : "") . "($arg)$times"; + } + + $reg_formula .= ")\n"; + $lim_formula =~ s/^\(([a-z0-9]+)\)$/$1/i; + + print $H tabconv sprintf("#define %-62s %s", "CCS_R_" . (uc $this{name}) . + $this{arglist}, $reg_formula); + + print $H tabconv $hdr_data; + undef $hdr_data; + + # Sort arguments in descending order by size + @{$this{sorted_args}} = sort { + $h->{$a}->{elsize} <= $h->{$b}->{elsize} + } @{$this{args}}; + + if (defined $this{discontig}) { + my $da = $this{argparams}->{$this{discontig}}; + my ($first_discontig) = split /,/, $da->{discontig}->[0]; + my $max = $da->{max}; + + $da->{max} = $first_discontig - 1; + print_args(\%this, "", 0); + + $da->{min} = $da->{max} + 1; + $da->{max} = $max; + print_args(\%this, $first_discontig, 1); + } else { + print_args(\%this, "", 0); + } + + next unless is_limit_reg $this{base_addr}; + + print $LH tabconv sprintf "#define %-63s%s\n", + "CCS_L_" . (uc $this{name}) . "_OFFSET(" . + (join ", ", @{$this{args}}) . ")", "($lim_formula)"; + } + + if (! @{$this{args}}) { + print $H tabconv($hdr_data); + undef $hdr_data; + } + + next; + } + + my ($name, $addr, @flags) = split /\t+/, $_; + my $args = []; + + my $sp; + + ($name, $addr, $args) = name_split($name, $addr) if /\(.*\)/; + + $name =~ s/[,\.-]/_/g; + + my $flagstring = ""; + my $size = elem_size(@flags); + $flagstring .= "| CCS_FL_16BIT " if $size eq "2"; + $flagstring .= "| CCS_FL_32BIT " if $size eq "4"; + $flagstring .= "| CCS_FL_FLOAT_IREAL " if grep /^float_ireal$/, @flags; + $flagstring .= "| CCS_FL_IREAL " if grep /^ireal$/, @flags; + $flagstring =~ s/^\| //; + $flagstring =~ s/ $//; + $flagstring = "($flagstring)" if $flagstring =~ /\|/; + my $base_addr = $addr; + $addr = "($addr | $flagstring)" if $flagstring ne ""; + + my $arglist = @$args ? "(" . (join ", ", @$args) . ")" : ""; + $hdr_data .= sprintf "#define %-62s %s\n", "CCS_R_" . (uc $name), $addr + if !@$args; + + $name =~ s/\(.*//; + + %this = ( name => $name, + addr => $addr, + base_addr => $base_addr, + argparams => {}, + args => $args, + arglist => $arglist, + elsize => $size, + ); + + if (!@$args) { + $reglist .= "\t{ CCS_R_" . (uc $name) . ", 1, 0, \"" . (lc $name) . "\", NULL },\n"; + print $H tabconv $hdr_data; + undef $hdr_data; + + print $LC tabconv sprintf "\t{ CCS_R_" . (uc $name) . ", " . + $this{elsize} . ", 0, \"$name\" },\n" + if is_limit_reg $this{base_addr}; + } + + print $LH tabconv sprintf "#define %-63s%s\n", + "CCS_L_" . (uc $this{name}), $limitcount++ + if is_limit_reg $this{base_addr}; +} + +if (defined $A) { + print $A $argdescs, $reglist; + + print $A "\t{ 0 }\n"; + + print $A "};\n"; +} + +print $H "\n#endif /* __${uc_header}__ */\n"; + +print $LH tabconv sprintf "#define %-63s%s\n", "CCS_L_LAST", $limitcount; + +print $LH "\n#endif /* __${uc_limith}__ */\n"; + +print $LC "\t{ 0 } /* Guardian */\n"; +print $LC "};\n"; + +close($R); +close($H); +close($A) if defined $A; +close($LC); +close($LH); diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst index eb7011782863..426cda633bf0 100644 --- a/Documentation/driver-api/media/drivers/index.rst +++ b/Documentation/driver-api/media/drivers/index.rst @@ -26,6 +26,7 @@ Video4Linux (V4L) drivers tuners vimc-devel zoran + ccs/ccs Digital TV drivers diff --git a/Documentation/driver-api/media/dtv-frontend.rst b/Documentation/driver-api/media/dtv-frontend.rst index 91f77fe58e83..ea43cdb5b0e4 100644 --- a/Documentation/driver-api/media/dtv-frontend.rst +++ b/Documentation/driver-api/media/dtv-frontend.rst @@ -244,7 +244,7 @@ Carrier Signal to Noise ratio (:ref:`DTV-STAT-CNR`) Having it available after inner FEC is more common. Bit counts post-FEC (:ref:`DTV-STAT-POST-ERROR-BIT-COUNT` and :ref:`DTV-STAT-POST-TOTAL-BIT-COUNT`) - - Those counters measure the number of bits and bit errors errors after + - Those counters measure the number of bits and bit errors after the forward error correction (FEC) on the inner coding block (after Viterbi, LDPC or other inner code). @@ -253,7 +253,7 @@ Bit counts post-FEC (:ref:`DTV-STAT-POST-ERROR-BIT-COUNT` and :ref:`DTV-STAT-POS see :c:type:`fe_status`). Bit counts pre-FEC (:ref:`DTV-STAT-PRE-ERROR-BIT-COUNT` and :ref:`DTV-STAT-PRE-TOTAL-BIT-COUNT`) - - Those counters measure the number of bits and bit errors errors before + - Those counters measure the number of bits and bit errors before the forward error correction (FEC) on the inner coding block (before Viterbi, LDPC or other inner code). @@ -263,7 +263,7 @@ Bit counts pre-FEC (:ref:`DTV-STAT-PRE-ERROR-BIT-COUNT` and :ref:`DTV-STAT-PRE-T after ``FE_HAS_VITERBI``, see :c:type:`fe_status`). Block counts (:ref:`DTV-STAT-ERROR-BLOCK-COUNT` and :ref:`DTV-STAT-TOTAL-BLOCK-COUNT`) - - Those counters measure the number of blocks and block errors errors after + - Those counters measure the number of blocks and block errors after the forward error correction (FEC) on the inner coding block (before Viterbi, LDPC or other inner code). diff --git a/Documentation/driver-api/media/v4l2-controls.rst b/Documentation/driver-api/media/v4l2-controls.rst index 77f42ea3bac7..b2e91804829b 100644 --- a/Documentation/driver-api/media/v4l2-controls.rst +++ b/Documentation/driver-api/media/v4l2-controls.rst @@ -335,7 +335,7 @@ current and new values: union v4l2_ctrl_ptr p_new; union v4l2_ctrl_ptr p_cur; -If the control has a simple s32 type type, then: +If the control has a simple s32 type, then: .. code-block:: c @@ -349,7 +349,7 @@ Within the control ops you can freely use these. The val and cur.val speak for themselves. The p_char pointers point to character buffers of length ctrl->maximum + 1, and are always 0-terminated. -Unless the control is marked volatile the p_cur field points to the the +Unless the control is marked volatile the p_cur field points to the current cached control value. When you create a new control this value is made identical to the default value. After calling v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally a good idea to call this diff --git a/Documentation/driver-api/media/v4l2-dev.rst b/Documentation/driver-api/media/v4l2-dev.rst index 666330af31ed..99e3b5fa7444 100644 --- a/Documentation/driver-api/media/v4l2-dev.rst +++ b/Documentation/driver-api/media/v4l2-dev.rst @@ -212,7 +212,7 @@ types exist: ========================== ==================== ============================== The last argument gives you a certain amount of control over the device -device node number used (i.e. the X in ``videoX``). Normally you will pass -1 +node number used (i.e. the X in ``videoX``). Normally you will pass -1 to let the v4l2 framework pick the first free number. But sometimes users want to select a specific node number. It is common that drivers allow the user to select a specific device node number through a driver module diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 895e9711ed88..e0204a23e997 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -27,9 +27,9 @@ automatically verified against the file's Merkle tree. Reads of any corrupted data, including mmap reads, will fail. Userspace can use another ioctl to retrieve the root hash (actually -the "file measurement", which is a hash that includes the root hash) -that fs-verity is enforcing for the file. This ioctl executes in -constant time, regardless of the file size. +the "fs-verity file digest", which is a hash that includes the Merkle +tree root hash) that fs-verity is enforcing for the file. This ioctl +executes in constant time, regardless of the file size. fs-verity is essentially a way to hash a file in constant time, subject to the caveat that reads which would violate the hash will @@ -177,9 +177,10 @@ FS_IOC_ENABLE_VERITY can fail with the following errors: FS_IOC_MEASURE_VERITY --------------------- -The FS_IOC_MEASURE_VERITY ioctl retrieves the measurement of a verity -file. The file measurement is a digest that cryptographically -identifies the file contents that are being enforced on reads. +The FS_IOC_MEASURE_VERITY ioctl retrieves the digest of a verity file. +The fs-verity file digest is a cryptographic digest that identifies +the file contents that are being enforced on reads; it is computed via +a Merkle tree and is different from a traditional full-file digest. This ioctl takes in a pointer to a variable-length structure:: @@ -197,7 +198,7 @@ On success, 0 is returned and the kernel fills in the structure as follows: - ``digest_algorithm`` will be the hash algorithm used for the file - measurement. It will match ``fsverity_enable_arg::hash_algorithm``. + digest. It will match ``fsverity_enable_arg::hash_algorithm``. - ``digest_size`` will be the size of the digest in bytes, e.g. 32 for SHA-256. (This can be redundant with ``digest_algorithm``.) - ``digest`` will be the actual bytes of the digest. @@ -257,25 +258,24 @@ non-verity one, with the following exceptions: with EIO (for read()) or SIGBUS (for mmap() reads). - If the sysctl "fs.verity.require_signatures" is set to 1 and the - file's verity measurement is not signed by a key in the fs-verity - keyring, then opening the file will fail. See `Built-in signature - verification`_. + file is not signed by a key in the fs-verity keyring, then opening + the file will fail. See `Built-in signature verification`_. Direct access to the Merkle tree is not supported. Therefore, if a verity file is copied, or is backed up and restored, then it will lose its "verity"-ness. fs-verity is primarily meant for files like executables that are managed by a package manager. -File measurement computation -============================ +File digest computation +======================= This section describes how fs-verity hashes the file contents using a -Merkle tree to produce the "file measurement" which cryptographically -identifies the file contents. This algorithm is the same for all -filesystems that support fs-verity. +Merkle tree to produce the digest which cryptographically identifies +the file contents. This algorithm is the same for all filesystems +that support fs-verity. Userspace only needs to be aware of this algorithm if it needs to -compute the file measurement itself, e.g. in order to sign the file. +compute fs-verity file digests itself, e.g. in order to sign files. .. _fsverity_merkle_tree: @@ -325,26 +325,22 @@ can't a distinguish a large file from a small second file whose data is exactly the top-level hash block of the first file. Ambiguities also arise from the convention of padding to the next block boundary. -To solve this problem, the verity file measurement is actually -computed as a hash of the following structure, which contains the -Merkle tree root hash as well as other fields such as the file size:: +To solve this problem, the fs-verity file digest is actually computed +as a hash of the following structure, which contains the Merkle tree +root hash as well as other fields such as the file size:: struct fsverity_descriptor { __u8 version; /* must be 1 */ __u8 hash_algorithm; /* Merkle tree hash algorithm */ __u8 log_blocksize; /* log2 of size of data and tree blocks */ __u8 salt_size; /* size of salt in bytes; 0 if none */ - __le32 sig_size; /* must be 0 */ + __le32 __reserved_0x04; /* must be 0 */ __le64 data_size; /* size of file the Merkle tree is built over */ __u8 root_hash[64]; /* Merkle tree root hash */ __u8 salt[32]; /* salt prepended to each hashed block */ __u8 __reserved[144]; /* must be 0's */ }; -Note that the ``sig_size`` field must be set to 0 for the purpose of -computing the file measurement, even if a signature was provided (or -will be provided) to `FS_IOC_ENABLE_VERITY`_. - Built-in signature verification =============================== @@ -359,20 +355,20 @@ kernel. Specifically, it adds support for: certificates from being added. 2. `FS_IOC_ENABLE_VERITY`_ accepts a pointer to a PKCS#7 formatted - detached signature in DER format of the file measurement. On - success, this signature is persisted alongside the Merkle tree. + detached signature in DER format of the file's fs-verity digest. + On success, this signature is persisted alongside the Merkle tree. Then, any time the file is opened, the kernel will verify the - file's actual measurement against this signature, using the - certificates in the ".fs-verity" keyring. + file's actual digest against this signature, using the certificates + in the ".fs-verity" keyring. 3. A new sysctl "fs.verity.require_signatures" is made available. When set to 1, the kernel requires that all verity files have a - correctly signed file measurement as described in (2). + correctly signed digest as described in (2). -File measurements must be signed in the following format, which is -similar to the structure used by `FS_IOC_MEASURE_VERITY`_:: +fs-verity file digests must be signed in the following format, which +is similar to the structure used by `FS_IOC_MEASURE_VERITY`_:: - struct fsverity_signed_digest { + struct fsverity_formatted_digest { char magic[8]; /* must be "FSVerity" */ __le16 digest_algorithm; __le16 digest_size; @@ -421,8 +417,8 @@ can only be set by `FS_IOC_ENABLE_VERITY`_, and it cannot be cleared. ext4 also supports encryption, which can be used simultaneously with fs-verity. In this case, the plaintext data is verified rather than -the ciphertext. This is necessary in order to make the file -measurement meaningful, since every file is encrypted differently. +the ciphertext. This is necessary in order to make the fs-verity file +digest meaningful, since every file is encrypted differently. ext4 stores the verity metadata (Merkle tree and fsverity_descriptor) past the end of the file, starting at the first 64K boundary beyond @@ -592,8 +588,8 @@ weren't already directly answered in other parts of this document. :Q: Isn't fs-verity useless because the attacker can just modify the hashes in the Merkle tree, which is stored on-disk? :A: To verify the authenticity of an fs-verity file you must verify - the authenticity of the "file measurement", which is basically the - root hash of the Merkle tree. See `Use cases`_. + the authenticity of the "fs-verity file digest", which + incorporates the root hash of the Merkle tree. See `Use cases`_. :Q: Isn't fs-verity useless because the attacker can just replace a verity file with a non-verity one? diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 55a2d9b2ce33..a4c75a28c839 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -323,6 +323,7 @@ Code Seq# Include File Comments <mailto:tlewis@mindspring.com> 0xA3 90-9F linux/dtlk.h 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem +0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org> 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h diff --git a/Documentation/userspace-api/media/cec/cec-ioc-g-mode.rst b/Documentation/userspace-api/media/cec/cec-ioc-g-mode.rst index d3387b1fa7c5..663bdef8d6da 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-g-mode.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-g-mode.rst @@ -188,7 +188,7 @@ Available follower modes are: in combination with :ref:`CEC_MODE_NO_INITIATOR <CEC-MODE-NO-INITIATOR>`, otherwise the ``EINVAL`` error code will be returned. In 'monitor all' mode all messages this CEC device transmits and all messages it receives, including - directed messages for other CEC devices will be reported. This is + directed messages for other CEC devices, will be reported. This is very useful for debugging, but not all devices support this. This mode requires that the :ref:`CEC_CAP_MONITOR_ALL <CEC-CAP-MONITOR-ALL>` capability is set, otherwise the ``EINVAL`` error code is returned. This is only allowed if diff --git a/Documentation/userspace-api/media/dvb/audio.rst b/Documentation/userspace-api/media/dvb/audio.rst index 071abac9d52d..eaae5675a47d 100644 --- a/Documentation/userspace-api/media/dvb/audio.rst +++ b/Documentation/userspace-api/media/dvb/audio.rst @@ -8,7 +8,7 @@ Digital TV Audio Device The Digital TV audio device controls the MPEG2 audio decoder of the Digital TV hardware. It can be accessed through ``/dev/dvb/adapter?/audio?``. Data -types and and ioctl definitions can be accessed by including +types and ioctl definitions can be accessed by including ``linux/dvb/audio.h`` in your application. Please note that some Digital TV cards don’t have their own MPEG decoder, which diff --git a/Documentation/userspace-api/media/dvb/ca.rst b/Documentation/userspace-api/media/dvb/ca.rst index 6f6821e322a9..0dc00e5e4a8c 100644 --- a/Documentation/userspace-api/media/dvb/ca.rst +++ b/Documentation/userspace-api/media/dvb/ca.rst @@ -7,7 +7,7 @@ Digital TV CA Device #################### The Digital TV CA device controls the conditional access hardware. It -can be accessed through ``/dev/dvb/adapter?/ca?``. Data types and and ioctl +can be accessed through ``/dev/dvb/adapter?/ca?``. Data types and ioctl definitions can be accessed by including ``linux/dvb/ca.h`` in your application. diff --git a/Documentation/userspace-api/media/dvb/demux.rst b/Documentation/userspace-api/media/dvb/demux.rst index 364ef48472ee..ef05abcf14d1 100644 --- a/Documentation/userspace-api/media/dvb/demux.rst +++ b/Documentation/userspace-api/media/dvb/demux.rst @@ -11,7 +11,7 @@ digital TV. If the driver and hardware supports, those filters are implemented at the hardware. Otherwise, the Kernel provides a software emulation. -It can be accessed through ``/dev/adapter?/demux?``. Data types and and +It can be accessed through ``/dev/adapter?/demux?``. Data types and ioctl definitions can be accessed by including ``linux/dvb/dmx.h`` in your application. diff --git a/Documentation/userspace-api/media/dvb/dmx-qbuf.rst b/Documentation/userspace-api/media/dvb/dmx-qbuf.rst index 17e70143c1b0..1a13a33834db 100644 --- a/Documentation/userspace-api/media/dvb/dmx-qbuf.rst +++ b/Documentation/userspace-api/media/dvb/dmx-qbuf.rst @@ -50,7 +50,7 @@ by a :ref:`DMX_QUERYBUF` ioctl will do as well. When ``DMX_QBUF`` is called with a pointer to this structure, it locks the memory pages of the buffer in physical memory, so they cannot be swapped out to disk. Buffers remain locked until dequeued, until the -the device is closed. +device is closed. Applications call the ``DMX_DQBUF`` ioctl to dequeue a filled (capturing) buffer from the driver's outgoing queue. diff --git a/Documentation/userspace-api/media/dvb/net.rst b/Documentation/userspace-api/media/dvb/net.rst index 33368f5150c5..020a023d3b61 100644 --- a/Documentation/userspace-api/media/dvb/net.rst +++ b/Documentation/userspace-api/media/dvb/net.rst @@ -23,7 +23,7 @@ types that are present on the transport stream. This is done through virtual ``dvb?_?`` network interfaces, and will be controlled/routed via the standard ip tools (like ip, route, netstat, ifconfig, etc). -Data types and and ioctl definitions are defined via ``linux/dvb/net.h`` +Data types and ioctl definitions are defined via ``linux/dvb/net.h`` header. diff --git a/Documentation/userspace-api/media/dvb/video.rst b/Documentation/userspace-api/media/dvb/video.rst index 3ed1bbfb93c3..38a8d39a1d25 100644 --- a/Documentation/userspace-api/media/dvb/video.rst +++ b/Documentation/userspace-api/media/dvb/video.rst @@ -8,7 +8,7 @@ Digital TV Video Device The Digital TV video device controls the MPEG2 video decoder of the Digital TV hardware. It can be accessed through **/dev/dvb/adapter0/video0**. Data -types and and ioctl definitions can be accessed by including +types and ioctl definitions can be accessed by including **linux/dvb/video.h** in your application. Note that the Digital TV video device only controls decoding of the MPEG video diff --git a/Documentation/userspace-api/media/lirc.h.rst.exceptions b/Documentation/userspace-api/media/lirc.h.rst.exceptions index ac768d769113..e74b73cd0e9e 100644 --- a/Documentation/userspace-api/media/lirc.h.rst.exceptions +++ b/Documentation/userspace-api/media/lirc.h.rst.exceptions @@ -64,6 +64,7 @@ ignore symbol RC_PROTO_RCMM12 ignore symbol RC_PROTO_RCMM24 ignore symbol RC_PROTO_RCMM32 ignore symbol RC_PROTO_XBOX_DVD +ignore symbol RC_PROTO_MAX # Undocumented macros diff --git a/Documentation/userspace-api/media/rc/keytable.c.rst b/Documentation/userspace-api/media/rc/keytable.c.rst index 0b50cfaf2d86..243e02d2611f 100644 --- a/Documentation/userspace-api/media/rc/keytable.c.rst +++ b/Documentation/userspace-api/media/rc/keytable.c.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later file: uapi/v4l/keytable.c ========================= diff --git a/Documentation/userspace-api/media/rc/lirc-dev-intro.rst b/Documentation/userspace-api/media/rc/lirc-dev-intro.rst index 167b354bf051..c88973732282 100644 --- a/Documentation/userspace-api/media/rc/lirc-dev-intro.rst +++ b/Documentation/userspace-api/media/rc/lirc-dev-intro.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _lirc_dev_intro: @@ -57,12 +57,12 @@ on the following table. This mode is for both sending and receiving IR. - For transmitting (aka sending), create a ``struct lirc_scancode`` with + For transmitting (aka sending), create a struct lirc_scancode with the desired scancode set in the ``scancode`` member, :c:type:`rc_proto` set to the :ref:`IR protocol <Remote_controllers_Protocols>`, and all other members set to 0. Write this struct to the lirc device. - For receiving, you read ``struct lirc_scancode`` from the LIRC device. + For receiving, you read struct lirc_scancode from the LIRC device. The ``scancode`` field is set to the received scancode and the :ref:`IR protocol <Remote_controllers_Protocols>` is set in :c:type:`rc_proto`. If the scancode maps to a valid key code, this is set @@ -136,6 +136,13 @@ on the following table. This mode is used only for IR send. +************************************* +Data types used by LIRC_MODE_SCANCODE +************************************* + +.. kernel-doc:: include/uapi/linux/lirc.h + :identifiers: lirc_scancode rc_proto + ******************** BPF based IR decoder ******************** diff --git a/Documentation/userspace-api/media/rc/lirc-dev.rst b/Documentation/userspace-api/media/rc/lirc-dev.rst index 5510dc02a822..978f86d30fae 100644 --- a/Documentation/userspace-api/media/rc/lirc-dev.rst +++ b/Documentation/userspace-api/media/rc/lirc-dev.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _lirc_dev: diff --git a/Documentation/userspace-api/media/rc/lirc-func.rst b/Documentation/userspace-api/media/rc/lirc-func.rst index 420a3dbf0d6b..793f295d3ac9 100644 --- a/Documentation/userspace-api/media/rc/lirc-func.rst +++ b/Documentation/userspace-api/media/rc/lirc-func.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _lirc_func: diff --git a/Documentation/userspace-api/media/rc/lirc-get-features.rst b/Documentation/userspace-api/media/rc/lirc-get-features.rst index 66a243dbd437..4bf25860f932 100644 --- a/Documentation/userspace-api/media/rc/lirc-get-features.rst +++ b/Documentation/userspace-api/media/rc/lirc-get-features.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_get_features: diff --git a/Documentation/userspace-api/media/rc/lirc-get-rec-mode.rst b/Documentation/userspace-api/media/rc/lirc-get-rec-mode.rst index 188478ed1233..628fe31792b2 100644 --- a/Documentation/userspace-api/media/rc/lirc-get-rec-mode.rst +++ b/Documentation/userspace-api/media/rc/lirc-get-rec-mode.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_get_rec_mode: diff --git a/Documentation/userspace-api/media/rc/lirc-get-rec-resolution.rst b/Documentation/userspace-api/media/rc/lirc-get-rec-resolution.rst index e29445c5ce16..4dfa9c23634b 100644 --- a/Documentation/userspace-api/media/rc/lirc-get-rec-resolution.rst +++ b/Documentation/userspace-api/media/rc/lirc-get-rec-resolution.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_get_rec_resolution: diff --git a/Documentation/userspace-api/media/rc/lirc-get-send-mode.rst b/Documentation/userspace-api/media/rc/lirc-get-send-mode.rst index 77472fb5608a..637871805be6 100644 --- a/Documentation/userspace-api/media/rc/lirc-get-send-mode.rst +++ b/Documentation/userspace-api/media/rc/lirc-get-send-mode.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_get_send_mode: diff --git a/Documentation/userspace-api/media/rc/lirc-get-timeout.rst b/Documentation/userspace-api/media/rc/lirc-get-timeout.rst index f5f3e06d6206..597c3282ae12 100644 --- a/Documentation/userspace-api/media/rc/lirc-get-timeout.rst +++ b/Documentation/userspace-api/media/rc/lirc-get-timeout.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_get_min_timeout: diff --git a/Documentation/userspace-api/media/rc/lirc-header.rst b/Documentation/userspace-api/media/rc/lirc-header.rst index 8bd0acc9913a..54cb40b8a065 100644 --- a/Documentation/userspace-api/media/rc/lirc-header.rst +++ b/Documentation/userspace-api/media/rc/lirc-header.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _lirc_header: diff --git a/Documentation/userspace-api/media/rc/lirc-read.rst b/Documentation/userspace-api/media/rc/lirc-read.rst index d589560214f4..ce34318698b7 100644 --- a/Documentation/userspace-api/media/rc/lirc-read.rst +++ b/Documentation/userspace-api/media/rc/lirc-read.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc-read: diff --git a/Documentation/userspace-api/media/rc/lirc-set-measure-carrier-mode.rst b/Documentation/userspace-api/media/rc/lirc-set-measure-carrier-mode.rst index 9bf9811a905a..04ced1aa664b 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-measure-carrier-mode.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-measure-carrier-mode.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_measure_carrier_mode: diff --git a/Documentation/userspace-api/media/rc/lirc-set-rec-carrier-range.rst b/Documentation/userspace-api/media/rc/lirc-set-rec-carrier-range.rst index 530bc223930a..7512dc86b03a 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-rec-carrier-range.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-rec-carrier-range.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_rec_carrier_range: diff --git a/Documentation/userspace-api/media/rc/lirc-set-rec-carrier.rst b/Documentation/userspace-api/media/rc/lirc-set-rec-carrier.rst index 28c928f1cc14..60e321446ea7 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-rec-carrier.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-rec-carrier.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_rec_carrier: diff --git a/Documentation/userspace-api/media/rc/lirc-set-rec-timeout-reports.rst b/Documentation/userspace-api/media/rc/lirc-set-rec-timeout-reports.rst index 83e7155c5796..aebe81012939 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-rec-timeout-reports.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-rec-timeout-reports.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_rec_timeout_reports: diff --git a/Documentation/userspace-api/media/rc/lirc-set-rec-timeout.rst b/Documentation/userspace-api/media/rc/lirc-set-rec-timeout.rst index 8f3f9adf54ab..bf9fb2cc61c3 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-rec-timeout.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-rec-timeout.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_rec_timeout: diff --git a/Documentation/userspace-api/media/rc/lirc-set-send-carrier.rst b/Documentation/userspace-api/media/rc/lirc-set-send-carrier.rst index e3810ba58746..a003f9447553 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-send-carrier.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-send-carrier.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_send_carrier: diff --git a/Documentation/userspace-api/media/rc/lirc-set-send-duty-cycle.rst b/Documentation/userspace-api/media/rc/lirc-set-send-duty-cycle.rst index 52a072529af9..2979752acbcd 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-send-duty-cycle.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-send-duty-cycle.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_send_duty_cycle: diff --git a/Documentation/userspace-api/media/rc/lirc-set-transmitter-mask.rst b/Documentation/userspace-api/media/rc/lirc-set-transmitter-mask.rst index 68f4cc2e3ae3..38acbcd6e91c 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-transmitter-mask.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-transmitter-mask.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_transmitter_mask: diff --git a/Documentation/userspace-api/media/rc/lirc-set-wideband-receiver.rst b/Documentation/userspace-api/media/rc/lirc-set-wideband-receiver.rst index be5321c4a91f..c9d578e291b8 100644 --- a/Documentation/userspace-api/media/rc/lirc-set-wideband-receiver.rst +++ b/Documentation/userspace-api/media/rc/lirc-set-wideband-receiver.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc_set_wideband_receiver: diff --git a/Documentation/userspace-api/media/rc/lirc-write.rst b/Documentation/userspace-api/media/rc/lirc-write.rst index c1c3230d4fd6..970a8b3fa1ca 100644 --- a/Documentation/userspace-api/media/rc/lirc-write.rst +++ b/Documentation/userspace-api/media/rc/lirc-write.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. c:namespace:: RC .. _lirc-write: diff --git a/Documentation/userspace-api/media/rc/rc-intro.rst b/Documentation/userspace-api/media/rc/rc-intro.rst index 1338478e2bd4..2ba62cde2369 100644 --- a/Documentation/userspace-api/media/rc/rc-intro.rst +++ b/Documentation/userspace-api/media/rc/rc-intro.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _Remote_controllers_Intro: diff --git a/Documentation/userspace-api/media/rc/rc-protos.rst b/Documentation/userspace-api/media/rc/rc-protos.rst index 2e290584a210..a2eab3b45647 100644 --- a/Documentation/userspace-api/media/rc/rc-protos.rst +++ b/Documentation/userspace-api/media/rc/rc-protos.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _Remote_controllers_Protocols: diff --git a/Documentation/userspace-api/media/rc/rc-sysfs-nodes.rst b/Documentation/userspace-api/media/rc/rc-sysfs-nodes.rst index 43c442696438..34d6a0a1f4d3 100644 --- a/Documentation/userspace-api/media/rc/rc-sysfs-nodes.rst +++ b/Documentation/userspace-api/media/rc/rc-sysfs-nodes.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _remote_controllers_sysfs_nodes: diff --git a/Documentation/userspace-api/media/rc/rc-table-change.rst b/Documentation/userspace-api/media/rc/rc-table-change.rst index 61c77b080ae8..d7de8a56ddfe 100644 --- a/Documentation/userspace-api/media/rc/rc-table-change.rst +++ b/Documentation/userspace-api/media/rc/rc-table-change.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _Remote_controllers_table_change: diff --git a/Documentation/userspace-api/media/rc/rc-tables.rst b/Documentation/userspace-api/media/rc/rc-tables.rst index 8dc11657fc23..aafbfda1f401 100644 --- a/Documentation/userspace-api/media/rc/rc-tables.rst +++ b/Documentation/userspace-api/media/rc/rc-tables.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. _Remote_controllers_tables: diff --git a/Documentation/userspace-api/media/rc/remote_controllers.rst b/Documentation/userspace-api/media/rc/remote_controllers.rst index 2d9078accb35..f89291838637 100644 --- a/Documentation/userspace-api/media/rc/remote_controllers.rst +++ b/Documentation/userspace-api/media/rc/remote_controllers.rst @@ -1,4 +1,4 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later +.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later .. include:: <isonum.txt> .. _remote_controllers: diff --git a/Documentation/userspace-api/media/v4l/biblio.rst b/Documentation/userspace-api/media/v4l/biblio.rst index 7869b6f6ff72..64d241daf63c 100644 --- a/Documentation/userspace-api/media/v4l/biblio.rst +++ b/Documentation/userspace-api/media/v4l/biblio.rst @@ -270,7 +270,17 @@ EBU Tech 3213 ============= -:title: E.B.U. Standard for Chromaticity Tolerances for Studio Monitors" +:title: E.B.U. Standard for Chromaticity Tolerances for Studio Monitors + +:author: European Broadcast Union (http://www.ebu.ch) + +.. _tech3321: + +EBU Tech 3321 +============= + + +:title: E.B.U. guidelines for Consumer Flat Panel Displays (FPDs) :author: European Broadcast Union (http://www.ebu.ch) diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst index 7dbdfbb4a0a9..1b0fdc160533 100644 --- a/Documentation/userspace-api/media/v4l/buffer.rst +++ b/Documentation/userspace-api/media/v4l/buffer.rst @@ -604,7 +604,7 @@ Buffer Flags :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl is called. Due to hardware limitations, the last buffer may be empty. In this case the driver will set the ``bytesused`` field to 0, regardless of - the format. Any Any subsequent call to the + the format. Any subsequent call to the :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>` ioctl will not block anymore, but return an ``EPIPE`` error code. * .. _`V4L2-BUF-FLAG-REQUEST-FD`: diff --git a/Documentation/userspace-api/media/v4l/colorspaces-details.rst b/Documentation/userspace-api/media/v4l/colorspaces-details.rst index 014e7c9fc655..126f66482a0d 100644 --- a/Documentation/userspace-api/media/v4l/colorspaces-details.rst +++ b/Documentation/userspace-api/media/v4l/colorspaces-details.rst @@ -674,8 +674,9 @@ Colorspace EBU Tech. 3213 (V4L2_COLORSPACE_470_SYSTEM_BG) ========================================================= The :ref:`tech3213` standard defines the colorspace used by PAL/SECAM -in 1975. In practice this colorspace is obsolete and SMPTE 170M should -be used instead. The default transfer function is +in 1975. Note that this colorspace is not supported by the HDMI interface. +Instead :ref:`tech3321` recommends that Rec. 709 is used instead for HDMI. +The default transfer function is ``V4L2_XFER_FUNC_709``. The default Y'CbCr encoding is ``V4L2_YCBCR_ENC_601``. The default Y'CbCr quantization is limited range. The chromaticities of the primary colors and the white reference diff --git a/Documentation/userspace-api/media/v4l/common.rst b/Documentation/userspace-api/media/v4l/common.rst index d84aeb703165..8c263c5a85d8 100644 --- a/Documentation/userspace-api/media/v4l/common.rst +++ b/Documentation/userspace-api/media/v4l/common.rst @@ -44,6 +44,7 @@ applicable to all devices. ext-ctrls-image-source ext-ctrls-image-process ext-ctrls-codec + ext-ctrls-codec-stateless ext-ctrls-jpeg ext-ctrls-dv ext-ctrls-rf-tuner diff --git a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst index d8db46886555..7041bb3d5b8d 100644 --- a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst +++ b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst @@ -32,7 +32,7 @@ file handle is visible through another file handle). One of the most common memory-to-memory device is the codec. Codecs are more complicated than most and require additional setup for their codec parameters. This is done through codec controls. -See :ref:`mpeg-controls`. More details on how to use codec memory-to-memory +See :ref:`codec-controls`. More details on how to use codec memory-to-memory devices are given in the following sections. .. toctree:: diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst new file mode 100644 index 000000000000..01e3b1a3fb99 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -0,0 +1,793 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _codec-stateless-controls: + +********************************* +Stateless Codec Control Reference +********************************* + +The Stateless Codec control class is intended to support +stateless decoder and encoders (i.e. hardware accelerators). + +These drivers are typically supported by the :ref:`stateless_decoder`, +and deal with parsed pixel formats such as V4L2_PIX_FMT_H264_SLICE. + +Stateless Codec Control ID +========================== + +.. _codec-stateless-control-id: + +``V4L2_CID_CODEC_STATELESS_CLASS (class)`` + The Stateless Codec class descriptor. + +.. _v4l2-codec-stateless-h264: + +``V4L2_CID_STATELESS_H264_SPS (struct)`` + Specifies the sequence parameter set (as extracted from the + bitstream) for the associated H264 slice data. This includes the + necessary parameters for configuring a stateless hardware decoding + pipeline for H264. The bitstream parameters are defined according + to :ref:`h264`, section 7.4.2.1.1 "Sequence Parameter Set Data + Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + +.. c:type:: v4l2_ctrl_h264_sps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_sps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``profile_idc`` + - + * - __u8 + - ``constraint_set_flags`` + - See :ref:`Sequence Parameter Set Constraints Set Flags <h264_sps_constraints_set_flags>` + * - __u8 + - ``level_idc`` + - + * - __u8 + - ``seq_parameter_set_id`` + - + * - __u8 + - ``chroma_format_idc`` + - + * - __u8 + - ``bit_depth_luma_minus8`` + - + * - __u8 + - ``bit_depth_chroma_minus8`` + - + * - __u8 + - ``log2_max_frame_num_minus4`` + - + * - __u8 + - ``pic_order_cnt_type`` + - + * - __u8 + - ``log2_max_pic_order_cnt_lsb_minus4`` + - + * - __u8 + - ``max_num_ref_frames`` + - + * - __u8 + - ``num_ref_frames_in_pic_order_cnt_cycle`` + - + * - __s32 + - ``offset_for_ref_frame[255]`` + - + * - __s32 + - ``offset_for_non_ref_pic`` + - + * - __s32 + - ``offset_for_top_to_bottom_field`` + - + * - __u16 + - ``pic_width_in_mbs_minus1`` + - + * - __u16 + - ``pic_height_in_map_units_minus1`` + - + * - __u32 + - ``flags`` + - See :ref:`Sequence Parameter Set Flags <h264_sps_flags>` + +.. _h264_sps_constraints_set_flags: + +``Sequence Parameter Set Constraints Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SPS_CONSTRAINT_SET0_FLAG`` + - 0x00000001 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET1_FLAG`` + - 0x00000002 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET2_FLAG`` + - 0x00000004 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET3_FLAG`` + - 0x00000008 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET4_FLAG`` + - 0x00000010 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET5_FLAG`` + - 0x00000020 + - + +.. _h264_sps_flags: + +``Sequence Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE`` + - 0x00000001 + - + * - ``V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS`` + - 0x00000002 + - + * - ``V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO`` + - 0x00000004 + - + * - ``V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED`` + - 0x00000008 + - + * - ``V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY`` + - 0x00000010 + - + * - ``V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD`` + - 0x00000020 + - + * - ``V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE`` + - 0x00000040 + - + +``V4L2_CID_STATELESS_H264_PPS (struct)`` + Specifies the picture parameter set (as extracted from the + bitstream) for the associated H264 slice data. This includes the + necessary parameters for configuring a stateless hardware decoding + pipeline for H264. The bitstream parameters are defined according + to :ref:`h264`, section 7.4.2.2 "Picture Parameter Set RBSP + Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + +.. c:type:: v4l2_ctrl_h264_pps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_pps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``pic_parameter_set_id`` + - + * - __u8 + - ``seq_parameter_set_id`` + - + * - __u8 + - ``num_slice_groups_minus1`` + - + * - __u8 + - ``num_ref_idx_l0_default_active_minus1`` + - + * - __u8 + - ``num_ref_idx_l1_default_active_minus1`` + - + * - __u8 + - ``weighted_bipred_idc`` + - + * - __s8 + - ``pic_init_qp_minus26`` + - + * - __s8 + - ``pic_init_qs_minus26`` + - + * - __s8 + - ``chroma_qp_index_offset`` + - + * - __s8 + - ``second_chroma_qp_index_offset`` + - + * - __u16 + - ``flags`` + - See :ref:`Picture Parameter Set Flags <h264_pps_flags>` + +.. _h264_pps_flags: + +``Picture Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE`` + - 0x00000001 + - + * - ``V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT`` + - 0x00000002 + - + * - ``V4L2_H264_PPS_FLAG_WEIGHTED_PRED`` + - 0x00000004 + - + * - ``V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT`` + - 0x00000008 + - + * - ``V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED`` + - 0x00000010 + - + * - ``V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT`` + - 0x00000020 + - + * - ``V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE`` + - 0x00000040 + - + * - ``V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT`` + - 0x00000080 + - Indicates that ``V4L2_CID_STATELESS_H264_SCALING_MATRIX`` + must be used for this picture. + +``V4L2_CID_STATELESS_H264_SCALING_MATRIX (struct)`` + Specifies the scaling matrix (as extracted from the bitstream) for + the associated H264 slice data. The bitstream parameters are + defined according to :ref:`h264`, section 7.4.2.1.1.1 "Scaling + List Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + +.. c:type:: v4l2_ctrl_h264_scaling_matrix + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_scaling_matrix + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``scaling_list_4x4[6][16]`` + - Scaling matrix after applying the inverse scanning process. + Expected list order is Intra Y, Intra Cb, Intra Cr, Inter Y, + Inter Cb, Inter Cr. The values on each scaling list are + expected in raster scan order. + * - __u8 + - ``scaling_list_8x8[6][64]`` + - Scaling matrix after applying the inverse scanning process. + Expected list order is Intra Y, Inter Y, Intra Cb, Inter Cb, + Intra Cr, Inter Cr. The values on each scaling list are + expected in raster scan order. + +``V4L2_CID_STATELESS_H264_SLICE_PARAMS (struct)`` + Specifies the slice parameters (as extracted from the bitstream) + for the associated H264 slice data. This includes the necessary + parameters for configuring a stateless hardware decoding pipeline + for H264. The bitstream parameters are defined according to + :ref:`h264`, section 7.4.3 "Slice Header Semantics". For further + documentation, refer to the above specification, unless there is + an explicit comment stating otherwise. + +.. c:type:: v4l2_ctrl_h264_slice_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_slice_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``header_bit_size`` + - Offset in bits to slice_data() from the beginning of this slice. + * - __u32 + - ``first_mb_in_slice`` + - + * - __u8 + - ``slice_type`` + - + * - __u8 + - ``colour_plane_id`` + - + * - __u8 + - ``redundant_pic_cnt`` + - + * - __u8 + - ``cabac_init_idc`` + - + * - __s8 + - ``slice_qp_delta`` + - + * - __s8 + - ``slice_qs_delta`` + - + * - __u8 + - ``disable_deblocking_filter_idc`` + - + * - __s8 + - ``slice_alpha_c0_offset_div2`` + - + * - __s8 + - ``slice_beta_offset_div2`` + - + * - __u8 + - ``num_ref_idx_l0_active_minus1`` + - If num_ref_idx_active_override_flag is not set, this field must be + set to the value of num_ref_idx_l0_default_active_minus1. + * - __u8 + - ``num_ref_idx_l1_active_minus1`` + - If num_ref_idx_active_override_flag is not set, this field must be + set to the value of num_ref_idx_l1_default_active_minus1. + * - __u8 + - ``reserved`` + - Applications and drivers must set this to zero. + * - struct :c:type:`v4l2_h264_reference` + - ``ref_pic_list0[32]`` + - Reference picture list after applying the per-slice modifications + * - struct :c:type:`v4l2_h264_reference` + - ``ref_pic_list1[32]`` + - Reference picture list after applying the per-slice modifications + * - __u32 + - ``flags`` + - See :ref:`Slice Parameter Flags <h264_slice_flags>` + +.. _h264_slice_flags: + +``Slice Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED`` + - 0x00000001 + - + * - ``V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH`` + - 0x00000002 + - + +``V4L2_CID_STATELESS_H264_PRED_WEIGHTS (struct)`` + Prediction weight table defined according to :ref:`h264`, + section 7.4.3.2 "Prediction Weight Table Semantics". + The prediction weight table must be passed by applications + under the conditions explained in section 7.3.3 "Slice header + syntax". + +.. c:type:: v4l2_ctrl_h264_pred_weights + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_pred_weights + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u16 + - ``luma_log2_weight_denom`` + - + * - __u16 + - ``chroma_log2_weight_denom`` + - + * - struct :c:type:`v4l2_h264_weight_factors` + - ``weight_factors[2]`` + - The weight factors at index 0 are the weight factors for the reference + list 0, the one at index 1 for the reference list 1. + +.. c:type:: v4l2_h264_weight_factors + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_weight_factors + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __s16 + - ``luma_weight[32]`` + - + * - __s16 + - ``luma_offset[32]`` + - + * - __s16 + - ``chroma_weight[32][2]`` + - + * - __s16 + - ``chroma_offset[32][2]`` + - + +``Picture Reference`` + +.. c:type:: v4l2_h264_reference + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_reference + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``fields`` + - Specifies how the picture is referenced. See :ref:`Reference Fields <h264_ref_fields>` + * - __u8 + - ``index`` + - Index into the :c:type:`v4l2_ctrl_h264_decode_params`.dpb array. + +.. _h264_ref_fields: + +``Reference Fields`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_TOP_FIELD_REF`` + - 0x1 + - The top field in field pair is used for short-term reference. + * - ``V4L2_H264_BOTTOM_FIELD_REF`` + - 0x2 + - The bottom field in field pair is used for short-term reference. + * - ``V4L2_H264_FRAME_REF`` + - 0x3 + - The frame (or the top/bottom fields, if it's a field pair) + is used for short-term reference. + +``V4L2_CID_STATELESS_H264_DECODE_PARAMS (struct)`` + Specifies the decode parameters (as extracted from the bitstream) + for the associated H264 slice data. This includes the necessary + parameters for configuring a stateless hardware decoding pipeline + for H264. The bitstream parameters are defined according to + :ref:`h264`. For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + +.. c:type:: v4l2_ctrl_h264_decode_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_decode_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - struct :c:type:`v4l2_h264_dpb_entry` + - ``dpb[16]`` + - + * - __u16 + - ``nal_ref_idc`` + - NAL reference ID value coming from the NAL Unit header + * - __u16 + - ``frame_num`` + - + * - __s32 + - ``top_field_order_cnt`` + - Picture Order Count for the coded top field + * - __s32 + - ``bottom_field_order_cnt`` + - Picture Order Count for the coded bottom field + * - __u16 + - ``idr_pic_id`` + - + * - __u16 + - ``pic_order_cnt_lsb`` + - + * - __s32 + - ``delta_pic_order_cnt_bottom`` + - + * - __s32 + - ``delta_pic_order_cnt0`` + - + * - __s32 + - ``delta_pic_order_cnt1`` + - + * - __u32 + - ``dec_ref_pic_marking_bit_size`` + - Size in bits of the dec_ref_pic_marking() syntax element. + * - __u32 + - ``pic_order_cnt_bit_size`` + - Combined size in bits of the picture order count related syntax + elements: pic_order_cnt_lsb, delta_pic_order_cnt_bottom, + delta_pic_order_cnt0, and delta_pic_order_cnt1. + * - __u32 + - ``slice_group_change_cycle`` + - + * - __u32 + - ``reserved`` + - Applications and drivers must set this to zero. + * - __u32 + - ``flags`` + - See :ref:`Decode Parameters Flags <h264_decode_params_flags>` + +.. _h264_decode_params_flags: + +``Decode Parameters Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC`` + - 0x00000001 + - That picture is an IDR picture + * - ``V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC`` + - 0x00000002 + - + * - ``V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD`` + - 0x00000004 + - + +.. c:type:: v4l2_h264_dpb_entry + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_dpb_entry + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u64 + - ``reference_ts`` + - Timestamp of the V4L2 capture buffer to use as reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u32 + - ``pic_num`` + - + * - __u16 + - ``frame_num`` + - + * - __u8 + - ``fields`` + - Specifies how the DPB entry is referenced. See :ref:`Reference Fields <h264_ref_fields>` + * - __u8 + - ``reserved[5]`` + - Applications and drivers must set this to zero. + * - __s32 + - ``top_field_order_cnt`` + - + * - __s32 + - ``bottom_field_order_cnt`` + - + * - __u32 + - ``flags`` + - See :ref:`DPB Entry Flags <h264_dpb_flags>` + +.. _h264_dpb_flags: + +``DPB Entries Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_DPB_ENTRY_FLAG_VALID`` + - 0x00000001 + - The DPB entry is valid (non-empty) and should be considered. + * - ``V4L2_H264_DPB_ENTRY_FLAG_ACTIVE`` + - 0x00000002 + - The DPB entry is used for reference. + * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` + - 0x00000004 + - The DPB entry is used for long-term reference. + * - ``V4L2_H264_DPB_ENTRY_FLAG_FIELD`` + - 0x00000008 + - The DPB entry is a single field or a complementary field pair. + +``V4L2_CID_STATELESS_H264_DECODE_MODE (enum)`` + Specifies the decoding mode to use. Currently exposes slice-based and + frame-based decoding but new modes might be added later on. + This control is used as a modifier for V4L2_PIX_FMT_H264_SLICE + pixel format. Applications that support V4L2_PIX_FMT_H264_SLICE + are required to set this control in order to specify the decoding mode + that is expected for the buffer. + Drivers may expose a single or multiple decoding modes, depending + on what they can support. + +.. c:type:: v4l2_stateless_h264_decode_mode + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED`` + - 0 + - Decoding is done at the slice granularity. + The OUTPUT buffer must contain a single slice. + When this mode is selected, the ``V4L2_CID_STATELESS_H264_SLICE_PARAMS`` + control shall be set. When multiple slices compose a frame, + use of ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` flag + is required. + * - ``V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED`` + - 1 + - Decoding is done at the frame granularity, + The OUTPUT buffer must contain all slices needed to decode the + frame. The OUTPUT buffer must also contain both fields. + This mode will be supported by devices that + parse the slice(s) header(s) in hardware. When this mode is + selected, the ``V4L2_CID_STATELESS_H264_SLICE_PARAMS`` + control shall not be set. + +``V4L2_CID_STATELESS_H264_START_CODE (enum)`` + Specifies the H264 slice start code expected for each slice. + This control is used as a modifier for V4L2_PIX_FMT_H264_SLICE + pixel format. Applications that support V4L2_PIX_FMT_H264_SLICE + are required to set this control in order to specify the start code + that is expected for the buffer. + Drivers may expose a single or multiple start codes, depending + on what they can support. + +.. c:type:: v4l2_stateless_h264_start_code + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_STATELESS_H264_START_CODE_NONE`` + - 0 + - Selecting this value specifies that H264 slices are passed + to the driver without any start code. + * - ``V4L2_STATELESS_H264_START_CODE_ANNEX_B`` + - 1 + - Selecting this value specifies that H264 slices are expected + to be prefixed by Annex B start codes. According to :ref:`h264` + valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001. + + +.. _codec-stateless-fwht: + +``V4L2_CID_STATELESS_FWHT_PARAMS (struct)`` + Specifies the FWHT (Fast Walsh Hadamard Transform) parameters (as extracted + from the bitstream) for the associated FWHT data. This includes the necessary + parameters for configuring a stateless hardware decoding pipeline for FWHT. + This codec is specific to the vicodec test driver. + +.. c:type:: v4l2_ctrl_fwht_params + +.. cssclass:: longtable + +.. tabularcolumns:: |p{1.4cm}|p{4.3cm}|p{11.8cm}| + +.. flat-table:: struct v4l2_ctrl_fwht_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u64 + - ``backward_ref_ts`` + - Timestamp of the V4L2 capture buffer to use as backward reference, used + with P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u32 + - ``version`` + - The version of the codec. Set to ``V4L2_FWHT_VERSION``. + * - __u32 + - ``width`` + - The width of the frame. + * - __u32 + - ``height`` + - The height of the frame. + * - __u32 + - ``flags`` + - The flags of the frame, see :ref:`fwht-flags`. + * - __u32 + - ``colorspace`` + - The colorspace of the frame, from enum :c:type:`v4l2_colorspace`. + * - __u32 + - ``xfer_func`` + - The transfer function, from enum :c:type:`v4l2_xfer_func`. + * - __u32 + - ``ycbcr_enc`` + - The Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. + * - __u32 + - ``quantization`` + - The quantization range, from enum :c:type:`v4l2_quantization`. + + + +.. _fwht-flags: + +FWHT Flags +========== + +.. cssclass:: longtable + +.. tabularcolumns:: |p{6.8cm}|p{2.4cm}|p{8.3cm}| + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 3 1 4 + + * - ``V4L2_FWHT_FL_IS_INTERLACED`` + - 0x00000001 + - Set if this is an interlaced format. + * - ``V4L2_FWHT_FL_IS_BOTTOM_FIRST`` + - 0x00000002 + - Set if this is a bottom-first (NTSC) interlaced format. + * - ``V4L2_FWHT_FL_IS_ALTERNATE`` + - 0x00000004 + - Set if each 'frame' contains just one field. + * - ``V4L2_FWHT_FL_IS_BOTTOM_FIELD`` + - 0x00000008 + - If V4L2_FWHT_FL_IS_ALTERNATE was set, then this is set if this 'frame' is the + bottom field, else it is the top field. + * - ``V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED`` + - 0x00000010 + - Set if the Y' (luma) plane is uncompressed. + * - ``V4L2_FWHT_FL_CB_IS_UNCOMPRESSED`` + - 0x00000020 + - Set if the Cb plane is uncompressed. + * - ``V4L2_FWHT_FL_CR_IS_UNCOMPRESSED`` + - 0x00000040 + - Set if the Cr plane is uncompressed. + * - ``V4L2_FWHT_FL_CHROMA_FULL_HEIGHT`` + - 0x00000080 + - Set if the chroma plane has the same height as the luma plane, + else the chroma plane is half the height of the luma plane. + * - ``V4L2_FWHT_FL_CHROMA_FULL_WIDTH`` + - 0x00000100 + - Set if the chroma plane has the same width as the luma plane, + else the chroma plane is half the width of the luma plane. + * - ``V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED`` + - 0x00000200 + - Set if the alpha plane is uncompressed. + * - ``V4L2_FWHT_FL_I_FRAME`` + - 0x00000400 + - Set if this is an I-frame. + * - ``V4L2_FWHT_FL_COMPONENTS_NUM_MSK`` + - 0x00070000 + - The number of color components - 1. + * - ``V4L2_FWHT_FL_PIXENC_MSK`` + - 0x00180000 + - The mask for the pixel encoding. + * - ``V4L2_FWHT_FL_PIXENC_YUV`` + - 0x00080000 + - Set if the pixel encoding is YUV. + * - ``V4L2_FWHT_FL_PIXENC_RGB`` + - 0x00100000 + - Set if the pixel encoding is RGB. + * - ``V4L2_FWHT_FL_PIXENC_HSV`` + - 0x00180000 + - Set if the pixel encoding is HSV. diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index ce728c757eaf..454ecd9a0f83 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -1,6 +1,6 @@ .. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. _mpeg-controls: +.. _codec-controls: *********************** Codec Control Reference @@ -26,7 +26,7 @@ Generic Codec Controls Codec Control IDs ----------------- -``V4L2_CID_MPEG_CLASS (class)`` +``V4L2_CID_CODEC_CLASS (class)`` The Codec class descriptor. Calling :ref:`VIDIOC_QUERYCTRL` for this control will return a description of this control class. This description can be @@ -1502,698 +1502,6 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - Layer number -.. _v4l2-mpeg-h264: - -``V4L2_CID_MPEG_VIDEO_H264_SPS (struct)`` - Specifies the sequence parameter set (as extracted from the - bitstream) for the associated H264 slice data. This includes the - necessary parameters for configuring a stateless hardware decoding - pipeline for H264. The bitstream parameters are defined according - to :ref:`h264`, section 7.4.2.1.1 "Sequence Parameter Set Data - Semantics". For further documentation, refer to the above - specification, unless there is an explicit comment stating - otherwise. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_h264_sps - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_sps - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``profile_idc`` - - - * - __u8 - - ``constraint_set_flags`` - - See :ref:`Sequence Parameter Set Constraints Set Flags <h264_sps_constraints_set_flags>` - * - __u8 - - ``level_idc`` - - - * - __u8 - - ``seq_parameter_set_id`` - - - * - __u8 - - ``chroma_format_idc`` - - - * - __u8 - - ``bit_depth_luma_minus8`` - - - * - __u8 - - ``bit_depth_chroma_minus8`` - - - * - __u8 - - ``log2_max_frame_num_minus4`` - - - * - __u8 - - ``pic_order_cnt_type`` - - - * - __u8 - - ``log2_max_pic_order_cnt_lsb_minus4`` - - - * - __u8 - - ``max_num_ref_frames`` - - - * - __u8 - - ``num_ref_frames_in_pic_order_cnt_cycle`` - - - * - __s32 - - ``offset_for_ref_frame[255]`` - - - * - __s32 - - ``offset_for_non_ref_pic`` - - - * - __s32 - - ``offset_for_top_to_bottom_field`` - - - * - __u16 - - ``pic_width_in_mbs_minus1`` - - - * - __u16 - - ``pic_height_in_map_units_minus1`` - - - * - __u32 - - ``flags`` - - See :ref:`Sequence Parameter Set Flags <h264_sps_flags>` - -.. _h264_sps_constraints_set_flags: - -``Sequence Parameter Set Constraints Set Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_SPS_CONSTRAINT_SET0_FLAG`` - - 0x00000001 - - - * - ``V4L2_H264_SPS_CONSTRAINT_SET1_FLAG`` - - 0x00000002 - - - * - ``V4L2_H264_SPS_CONSTRAINT_SET2_FLAG`` - - 0x00000004 - - - * - ``V4L2_H264_SPS_CONSTRAINT_SET3_FLAG`` - - 0x00000008 - - - * - ``V4L2_H264_SPS_CONSTRAINT_SET4_FLAG`` - - 0x00000010 - - - * - ``V4L2_H264_SPS_CONSTRAINT_SET5_FLAG`` - - 0x00000020 - - - -.. _h264_sps_flags: - -``Sequence Parameter Set Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE`` - - 0x00000001 - - - * - ``V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS`` - - 0x00000002 - - - * - ``V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO`` - - 0x00000004 - - - * - ``V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED`` - - 0x00000008 - - - * - ``V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY`` - - 0x00000010 - - - * - ``V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD`` - - 0x00000020 - - - * - ``V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE`` - - 0x00000040 - - - -``V4L2_CID_MPEG_VIDEO_H264_PPS (struct)`` - Specifies the picture parameter set (as extracted from the - bitstream) for the associated H264 slice data. This includes the - necessary parameters for configuring a stateless hardware decoding - pipeline for H264. The bitstream parameters are defined according - to :ref:`h264`, section 7.4.2.2 "Picture Parameter Set RBSP - Semantics". For further documentation, refer to the above - specification, unless there is an explicit comment stating - otherwise. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_h264_pps - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_pps - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``pic_parameter_set_id`` - - - * - __u8 - - ``seq_parameter_set_id`` - - - * - __u8 - - ``num_slice_groups_minus1`` - - - * - __u8 - - ``num_ref_idx_l0_default_active_minus1`` - - - * - __u8 - - ``num_ref_idx_l1_default_active_minus1`` - - - * - __u8 - - ``weighted_bipred_idc`` - - - * - __s8 - - ``pic_init_qp_minus26`` - - - * - __s8 - - ``pic_init_qs_minus26`` - - - * - __s8 - - ``chroma_qp_index_offset`` - - - * - __s8 - - ``second_chroma_qp_index_offset`` - - - * - __u16 - - ``flags`` - - See :ref:`Picture Parameter Set Flags <h264_pps_flags>` - -.. _h264_pps_flags: - -``Picture Parameter Set Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE`` - - 0x00000001 - - - * - ``V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT`` - - 0x00000002 - - - * - ``V4L2_H264_PPS_FLAG_WEIGHTED_PRED`` - - 0x00000004 - - - * - ``V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT`` - - 0x00000008 - - - * - ``V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED`` - - 0x00000010 - - - * - ``V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT`` - - 0x00000020 - - - * - ``V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE`` - - 0x00000040 - - - * - ``V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT`` - - 0x00000080 - - Indicates that ``V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX`` - must be used for this picture. - -``V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX (struct)`` - Specifies the scaling matrix (as extracted from the bitstream) for - the associated H264 slice data. The bitstream parameters are - defined according to :ref:`h264`, section 7.4.2.1.1.1 "Scaling - List Semantics". For further documentation, refer to the above - specification, unless there is an explicit comment stating - otherwise. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_h264_scaling_matrix - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_scaling_matrix - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``scaling_list_4x4[6][16]`` - - Scaling matrix after applying the inverse scanning process. - Expected list order is Intra Y, Intra Cb, Intra Cr, Inter Y, - Inter Cb, Inter Cr. The values on each scaling list are - expected in raster scan order. - * - __u8 - - ``scaling_list_8x8[6][64]`` - - Scaling matrix after applying the inverse scanning process. - Expected list order is Intra Y, Inter Y, Intra Cb, Inter Cb, - Intra Cr, Inter Cr. The values on each scaling list are - expected in raster scan order. - -``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS (struct)`` - Specifies the slice parameters (as extracted from the bitstream) - for the associated H264 slice data. This includes the necessary - parameters for configuring a stateless hardware decoding pipeline - for H264. The bitstream parameters are defined according to - :ref:`h264`, section 7.4.3 "Slice Header Semantics". For further - documentation, refer to the above specification, unless there is - an explicit comment stating otherwise. - - .. note:: - - This compound control is not yet part of the public kernel API - and it is expected to change. - -.. c:type:: v4l2_ctrl_h264_slice_params - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_slice_params - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u32 - - ``header_bit_size`` - - Offset in bits to slice_data() from the beginning of this slice. - * - __u32 - - ``first_mb_in_slice`` - - - * - __u8 - - ``slice_type`` - - - * - __u8 - - ``colour_plane_id`` - - - * - __u8 - - ``redundant_pic_cnt`` - - - * - __u8 - - ``cabac_init_idc`` - - - * - __s8 - - ``slice_qp_delta`` - - - * - __s8 - - ``slice_qs_delta`` - - - * - __u8 - - ``disable_deblocking_filter_idc`` - - - * - __s8 - - ``slice_alpha_c0_offset_div2`` - - - * - __s8 - - ``slice_beta_offset_div2`` - - - * - __u8 - - ``num_ref_idx_l0_active_minus1`` - - If num_ref_idx_active_override_flag is not set, this field must be - set to the value of num_ref_idx_l0_default_active_minus1. - * - __u8 - - ``num_ref_idx_l1_active_minus1`` - - If num_ref_idx_active_override_flag is not set, this field must be - set to the value of num_ref_idx_l1_default_active_minus1. - * - __u8 - - ``reserved`` - - Applications and drivers must set this to zero. - * - struct :c:type:`v4l2_h264_reference` - - ``ref_pic_list0[32]`` - - Reference picture list after applying the per-slice modifications - * - struct :c:type:`v4l2_h264_reference` - - ``ref_pic_list1[32]`` - - Reference picture list after applying the per-slice modifications - * - __u32 - - ``flags`` - - See :ref:`Slice Parameter Flags <h264_slice_flags>` - -.. _h264_slice_flags: - -``Slice Parameter Set Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED`` - - 0x00000001 - - - * - ``V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH`` - - 0x00000002 - - - -``V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS (struct)`` - Prediction weight table defined according to :ref:`h264`, - section 7.4.3.2 "Prediction Weight Table Semantics". - The prediction weight table must be passed by applications - under the conditions explained in section 7.3.3 "Slice header - syntax". - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_h264_pred_weights - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_pred_weights - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u16 - - ``luma_log2_weight_denom`` - - - * - __u16 - - ``chroma_log2_weight_denom`` - - - * - struct :c:type:`v4l2_h264_weight_factors` - - ``weight_factors[2]`` - - The weight factors at index 0 are the weight factors for the reference - list 0, the one at index 1 for the reference list 1. - -.. c:type:: v4l2_h264_weight_factors - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_h264_weight_factors - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __s16 - - ``luma_weight[32]`` - - - * - __s16 - - ``luma_offset[32]`` - - - * - __s16 - - ``chroma_weight[32][2]`` - - - * - __s16 - - ``chroma_offset[32][2]`` - - - -``Picture Reference`` - -.. c:type:: v4l2_h264_reference - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_h264_reference - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u8 - - ``fields`` - - Specifies how the picture is referenced. See :ref:`Reference Fields <h264_ref_fields>` - * - __u8 - - ``index`` - - Index into the :c:type:`v4l2_ctrl_h264_decode_params`.dpb array. - -.. _h264_ref_fields: - -``Reference Fields`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_TOP_FIELD_REF`` - - 0x1 - - The top field in field pair is used for short-term reference. - * - ``V4L2_H264_BOTTOM_FIELD_REF`` - - 0x2 - - The bottom field in field pair is used for short-term reference. - * - ``V4L2_H264_FRAME_REF`` - - 0x3 - - The frame (or the top/bottom fields, if it's a field pair) - is used for short-term reference. - -``V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS (struct)`` - Specifies the decode parameters (as extracted from the bitstream) - for the associated H264 slice data. This includes the necessary - parameters for configuring a stateless hardware decoding pipeline - for H264. The bitstream parameters are defined according to - :ref:`h264`. For further documentation, refer to the above - specification, unless there is an explicit comment stating - otherwise. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_h264_decode_params - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_ctrl_h264_decode_params - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - struct :c:type:`v4l2_h264_dpb_entry` - - ``dpb[16]`` - - - * - __u16 - - ``nal_ref_idc`` - - NAL reference ID value coming from the NAL Unit header - * - __u16 - - ``frame_num`` - - - * - __s32 - - ``top_field_order_cnt`` - - Picture Order Count for the coded top field - * - __s32 - - ``bottom_field_order_cnt`` - - Picture Order Count for the coded bottom field - * - __u16 - - ``idr_pic_id`` - - - * - __u16 - - ``pic_order_cnt_lsb`` - - - * - __s32 - - ``delta_pic_order_cnt_bottom`` - - - * - __s32 - - ``delta_pic_order_cnt0`` - - - * - __s32 - - ``delta_pic_order_cnt1`` - - - * - __u32 - - ``dec_ref_pic_marking_bit_size`` - - Size in bits of the dec_ref_pic_marking() syntax element. - * - __u32 - - ``pic_order_cnt_bit_size`` - - Combined size in bits of the picture order count related syntax - elements: pic_order_cnt_lsb, delta_pic_order_cnt_bottom, - delta_pic_order_cnt0, and delta_pic_order_cnt1. - * - __u32 - - ``slice_group_change_cycle`` - - - * - __u32 - - ``reserved`` - - Applications and drivers must set this to zero. - * - __u32 - - ``flags`` - - See :ref:`Decode Parameters Flags <h264_decode_params_flags>` - -.. _h264_decode_params_flags: - -``Decode Parameters Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC`` - - 0x00000001 - - That picture is an IDR picture - * - ``V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC`` - - 0x00000002 - - - * - ``V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD`` - - 0x00000004 - - - -.. c:type:: v4l2_h264_dpb_entry - -.. cssclass:: longtable - -.. flat-table:: struct v4l2_h264_dpb_entry - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u64 - - ``reference_ts`` - - Timestamp of the V4L2 capture buffer to use as reference, used - with B-coded and P-coded frames. The timestamp refers to the - ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the - :c:func:`v4l2_timeval_to_ns()` function to convert the struct - :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. - * - __u32 - - ``pic_num`` - - - * - __u16 - - ``frame_num`` - - - * - __u8 - - ``fields`` - - Specifies how the DPB entry is referenced. See :ref:`Reference Fields <h264_ref_fields>` - * - __u8 - - ``reserved[5]`` - - Applications and drivers must set this to zero. - * - __s32 - - ``top_field_order_cnt`` - - - * - __s32 - - ``bottom_field_order_cnt`` - - - * - __u32 - - ``flags`` - - See :ref:`DPB Entry Flags <h264_dpb_flags>` - -.. _h264_dpb_flags: - -``DPB Entries Flags`` - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_H264_DPB_ENTRY_FLAG_VALID`` - - 0x00000001 - - The DPB entry is valid (non-empty) and should be considered. - * - ``V4L2_H264_DPB_ENTRY_FLAG_ACTIVE`` - - 0x00000002 - - The DPB entry is used for reference. - * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` - - 0x00000004 - - The DPB entry is used for long-term reference. - * - ``V4L2_H264_DPB_ENTRY_FLAG_FIELD`` - - 0x00000008 - - The DPB entry is a single field or a complementary field pair. - -``V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (enum)`` - Specifies the decoding mode to use. Currently exposes slice-based and - frame-based decoding but new modes might be added later on. - This control is used as a modifier for V4L2_PIX_FMT_H264_SLICE - pixel format. Applications that support V4L2_PIX_FMT_H264_SLICE - are required to set this control in order to specify the decoding mode - that is expected for the buffer. - Drivers may expose a single or multiple decoding modes, depending - on what they can support. - - .. note:: - - This menu control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_mpeg_video_h264_decode_mode - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED`` - - 0 - - Decoding is done at the slice granularity. - The OUTPUT buffer must contain a single slice. - When this mode is selected, the ``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS`` - control shall be set. When multiple slices compose a frame, - use of ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` flag - is required. - * - ``V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED`` - - 1 - - Decoding is done at the frame granularity, - The OUTPUT buffer must contain all slices needed to decode the - frame. The OUTPUT buffer must also contain both fields. - This mode will be supported by devices that - parse the slice(s) header(s) in hardware. When this mode is - selected, the ``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS`` - control shall not be set. - -``V4L2_CID_MPEG_VIDEO_H264_START_CODE (enum)`` - Specifies the H264 slice start code expected for each slice. - This control is used as a modifier for V4L2_PIX_FMT_H264_SLICE - pixel format. Applications that support V4L2_PIX_FMT_H264_SLICE - are required to set this control in order to specify the start code - that is expected for the buffer. - Drivers may expose a single or multiple start codes, depending - on what they can support. - - .. note:: - - This menu control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_mpeg_video_h264_start_code - -.. cssclass:: longtable - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - ``V4L2_MPEG_VIDEO_H264_START_CODE_NONE`` - - 0 - - Selecting this value specifies that H264 slices are passed - to the driver without any start code. - * - ``V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B`` - - 1 - - Selecting this value specifies that H264 slices are expected - to be prefixed by Annex B start codes. According to :ref:`h264` - valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001. - .. _v4l2-mpeg-mpeg2: ``V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (struct)`` @@ -2905,127 +2213,6 @@ enum v4l2_mpeg_mfc51_video_force_frame_type - - Force a non-coded frame. -.. _v4l2-mpeg-fwht: - -``V4L2_CID_MPEG_VIDEO_FWHT_PARAMS (struct)`` - Specifies the fwht parameters (as extracted from the bitstream) for the - associated FWHT data. This includes the necessary parameters for - configuring a stateless hardware decoding pipeline for FWHT. - - .. note:: - - This compound control is not yet part of the public kernel API and - it is expected to change. - -.. c:type:: v4l2_ctrl_fwht_params - -.. cssclass:: longtable - -.. tabularcolumns:: |p{1.4cm}|p{4.3cm}|p{11.8cm}| - -.. flat-table:: struct v4l2_ctrl_fwht_params - :header-rows: 0 - :stub-columns: 0 - :widths: 1 1 2 - - * - __u64 - - ``backward_ref_ts`` - - Timestamp of the V4L2 capture buffer to use as backward reference, used - with P-coded frames. The timestamp refers to the - ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the - :c:func:`v4l2_timeval_to_ns()` function to convert the struct - :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. - * - __u32 - - ``version`` - - The version of the codec - * - __u32 - - ``width`` - - The width of the frame - * - __u32 - - ``height`` - - The height of the frame - * - __u32 - - ``flags`` - - The flags of the frame, see :ref:`fwht-flags`. - * - __u32 - - ``colorspace`` - - The colorspace of the frame, from enum :c:type:`v4l2_colorspace`. - * - __u32 - - ``xfer_func`` - - The transfer function, from enum :c:type:`v4l2_xfer_func`. - * - __u32 - - ``ycbcr_enc`` - - The Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. - * - __u32 - - ``quantization`` - - The quantization range, from enum :c:type:`v4l2_quantization`. - - - -.. _fwht-flags: - -FWHT Flags -============ - -.. cssclass:: longtable - -.. tabularcolumns:: |p{6.8cm}|p{2.4cm}|p{8.3cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 3 1 4 - - * - ``FWHT_FL_IS_INTERLACED`` - - 0x00000001 - - Set if this is an interlaced format - * - ``FWHT_FL_IS_BOTTOM_FIRST`` - - 0x00000002 - - Set if this is a bottom-first (NTSC) interlaced format - * - ``FWHT_FL_IS_ALTERNATE`` - - 0x00000004 - - Set if each 'frame' contains just one field - * - ``FWHT_FL_IS_BOTTOM_FIELD`` - - 0x00000008 - - If FWHT_FL_IS_ALTERNATE was set, then this is set if this 'frame' is the - bottom field, else it is the top field. - * - ``FWHT_FL_LUMA_IS_UNCOMPRESSED`` - - 0x00000010 - - Set if the luma plane is uncompressed - * - ``FWHT_FL_CB_IS_UNCOMPRESSED`` - - 0x00000020 - - Set if the cb plane is uncompressed - * - ``FWHT_FL_CR_IS_UNCOMPRESSED`` - - 0x00000040 - - Set if the cr plane is uncompressed - * - ``FWHT_FL_CHROMA_FULL_HEIGHT`` - - 0x00000080 - - Set if the chroma plane has the same height as the luma plane, - else the chroma plane is half the height of the luma plane - * - ``FWHT_FL_CHROMA_FULL_WIDTH`` - - 0x00000100 - - Set if the chroma plane has the same width as the luma plane, - else the chroma plane is half the width of the luma plane - * - ``FWHT_FL_ALPHA_IS_UNCOMPRESSED`` - - 0x00000200 - - Set if the alpha plane is uncompressed - * - ``FWHT_FL_I_FRAME`` - - 0x00000400 - - Set if this is an I-frame - * - ``FWHT_FL_COMPONENTS_NUM_MSK`` - - 0x00070000 - - A 4-values flag - the number of components - 1 - * - ``FWHT_FL_PIXENC_YUV`` - - 0x00080000 - - Set if the pixel encoding is YUV - * - ``FWHT_FL_PIXENC_RGB`` - - 0x00100000 - - Set if the pixel encoding is RGB - * - ``FWHT_FL_PIXENC_HSV`` - - 0x00180000 - - Set if the pixel encoding is HSV - - CX2341x MPEG Controls ===================== diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst index 9457dc340c31..de43f5c8486d 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst @@ -58,3 +58,17 @@ Image Source Control IDs The unit cell consists of the whole area of the pixel, sensitive and non-sensitive. This control is required for automatic calibration of sensors/cameras. + +.. c:type:: v4l2_area + +.. flat-table:: struct v4l2_area + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``width`` + - Width of the area. + * - __u32 + - ``height`` + - Height of the area. diff --git a/Documentation/userspace-api/media/v4l/extended-controls.rst b/Documentation/userspace-api/media/v4l/extended-controls.rst index 70301538d222..44fcd67f20bf 100644 --- a/Documentation/userspace-api/media/v4l/extended-controls.rst +++ b/Documentation/userspace-api/media/v4l/extended-controls.rst @@ -55,8 +55,8 @@ controls in that array and a control class. Control classes are used to group similar controls into a single class. For example, control class ``V4L2_CTRL_CLASS_USER`` contains all user controls (i. e. all controls that can also be set using the old :ref:`VIDIOC_S_CTRL <VIDIOC_G_CTRL>` -ioctl). Control class ``V4L2_CTRL_CLASS_MPEG`` contains all controls -relating to MPEG encoding, etc. +ioctl). Control class ``V4L2_CTRL_CLASS_CODEC`` contains controls +relating to codecs. All controls in the control array must belong to the specified control class. An error is returned if this is not the case. @@ -130,9 +130,9 @@ control class is found: .. code-block:: c - qctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL; + qctrl.id = V4L2_CTRL_CLASS_CODEC | V4L2_CTRL_FLAG_NEXT_CTRL; while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &qctrl)) { - if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_MPEG) + if (V4L2_CTRL_ID2CLASS(qctrl.id) != V4L2_CTRL_CLASS_CODEC) break; /* ... */ qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL; diff --git a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst index d585909bc4e2..acad5f3ca0c1 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst @@ -57,18 +57,18 @@ Compressed Formats - H264 parsed slice data, including slice headers, either with or without the start code, as extracted from the H264 bitstream. This format is adapted for stateless video decoders that implement an - H264 pipeline (using the :ref:`mem2mem` and :ref:`media-request-api`). + H264 pipeline with the :ref:`stateless_decoder`. This pixelformat has two modifiers that must be set at least once - through the ``V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE`` - and ``V4L2_CID_MPEG_VIDEO_H264_START_CODE`` controls. + through the ``V4L2_CID_STATELESS_H264_DECODE_MODE`` + and ``V4L2_CID_STATELESS_H264_START_CODE`` controls. In addition, metadata associated with the frame to decode are - required to be passed through the ``V4L2_CID_MPEG_VIDEO_H264_SPS``, - ``V4L2_CID_MPEG_VIDEO_H264_PPS``, - ``V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX``, - ``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS`` and - ``V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS`` controls. See the - :ref:`associated Codec Control IDs <v4l2-mpeg-h264>`. Exactly - one output and one capture buffer must be provided for use + required to be passed through the ``V4L2_CID_STATELESS_H264_SPS``, + ``V4L2_CID_STATELESS_H264_PPS``, + ``V4L2_CID_STATELESS_H264_SCALING_MATRIX``, + ``V4L2_CID_STATELESS_H264_SLICE_PARAMS`` and + ``V4L2_CID_STATELESS_H264_DECODE_PARAMS`` controls. See the + :ref:`associated Codec Control IDs <v4l2-codec-stateless-h264>`. + Exactly one output and one capture buffer must be provided for use with this pixel format. The output buffer must contain the appropriate number of macroblocks to decode a full corresponding frame to the matching capture buffer. @@ -77,11 +77,6 @@ Compressed Formats 7.3.2.8 "Slice layer without partitioning RBSP syntax" and the following sections. - .. note:: - - This format is not yet part of the public kernel API and it - is expected to change. - * .. _V4L2-PIX-FMT-H263: - ``V4L2_PIX_FMT_H263`` @@ -196,10 +191,10 @@ Compressed Formats through the ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE`` and ``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE`` controls. Metadata associated with the frame to decode is required to be passed - through the following controls : - * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS`` - * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS`` - * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS`` + through the following controls: + ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``, + ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``, and + ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``. See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`. Buffers associated with this pixel format must contain the appropriate number of macroblocks to decode a full corresponding frame. @@ -222,4 +217,6 @@ Compressed Formats - ``V4L2_PIX_FMT_FWHT_STATELESS`` - 'SFWH' - Same format as V4L2_PIX_FMT_FWHT but requires stateless codec implementation. - See the :ref:`associated Codec Control IDs <v4l2-mpeg-fwht>`. + Metadata associated with the frame to decode is required to be passed + through the ``V4L2_CID_STATELESS_FWHT_PARAMS`` control. + See the :ref:`associated Codec Control ID <codec-stateless-fwht>`. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-grey.rst b/Documentation/userspace-api/media/v4l/pixfmt-grey.rst deleted file mode 100644 index 121365b03c57..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-grey.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-GREY: - -************************** -V4L2_PIX_FMT_GREY ('GREY') -************************** - -Grey-scale image - - -Description -=========== - -This is a grey-scale image. It is really a degenerate Y'CbCr format -which simply contains no Cb or Cr data. - -**Byte Order.** -Each cell is one byte. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-m420.rst b/Documentation/userspace-api/media/v4l/pixfmt-m420.rst index 13cf36a8cd5c..c01a949e7c11 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-m420.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-m420.rst @@ -67,60 +67,5 @@ Each cell is one byte. **Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 1 - - Y - - - - Y - - Y - - - - Y - * - - * - 2 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 3 - - Y - - - - Y - - Y - - - - Y +Chroma samples are :ref:`interstitially sited<yuv-chroma-centered>` +horizontally and vertically. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst b/Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst index 7e43837ed260..fa04f00bcd2e 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst @@ -1,7 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 -.. _v4l2-meta-fmt-params-rkisp1: -.. _v4l2-meta-fmt-stat-rkisp1: +.. _v4l2-meta-fmt-rk-isp1-params: + +.. _v4l2-meta-fmt-rk-isp1-stat-3a: ***************************************************************************** V4L2_META_FMT_RK_ISP1_PARAMS ('rk1p'), V4L2_META_FMT_RK_ISP1_STAT_3A ('rk1s') @@ -46,4 +47,4 @@ important tuning tools using software control loop. rkisp1 uAPI data types ====================== -.. kernel-doc:: drivers/staging/media/rkisp1/uapi/rkisp1-config.h +.. kernel-doc:: include/uapi/linux/rkisp1-config.h diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst deleted file mode 100644 index dd2f38129fe6..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv12.rst +++ /dev/null @@ -1,129 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV12: -.. _V4L2-PIX-FMT-NV21: - -****************************************************** -V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21') -****************************************************** - - -V4L2_PIX_FMT_NV21 -Formats with ½ horizontal and vertical chroma resolution, also known as -YUV 4:2:0. One luminance and one chrominance plane with alternating -chroma samples as opposed to ``V4L2_PIX_FMT_YVU420`` - - -Description -=========== - -These are two-plane versions of the YUV 4:2:0 format. The three -components are separated into two sub-images or planes. The Y plane is -first. The Y plane has one byte per pixel. For ``V4L2_PIX_FMT_NV12``, a -combined CbCr plane immediately follows the Y plane in memory. The CbCr -plane is the same width, in bytes, as the Y plane (and of the image), -but is half as tall in pixels. Each CbCr pair belongs to four pixels. -For example, Cb\ :sub:`0`/Cr\ :sub:`0` belongs to Y'\ :sub:`00`, -Y'\ :sub:`01`, Y'\ :sub:`10`, Y'\ :sub:`11`. ``V4L2_PIX_FMT_NV21`` is -the same except the Cb and Cr bytes are swapped, the CrCb plane starts -with a Cr byte. - -If the Y plane has pad bytes after each row, then the CbCr plane has as -many pad bytes after its rows. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cb\ :sub:`00` - - Cr\ :sub:`00` - - Cb\ :sub:`01` - - Cr\ :sub:`01` - * - start + 20: - - Cb\ :sub:`10` - - Cr\ :sub:`10` - - Cb\ :sub:`11` - - Cr\ :sub:`11` - - -**Color Sample Location:** - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 1 - - Y - - - - Y - - Y - - - - Y - * - - * - 2 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 3 - - Y - - - - Y - - Y - - - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv12m.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv12m.rst deleted file mode 100644 index 250f8b977605..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv12m.rst +++ /dev/null @@ -1,144 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV12M: -.. _v4l2-pix-fmt-nv12mt-16x16: -.. _V4L2-PIX-FMT-NV21M: - -*********************************************************************************** -V4L2_PIX_FMT_NV12M ('NM12'), V4L2_PIX_FMT_NV21M ('NM21'), V4L2_PIX_FMT_NV12MT_16X16 -*********************************************************************************** - - -V4L2_PIX_FMT_NV21M -V4L2_PIX_FMT_NV12MT_16X16 -Variation of ``V4L2_PIX_FMT_NV12`` and ``V4L2_PIX_FMT_NV21`` with planes -non contiguous in memory. - - -Description -=========== - -This is a multi-planar, two-plane version of the YUV 4:2:0 format. The -three components are separated into two sub-images or planes. -``V4L2_PIX_FMT_NV12M`` differs from ``V4L2_PIX_FMT_NV12`` in that the -two planes are non-contiguous in memory, i.e. the chroma plane do not -necessarily immediately follows the luma plane. The luminance data -occupies the first plane. The Y plane has one byte per pixel. In the -second plane there is a chrominance data with alternating chroma -samples. The CbCr plane is the same width, in bytes, as the Y plane (and -of the image), but is half as tall in pixels. Each CbCr pair belongs to -four pixels. For example, Cb\ :sub:`0`/Cr\ :sub:`0` belongs to -Y'\ :sub:`00`, Y'\ :sub:`01`, Y'\ :sub:`10`, Y'\ :sub:`11`. -``V4L2_PIX_FMT_NV12MT_16X16`` is the tiled version of -``V4L2_PIX_FMT_NV12M`` with 16x16 macroblock tiles. Here pixels are -arranged in 16x16 2D tiles and tiles are arranged in linear order in -memory. ``V4L2_PIX_FMT_NV21M`` is the same as ``V4L2_PIX_FMT_NV12M`` -except the Cb and Cr bytes are swapped, the CrCb plane starts with a Cr -byte. - -``V4L2_PIX_FMT_NV12M`` is intended to be used only in drivers and -applications that support the multi-planar API, described in -:ref:`planar-apis`. - -If the Y plane has pad bytes after each row, then the CbCr plane has as -many pad bytes after its rows. - -**Byte Order.** -Each cell is one byte. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start0 + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start0 + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start0 + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start0 + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - - * - start1 + 0: - - Cb\ :sub:`00` - - Cr\ :sub:`00` - - Cb\ :sub:`01` - - Cr\ :sub:`01` - * - start1 + 4: - - Cb\ :sub:`10` - - Cr\ :sub:`10` - - Cb\ :sub:`11` - - Cr\ :sub:`11` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 1 - - Y - - - - Y - - Y - - - - Y - * - - * - 2 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - - - C - - - * - 3 - - Y - - - - Y - - Y - - - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv12mt.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv12mt.rst deleted file mode 100644 index 46f63d793ec5..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv12mt.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV12MT: - -**************************** -V4L2_PIX_FMT_NV12MT ('TM12') -**************************** - -Formats with ½ horizontal and vertical chroma resolution. This format -has two planes - one for luminance and one for chrominance. Chroma -samples are interleaved. The difference to ``V4L2_PIX_FMT_NV12`` is the -memory layout. Pixels are grouped in macroblocks of 64x32 size. The -order of macroblocks in memory is also not standard. - - -Description -=========== - -This is the two-plane versions of the YUV 4:2:0 format where data is -grouped into 64x32 macroblocks. The three components are separated into -two sub-images or planes. The Y plane has one byte per pixel and pixels -are grouped into 64x32 macroblocks. The CbCr plane has the same width, -in bytes, as the Y plane (and the image), but is half as tall in pixels. -The chroma plane is also grouped into 64x32 macroblocks. - -Width of the buffer has to be aligned to the multiple of 128, and height -alignment is 32. Every four adjacent buffers - two horizontally and two -vertically are grouped together and are located in memory in Z or -flipped Z order. - -Layout of macroblocks in memory is presented in the following figure. - - -.. _nv12mt: - -.. kernel-figure:: nv12mt.svg - :alt: nv12mt.svg - :align: center - - V4L2_PIX_FMT_NV12MT macroblock Z shape memory layout - -The requirement that width is multiple of 128 is implemented because, -the Z shape cannot be cut in half horizontally. In case the vertical -resolution of macroblocks is odd then the last row of macroblocks is -arranged in a linear order. - -In case of chroma the layout is identical. Cb and Cr samples are -interleaved. Height of the buffer is aligned to 32. - - -.. _nv12mt_ex: - -.. kernel-figure:: nv12mt_example.svg - :alt: nv12mt_example.svg - :align: center - - Example V4L2_PIX_FMT_NV12MT memory layout of macroblocks - -Memory layout of macroblocks of ``V4L2_PIX_FMT_NV12MT`` format in most -extreme case. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv16.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv16.rst deleted file mode 100644 index 22295fc0c359..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv16.rst +++ /dev/null @@ -1,153 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV16: -.. _V4L2-PIX-FMT-NV61: - -****************************************************** -V4L2_PIX_FMT_NV16 ('NV16'), V4L2_PIX_FMT_NV61 ('NV61') -****************************************************** - -V4L2_PIX_FMT_NV61 -Formats with ½ horizontal chroma resolution, also known as YUV 4:2:2. -One luminance and one chrominance plane with alternating chroma samples -as opposed to ``V4L2_PIX_FMT_YVU420`` - - -Description -=========== - -These are two-plane versions of the YUV 4:2:2 format. The three -components are separated into two sub-images or planes. The Y plane is -first. The Y plane has one byte per pixel. For ``V4L2_PIX_FMT_NV16``, a -combined CbCr plane immediately follows the Y plane in memory. The CbCr -plane is the same width and height, in bytes, as the Y plane (and of the -image). Each CbCr pair belongs to two pixels. For example, -Cb\ :sub:`0`/Cr\ :sub:`0` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`. -``V4L2_PIX_FMT_NV61`` is the same except the Cb and Cr bytes are -swapped, the CrCb plane starts with a Cr byte. - -If the Y plane has pad bytes after each row, then the CbCr plane has as -many pad bytes after its rows. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cb\ :sub:`00` - - Cr\ :sub:`00` - - Cb\ :sub:`01` - - Cr\ :sub:`01` - * - start + 20: - - Cb\ :sub:`10` - - Cr\ :sub:`10` - - Cb\ :sub:`11` - - Cr\ :sub:`11` - * - start + 24: - - Cb\ :sub:`20` - - Cr\ :sub:`20` - - Cb\ :sub:`21` - - Cr\ :sub:`21` - * - start + 28: - - Cb\ :sub:`30` - - Cr\ :sub:`30` - - Cb\ :sub:`31` - - Cr\ :sub:`31` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 1 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - - * - 2 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 3 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv16m.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv16m.rst deleted file mode 100644 index 812bf2ccabf0..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv16m.rst +++ /dev/null @@ -1,157 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV16M: -.. _v4l2-pix-fmt-nv61m: - -******************************************************** -V4L2_PIX_FMT_NV16M ('NM16'), V4L2_PIX_FMT_NV61M ('NM61') -******************************************************** - -V4L2_PIX_FMT_NV61M -Variation of ``V4L2_PIX_FMT_NV16`` and ``V4L2_PIX_FMT_NV61`` with planes -non contiguous in memory. - - -Description -=========== - -This is a multi-planar, two-plane version of the YUV 4:2:2 format. The -three components are separated into two sub-images or planes. -``V4L2_PIX_FMT_NV16M`` differs from ``V4L2_PIX_FMT_NV16`` in that the -two planes are non-contiguous in memory, i.e. the chroma plane does not -necessarily immediately follow the luma plane. The luminance data -occupies the first plane. The Y plane has one byte per pixel. In the -second plane there is chrominance data with alternating chroma samples. -The CbCr plane is the same width and height, in bytes, as the Y plane. -Each CbCr pair belongs to two pixels. For example, -Cb\ :sub:`0`/Cr\ :sub:`0` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`. -``V4L2_PIX_FMT_NV61M`` is the same as ``V4L2_PIX_FMT_NV16M`` except the -Cb and Cr bytes are swapped, the CrCb plane starts with a Cr byte. - -``V4L2_PIX_FMT_NV16M`` and ``V4L2_PIX_FMT_NV61M`` are intended to be -used only in drivers and applications that support the multi-planar API, -described in :ref:`planar-apis`. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start0 + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start0 + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start0 + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start0 + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - - * - start1 + 0: - - Cb\ :sub:`00` - - Cr\ :sub:`00` - - Cb\ :sub:`02` - - Cr\ :sub:`02` - * - start1 + 4: - - Cb\ :sub:`10` - - Cr\ :sub:`10` - - Cb\ :sub:`12` - - Cr\ :sub:`12` - * - start1 + 8: - - Cb\ :sub:`20` - - Cr\ :sub:`20` - - Cb\ :sub:`22` - - Cr\ :sub:`22` - * - start1 + 12: - - Cb\ :sub:`30` - - Cr\ :sub:`30` - - Cb\ :sub:`32` - - Cr\ :sub:`32` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 1 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - - * - 2 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - - * - 3 - - Y - - - - Y - - Y - - - - Y - * - - - - - C - - - - - - C - - diff --git a/Documentation/userspace-api/media/v4l/pixfmt-nv24.rst b/Documentation/userspace-api/media/v4l/pixfmt-nv24.rst deleted file mode 100644 index bf1b94062fc2..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-nv24.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-NV24: -.. _V4L2-PIX-FMT-NV42: - -****************************************************** -V4L2_PIX_FMT_NV24 ('NV24'), V4L2_PIX_FMT_NV42 ('NV42') -****************************************************** - -V4L2_PIX_FMT_NV42 -Formats with full horizontal and vertical chroma resolutions, also known -as YUV 4:4:4. One luminance and one chrominance plane with alternating -chroma samples as opposed to ``V4L2_PIX_FMT_YVU420`` - - -Description -=========== - -These are two-plane versions of the YUV 4:4:4 format. The three -components are separated into two sub-images or planes. The Y plane is -first, with each Y sample stored in one byte per pixel. For -``V4L2_PIX_FMT_NV24``, a combined CbCr plane immediately follows the Y -plane in memory. The CbCr plane has the same width and height, in -pixels, as the Y plane (and the image). Each line contains one CbCr pair -per pixel, with each Cb and Cr sample stored in one byte. -``V4L2_PIX_FMT_NV42`` is the same except that the Cb and Cr samples are -swapped, the CrCb plane starts with a Cr sample. - -If the Y plane has pad bytes after each row, then the CbCr plane has -twice as many pad bytes after its rows. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cb\ :sub:`00` - - Cr\ :sub:`00` - - Cb\ :sub:`01` - - Cr\ :sub:`01` - - Cb\ :sub:`02` - - Cr\ :sub:`02` - - Cb\ :sub:`03` - - Cr\ :sub:`03` - * - start + 24: - - Cb\ :sub:`10` - - Cr\ :sub:`10` - - Cb\ :sub:`11` - - Cr\ :sub:`11` - - Cb\ :sub:`12` - - Cr\ :sub:`12` - - Cb\ :sub:`13` - - Cr\ :sub:`13` - * - start + 32: - - Cb\ :sub:`20` - - Cr\ :sub:`20` - - Cb\ :sub:`21` - - Cr\ :sub:`21` - - Cb\ :sub:`22` - - Cr\ :sub:`22` - - Cb\ :sub:`23` - - Cr\ :sub:`23` - * - start + 40: - - Cb\ :sub:`30` - - Cr\ :sub:`30` - - Cb\ :sub:`31` - - Cr\ :sub:`31` - - Cb\ :sub:`32` - - Cr\ :sub:`32` - - Cb\ :sub:`33` - - Cr\ :sub:`33` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst b/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst index 84262208dd1c..eb551b57557e 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst @@ -6,12 +6,32 @@ Packed YUV formats ****************** -Description -=========== +Similarly to the packed RGB formats, the packed YUV formats store the Y, Cb and +Cr components consecutively in memory. They may apply subsampling to the chroma +components and thus differ in how they interlave the three components. -Similar to the packed RGB formats these formats store the Y, Cb and Cr -component of each pixel in one 16 or 32 bit word. +.. note:: + + - In all the tables that follow, bit 7 is the most significant bit in a byte. + - 'Y', 'Cb' and 'Cr' denote bits of the luma, blue chroma (also known as + 'U') and red chroma (also known as 'V') components respectively. 'A' + denotes bits of the alpha component (if supported by the format), and 'X' + denotes padding bits. + + +4:4:4 Subsampling +================= +These formats do not subsample the chroma components and store each pixels as a +full triplet of Y, Cb and Cr values. + +The next table lists the packed YUV 4:4:4 formats with less than 8 bits per +component. They are named based on the order of the Y, Cb and Cr components as +seen in a 16-bit word, which is then stored in memory in little endian byte +order, and on the number of bits for each component. For instance the YUV565 +format stores a pixel in a 16-bit word [15:0] laid out at as [Y'\ :sub:`4-0` +Cb\ :sub:`5-0` Cr\ :sub:`4-0`], and stored in memory in two bytes, +[Cb\ :sub:`2-0` Cr\ :sub:`4-0`] followed by [Y'\ :sub:`4-0` Cb\ :sub:`5-3`]. .. raw:: latex @@ -19,11 +39,9 @@ component of each pixel in one 16 or 32 bit word. \tiny \setlength{\tabcolsep}{2pt} -.. _packed-yuv-formats: - -.. tabularcolumns:: |p{2.5cm}|p{0.69cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}| +.. tabularcolumns:: |p{2.5cm}|p{0.69cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}|p{0.31cm}| -.. flat-table:: Packed YUV Image Formats +.. flat-table:: Packed YUV 4:4:4 Image Formats (less than 8bpc) :header-rows: 2 :stub-columns: 0 @@ -34,10 +52,6 @@ component of each pixel in one 16 or 32 bit word. - :cspan:`7` Byte 1 - - :cspan:`7` Byte 2 - - - :cspan:`7` Byte 3 - * - - - 7 @@ -58,24 +72,6 @@ component of each pixel in one 16 or 32 bit word. - 1 - 0 - - 7 - - 6 - - 5 - - 4 - - 3 - - 2 - - 1 - - 0 - - - 7 - - 6 - - 5 - - 4 - - 3 - - 2 - - 1 - - 0 - * .. _V4L2-PIX-FMT-YUV444: - ``V4L2_PIX_FMT_YUV444`` @@ -99,8 +95,6 @@ component of each pixel in one 16 or 32 bit word. - Y'\ :sub:`1` - Y'\ :sub:`0` - - :cspan:`15` - * .. _V4L2-PIX-FMT-YUV555: - ``V4L2_PIX_FMT_YUV555`` @@ -124,7 +118,6 @@ component of each pixel in one 16 or 32 bit word. - Cb\ :sub:`4` - Cb\ :sub:`3` - - :cspan:`15` * .. _V4L2-PIX-FMT-YUV565: - ``V4L2_PIX_FMT_YUV565`` @@ -148,226 +141,219 @@ component of each pixel in one 16 or 32 bit word. - Cb\ :sub:`4` - Cb\ :sub:`3` - - :cspan:`15` +.. raw:: latex - * .. _V4L2-PIX-FMT-YUV32: + \endgroup - - ``V4L2_PIX_FMT_YUV32`` - - 'YUV4' +.. note:: - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` + For the YUV444 and YUV555 formats, the value of alpha bits is undefined + when reading from the driver, ignored when writing to the driver, except + when alpha blending has been negotiated for a :ref:`Video Overlay + <overlay>` or :ref:`Video Output Overlay <osd>`. - - Y'\ :sub:`7` - - Y'\ :sub:`6` - - Y'\ :sub:`5` - - Y'\ :sub:`4` - - Y'\ :sub:`3` - - Y'\ :sub:`2` - - Y'\ :sub:`1` - - Y'\ :sub:`0` - - Cb\ :sub:`7` - - Cb\ :sub:`6` - - Cb\ :sub:`5` - - Cb\ :sub:`4` - - Cb\ :sub:`3` - - Cb\ :sub:`2` - - Cb\ :sub:`1` - - Cb\ :sub:`0` +The next table lists the packed YUV 4:4:4 formats with 8 bits per component. +They are named based on the order of the Y, Cb and Cr components as stored in +memory, and on the total number of bits per pixel. For instance, the VUYX32 +format stores a pixel with Cr\ :sub:`7-0` in the first byte, Cb\ :sub:`7-0` in +the second byte and Y'\ :sub:`7-0` in the third byte. - - Cr\ :sub:`7` - - Cr\ :sub:`6` - - Cr\ :sub:`5` - - Cr\ :sub:`4` - - Cr\ :sub:`3` - - Cr\ :sub:`2` - - Cr\ :sub:`1` - - Cr\ :sub:`0` +.. flat-table:: Packed YUV Image Formats (8bpc) + :header-rows: 1 + :stub-columns: 0 - * .. _V4L2-PIX-FMT-AYUV32: + * - Identifier + - Code + - Byte 0 + - Byte 1 + - Byte 2 + - Byte 3 - - ``V4L2_PIX_FMT_AYUV32`` - - 'AYUV' + * .. _V4L2-PIX-FMT-YUV32: - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` + - ``V4L2_PIX_FMT_YUV32`` + - 'YUV4' - - Y'\ :sub:`7` - - Y'\ :sub:`6` - - Y'\ :sub:`5` - - Y'\ :sub:`4` - - Y'\ :sub:`3` - - Y'\ :sub:`2` - - Y'\ :sub:`1` - - Y'\ :sub:`0` + - A\ :sub:`7-0` + - Y'\ :sub:`7-0` + - Cb\ :sub:`7-0` + - Cr\ :sub:`7-0` - - Cb\ :sub:`7` - - Cb\ :sub:`6` - - Cb\ :sub:`5` - - Cb\ :sub:`4` - - Cb\ :sub:`3` - - Cb\ :sub:`2` - - Cb\ :sub:`1` - - Cb\ :sub:`0` + * .. _V4L2-PIX-FMT-AYUV32: - - Cr\ :sub:`7` - - Cr\ :sub:`6` - - Cr\ :sub:`5` - - Cr\ :sub:`4` - - Cr\ :sub:`3` - - Cr\ :sub:`2` - - Cr\ :sub:`1` - - Cr\ :sub:`0` + - ``V4L2_PIX_FMT_AYUV32`` + - 'AYUV' + + - A\ :sub:`7-0` + - Y'\ :sub:`7-0` + - Cb\ :sub:`7-0` + - Cr\ :sub:`7-0` * .. _V4L2-PIX-FMT-XYUV32: - ``V4L2_PIX_FMT_XYUV32`` - 'XYUV' - - - - - - - - - - - - - - - - - - - Y'\ :sub:`7` - - Y'\ :sub:`6` - - Y'\ :sub:`5` - - Y'\ :sub:`4` - - Y'\ :sub:`3` - - Y'\ :sub:`2` - - Y'\ :sub:`1` - - Y'\ :sub:`0` - - - Cb\ :sub:`7` - - Cb\ :sub:`6` - - Cb\ :sub:`5` - - Cb\ :sub:`4` - - Cb\ :sub:`3` - - Cb\ :sub:`2` - - Cb\ :sub:`1` - - Cb\ :sub:`0` - - - Cr\ :sub:`7` - - Cr\ :sub:`6` - - Cr\ :sub:`5` - - Cr\ :sub:`4` - - Cr\ :sub:`3` - - Cr\ :sub:`2` - - Cr\ :sub:`1` - - Cr\ :sub:`0` + - X\ :sub:`7-0` + - Y'\ :sub:`7-0` + - Cb\ :sub:`7-0` + - Cr\ :sub:`7-0` * .. _V4L2-PIX-FMT-VUYA32: - ``V4L2_PIX_FMT_VUYA32`` - 'VUYA' - - Cr\ :sub:`7` - - Cr\ :sub:`6` - - Cr\ :sub:`5` - - Cr\ :sub:`4` - - Cr\ :sub:`3` - - Cr\ :sub:`2` - - Cr\ :sub:`1` - - Cr\ :sub:`0` - - - Cb\ :sub:`7` - - Cb\ :sub:`6` - - Cb\ :sub:`5` - - Cb\ :sub:`4` - - Cb\ :sub:`3` - - Cb\ :sub:`2` - - Cb\ :sub:`1` - - Cb\ :sub:`0` - - - Y'\ :sub:`7` - - Y'\ :sub:`6` - - Y'\ :sub:`5` - - Y'\ :sub:`4` - - Y'\ :sub:`3` - - Y'\ :sub:`2` - - Y'\ :sub:`1` - - Y'\ :sub:`0` - - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` + - Cr\ :sub:`7-0` + - Cb\ :sub:`7-0` + - Y'\ :sub:`7-0` + - A\ :sub:`7-0` * .. _V4L2-PIX-FMT-VUYX32: - ``V4L2_PIX_FMT_VUYX32`` - 'VUYX' - - Cr\ :sub:`7` - - Cr\ :sub:`6` - - Cr\ :sub:`5` - - Cr\ :sub:`4` - - Cr\ :sub:`3` - - Cr\ :sub:`2` - - Cr\ :sub:`1` - - Cr\ :sub:`0` + - Cr\ :sub:`7-0` + - Cb\ :sub:`7-0` + - Y'\ :sub:`7-0` + - X\ :sub:`7-0` - - Cb\ :sub:`7` - - Cb\ :sub:`6` - - Cb\ :sub:`5` - - Cb\ :sub:`4` - - Cb\ :sub:`3` - - Cb\ :sub:`2` - - Cb\ :sub:`1` - - Cb\ :sub:`0` +.. note:: - - Y'\ :sub:`7` - - Y'\ :sub:`6` - - Y'\ :sub:`5` - - Y'\ :sub:`4` + - The alpha component is expected to contain a meaningful value that can be + used by drivers and applications. + - The padding bits contain undefined values that must be ignored by all + applications and drivers. + + +4:2:2 Subsampling +================= + +These formats, commonly referred to as YUYV or YUY2, subsample the chroma +components horizontally by 2, storing 2 pixels in 4 bytes. + +.. flat-table:: Packed YUV 4:2:2 Formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Byte 0 + - Byte 1 + - Byte 2 + - Byte 3 + - Byte 4 + - Byte 5 + - Byte 6 + - Byte 7 + * .. _V4L2-PIX-FMT-UYVY: + + - ``V4L2_PIX_FMT_UYVY`` + - 'UYVY' + + - Cb\ :sub:`0` + - Y'\ :sub:`0` + - Cr\ :sub:`0` + - Y'\ :sub:`1` + - Cb\ :sub:`2` + - Y'\ :sub:`2` + - Cr\ :sub:`2` - Y'\ :sub:`3` + * .. _V4L2-PIX-FMT-VYUY: + + - ``V4L2_PIX_FMT_VYUY`` + - 'VYUY' + + - Cr\ :sub:`0` + - Y'\ :sub:`0` + - Cb\ :sub:`0` + - Y'\ :sub:`1` + - Cr\ :sub:`2` - Y'\ :sub:`2` + - Cb\ :sub:`2` + - Y'\ :sub:`3` + * .. _V4L2-PIX-FMT-YUYV: + + - ``V4L2_PIX_FMT_YUYV`` + - 'YUYV' + + - Y'\ :sub:`0` + - Cb\ :sub:`0` - Y'\ :sub:`1` + - Cr\ :sub:`0` + - Y'\ :sub:`2` + - Cb\ :sub:`2` + - Y'\ :sub:`3` + - Cr\ :sub:`2` + * .. _V4L2-PIX-FMT-YVYU: + + - ``V4L2_PIX_FMT_YVYU`` + - 'YVYU' + - Y'\ :sub:`0` + - Cr\ :sub:`0` + - Y'\ :sub:`1` + - Cb\ :sub:`0` + - Y'\ :sub:`2` + - Cr\ :sub:`2` + - Y'\ :sub:`3` + - Cb\ :sub:`2` - - - - - - - - - - - - - - - - +**Color Sample Location:** +Chroma samples are :ref:`interstitially sited<yuv-chroma-centered>` +horizontally. -.. raw:: latex - \endgroup +4:1:1 Subsampling +================= + +This format subsamples the chroma components horizontally by 4, storing 8 +pixels in 12 bytes. + +.. flat-table:: Packed YUV 4:1:1 Formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Byte 0 + - Byte 1 + - Byte 2 + - Byte 3 + - Byte 4 + - Byte 5 + - Byte 6 + - Byte 7 + - Byte 8 + - Byte 9 + - Byte 10 + - Byte 11 + * .. _V4L2-PIX-FMT-Y41P: + + - ``V4L2_PIX_FMT_Y41P`` + - 'Y41P' + + - Cb\ :sub:`0` + - Y'\ :sub:`0` + - Cr\ :sub:`0` + - Y'\ :sub:`1` + - Cb\ :sub:`4` + - Y'\ :sub:`2` + - Cr\ :sub:`4` + - Y'\ :sub:`3` + - Y'\ :sub:`4` + - Y'\ :sub:`5` + - Y'\ :sub:`6` + - Y'\ :sub:`7` .. note:: - #) Bit 7 is the most significant bit; + Do not confuse ``V4L2_PIX_FMT_Y41P`` with + :ref:`V4L2_PIX_FMT_YUV411P <V4L2-PIX-FMT-YUV411P>`. Y41P is derived from + "YUV 4:1:1 *packed*", while YUV411P stands for "YUV 4:1:1 *planar*". - #) The value of a = alpha bits is undefined when reading from the driver, - ignored when writing to the driver, except when alpha blending has - been negotiated for a :ref:`Video Overlay <overlay>` or - :ref:`Video Output Overlay <osd>` for the formats Y444, YUV555 and - YUV4. However, for formats AYUV32 and VUYA32, the alpha component is - expected to contain a meaningful value that can be used by drivers - and applications. And, the formats XYUV32 and VUYX32 contain undefined - alpha values that must be ignored by all applications and drivers. +**Color Sample Location:** +Chroma samples are :ref:`interstitially sited<yuv-chroma-centered>` +horizontally. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst index 9d827097c1d9..897676ee2c9d 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst @@ -6,13 +6,62 @@ RGB Formats *********** -Description -=========== - -These formats are designed to match the pixel formats of typical PC -graphics frame buffers. They occupy 8, 16, 24 or 32 bits per pixel. -These are all packed-pixel formats, meaning all the data for a pixel lie -next to each other in memory. +These formats encode each pixel as a triplet of RGB values. They are packed +formats, meaning that the RGB values for one pixel are stored consecutively in +memory and each pixel consumes an integer number of bytes. When the number of +bits required to store a pixel is not aligned to a byte boundary, the data is +padded with additional bits to fill the remaining byte. + +The formats differ by the number of bits per RGB component (typically but not +always the same for all components), the order of components in memory, and the +presence of an alpha component or additional padding bits. + +The usage and value of the alpha bits in formats that support them (named ARGB +or a permutation thereof, collectively referred to as alpha formats) depend on +the device type and hardware operation. :ref:`Capture <capture>` devices +(including capture queues of mem-to-mem devices) fill the alpha component in +memory. When the device captures an alpha channel the alpha component will have +a meaningful value. Otherwise, when the device doesn't capture an alpha channel +but can set the alpha bit to a user-configurable value, the +:ref:`V4L2_CID_ALPHA_COMPONENT <v4l2-alpha-component>` control is used to +specify that alpha value, and the alpha component of all pixels will be set to +the value specified by that control. Otherwise a corresponding format without +an alpha component (XRGB or XBGR) must be used instead of an alpha format. + +:ref:`Output <output>` devices (including output queues of mem-to-mem devices +and :ref:`video output overlay <osd>` devices) read the alpha component from +memory. When the device processes the alpha channel the alpha component must be +filled with meaningful values by applications. Otherwise a corresponding format +without an alpha component (XRGB or XBGR) must be used instead of an alpha +format. + +Formats that contain padding bits are named XRGB (or a permutation thereof). +The padding bits contain undefined values and must be ignored by applications, +devices and drivers, for both :ref:`capture` and :ref:`output` devices. + +.. note:: + + - In all the tables that follow, bit 7 is the most significant bit in a byte. + - 'r', 'g' and 'b' denote bits of the red, green and blue components + respectively. 'a' denotes bits of the alpha component (if supported by the + format), and 'x' denotes padding bits. + + +Less Than 8 Bits Per Component +============================== + +These formats store an RGB triplet in one, two or four bytes. They are named +based on the order of the RGB components as seen in a 8-, 16- or 32-bit word, +which is then stored in memory in little endian byte order (unless otherwise +noted by the presence of bit 31 in the 4CC value), and on the number of bits +for each component. For instance, the RGB565 format stores a pixel in a 16-bit +word [15:0] laid out at as [R\ :sub:`4` R\ :sub:`3` R\ :sub:`2` R\ :sub:`1` +R\ :sub:`0` G\ :sub:`5` G\ :sub:`4` G\ :sub:`3` G\ :sub:`2` G\ :sub:`1` +G\ :sub:`0` B\ :sub:`4` B\ :sub:`3` B\ :sub:`2` B\ :sub:`1` B\ :sub:`0`], and +stored in memory in two bytes, [R\ :sub:`4` R\ :sub:`3` R\ :sub:`2` R\ :sub:`1` +R\ :sub:`0` G\ :sub:`5` G\ :sub:`4` G\ :sub:`3`] followed by [G\ :sub:`2` +G\ :sub:`1` G\ :sub:`0` B\ :sub:`4` B\ :sub:`3` B\ :sub:`2` B\ :sub:`1` +B\ :sub:`0`]. .. raw:: latex @@ -23,7 +72,7 @@ next to each other in memory. .. tabularcolumns:: |p{2.8cm}|p{2.0cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}| -.. flat-table:: RGB Image Formats +.. flat-table:: RGB Formats With Less Than 8 Bits Per Component :header-rows: 2 :stub-columns: 0 @@ -121,10 +170,10 @@ next to each other in memory. - b\ :sub:`1` - b\ :sub:`0` - - `-` - - `-` - - `-` - - `-` + - x + - x + - x + - x - r\ :sub:`3` - r\ :sub:`2` - r\ :sub:`1` @@ -162,10 +211,10 @@ next to each other in memory. - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` - - `-` - - `-` - - `-` - - `-` + - x + - x + - x + - x - r\ :sub:`3` - r\ :sub:`2` @@ -213,10 +262,10 @@ next to each other in memory. - r\ :sub:`1` - r\ :sub:`0` - - `-` - - `-` - - `-` - - `-` + - x + - x + - x + - x - b\ :sub:`3` - b\ :sub:`2` - b\ :sub:`1` @@ -254,10 +303,10 @@ next to each other in memory. - r\ :sub:`2` - r\ :sub:`1` - r\ :sub:`0` - - `-` - - `-` - - `-` - - `-` + - x + - x + - x + - x - b\ :sub:`3` - b\ :sub:`2` @@ -305,7 +354,7 @@ next to each other in memory. - b\ :sub:`1` - b\ :sub:`0` - - `-` + - x - r\ :sub:`4` - r\ :sub:`3` - r\ :sub:`2` @@ -349,7 +398,7 @@ next to each other in memory. - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` - - `-` + - x - r\ :sub:`4` - r\ :sub:`3` @@ -397,7 +446,7 @@ next to each other in memory. - r\ :sub:`1` - r\ :sub:`0` - - `-` + - x - b\ :sub:`4` - b\ :sub:`3` - b\ :sub:`2` @@ -441,7 +490,7 @@ next to each other in memory. - r\ :sub:`2` - r\ :sub:`1` - r\ :sub:`0` - - `-` + - x - b\ :sub:`4` - b\ :sub:`3` @@ -503,7 +552,7 @@ next to each other in memory. - ``V4L2_PIX_FMT_XRGB555X`` - 'XR15' | (1 << 31) - - `-` + - x - r\ :sub:`4` - r\ :sub:`3` - r\ :sub:`2` @@ -544,538 +593,188 @@ next to each other in memory. - b\ :sub:`1` - b\ :sub:`0` - - * .. _V4L2-PIX-FMT-BGR24: + * .. _V4L2-PIX-FMT-BGR666: - - ``V4L2_PIX_FMT_BGR24`` - - 'BGR3' + - ``V4L2_PIX_FMT_BGR666`` + - 'BGRH' - - b\ :sub:`7` - - b\ :sub:`6` - b\ :sub:`5` - b\ :sub:`4` - b\ :sub:`3` - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - g\ :sub:`5` - g\ :sub:`4` + - g\ :sub:`3` - g\ :sub:`2` - g\ :sub:`1` - g\ :sub:`0` - - - r\ :sub:`7` - - r\ :sub:`6` - r\ :sub:`5` - r\ :sub:`4` - r\ :sub:`3` - r\ :sub:`2` + - r\ :sub:`1` - r\ :sub:`0` - - - * .. _V4L2-PIX-FMT-RGB24: + - x + - x + - x + - x + - x + - x - - ``V4L2_PIX_FMT_RGB24`` - - 'RGB3' + - x + - x + - x + - x + - x + - x + - x + - x - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` +.. raw:: latex - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` + \endgroup - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - - * .. _V4L2-PIX-FMT-BGR666: - - ``V4L2_PIX_FMT_BGR666`` - - 'BGRH' +8 Bits Per Component +==================== - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - g\ :sub:`5` - - g\ :sub:`4` +These formats store an RGB triplet in three or four bytes. They are named based +on the order of the RGB components as stored in memory, and on the total number +of bits per pixel. For instance, RGB24 format stores a pixel with [R\ :sub:`7` +R\ :sub:`6` R\ :sub:`5` R\ :sub:`4` R\ :sub:`3` R\ :sub:`2` R\ :sub:`1` +R\ :sub:`0`] in the first byte, [G\ :sub:`7` G\ :sub:`6` G\ :sub:`5` G\ :sub:`4` +G\ :sub:`3` G\ :sub:`2` G\ :sub:`1` G\ :sub:`0`] in the second byte and +[B\ :sub:`7` B\ :sub:`6` B\ :sub:`5` B\ :sub:`4` B\ :sub:`3` B\ :sub:`2` +B\ :sub:`1` B\ :sub:`0`] in the third byte. This differs from the DRM format +nomenclature that instead use the order of components as seen in a 24- or +32-bit little endian word. - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` +.. raw:: latex - - r\ :sub:`1` - - r\ :sub:`0` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - * .. _V4L2-PIX-FMT-ABGR32: + \begingroup + \tiny + \setlength{\tabcolsep}{2pt} - - ``V4L2_PIX_FMT_ABGR32`` - - 'AR24' +.. tabularcolumns:: |p{2.8cm}|p{2.0cm}|p{2.0cm}|p{2.0cm}|p{2.0cm}|p{2.0cm}|p{2.0cm}|p{2.0cm}| - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` +.. flat-table:: RGB Formats With 8 Bits Per Component + :header-rows: 1 + :stub-columns: 0 - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` + * - Identifier + - Code + - Byte 0 in memory + - Byte 1 + - Byte 2 + - Byte 3 + * .. _V4L2-PIX-FMT-BGR24: - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` - * .. _V4L2-PIX-FMT-XBGR32: + - ``V4L2_PIX_FMT_BGR24`` + - 'BGR3' - - ``V4L2_PIX_FMT_XBGR32`` - - 'XR24' + - G\ :sub:`7-0` + - B\ :sub:`7-0` + - R\ :sub:`7-0` + - + * .. _V4L2-PIX-FMT-RGB24: - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` + - ``V4L2_PIX_FMT_RGB24`` + - 'RGB3' - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` + - R\ :sub:`7-0` + - G\ :sub:`7-0` + - B\ :sub:`7-0` + - + * .. _V4L2-PIX-FMT-ABGR32: - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` + - ``V4L2_PIX_FMT_ABGR32`` + - 'AR24' - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` + - B\ :sub:`7-0` + - G\ :sub:`7-0` + - R\ :sub:`7-0` + - A\ :sub:`7-0` + * .. _V4L2-PIX-FMT-XBGR32: + + - ``V4L2_PIX_FMT_XBGR32`` + - 'XR24' + + - B\ :sub:`7-0` + - G\ :sub:`7-0` + - R\ :sub:`7-0` + - X\ :sub:`7-0` * .. _V4L2-PIX-FMT-BGRA32: - ``V4L2_PIX_FMT_BGRA32`` - 'RA24' - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` + - A\ :sub:`7-0` + - B\ :sub:`7-0` + - G\ :sub:`7-0` + - R\ :sub:`7-0` * .. _V4L2-PIX-FMT-BGRX32: - ``V4L2_PIX_FMT_BGRX32`` - 'RX24' - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` + - X\ :sub:`7-0` + - B\ :sub:`7-0` + - G\ :sub:`7-0` + - R\ :sub:`7-0` * .. _V4L2-PIX-FMT-RGBA32: - ``V4L2_PIX_FMT_RGBA32`` - 'AB24' - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` + - R\ :sub:`7-0` + - G\ :sub:`7-0` + - B\ :sub:`7-0` + - A\ :sub:`7-0` * .. _V4L2-PIX-FMT-RGBX32: - ``V4L2_PIX_FMT_RGBX32`` - 'XB24' - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` - - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` + - R\ :sub:`7-0` + - G\ :sub:`7-0` + - B\ :sub:`7-0` + - X\ :sub:`7-0` * .. _V4L2-PIX-FMT-ARGB32: - ``V4L2_PIX_FMT_ARGB32`` - 'BA24' - - a\ :sub:`7` - - a\ :sub:`6` - - a\ :sub:`5` - - a\ :sub:`4` - - a\ :sub:`3` - - a\ :sub:`2` - - a\ :sub:`1` - - a\ :sub:`0` - - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` + - A\ :sub:`7-0` + - R\ :sub:`7-0` + - G\ :sub:`7-0` + - B\ :sub:`7-0` * .. _V4L2-PIX-FMT-XRGB32: - ``V4L2_PIX_FMT_XRGB32`` - 'BX24' - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - `-` - - - r\ :sub:`7` - - r\ :sub:`6` - - r\ :sub:`5` - - r\ :sub:`4` - - r\ :sub:`3` - - r\ :sub:`2` - - r\ :sub:`1` - - r\ :sub:`0` - - - g\ :sub:`7` - - g\ :sub:`6` - - g\ :sub:`5` - - g\ :sub:`4` - - g\ :sub:`3` - - g\ :sub:`2` - - g\ :sub:`1` - - g\ :sub:`0` - - - b\ :sub:`7` - - b\ :sub:`6` - - b\ :sub:`5` - - b\ :sub:`4` - - b\ :sub:`3` - - b\ :sub:`2` - - b\ :sub:`1` - - b\ :sub:`0` + - X\ :sub:`7-0` + - R\ :sub:`7-0` + - G\ :sub:`7-0` + - B\ :sub:`7-0` .. raw:: latex \endgroup -.. note:: Bit 7 is the most significant bit. - -The usage and value of the alpha bits (a) in the ARGB and ABGR formats -(collectively referred to as alpha formats) depend on the device type -and hardware operation. :ref:`Capture <capture>` devices (including -capture queues of mem-to-mem devices) fill the alpha component in -memory. When the device outputs an alpha channel the alpha component -will have a meaningful value. Otherwise, when the device doesn't output -an alpha channel but can set the alpha bit to a user-configurable value, -the :ref:`V4L2_CID_ALPHA_COMPONENT <v4l2-alpha-component>` control -is used to specify that alpha value, and the alpha component of all -pixels will be set to the value specified by that control. Otherwise a -corresponding format without an alpha component (XRGB or XBGR) must be -used instead of an alpha format. - -:ref:`Output <output>` devices (including output queues of mem-to-mem -devices and :ref:`video output overlay <osd>` devices) read the alpha -component from memory. When the device processes the alpha channel the -alpha component must be filled with meaningful values by applications. -Otherwise a corresponding format without an alpha component (XRGB or -XBGR) must be used instead of an alpha format. - -The XRGB and XBGR formats contain undefined bits (-). Applications, -devices and drivers must ignore those bits, for both -:ref:`capture` and :ref:`output` devices. - -**Byte Order.** -Each cell is one byte. - - -.. raw:: latex - - \small - -.. tabularcolumns:: |p{3.1cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}|p{0.8cm}| - -.. flat-table:: RGB byte order - :header-rows: 0 - :stub-columns: 0 - :widths: 11 3 3 3 3 3 3 3 3 3 3 3 3 - - * - start + 0: - - B\ :sub:`00` - - G\ :sub:`00` - - R\ :sub:`00` - - B\ :sub:`01` - - G\ :sub:`01` - - R\ :sub:`01` - - B\ :sub:`02` - - G\ :sub:`02` - - R\ :sub:`02` - - B\ :sub:`03` - - G\ :sub:`03` - - R\ :sub:`03` - * - start + 12: - - B\ :sub:`10` - - G\ :sub:`10` - - R\ :sub:`10` - - B\ :sub:`11` - - G\ :sub:`11` - - R\ :sub:`11` - - B\ :sub:`12` - - G\ :sub:`12` - - R\ :sub:`12` - - B\ :sub:`13` - - G\ :sub:`13` - - R\ :sub:`13` - * - start + 24: - - B\ :sub:`20` - - G\ :sub:`20` - - R\ :sub:`20` - - B\ :sub:`21` - - G\ :sub:`21` - - R\ :sub:`21` - - B\ :sub:`22` - - G\ :sub:`22` - - R\ :sub:`22` - - B\ :sub:`23` - - G\ :sub:`23` - - R\ :sub:`23` - * - start + 36: - - B\ :sub:`30` - - G\ :sub:`30` - - R\ :sub:`30` - - B\ :sub:`31` - - G\ :sub:`31` - - R\ :sub:`31` - - B\ :sub:`32` - - G\ :sub:`32` - - R\ :sub:`32` - - B\ :sub:`33` - - G\ :sub:`33` - - R\ :sub:`33` - -.. raw:: latex - - \normalsize -Formats defined in :ref:`pixfmt-rgb-deprecated` are deprecated and -must not be used by new drivers. They are documented here for reference. -The meaning of their alpha bits ``(a)`` are ill-defined and interpreted as in -either the corresponding ARGB or XRGB format, depending on the driver. +Deprecated RGB Formats +====================== +Formats defined in :ref:`pixfmt-rgb-deprecated` are deprecated and must not be +used by new drivers. They are documented here for reference. The meaning of +their alpha bits ``(a)`` is ill-defined and they are interpreted as in either +the corresponding ARGB or XRGB format, depending on the driver. .. raw:: latex diff --git a/Documentation/userspace-api/media/v4l/pixfmt-uyvy.rst b/Documentation/userspace-api/media/v4l/pixfmt-uyvy.rst deleted file mode 100644 index bae975fb14f6..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-uyvy.rst +++ /dev/null @@ -1,110 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-UYVY: - -************************** -V4L2_PIX_FMT_UYVY ('UYVY') -************************** - - -Variation of ``V4L2_PIX_FMT_YUYV`` with different order of samples in -memory - - -Description -=========== - -In this format each four bytes is two pixels. Each four bytes is two -Y's, a Cb and a Cr. Each Y goes to one of the pixels, and the Cb and Cr -belong to both pixels. As you can see, the Cr and Cb components have -half the horizontal resolution of the Y component. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Cb\ :sub:`00` - - Y'\ :sub:`00` - - Cr\ :sub:`00` - - Y'\ :sub:`01` - - Cb\ :sub:`01` - - Y'\ :sub:`02` - - Cr\ :sub:`01` - - Y'\ :sub:`03` - * - start + 8: - - Cb\ :sub:`10` - - Y'\ :sub:`10` - - Cr\ :sub:`10` - - Y'\ :sub:`11` - - Cb\ :sub:`11` - - Y'\ :sub:`12` - - Cr\ :sub:`11` - - Y'\ :sub:`13` - * - start + 16: - - Cb\ :sub:`20` - - Y'\ :sub:`20` - - Cr\ :sub:`20` - - Y'\ :sub:`21` - - Cb\ :sub:`21` - - Y'\ :sub:`22` - - Cr\ :sub:`21` - - Y'\ :sub:`23` - * - start + 24: - - Cb\ :sub:`30` - - Y'\ :sub:`30` - - Cr\ :sub:`30` - - Y'\ :sub:`31` - - Cb\ :sub:`31` - - Y'\ :sub:`32` - - Cr\ :sub:`31` - - Y'\ :sub:`33` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - C - - Y - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-vyuy.rst b/Documentation/userspace-api/media/v4l/pixfmt-vyuy.rst deleted file mode 100644 index aff8588b67a9..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-vyuy.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-VYUY: - -************************** -V4L2_PIX_FMT_VYUY ('VYUY') -************************** - - -Variation of ``V4L2_PIX_FMT_YUYV`` with different order of samples in -memory - - -Description -=========== - -In this format each four bytes is two pixels. Each four bytes is two -Y's, a Cb and a Cr. Each Y goes to one of the pixels, and the Cb and Cr -belong to both pixels. As you can see, the Cr and Cb components have -half the horizontal resolution of the Y component. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Cr\ :sub:`00` - - Y'\ :sub:`00` - - Cb\ :sub:`00` - - Y'\ :sub:`01` - - Cr\ :sub:`01` - - Y'\ :sub:`02` - - Cb\ :sub:`01` - - Y'\ :sub:`03` - * - start + 8: - - Cr\ :sub:`10` - - Y'\ :sub:`10` - - Cb\ :sub:`10` - - Y'\ :sub:`11` - - Cr\ :sub:`11` - - Y'\ :sub:`12` - - Cb\ :sub:`11` - - Y'\ :sub:`13` - * - start + 16: - - Cr\ :sub:`20` - - Y'\ :sub:`20` - - Cb\ :sub:`20` - - Y'\ :sub:`21` - - Cr\ :sub:`21` - - Y'\ :sub:`22` - - Cb\ :sub:`21` - - Y'\ :sub:`23` - * - start + 24: - - Cr\ :sub:`30` - - Y'\ :sub:`30` - - Cb\ :sub:`30` - - Y'\ :sub:`31` - - Cr\ :sub:`31` - - Y'\ :sub:`32` - - Cb\ :sub:`31` - - Y'\ :sub:`33` - - -**Color Sample Location:** - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - - - 2 - - 3 - * - 0 - - Y - - C - - Y - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y10.rst b/Documentation/userspace-api/media/v4l/pixfmt-y10.rst deleted file mode 100644 index 05f018dd883f..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y10.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y10: - -************************* -V4L2_PIX_FMT_Y10 ('Y10 ') -************************* - - -Grey-scale image - - -Description -=========== - -This is a grey-scale image with a depth of 10 bits per pixel. Pixels are -stored in 16-bit words with unused high bits padded with 0. The least -significant byte is stored at lower memory addresses (little-endian). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00low` - - Y'\ :sub:`00high` - - Y'\ :sub:`01low` - - Y'\ :sub:`01high` - - Y'\ :sub:`02low` - - Y'\ :sub:`02high` - - Y'\ :sub:`03low` - - Y'\ :sub:`03high` - * - start + 8: - - Y'\ :sub:`10low` - - Y'\ :sub:`10high` - - Y'\ :sub:`11low` - - Y'\ :sub:`11high` - - Y'\ :sub:`12low` - - Y'\ :sub:`12high` - - Y'\ :sub:`13low` - - Y'\ :sub:`13high` - * - start + 16: - - Y'\ :sub:`20low` - - Y'\ :sub:`20high` - - Y'\ :sub:`21low` - - Y'\ :sub:`21high` - - Y'\ :sub:`22low` - - Y'\ :sub:`22high` - - Y'\ :sub:`23low` - - Y'\ :sub:`23high` - * - start + 24: - - Y'\ :sub:`30low` - - Y'\ :sub:`30high` - - Y'\ :sub:`31low` - - Y'\ :sub:`31high` - - Y'\ :sub:`32low` - - Y'\ :sub:`32high` - - Y'\ :sub:`33low` - - Y'\ :sub:`33high` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y10b.rst b/Documentation/userspace-api/media/v4l/pixfmt-y10b.rst deleted file mode 100644 index 38d353b37df9..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y10b.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y10BPACK: - -****************************** -V4L2_PIX_FMT_Y10BPACK ('Y10B') -****************************** - -Grey-scale image as a bit-packed array - - -Description -=========== - -This is a packed grey-scale image format with a depth of 10 bits per -pixel. Pixels are stored in a bit-packed array of 10bit bits per pixel, -with no padding between them and with the most significant bits coming -first from the left. - -**Bit-packed representation.** - -pixels cross the byte boundary and have a ratio of 5 bytes for each 4 -pixels. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - Y'\ :sub:`00[9:2]` - - Y'\ :sub:`00[1:0]`\ Y'\ :sub:`01[9:4]` - - Y'\ :sub:`01[3:0]`\ Y'\ :sub:`02[9:6]` - - Y'\ :sub:`02[5:0]`\ Y'\ :sub:`03[9:8]` - - Y'\ :sub:`03[7:0]` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y10p.rst b/Documentation/userspace-api/media/v4l/pixfmt-y10p.rst deleted file mode 100644 index dd20d3438732..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y10p.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y10P: - -****************************** -V4L2_PIX_FMT_Y10P ('Y10P') -****************************** - -Grey-scale image as a MIPI RAW10 packed array - - -Description -=========== - -This is a packed grey-scale image format with a depth of 10 bits per -pixel. Every four consecutive pixels are packed into 5 bytes. Each of -the first 4 bytes contain the 8 high order bits of the pixels, and -the 5th byte contains the 2 least significants bits of each pixel, -in the same order. - -**Bit-packed representation.** - -.. raw:: latex - - \small - -.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 8 8 8 8 64 - - * - Y'\ :sub:`00[9:2]` - - Y'\ :sub:`01[9:2]` - - Y'\ :sub:`02[9:2]` - - Y'\ :sub:`03[9:2]` - - Y'\ :sub:`03[1:0]`\ (bits 7--6) Y'\ :sub:`02[1:0]`\ (bits 5--4) - Y'\ :sub:`01[1:0]`\ (bits 3--2) Y'\ :sub:`00[1:0]`\ (bits 1--0) - -.. raw:: latex - - \normalsize diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y12.rst b/Documentation/userspace-api/media/v4l/pixfmt-y12.rst deleted file mode 100644 index 20e12a18da72..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y12.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y12: - -************************* -V4L2_PIX_FMT_Y12 ('Y12 ') -************************* - - -Grey-scale image - - -Description -=========== - -This is a grey-scale image with a depth of 12 bits per pixel. Pixels are -stored in 16-bit words with unused high bits padded with 0. The least -significant byte is stored at lower memory addresses (little-endian). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00low` - - Y'\ :sub:`00high` - - Y'\ :sub:`01low` - - Y'\ :sub:`01high` - - Y'\ :sub:`02low` - - Y'\ :sub:`02high` - - Y'\ :sub:`03low` - - Y'\ :sub:`03high` - * - start + 8: - - Y'\ :sub:`10low` - - Y'\ :sub:`10high` - - Y'\ :sub:`11low` - - Y'\ :sub:`11high` - - Y'\ :sub:`12low` - - Y'\ :sub:`12high` - - Y'\ :sub:`13low` - - Y'\ :sub:`13high` - * - start + 16: - - Y'\ :sub:`20low` - - Y'\ :sub:`20high` - - Y'\ :sub:`21low` - - Y'\ :sub:`21high` - - Y'\ :sub:`22low` - - Y'\ :sub:`22high` - - Y'\ :sub:`23low` - - Y'\ :sub:`23high` - * - start + 24: - - Y'\ :sub:`30low` - - Y'\ :sub:`30high` - - Y'\ :sub:`31low` - - Y'\ :sub:`31high` - - Y'\ :sub:`32low` - - Y'\ :sub:`32high` - - Y'\ :sub:`33low` - - Y'\ :sub:`33high` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y14.rst b/Documentation/userspace-api/media/v4l/pixfmt-y14.rst deleted file mode 100644 index 2a4826b77105..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y14.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y14: - -************************* -V4L2_PIX_FMT_Y14 ('Y14 ') -************************* - - -Grey-scale image - - -Description -=========== - -This is a grey-scale image with a depth of 14 bits per pixel. Pixels are -stored in 16-bit words with unused high bits padded with 0. The least -significant byte is stored at lower memory addresses (little-endian). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00low` - - Y'\ :sub:`00high` - - Y'\ :sub:`01low` - - Y'\ :sub:`01high` - - Y'\ :sub:`02low` - - Y'\ :sub:`02high` - - Y'\ :sub:`03low` - - Y'\ :sub:`03high` - * - start + 8: - - Y'\ :sub:`10low` - - Y'\ :sub:`10high` - - Y'\ :sub:`11low` - - Y'\ :sub:`11high` - - Y'\ :sub:`12low` - - Y'\ :sub:`12high` - - Y'\ :sub:`13low` - - Y'\ :sub:`13high` - * - start + 16: - - Y'\ :sub:`20low` - - Y'\ :sub:`20high` - - Y'\ :sub:`21low` - - Y'\ :sub:`21high` - - Y'\ :sub:`22low` - - Y'\ :sub:`22high` - - Y'\ :sub:`23low` - - Y'\ :sub:`23high` - * - start + 24: - - Y'\ :sub:`30low` - - Y'\ :sub:`30high` - - Y'\ :sub:`31low` - - Y'\ :sub:`31high` - - Y'\ :sub:`32low` - - Y'\ :sub:`32high` - - Y'\ :sub:`33low` - - Y'\ :sub:`33high` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y16-be.rst b/Documentation/userspace-api/media/v4l/pixfmt-y16-be.rst deleted file mode 100644 index 6d70cd78cbf6..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y16-be.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y16-BE: - -**************************************** -V4L2_PIX_FMT_Y16_BE ('Y16 ' | (1 << 31)) -**************************************** - - -Grey-scale image - - -Description -=========== - -This is a grey-scale image with a depth of 16 bits per pixel. The most -significant byte is stored at lower memory addresses (big-endian). - -.. note:: - - The actual sampling precision may be lower than 16 bits, for - example 10 bits per pixel with values in range 0 to 1023. - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00high` - - Y'\ :sub:`00low` - - Y'\ :sub:`01high` - - Y'\ :sub:`01low` - - Y'\ :sub:`02high` - - Y'\ :sub:`02low` - - Y'\ :sub:`03high` - - Y'\ :sub:`03low` - * - start + 8: - - Y'\ :sub:`10high` - - Y'\ :sub:`10low` - - Y'\ :sub:`11high` - - Y'\ :sub:`11low` - - Y'\ :sub:`12high` - - Y'\ :sub:`12low` - - Y'\ :sub:`13high` - - Y'\ :sub:`13low` - * - start + 16: - - Y'\ :sub:`20high` - - Y'\ :sub:`20low` - - Y'\ :sub:`21high` - - Y'\ :sub:`21low` - - Y'\ :sub:`22high` - - Y'\ :sub:`22low` - - Y'\ :sub:`23high` - - Y'\ :sub:`23low` - * - start + 24: - - Y'\ :sub:`30high` - - Y'\ :sub:`30low` - - Y'\ :sub:`31high` - - Y'\ :sub:`31low` - - Y'\ :sub:`32high` - - Y'\ :sub:`32low` - - Y'\ :sub:`33high` - - Y'\ :sub:`33low` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y16.rst b/Documentation/userspace-api/media/v4l/pixfmt-y16.rst deleted file mode 100644 index 398ad8ba5d64..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y16.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y16: - -************************* -V4L2_PIX_FMT_Y16 ('Y16 ') -************************* - - -Grey-scale image - - -Description -=========== - -This is a grey-scale image with a depth of 16 bits per pixel. The least -significant byte is stored at lower memory addresses (little-endian). - -.. note:: - - The actual sampling precision may be lower than 16 bits, for - example 10 bits per pixel with values in range 0 to 1023. - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00low` - - Y'\ :sub:`00high` - - Y'\ :sub:`01low` - - Y'\ :sub:`01high` - - Y'\ :sub:`02low` - - Y'\ :sub:`02high` - - Y'\ :sub:`03low` - - Y'\ :sub:`03high` - * - start + 8: - - Y'\ :sub:`10low` - - Y'\ :sub:`10high` - - Y'\ :sub:`11low` - - Y'\ :sub:`11high` - - Y'\ :sub:`12low` - - Y'\ :sub:`12high` - - Y'\ :sub:`13low` - - Y'\ :sub:`13high` - * - start + 16: - - Y'\ :sub:`20low` - - Y'\ :sub:`20high` - - Y'\ :sub:`21low` - - Y'\ :sub:`21high` - - Y'\ :sub:`22low` - - Y'\ :sub:`22high` - - Y'\ :sub:`23low` - - Y'\ :sub:`23high` - * - start + 24: - - Y'\ :sub:`30low` - - Y'\ :sub:`30high` - - Y'\ :sub:`31low` - - Y'\ :sub:`31high` - - Y'\ :sub:`32low` - - Y'\ :sub:`32high` - - Y'\ :sub:`33low` - - Y'\ :sub:`33high` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y41p.rst b/Documentation/userspace-api/media/v4l/pixfmt-y41p.rst deleted file mode 100644 index d14cedf8f317..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-y41p.rst +++ /dev/null @@ -1,151 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-Y41P: - -************************** -V4L2_PIX_FMT_Y41P ('Y41P') -************************** - - -Format with ¼ horizontal chroma resolution, also known as YUV 4:1:1 - - -Description -=========== - -In this format each 12 bytes is eight pixels. In the twelve bytes are -two CbCr pairs and eight Y's. The first CbCr pair goes with the first -four Y's, and the second CbCr pair goes with the other four Y's. The Cb -and Cr components have one fourth the horizontal resolution of the Y -component. - -Do not confuse this format with -:ref:`V4L2_PIX_FMT_YUV411P <V4L2-PIX-FMT-YUV411P>`. Y41P is derived -from "YUV 4:1:1 *packed*", while YUV411P stands for "YUV 4:1:1 -*planar*". - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Cb\ :sub:`00` - - Y'\ :sub:`00` - - Cr\ :sub:`00` - - Y'\ :sub:`01` - - Cb\ :sub:`01` - - Y'\ :sub:`02` - - Cr\ :sub:`01` - - Y'\ :sub:`03` - - Y'\ :sub:`04` - - Y'\ :sub:`05` - - Y'\ :sub:`06` - - Y'\ :sub:`07` - * - start + 12: - - Cb\ :sub:`10` - - Y'\ :sub:`10` - - Cr\ :sub:`10` - - Y'\ :sub:`11` - - Cb\ :sub:`11` - - Y'\ :sub:`12` - - Cr\ :sub:`11` - - Y'\ :sub:`13` - - Y'\ :sub:`14` - - Y'\ :sub:`15` - - Y'\ :sub:`16` - - Y'\ :sub:`17` - * - start + 24: - - Cb\ :sub:`20` - - Y'\ :sub:`20` - - Cr\ :sub:`20` - - Y'\ :sub:`21` - - Cb\ :sub:`21` - - Y'\ :sub:`22` - - Cr\ :sub:`21` - - Y'\ :sub:`23` - - Y'\ :sub:`24` - - Y'\ :sub:`25` - - Y'\ :sub:`26` - - Y'\ :sub:`27` - * - start + 36: - - Cb\ :sub:`30` - - Y'\ :sub:`30` - - Cr\ :sub:`30` - - Y'\ :sub:`31` - - Cb\ :sub:`31` - - Y'\ :sub:`32` - - Cr\ :sub:`31` - - Y'\ :sub:`33` - - Y'\ :sub:`34` - - Y'\ :sub:`35` - - Y'\ :sub:`36` - - Y'\ :sub:`37` - - -**Color Sample Location:** - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - 1 - - - - 2 - - 3 - - 4 - - 5 - - - - 6 - - 7 - * - 0 - - Y - - Y - - C - - Y - - Y - - Y - - Y - - C - - Y - - Y - * - 1 - - Y - - Y - - C - - Y - - Y - - Y - - Y - - C - - Y - - Y - * - 2 - - Y - - Y - - C - - Y - - Y - - Y - - Y - - C - - Y - - Y - * - 3 - - Y - - Y - - C - - Y - - Y - - Y - - Y - - C - - Y - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst new file mode 100644 index 000000000000..0c8c5e0a380e --- /dev/null +++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst @@ -0,0 +1,126 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _yuv-luma-only: + +***************** +Luma-Only Formats +***************** + +This family of formats only store the luma component of a Y'CbCr image. They +are often referred to as greyscale formats. + +.. note:: + + - In all the tables that follow, bit 7 is the most significant bit in a byte. + - Formats are described with the minimum number of pixels needed to create a + byte-aligned repeating pattern. `...` indicates repetition of the pattern. + - Y'\ :sub:`x`\ [9:2] denotes bits 9 to 2 of the Y' value for pixel at colum + `x`. + - `0` denotes padding bits set to 0. + + +.. flat-table:: Luma-Only Image Formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Byte 0 + - Byte 1 + - Byte 2 + - Byte 3 + - Byte 4 + + * .. _V4L2-PIX-FMT-GREY: + + - ``V4L2_PIX_FMT_GREY`` + - 'GREY' + + - Y'\ :sub:`0`\ [7:0] + - ... + - ... + - ... + - ... + + * .. _V4L2-PIX-FMT-Y10: + + - ``V4L2_PIX_FMT_Y10`` + - 'Y10 ' + + - Y'\ :sub:`0`\ [7:0] + - `000000` Y'\ :sub:`0`\ [9:8] + - ... + - ... + - ... + + * .. _V4L2-PIX-FMT-Y10BPACK: + + - ``V4L2_PIX_FMT_Y10BPACK`` + - 'Y10B' + + - Y'\ :sub:`0`\ [9:2] + - Y'\ :sub:`0`\ [1:0] Y'\ :sub:`1`\ [9:4] + - Y'\ :sub:`1`\ [3:0] Y'\ :sub:`2`\ [9:6] + - Y'\ :sub:`2`\ [5:0] Y'\ :sub:`3`\ [9:8] + - Y'\ :sub:`3`\ [7:0] + + * .. _V4L2-PIX-FMT-Y10P: + + - ``V4L2_PIX_FMT_Y10P`` + - 'Y10P' + + - Y'\ :sub:`0`\ [7:0] + - Y'\ :sub:`1`\ [9:8] + - Y'\ :sub:`2`\ [9:2] + - Y'\ :sub:`3`\ [9:2] + - Y'\ :sub:`3`\ [1:0] Y'\ :sub:`2`\ [1:0] Y'\ :sub:`1`\ [1:0] Y'\ :sub:`0`\ [1:0] + + * .. _V4L2-PIX-FMT-Y12: + + - ``V4L2_PIX_FMT_Y12`` + - 'Y12 ' + + - Y'\ :sub:`0`\ [7:0] + - `0000` Y'\ :sub:`0`\ [11:8] + - ... + - ... + - ... + + * .. _V4L2-PIX-FMT-Y14: + + - ``V4L2_PIX_FMT_Y14`` + - 'Y14 ' + + - Y'\ :sub:`0`\ [7:0] + - `00` Y'\ :sub:`0`\ [13:8] + - ... + - ... + - ... + + * .. _V4L2-PIX-FMT-Y16: + + - ``V4L2_PIX_FMT_Y16`` + - 'Y16 ' + + - Y'\ :sub:`0`\ [7:0] + - Y'\ :sub:`0`\ [15:8] + - ... + - ... + - ... + + * .. _V4L2-PIX-FMT-Y16-BE: + + - ``V4L2_PIX_FMT_Y16_BE`` + - 'Y16 ' | (1U << 31) + + - Y'\ :sub:`0`\ [15:8] + - Y'\ :sub:`0`\ [7:0] + - ... + - ... + - ... + +.. note:: + + For the Y16 and Y16_BE formats, the actual sampling precision may be lower + than 16 bits. For example, 10 bits per pixel uses values in the range 0 to + 1023. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst new file mode 100644 index 000000000000..7d4d39201a3f --- /dev/null +++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst @@ -0,0 +1,950 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. planar-yuv: + +****************** +Planar YUV formats +****************** + +Planar formats split luma and chroma data in separate memory regions. They +exist in two variants: + +- Semi-planar formats use two planes. The first plane is the luma plane and + stores the Y components. The second plane is the chroma plane and stores the + Cb and Cr components interleaved. + +- Fully planar formats use three planes to store the Y, Cb and Cr components + separately. + +Within a plane, components are stored in pixel order, which may be linear or +tiled. Padding may be supported at the end of the lines, and the line stride of +the chroma planes may be constrained by the line stride of the luma plane. + +Some planar formats allow planes to be placed in independent memory locations. +They are identified by an 'M' suffix in their name (such as in +``V4L2_PIX_FMT_NV12M``). Those formats are intended to be used only in drivers +and applications that support the multi-planar API, described in +:ref:`planar-apis`. Unless explicitly documented as supporting non-contiguous +planes, formats require the planes to follow each other immediately in memory. + + +Semi-Planar YUV Formats +======================= + +These formats are commonly referred to as NV formats (NV12, NV16, ...). They +use two planes, and store the luma components in the first plane and the chroma +components in the second plane. The Cb and Cr components are interleaved in the +chroma plane, with Cb and Cr always stored in pairs. The chroma order is +exposed as different formats. + +For memory contiguous formats, the number of padding pixels at the end of the +chroma lines is identical to the padding of the luma lines. Without horizontal +subsampling, the chroma line stride (in bytes) is thus equal to twice the luma +line stride. With horizontal subsampling by 2, the chroma line stride is equal +to the luma line stride. Vertical subsampling doesn't affect the line stride. + +For non-contiguous formats, no constraints are enforced by the format on the +relationship between the luma and chroma line padding and stride. + +All components are stored with the same number of bits per component. + +.. flat-table:: Overview of Semi-Planar YUV Formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Bits per component + - Subsampling + - Chroma order [1]_ + - Contiguous [2]_ + - Tiling [3]_ + * - V4L2_PIX_FMT_NV12 + - 'NV12' + - 8 + - 4:2:0 + - Cb, Cr + - Yes + - Linear + * - V4L2_PIX_FMT_NV21 + - 'NV21' + - 8 + - 4:2:0 + - Cr, Cr + - Yes + - Linear + * - V4L2_PIX_FMT_NV12M + - 'NM12' + - 8 + - 4:2:0 + - Cb, Cr + - No + - Linear + * - V4L2_PIX_FMT_NV21M + - 'NM21' + - 8 + - 4:2:0 + - Cr, Cr + - No + - Linear + * - V4L2_PIX_FMT_NV12MT + - 'TM12' + - 8 + - 4:2:0 + - Cb, Cr + - No + - 64x32 macroblocks + + Horizontal Z order + * - V4L2_PIX_FMT_NV12MT_16X16 + - 'VM12' + - 8 + - 4:2:2 + - Cb, Cr + - No + - 16x16 macroblocks + * - V4L2_PIX_FMT_NV16 + - 'NV16' + - 8 + - 4:2:2 + - Cb, Cr + - Yes + - Linear + * - V4L2_PIX_FMT_NV61 + - 'NV61' + - 8 + - 4:2:2 + - Cr, Cr + - Yes + - Linear + * - V4L2_PIX_FMT_NV16M + - 'NM16' + - 8 + - 4:2:2 + - Cb, Cr + - No + - Linear + * - V4L2_PIX_FMT_NV61M + - 'NM61' + - 8 + - 4:2:2 + - Cr, Cr + - No + - Linear + * - V4L2_PIX_FMT_NV24 + - 'NV24' + - 8 + - 4:4:4 + - Cb, Cr + - Yes + - Linear + * - V4L2_PIX_FMT_NV42 + - 'NV42' + - 8 + - 4:4:4 + - Cr, Cr + - Yes + - Linear + +.. note:: + + .. [1] Order of chroma samples in the second plane + .. [2] Indicates if planes have to be contiguous in memory or can be + disjoint + .. [3] Macroblock size in pixels + + +**Color Sample Location:** +Chroma samples are :ref:`interstitially sited<yuv-chroma-centered>` +horizontally. + + +.. _V4L2-PIX-FMT-NV12: +.. _V4L2-PIX-FMT-NV21: +.. _V4L2-PIX-FMT-NV12M: +.. _V4L2-PIX-FMT-NV21M: + +NV12, NV21, NV12M and NV21M +--------------------------- + +Semi-planar YUV 4:2:0 formats. The chroma plane is subsampled by 2 in each +direction. Chroma lines contain half the number of pixels and the same number +of bytes as luma lines, and the chroma plane contains half the number of lines +of the luma plane. + +.. flat-table:: Sample 4x4 NV12 Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cb\ :sub:`00` + - Cr\ :sub:`00` + - Cb\ :sub:`01` + - Cr\ :sub:`01` + * - start + 20: + - Cb\ :sub:`10` + - Cr\ :sub:`10` + - Cb\ :sub:`11` + - Cr\ :sub:`11` + +.. flat-table:: Sample 4x4 NV12M Image + :header-rows: 0 + :stub-columns: 0 + + * - start0 + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start0 + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start0 + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start0 + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - + * - start1 + 0: + - Cb\ :sub:`00` + - Cr\ :sub:`00` + - Cb\ :sub:`01` + - Cr\ :sub:`01` + * - start1 + 4: + - Cb\ :sub:`10` + - Cr\ :sub:`10` + - Cb\ :sub:`11` + - Cr\ :sub:`11` + + +.. _V4L2-PIX-FMT-NV12MT: +.. _V4L2-PIX-FMT-NV12MT-16X16: + +NV12MT and MV12MT_16X16 +----------------------- + +Semi-planar YUV 4:2:0 formats, using macroblock tiling. The chroma plane is +subsampled by 2 in each direction. Chroma lines contain half the number of +pixels and the same number of bytes as luma lines, and the chroma plane +contains half the number of lines of the luma plane. + +``V4L2_PIX_FMT_NV12MT_16X16`` stores pixel in 2D 16x16 macroblocks, and stores +macroblocks linearly in memory. The line stride and image height must be +aligned to a multiple of 16. The layouts of the luma and chroma planes are +identical. + +``V4L2_PIX_FMT_NV12MT`` stores pixels in 2D 64x32 macroblocks, and stores 2x2 +groups of macroblocks in Z-order in memory, alternating Z and mirrored Z shapes +horizontally. The line stride must be a multiple of 128 pixels to ensure an +integer number of Z shapes. The image height must be a multiple of 32 pixels. +If the vertical resolution is an odd number of macroblocks, the last row of +macroblocks is stored in linear order. The layouts of the luma and chroma +planes are identical. + +.. _nv12mt: + +.. kernel-figure:: nv12mt.svg + :alt: nv12mt.svg + :align: center + + V4L2_PIX_FMT_NV12MT macroblock Z shape memory layout + +.. _nv12mt_ex: + +.. kernel-figure:: nv12mt_example.svg + :alt: nv12mt_example.svg + :align: center + + Example V4L2_PIX_FMT_NV12MT memory layout of macroblocks + + +.. _V4L2-PIX-FMT-NV16: +.. _V4L2-PIX-FMT-NV61: +.. _V4L2-PIX-FMT-NV16M: +.. _V4L2-PIX-FMT-NV61M: + +NV16, NV61, NV16M and NV61M +--------------------------- + +Semi-planar YUV 4:2:2 formats. The chroma plane is subsampled by 2 in the +horizontal direction. Chroma lines contain half the number of pixels and the +same number of bytes as luma lines, and the chroma plane contains the same +number of lines as the luma plane. + +.. flat-table:: Sample 4x4 NV16 Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cb\ :sub:`00` + - Cr\ :sub:`00` + - Cb\ :sub:`01` + - Cr\ :sub:`01` + * - start + 20: + - Cb\ :sub:`10` + - Cr\ :sub:`10` + - Cb\ :sub:`11` + - Cr\ :sub:`11` + * - start + 24: + - Cb\ :sub:`20` + - Cr\ :sub:`20` + - Cb\ :sub:`21` + - Cr\ :sub:`21` + * - start + 28: + - Cb\ :sub:`30` + - Cr\ :sub:`30` + - Cb\ :sub:`31` + - Cr\ :sub:`31` + +.. flat-table:: Sample 4x4 NV16M Image + :header-rows: 0 + :stub-columns: 0 + + * - start0 + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start0 + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start0 + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start0 + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - + * - start1 + 0: + - Cb\ :sub:`00` + - Cr\ :sub:`00` + - Cb\ :sub:`02` + - Cr\ :sub:`02` + * - start1 + 4: + - Cb\ :sub:`10` + - Cr\ :sub:`10` + - Cb\ :sub:`12` + - Cr\ :sub:`12` + * - start1 + 8: + - Cb\ :sub:`20` + - Cr\ :sub:`20` + - Cb\ :sub:`22` + - Cr\ :sub:`22` + * - start1 + 12: + - Cb\ :sub:`30` + - Cr\ :sub:`30` + - Cb\ :sub:`32` + - Cr\ :sub:`32` + + +.. _V4L2-PIX-FMT-NV24: +.. _V4L2-PIX-FMT-NV42: + +NV24 and NV42 +------------- + +Semi-planar YUV 4:4:4 formats. The chroma plane is subsampled by 2 in the +horizontal direction. Chroma lines contain half the number of pixels and the +same number of bytes as luma lines, and the chroma plane contains the same +number of lines as the luma plane. + +.. flat-table:: Sample 4x4 NV24 Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cb\ :sub:`00` + - Cr\ :sub:`00` + - Cb\ :sub:`01` + - Cr\ :sub:`01` + - Cb\ :sub:`02` + - Cr\ :sub:`02` + - Cb\ :sub:`03` + - Cr\ :sub:`03` + * - start + 24: + - Cb\ :sub:`10` + - Cr\ :sub:`10` + - Cb\ :sub:`11` + - Cr\ :sub:`11` + - Cb\ :sub:`12` + - Cr\ :sub:`12` + - Cb\ :sub:`13` + - Cr\ :sub:`13` + * - start + 32: + - Cb\ :sub:`20` + - Cr\ :sub:`20` + - Cb\ :sub:`21` + - Cr\ :sub:`21` + - Cb\ :sub:`22` + - Cr\ :sub:`22` + - Cb\ :sub:`23` + - Cr\ :sub:`23` + * - start + 40: + - Cb\ :sub:`30` + - Cr\ :sub:`30` + - Cb\ :sub:`31` + - Cr\ :sub:`31` + - Cb\ :sub:`32` + - Cr\ :sub:`32` + - Cb\ :sub:`33` + - Cr\ :sub:`33` + + +Fully Planar YUV Formats +======================== + +These formats store the Y, Cb and Cr components in three separate planes. The +luma plane comes first, and the order of the two chroma planes varies between +formats. The two chroma planes always use the same subsampling. + +For memory contiguous formats, the number of padding pixels at the end of the +chroma lines is identical to the padding of the luma lines. The chroma line +stride (in bytes) is thus equal to the luma line stride divided by the +horizontal subsampling factor. Vertical subsampling doesn't affect the line +stride. + +For non-contiguous formats, no constraints are enforced by the format on the +relationship between the luma and chroma line padding and stride. + +All components are stored with the same number of bits per component. + +.. flat-table:: Overview of Fully Planar YUV Formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Bits per component + - Subsampling + - Planes order [4]_ + - Contiguous [5]_ + + * - V4L2_PIX_FMT_YUV410 + - 'YUV9' + - 8 + - 4:1:0 + - Y, Cb, Cr + - Yes + * - V4L2_PIX_FMT_YVU410 + - 'YVU9' + - 8 + - 4:1:0 + - Y, Cr, Cb + - Yes + * - V4L2_PIX_FMT_YUV411P + - '411P' + - 8 + - 4:1:1 + - Y, Cb, Cr + - Yes + * - V4L2_PIX_FMT_YUV420M + - 'YM12' + - 8 + - 4:2:0 + - Y, Cb, Cr + - No + * - V4L2_PIX_FMT_YVU420M + - 'YM21' + - 8 + - 4:2:0 + - Y, Cr, Cb + - No + * - V4L2_PIX_FMT_YUV420 + - 'YU12' + - 8 + - 4:2:0 + - Y, Cb, Cr + - Yes + * - V4L2_PIX_FMT_YVU420 + - 'YV12' + - 8 + - 4:2:0 + - Y, Cr, Cb + - Yes + * - V4L2_PIX_FMT_YUV422P + - '422P' + - 8 + - 4:2:2 + - Y, Cb, Cr + - Yes + * - V4L2_PIX_FMT_YUV422M + - 'YM16' + - 8 + - 4:2:2 + - Y, Cb, Cr + - No + * - V4L2_PIX_FMT_YVU422M + - 'YM61' + - 8 + - 4:2:2 + - Y, Cr, Cb + - No + * - V4L2_PIX_FMT_YUV444M + - 'YM24' + - 8 + - 4:4:4 + - Y, Cb, Cr + - No + * - V4L2_PIX_FMT_YVU444M + - 'YM42' + - 8 + - 4:4:4 + - Y, Cr, Cb + - No + +.. note:: + + .. [4] Order of luma and chroma planes + .. [5] Indicates if planes have to be contiguous in memory or can be + disjoint + + +**Color Sample Location:** +Chroma samples are :ref:`interstitially sited<yuv-chroma-centered>` +horizontally. + +.. _V4L2-PIX-FMT-YUV410: +.. _V4L2-PIX-FMT-YVU410: + +YUV410 and YVU410 +----------------- + +Planar YUV 4:1:0 formats. The chroma planes are subsampled by 4 in each +direction. Chroma lines contain a quarter of the number of pixels and bytes of +the luma lines, and the chroma planes contain a quarter of the number of lines +of the luma plane. + +.. flat-table:: Sample 4x4 YUV410 Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cr\ :sub:`00` + * - start + 17: + - Cb\ :sub:`00` + + +.. _V4L2-PIX-FMT-YUV411P: + +YUV411P +------- + +Planar YUV 4:1:1 formats. The chroma planes are subsampled by 4 in the +horizontal direction. Chroma lines contain a quarter of the number of pixels +and bytes of the luma lines, and the chroma planes contain the same number of +lines as the luma plane. + +.. flat-table:: Sample 4x4 YUV411P Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cb\ :sub:`00` + * - start + 17: + - Cb\ :sub:`10` + * - start + 18: + - Cb\ :sub:`20` + * - start + 19: + - Cb\ :sub:`30` + * - start + 20: + - Cr\ :sub:`00` + * - start + 21: + - Cr\ :sub:`10` + * - start + 22: + - Cr\ :sub:`20` + * - start + 23: + - Cr\ :sub:`30` + + +.. _V4L2-PIX-FMT-YUV420: +.. _V4L2-PIX-FMT-YVU420: +.. _V4L2-PIX-FMT-YUV420M: +.. _V4L2-PIX-FMT-YVU420M: + +YUV420, YVU420, YUV420M and YVU420M +----------------------------------- + +Planar YUV 4:2:0 formats. The chroma planes are subsampled by 2 in each +direction. Chroma lines contain half of the number of pixels and bytes of the +luma lines, and the chroma planes contain half of the number of lines of the +luma plane. + +.. flat-table:: Sample 4x4 YUV420 Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cr\ :sub:`00` + - Cr\ :sub:`01` + * - start + 18: + - Cr\ :sub:`10` + - Cr\ :sub:`11` + * - start + 20: + - Cb\ :sub:`00` + - Cb\ :sub:`01` + * - start + 22: + - Cb\ :sub:`10` + - Cb\ :sub:`11` + +.. flat-table:: Sample 4x4 YUV420M Image + :header-rows: 0 + :stub-columns: 0 + + * - start0 + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start0 + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start0 + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start0 + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - + * - start1 + 0: + - Cb\ :sub:`00` + - Cb\ :sub:`01` + * - start1 + 2: + - Cb\ :sub:`10` + - Cb\ :sub:`11` + * - + * - start2 + 0: + - Cr\ :sub:`00` + - Cr\ :sub:`01` + * - start2 + 2: + - Cr\ :sub:`10` + - Cr\ :sub:`11` + + +.. _V4L2-PIX-FMT-YUV422P: +.. _V4L2-PIX-FMT-YUV422M: +.. _V4L2-PIX-FMT-YVU422M: + +YUV422P, YUV422M and YVU422M +---------------------------- + +Planar YUV 4:2:2 formats. The chroma planes are subsampled by 2 in the +horizontal direction. Chroma lines contain half of the number of pixels and +bytes of the luma lines, and the chroma planes contain the same number of lines +as the luma plane. + +.. flat-table:: Sample 4x4 YUV422P Image + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - start + 16: + - Cb\ :sub:`00` + - Cb\ :sub:`01` + * - start + 18: + - Cb\ :sub:`10` + - Cb\ :sub:`11` + * - start + 20: + - Cb\ :sub:`20` + - Cb\ :sub:`21` + * - start + 22: + - Cb\ :sub:`30` + - Cb\ :sub:`31` + * - start + 24: + - Cr\ :sub:`00` + - Cr\ :sub:`01` + * - start + 26: + - Cr\ :sub:`10` + - Cr\ :sub:`11` + * - start + 28: + - Cr\ :sub:`20` + - Cr\ :sub:`21` + * - start + 30: + - Cr\ :sub:`30` + - Cr\ :sub:`31` + +.. flat-table:: Sample 4x4 YUV422M Image + :header-rows: 0 + :stub-columns: 0 + + * - start0 + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start0 + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start0 + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start0 + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - + * - start1 + 0: + - Cb\ :sub:`00` + - Cb\ :sub:`01` + * - start1 + 2: + - Cb\ :sub:`10` + - Cb\ :sub:`11` + * - start1 + 4: + - Cb\ :sub:`20` + - Cb\ :sub:`21` + * - start1 + 6: + - Cb\ :sub:`30` + - Cb\ :sub:`31` + * - + * - start2 + 0: + - Cr\ :sub:`00` + - Cr\ :sub:`01` + * - start2 + 2: + - Cr\ :sub:`10` + - Cr\ :sub:`11` + * - start2 + 4: + - Cr\ :sub:`20` + - Cr\ :sub:`21` + * - start2 + 6: + - Cr\ :sub:`30` + - Cr\ :sub:`31` + + +.. _V4L2-PIX-FMT-YUV444M: +.. _V4L2-PIX-FMT-YVU444M: + +YUV444M and YVU444M +------------------- + +Planar YUV 4:4:4 formats. The chroma planes are no subsampled. Chroma lines +contain the same number of pixels and bytes of the luma lines, and the chroma +planes contain the same number of lines as the luma plane. + +.. flat-table:: Sample 4x4 YUV444M Image + :header-rows: 0 + :stub-columns: 0 + + * - start0 + 0: + - Y'\ :sub:`00` + - Y'\ :sub:`01` + - Y'\ :sub:`02` + - Y'\ :sub:`03` + * - start0 + 4: + - Y'\ :sub:`10` + - Y'\ :sub:`11` + - Y'\ :sub:`12` + - Y'\ :sub:`13` + * - start0 + 8: + - Y'\ :sub:`20` + - Y'\ :sub:`21` + - Y'\ :sub:`22` + - Y'\ :sub:`23` + * - start0 + 12: + - Y'\ :sub:`30` + - Y'\ :sub:`31` + - Y'\ :sub:`32` + - Y'\ :sub:`33` + * - + * - start1 + 0: + - Cb\ :sub:`00` + - Cb\ :sub:`01` + - Cb\ :sub:`02` + - Cb\ :sub:`03` + * - start1 + 4: + - Cb\ :sub:`10` + - Cb\ :sub:`11` + - Cb\ :sub:`12` + - Cb\ :sub:`13` + * - start1 + 8: + - Cb\ :sub:`20` + - Cb\ :sub:`21` + - Cb\ :sub:`22` + - Cb\ :sub:`23` + * - start1 + 12: + - Cb\ :sub:`20` + - Cb\ :sub:`21` + - Cb\ :sub:`32` + - Cb\ :sub:`33` + * - + * - start2 + 0: + - Cr\ :sub:`00` + - Cr\ :sub:`01` + - Cr\ :sub:`02` + - Cr\ :sub:`03` + * - start2 + 4: + - Cr\ :sub:`10` + - Cr\ :sub:`11` + - Cr\ :sub:`12` + - Cr\ :sub:`13` + * - start2 + 8: + - Cr\ :sub:`20` + - Cr\ :sub:`21` + - Cr\ :sub:`22` + - Cr\ :sub:`23` + * - start2 + 12: + - Cr\ :sub:`30` + - Cr\ :sub:`31` + - Cr\ :sub:`32` + - Cr\ :sub:`33` diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv410.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv410.rst deleted file mode 100644 index de2e519adc60..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv410.rst +++ /dev/null @@ -1,127 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YVU410: -.. _v4l2-pix-fmt-yuv410: - -********************************************************** -V4L2_PIX_FMT_YVU410 ('YVU9'), V4L2_PIX_FMT_YUV410 ('YUV9') -********************************************************** - - -V4L2_PIX_FMT_YUV410 -Planar formats with ¼ horizontal and vertical chroma resolution, also -known as YUV 4:1:0 - - -Description -=========== - -These are planar formats, as opposed to a packed format. The three -components are separated into three sub-images or planes. The Y plane is -first. The Y plane has one byte per pixel. For ``V4L2_PIX_FMT_YVU410``, -the Cr plane immediately follows the Y plane in memory. The Cr plane is -¼ the width and ¼ the height of the Y plane (and of the image). Each Cr -belongs to 16 pixels, a four-by-four square of the image. Following the -Cr plane is the Cb plane, just like the Cr plane. -``V4L2_PIX_FMT_YUV410`` is the same, except the Cb plane comes first, -then the Cr plane. - -If the Y plane has pad bytes after each row, then the Cr and Cb planes -have ¼ as many pad bytes after their rows. In other words, four Cx rows -(including padding) are exactly as long as one Y row (including -padding). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cr\ :sub:`00` - * - start + 17: - - Cb\ :sub:`00` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - - - Y - - - - Y - * - - * - 1 - - Y - - - - Y - - - - Y - - - - Y - * - - - - - - - - - C - - - - - - - * - 2 - - Y - - - - Y - - - - Y - - - - Y - * - - * - 3 - - Y - - - - Y - - - - Y - - - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv411p.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv411p.rst deleted file mode 100644 index 998aa9b1328f..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv411p.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUV411P: - -***************************** -V4L2_PIX_FMT_YUV411P ('411P') -***************************** - - -Format with ¼ horizontal chroma resolution, also known as YUV 4:1:1. -Planar layout as opposed to ``V4L2_PIX_FMT_Y41P`` - - -Description -=========== - -This format is not commonly used. This is a planar format similar to the -4:2:2 planar format except with half as many chroma. The three -components are separated into three sub-images or planes. The Y plane is -first. The Y plane has one byte per pixel. The Cb plane immediately -follows the Y plane in memory. The Cb plane is ¼ the width of the Y -plane (and of the image). Each Cb belongs to 4 pixels all on the same -row. For example, Cb\ :sub:`0` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`, -Y'\ :sub:`02` and Y'\ :sub:`03`. Following the Cb plane is the Cr plane, -just like the Cb plane. - -If the Y plane has pad bytes after each row, then the Cr and Cb planes -have ¼ as many pad bytes after their rows. In other words, four C x rows -(including padding) is exactly as long as one Y row (including padding). - -**Byte Order.** -Each cell is one byte. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cb\ :sub:`00` - * - start + 17: - - Cb\ :sub:`10` - * - start + 18: - - Cb\ :sub:`20` - * - start + 19: - - Cb\ :sub:`30` - * - start + 20: - - Cr\ :sub:`00` - * - start + 21: - - Cr\ :sub:`10` - * - start + 22: - - Cr\ :sub:`20` - * - start + 23: - - Cr\ :sub:`30` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - 1 - - - - 2 - - 3 - * - 0 - - Y - - Y - - C - - Y - - Y - * - 1 - - Y - - Y - - C - - Y - - Y - * - 2 - - Y - - Y - - C - - Y - - Y - * - 3 - - Y - - Y - - C - - Y - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv420.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv420.rst deleted file mode 100644 index f1c7baf32685..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv420.rst +++ /dev/null @@ -1,143 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YVU420: -.. _V4L2-PIX-FMT-YUV420: - -********************************************************** -V4L2_PIX_FMT_YVU420 ('YV12'), V4L2_PIX_FMT_YUV420 ('YU12') -********************************************************** - - -V4L2_PIX_FMT_YUV420 -Planar formats with ½ horizontal and vertical chroma resolution, also -known as YUV 4:2:0 - - -Description -=========== - -These are planar formats, as opposed to a packed format. The three -components are separated into three sub- images or planes. The Y plane -is first. The Y plane has one byte per pixel. For -``V4L2_PIX_FMT_YVU420``, the Cr plane immediately follows the Y plane in -memory. The Cr plane is half the width and half the height of the Y -plane (and of the image). Each Cr belongs to four pixels, a two-by-two -square of the image. For example, Cr\ :sub:`0` belongs to Y'\ :sub:`00`, -Y'\ :sub:`01`, Y'\ :sub:`10`, and Y'\ :sub:`11`. Following the Cr plane -is the Cb plane, just like the Cr plane. ``V4L2_PIX_FMT_YUV420`` is the -same except the Cb plane comes first, then the Cr plane. - -If the Y plane has pad bytes after each row, then the Cr and Cb planes -have half as many pad bytes after their rows. In other words, two Cx -rows (including padding) is exactly as long as one Y row (including -padding). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cr\ :sub:`00` - - Cr\ :sub:`01` - * - start + 18: - - Cr\ :sub:`10` - - Cr\ :sub:`11` - * - start + 20: - - Cb\ :sub:`00` - - Cb\ :sub:`01` - * - start + 22: - - Cb\ :sub:`10` - - Cb\ :sub:`11` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - - - Y - - - - Y - * - - - - - C - - - - - - - - C - - - * - 1 - - Y - - - - Y - - - - Y - - - - Y - * - - * - 2 - - Y - - - - Y - - - - Y - - - - Y - * - - - - - C - - - - - - - - C - - - * - 3 - - Y - - - - Y - - - - Y - - - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv420m.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv420m.rst deleted file mode 100644 index cd20a57e0621..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv420m.rst +++ /dev/null @@ -1,152 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUV420M: -.. _v4l2-pix-fmt-yvu420m: - -************************************************************ -V4L2_PIX_FMT_YUV420M ('YM12'), V4L2_PIX_FMT_YVU420M ('YM21') -************************************************************ - - -V4L2_PIX_FMT_YVU420M -Variation of ``V4L2_PIX_FMT_YUV420`` and ``V4L2_PIX_FMT_YVU420`` with -planes non contiguous in memory. - - -Description -=========== - -This is a multi-planar format, as opposed to a packed format. The three -components are separated into three sub-images or planes. - -The Y plane is first. The Y plane has one byte per pixel. For -``V4L2_PIX_FMT_YUV420M`` the Cb data constitutes the second plane which -is half the width and half the height of the Y plane (and of the image). -Each Cb belongs to four pixels, a two-by-two square of the image. For -example, Cb\ :sub:`0` belongs to Y'\ :sub:`00`, Y'\ :sub:`01`, -Y'\ :sub:`10`, and Y'\ :sub:`11`. The Cr data, just like the Cb plane, -is in the third plane. - -``V4L2_PIX_FMT_YVU420M`` is the same except the Cr data is stored in the -second plane and the Cb data in the third plane. - -If the Y plane has pad bytes after each row, then the Cb and Cr planes -have half as many pad bytes after their rows. In other words, two Cx -rows (including padding) is exactly as long as one Y row (including -padding). - -``V4L2_PIX_FMT_YUV420M`` and ``V4L2_PIX_FMT_YVU420M`` are intended to be -used only in drivers and applications that support the multi-planar API, -described in :ref:`planar-apis`. - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start0 + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start0 + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start0 + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start0 + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - - * - start1 + 0: - - Cb\ :sub:`00` - - Cb\ :sub:`01` - * - start1 + 2: - - Cb\ :sub:`10` - - Cb\ :sub:`11` - * - - * - start2 + 0: - - Cr\ :sub:`00` - - Cr\ :sub:`01` - * - start2 + 2: - - Cr\ :sub:`10` - - Cr\ :sub:`11` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - - - 2 - - - - 3 - * - 0 - - Y - - - - Y - - - - Y - - - - Y - * - - - - - C - - - - - - - - C - - - * - 1 - - Y - - - - Y - - - - Y - - - - Y - * - - * - 2 - - Y - - - - Y - - - - Y - - - - Y - * - - - - - C - - - - - - - - C - - - * - 3 - - Y - - - - Y - - - - Y - - - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv422m.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv422m.rst deleted file mode 100644 index 32bf15e1426e..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv422m.rst +++ /dev/null @@ -1,141 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUV422M: -.. _v4l2-pix-fmt-yvu422m: - -************************************************************ -V4L2_PIX_FMT_YUV422M ('YM16'), V4L2_PIX_FMT_YVU422M ('YM61') -************************************************************ - - -V4L2_PIX_FMT_YVU422M -Planar formats with ½ horizontal resolution, also known as YUV and YVU -4:2:2 - - -Description -=========== - -This is a multi-planar format, as opposed to a packed format. The three -components are separated into three sub-images or planes. - -The Y plane is first. The Y plane has one byte per pixel. For -``V4L2_PIX_FMT_YUV422M`` the Cb data constitutes the second plane which -is half the width of the Y plane (and of the image). Each Cb belongs to -two pixels. For example, Cb\ :sub:`0` belongs to Y'\ :sub:`00`, -Y'\ :sub:`01`. The Cr data, just like the Cb plane, is in the third -plane. - -``V4L2_PIX_FMT_YVU422M`` is the same except the Cr data is stored in the -second plane and the Cb data in the third plane. - -If the Y plane has pad bytes after each row, then the Cb and Cr planes -have half as many pad bytes after their rows. In other words, two Cx -rows (including padding) is exactly as long as one Y row (including -padding). - -``V4L2_PIX_FMT_YUV422M`` and ``V4L2_PIX_FMT_YVU422M`` are intended to be -used only in drivers and applications that support the multi-planar API, -described in :ref:`planar-apis`. - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start0 + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start0 + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start0 + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start0 + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - - * - start1 + 0: - - Cb\ :sub:`00` - - Cb\ :sub:`01` - * - start1 + 2: - - Cb\ :sub:`10` - - Cb\ :sub:`11` - * - start1 + 4: - - Cb\ :sub:`20` - - Cb\ :sub:`21` - * - start1 + 6: - - Cb\ :sub:`30` - - Cb\ :sub:`31` - * - - * - start2 + 0: - - Cr\ :sub:`00` - - Cr\ :sub:`01` - * - start2 + 2: - - Cr\ :sub:`10` - - Cr\ :sub:`11` - * - start2 + 4: - - Cr\ :sub:`20` - - Cr\ :sub:`21` - * - start2 + 6: - - Cr\ :sub:`30` - - Cr\ :sub:`31` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - C - - Y - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv422p.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv422p.rst deleted file mode 100644 index b178be558361..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv422p.rst +++ /dev/null @@ -1,129 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUV422P: - -***************************** -V4L2_PIX_FMT_YUV422P ('422P') -***************************** - - -Format with ½ horizontal chroma resolution, also known as YUV 4:2:2. -Planar layout as opposed to ``V4L2_PIX_FMT_YUYV`` - - -Description -=========== - -This format is not commonly used. This is a planar version of the YUYV -format. The three components are separated into three sub-images or -planes. The Y plane is first. The Y plane has one byte per pixel. The Cb -plane immediately follows the Y plane in memory. The Cb plane is half -the width of the Y plane (and of the image). Each Cb belongs to two -pixels. For example, Cb\ :sub:`0` belongs to Y'\ :sub:`00`, -Y'\ :sub:`01`. Following the Cb plane is the Cr plane, just like the Cb -plane. - -If the Y plane has pad bytes after each row, then the Cr and Cb planes -have half as many pad bytes after their rows. In other words, two Cx -rows (including padding) is exactly as long as one Y row (including -padding). - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - start + 16: - - Cb\ :sub:`00` - - Cb\ :sub:`01` - * - start + 18: - - Cb\ :sub:`10` - - Cb\ :sub:`11` - * - start + 20: - - Cb\ :sub:`20` - - Cb\ :sub:`21` - * - start + 22: - - Cb\ :sub:`30` - - Cb\ :sub:`31` - * - start + 24: - - Cr\ :sub:`00` - - Cr\ :sub:`01` - * - start + 26: - - Cr\ :sub:`10` - - Cr\ :sub:`11` - * - start + 28: - - Cr\ :sub:`20` - - Cr\ :sub:`21` - * - start + 30: - - Cr\ :sub:`30` - - Cr\ :sub:`31` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - C - - Y - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv444m.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv444m.rst deleted file mode 100644 index 90bdee2e2b0d..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv444m.rst +++ /dev/null @@ -1,141 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUV444M: -.. _v4l2-pix-fmt-yvu444m: - -************************************************************ -V4L2_PIX_FMT_YUV444M ('YM24'), V4L2_PIX_FMT_YVU444M ('YM42') -************************************************************ - - -V4L2_PIX_FMT_YVU444M -Planar formats with full horizontal resolution, also known as YUV and -YVU 4:4:4 - - -Description -=========== - -This is a multi-planar format, as opposed to a packed format. The three -components are separated into three sub-images or planes. - -The Y plane is first. The Y plane has one byte per pixel. For -``V4L2_PIX_FMT_YUV444M`` the Cb data constitutes the second plane which -is the same width and height as the Y plane (and as the image). The Cr -data, just like the Cb plane, is in the third plane. - -``V4L2_PIX_FMT_YVU444M`` is the same except the Cr data is stored in the -second plane and the Cb data in the third plane. - -If the Y plane has pad bytes after each row, then the Cb and Cr planes -have the same number of pad bytes after their rows. - -``V4L2_PIX_FMT_YUV444M`` and ``V4L2_PIX_FMT_YUV444M`` are intended to be -used only in drivers and applications that support the multi-planar API, -described in :ref:`planar-apis`. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start0 + 0: - - Y'\ :sub:`00` - - Y'\ :sub:`01` - - Y'\ :sub:`02` - - Y'\ :sub:`03` - * - start0 + 4: - - Y'\ :sub:`10` - - Y'\ :sub:`11` - - Y'\ :sub:`12` - - Y'\ :sub:`13` - * - start0 + 8: - - Y'\ :sub:`20` - - Y'\ :sub:`21` - - Y'\ :sub:`22` - - Y'\ :sub:`23` - * - start0 + 12: - - Y'\ :sub:`30` - - Y'\ :sub:`31` - - Y'\ :sub:`32` - - Y'\ :sub:`33` - * - - * - start1 + 0: - - Cb\ :sub:`00` - - Cb\ :sub:`01` - - Cb\ :sub:`02` - - Cb\ :sub:`03` - * - start1 + 4: - - Cb\ :sub:`10` - - Cb\ :sub:`11` - - Cb\ :sub:`12` - - Cb\ :sub:`13` - * - start1 + 8: - - Cb\ :sub:`20` - - Cb\ :sub:`21` - - Cb\ :sub:`22` - - Cb\ :sub:`23` - * - start1 + 12: - - Cb\ :sub:`20` - - Cb\ :sub:`21` - - Cb\ :sub:`32` - - Cb\ :sub:`33` - * - - * - start2 + 0: - - Cr\ :sub:`00` - - Cr\ :sub:`01` - - Cr\ :sub:`02` - - Cr\ :sub:`03` - * - start2 + 4: - - Cr\ :sub:`10` - - Cr\ :sub:`11` - - Cr\ :sub:`12` - - Cr\ :sub:`13` - * - start2 + 8: - - Cr\ :sub:`20` - - Cr\ :sub:`21` - - Cr\ :sub:`22` - - Cr\ :sub:`23` - * - start2 + 12: - - Cr\ :sub:`30` - - Cr\ :sub:`31` - - Cr\ :sub:`32` - - Cr\ :sub:`33` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - 1 - - 2 - - 3 - * - 0 - - YC - - YC - - YC - - YC - * - 1 - - YC - - YC - - YC - - YC - * - 2 - - YC - - YC - - YC - - YC - * - 3 - - YC - - YC - - YC - - YC diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuyv.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuyv.rst deleted file mode 100644 index ca073a5098a9..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuyv.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YUYV: - -************************** -V4L2_PIX_FMT_YUYV ('YUYV') -************************** - - -Packed format with ½ horizontal chroma resolution, also known as YUV -4:2:2 - - -Description -=========== - -In this format each four bytes is two pixels. Each four bytes is two -Y's, a Cb and a Cr. Each Y goes to one of the pixels, and the Cb and Cr -belong to both pixels. As you can see, the Cr and Cb components have -half the horizontal resolution of the Y component. ``V4L2_PIX_FMT_YUYV`` -is known in the Windows environment as YUY2. - -**Byte Order.** -Each cell is one byte. - - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Cb\ :sub:`00` - - Y'\ :sub:`01` - - Cr\ :sub:`00` - - Y'\ :sub:`02` - - Cb\ :sub:`01` - - Y'\ :sub:`03` - - Cr\ :sub:`01` - * - start + 8: - - Y'\ :sub:`10` - - Cb\ :sub:`10` - - Y'\ :sub:`11` - - Cr\ :sub:`10` - - Y'\ :sub:`12` - - Cb\ :sub:`11` - - Y'\ :sub:`13` - - Cr\ :sub:`11` - * - start + 16: - - Y'\ :sub:`20` - - Cb\ :sub:`20` - - Y'\ :sub:`21` - - Cr\ :sub:`20` - - Y'\ :sub:`22` - - Cb\ :sub:`21` - - Y'\ :sub:`23` - - Cr\ :sub:`21` - * - start + 24: - - Y'\ :sub:`30` - - Cb\ :sub:`30` - - Y'\ :sub:`31` - - Cr\ :sub:`30` - - Y'\ :sub:`32` - - Cb\ :sub:`31` - - Y'\ :sub:`33` - - Cr\ :sub:`31` - - -**Color Sample Location:** - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - - - 2 - - - - 3 - * - 0 - - Y - - C - - Y - - - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yvyu.rst b/Documentation/userspace-api/media/v4l/pixfmt-yvyu.rst deleted file mode 100644 index 81ebec525ae5..000000000000 --- a/Documentation/userspace-api/media/v4l/pixfmt-yvyu.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _V4L2-PIX-FMT-YVYU: - -************************** -V4L2_PIX_FMT_YVYU ('YVYU') -************************** - - -Variation of ``V4L2_PIX_FMT_YUYV`` with different order of samples in -memory - - -Description -=========== - -In this format each four bytes is two pixels. Each four bytes is two -Y's, a Cb and a Cr. Each Y goes to one of the pixels, and the Cb and Cr -belong to both pixels. As you can see, the Cr and Cb components have -half the horizontal resolution of the Y component. - -**Byte Order.** -Each cell is one byte. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - start + 0: - - Y'\ :sub:`00` - - Cr\ :sub:`00` - - Y'\ :sub:`01` - - Cb\ :sub:`00` - - Y'\ :sub:`02` - - Cr\ :sub:`01` - - Y'\ :sub:`03` - - Cb\ :sub:`01` - * - start + 8: - - Y'\ :sub:`10` - - Cr\ :sub:`10` - - Y'\ :sub:`11` - - Cb\ :sub:`10` - - Y'\ :sub:`12` - - Cr\ :sub:`11` - - Y'\ :sub:`13` - - Cb\ :sub:`11` - * - start + 16: - - Y'\ :sub:`20` - - Cr\ :sub:`20` - - Y'\ :sub:`21` - - Cb\ :sub:`20` - - Y'\ :sub:`22` - - Cr\ :sub:`21` - - Y'\ :sub:`23` - - Cb\ :sub:`21` - * - start + 24: - - Y'\ :sub:`30` - - Cr\ :sub:`30` - - Y'\ :sub:`31` - - Cb\ :sub:`30` - - Y'\ :sub:`32` - - Cr\ :sub:`31` - - Y'\ :sub:`33` - - Cb\ :sub:`31` - - -**Color Sample Location:** - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - * - - - 0 - - - - 1 - - 2 - - - - 3 - * - 0 - - Y - - C - - Y - - Y - - C - - Y - * - 1 - - Y - - C - - Y - - Y - - C - - Y - * - 2 - - Y - - C - - Y - - Y - - C - - Y - * - 3 - - Y - - C - - Y - - Y - - C - - Y diff --git a/Documentation/userspace-api/media/v4l/selection-api-configuration.rst b/Documentation/userspace-api/media/v4l/selection-api-configuration.rst index 37617eda2fa6..fee49bf1a1c0 100644 --- a/Documentation/userspace-api/media/v4l/selection-api-configuration.rst +++ b/Documentation/userspace-api/media/v4l/selection-api-configuration.rst @@ -94,7 +94,7 @@ specified using :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl. The top left corner, width and height of the source rectangle, that is the area from which image date are processed by the hardware, is given -by the ``V4L2_SEL_TGT_CROP``. Its coordinates are expressed in in the +by the ``V4L2_SEL_TGT_CROP``. Its coordinates are expressed in the same coordinate system as the bounds rectangle. The active cropping area must lie completely inside the crop boundaries and the driver may further adjust the requested size and/or position according to hardware diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index c9b7bb3ca089..7f16cbe46e5c 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -7899,3 +7899,30 @@ formats. - 0x5001 - Interleaved raw UYVY and JPEG image format with embedded meta-data used by Samsung S3C73MX camera sensors. + +.. _v4l2-mbus-metadata-fmts: + +Metadata Formats +^^^^^^^^^^^^^^^^ + +This section lists all metadata formats. + +The following table lists the existing metadata formats. + +.. tabularcolumns:: |p{8.0cm}|p{1.4cm}|p{7.7cm}| + +.. flat-table:: Metadata formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Comments + * .. _MEDIA-BUS-FMT-METADATA-FIXED: + + - MEDIA_BUS_FMT_METADATA_FIXED + - 0x7001 + - This format should be used when the same driver handles + both sides of the link and the bus format is a fixed + metadata format that is not configurable from userspace. + Width and height will be set to 0 for this format. diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst index f2173e310d67..b9c62affbb5a 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst @@ -53,7 +53,7 @@ by the ``controls`` fields. To get the current value of a set of controls applications initialize the ``id``, ``size`` and ``reserved2`` fields of each struct :c:type:`v4l2_ext_control` and call the -:ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctl. String controls controls must also set the +:ref:`VIDIOC_G_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` ioctl. String controls must also set the ``string`` field. Controls of compound types (``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set) must set the ``ptr`` field. @@ -180,10 +180,38 @@ still cause this situation. - ``p_u32`` - A pointer to a matrix control of unsigned 32-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U32``. - * - :c:type:`v4l2_area` * + * - struct :c:type:`v4l2_area` * - ``p_area`` - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is of type ``V4L2_CTRL_TYPE_AREA``. + * - struct :c:type:`v4l2_ctrl_h264_sps` * + - ``p_h264_sps`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_SPS``. + * - struct :c:type:`v4l2_ctrl_h264_pps` * + - ``p_h264_pps`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_pps`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_PPS``. + * - struct :c:type:`v4l2_ctrl_h264_scaling_matrix` * + - ``p_h264_scaling_matrix`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_scaling_matrix`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_SCALING_MATRIX``. + * - struct :c:type:`v4l2_ctrl_h264_pred_weights` * + - ``p_h264_pred_weights`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_pred_weights`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_PRED_WEIGHTS``. + * - struct :c:type:`v4l2_ctrl_h264_slice_params` * + - ``p_h264_slice_params`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_slice_params`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_SLICE_PARAMS``. + * - struct :c:type:`v4l2_ctrl_h264_decode_params` * + - ``p_h264_decode_params`` + - A pointer to a struct :c:type:`v4l2_ctrl_h264_decode_params`. Valid if this control is + of type ``V4L2_CTRL_TYPE_H264_DECODE_PARAMS``. + * - struct :c:type:`v4l2_ctrl_fwht_params` * + - ``p_fwht_params`` + - A pointer to a struct :c:type:`v4l2_ctrl_fwht_params`. Valid if this control is + of type ``V4L2_CTRL_TYPE_FWHT_PARAMS``. * - void * - ``ptr`` - A pointer to a compound type which can be an N-dimensional array @@ -322,10 +350,10 @@ still cause this situation. :ref:`VIDIOC_S_CTRL <VIDIOC_G_CTRL>` and :ref:`VIDIOC_G_CTRL <VIDIOC_G_CTRL>` ioctl belong to this class. - * - ``V4L2_CTRL_CLASS_MPEG`` + * - ``V4L2_CTRL_CLASS_CODEC`` - 0x990000 - - The class containing MPEG compression controls. These controls are - described in :ref:`mpeg-controls`. + - The class containing stateful codec controls. These controls are + described in :ref:`codec-controls`. * - ``V4L2_CTRL_CLASS_CAMERA`` - 0x9a0000 - The class containing camera controls. These controls are described @@ -358,6 +386,14 @@ still cause this situation. - 0xa20000 - The class containing RF tuner controls. These controls are described in :ref:`rf-tuner-controls`. + * - ``V4L2_CTRL_CLASS_DETECT`` + - 0xa30000 + - The class containing motion or object detection controls. These controls + are described in :ref:`detect-controls`. + * - ``V4L2_CTRL_CLASS_CODEC_STATELESS`` + - 0xa40000 + - The class containing stateless codec controls. These controls are + described in :ref:`codec-stateless-controls`. Return Value ============ diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-output.rst b/Documentation/userspace-api/media/v4l/vidioc-g-output.rst index 3138c4cc8fe3..dbcdd51dd2a8 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-output.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-output.rst @@ -46,7 +46,7 @@ To select a video output applications store the number of the desired output in an integer and call the :ref:`VIDIOC_S_OUTPUT <VIDIOC_G_OUTPUT>` ioctl with a pointer to this integer. Side effects are possible. For example outputs may support different video standards, so the driver may implicitly -switch the current standard. standard. Because of these possible side +switch the current standard. Because of these possible side effects applications must select an output before querying or negotiating any other parameters. diff --git a/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst b/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst index fbf8c5962d8a..77e0747a6d28 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst @@ -163,7 +163,7 @@ EINVAL The buffer ``type`` is not supported, or the ``index`` is out of bounds, or no buffers have been allocated yet, or the ``userptr`` or ``length`` are invalid, or the ``V4L2_BUF_FLAG_REQUEST_FD`` flag was - set but the the given ``request_fd`` was invalid, or ``m.fd`` was + set but the given ``request_fd`` was invalid, or ``m.fd`` was an invalid DMABUF file descriptor. EIO diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst index 9b8716f90f12..82f61f1e2fb8 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst @@ -462,6 +462,12 @@ See also the examples in :ref:`control`. - n/a - A struct :c:type:`v4l2_ctrl_h264_decode_params`, containing H264 decode parameters for stateless video decoders. + * - ``V4L2_CTRL_TYPE_FWHT_PARAMS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_fwht_params`, containing FWHT + parameters for stateless video decoders. * - ``V4L2_CTRL_TYPE_HEVC_SPS`` - n/a - n/a diff --git a/Documentation/userspace-api/media/v4l/yuv-formats.rst b/Documentation/userspace-api/media/v4l/yuv-formats.rst index 4a05a105a9e6..24b34cdfa6fe 100644 --- a/Documentation/userspace-api/media/v4l/yuv-formats.rst +++ b/Documentation/userspace-api/media/v4l/yuv-formats.rst @@ -14,44 +14,260 @@ reconstructed by subtracting from the brightness component. See :ref:`colorspaces` for conversion examples. YUV was chosen because early television would only transmit brightness information. To add color in a way compatible with existing receivers a new signal carrier -was added to transmit the color difference signals. Secondary in the YUV -format the U and V components usually have lower resolution than the Y -component. This is an analog video compression technique taking -advantage of a property of the human visual system, being more sensitive -to brightness information. +was added to transmit the color difference signals. + + +Subsampling +=========== + +YUV formats commonly encode images with a lower resolution for the chroma +components than for the luma component. This compression technique, taking +advantage of the human eye being more sensitive to luminance than color +differences, is called chroma subsampling. + +While many combinations of subsampling factors in the horizontal and vertical +direction are possible, common factors are 1 (no subsampling), 2 and 4, with +horizontal subsampling always larger than or equal to vertical subsampling. +Common combinations are named as follows. + +- `4:4:4`: No subsampling +- `4:2:2`: Horizontal subsampling by 2, no vertical subsampling +- `4:2:0`: Horizontal subsampling by 2, vertical subsampling by 2 +- `4:1:1`: Horizontal subsampling by 4, no vertical subsampling +- `4:1:0`: Horizontal subsampling by 4, vertical subsampling by 4 + +Subsampling the chroma component effectively creates chroma values that can be +located in different spatial locations: + +- .. _yuv-chroma-centered: + + The subsampled chroma value may be calculated by simply averaging the chroma + value of two consecutive pixels. It effectively models the chroma of a pixel + sited between the two original pixels. This is referred to as centered or + interstitially sited chroma. + +- .. _yuv-chroma-cosited: + + The other option is to subsample chroma values in a way that place them in + the same spatial sites as the pixels. This may be performed by skipping every + other chroma sample (creating aliasing artifacts), or with filters using an + odd number of taps. This is referred to as co-sited chroma. + +The following examples show different combination of chroma siting in a 4x4 +image. + +.. flat-table:: 4:2:2 subsampling, interstitially sited + :header-rows: 1 + :stub-columns: 1 + + * - + - 0 + - + - 1 + - + - 2 + - + - 3 + * - 0 + - Y + - C + - Y + - + - Y + - C + - Y + * - 1 + - Y + - C + - Y + - + - Y + - C + - Y + * - 2 + - Y + - C + - Y + - + - Y + - C + - Y + * - 3 + - Y + - C + - Y + - + - Y + - C + - Y + +.. flat-table:: 4:2:2 subsampling, co-sited + :header-rows: 1 + :stub-columns: 1 + + * - + - 0 + - + - 1 + - + - 2 + - + - 3 + * - 0 + - Y/C + - + - Y + - + - Y/C + - + - Y + * - 1 + - Y/C + - + - Y + - + - Y/C + - + - Y + * - 2 + - Y/C + - + - Y + - + - Y/C + - + - Y + * - 3 + - Y/C + - + - Y + - + - Y/C + - + - Y + +.. flat-table:: 4:2:0 subsampling, horizontally interstitially sited, vertically co-sited + :header-rows: 1 + :stub-columns: 1 + + * - + - 0 + - + - 1 + - + - 2 + - + - 3 + * - 0 + - Y + - C + - Y + - + - Y + - C + - Y + * - 1 + - Y + - + - Y + - + - Y + - + - Y + * - 2 + - Y + - C + - Y + - + - Y + - C + - Y + * - 3 + - Y + - + - Y + - + - Y + - + - Y + +.. flat-table:: 4:1:0 subsampling, horizontally and vertically interstitially sited + :header-rows: 1 + :stub-columns: 1 + + * - + - 0 + - + - 1 + - + - 2 + - + - 3 + * - 0 + - Y + - + - Y + - + - Y + - + - Y + * - + - + - + - + - + - + - + - + * - 1 + - Y + - + - Y + - + - Y + - + - Y + * - + - + - + - + - C + - + - + - + * - 2 + - Y + - + - Y + - + - Y + - + - Y + * - + - + - + - + - + - + - + - + * - 3 + - Y + - + - Y + - + - Y + - + - Y .. toctree:: :maxdepth: 1 pixfmt-packed-yuv - pixfmt-grey - pixfmt-y10 - pixfmt-y12 - pixfmt-y14 - pixfmt-y10b - pixfmt-y10p - pixfmt-y16 - pixfmt-y16-be + pixfmt-yuv-planar + pixfmt-yuv-luma pixfmt-y8i pixfmt-y12i pixfmt-uv8 - pixfmt-yuyv - pixfmt-uyvy - pixfmt-yvyu - pixfmt-vyuy - pixfmt-y41p - pixfmt-yuv420 - pixfmt-yuv420m - pixfmt-yuv422m - pixfmt-yuv444m - pixfmt-yuv410 - pixfmt-yuv422p - pixfmt-yuv411p - pixfmt-nv12 - pixfmt-nv12m - pixfmt-nv12mt - pixfmt-nv16 - pixfmt-nv16m - pixfmt-nv24 pixfmt-m420 diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions index 121e396a2779..0ed170c6e720 100644 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions @@ -139,12 +139,14 @@ replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTIZATION :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type` # V4L2 capability defines replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst index b224d12c880b..e7eb84484ddc 100644 --- a/Documentation/x86/index.rst +++ b/Documentation/x86/index.rst @@ -27,9 +27,10 @@ x86-specific Documentation pti mds microcode - resctrl_ui + resctrl tsx_async_abort usb-legacy-support i386/index x86_64/index sva + sgx diff --git a/Documentation/x86/resctrl_ui.rst b/Documentation/x86/resctrl.rst index e59b7b93a9b4..71a531061e4e 100644 --- a/Documentation/x86/resctrl_ui.rst +++ b/Documentation/x86/resctrl.rst @@ -1209,3 +1209,96 @@ View the llc occupancy snapshot:: # cat /sys/fs/resctrl/p1/mon_data/mon_L3_00/llc_occupancy 11234000 + +Intel RDT Errata +================ + +Intel MBM Counters May Report System Memory Bandwidth Incorrectly +----------------------------------------------------------------- + +Errata SKX99 for Skylake server and BDF102 for Broadwell server. + +Problem: Intel Memory Bandwidth Monitoring (MBM) counters track metrics +according to the assigned Resource Monitor ID (RMID) for that logical +core. The IA32_QM_CTR register (MSR 0xC8E), used to report these +metrics, may report incorrect system bandwidth for certain RMID values. + +Implication: Due to the errata, system memory bandwidth may not match +what is reported. + +Workaround: MBM total and local readings are corrected according to the +following correction factor table: + ++---------------+---------------+---------------+-----------------+ +|core count |rmid count |rmid threshold |correction factor| ++---------------+---------------+---------------+-----------------+ +|1 |8 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|2 |16 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|3 |24 |15 |0.969650 | ++---------------+---------------+---------------+-----------------+ +|4 |32 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|6 |48 |31 |0.969650 | ++---------------+---------------+---------------+-----------------+ +|7 |56 |47 |1.142857 | ++---------------+---------------+---------------+-----------------+ +|8 |64 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|9 |72 |63 |1.185115 | ++---------------+---------------+---------------+-----------------+ +|10 |80 |63 |1.066553 | ++---------------+---------------+---------------+-----------------+ +|11 |88 |79 |1.454545 | ++---------------+---------------+---------------+-----------------+ +|12 |96 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|13 |104 |95 |1.230769 | ++---------------+---------------+---------------+-----------------+ +|14 |112 |95 |1.142857 | ++---------------+---------------+---------------+-----------------+ +|15 |120 |95 |1.066667 | ++---------------+---------------+---------------+-----------------+ +|16 |128 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|17 |136 |127 |1.254863 | ++---------------+---------------+---------------+-----------------+ +|18 |144 |127 |1.185255 | ++---------------+---------------+---------------+-----------------+ +|19 |152 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|20 |160 |127 |1.066667 | ++---------------+---------------+---------------+-----------------+ +|21 |168 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|22 |176 |159 |1.454334 | ++---------------+---------------+---------------+-----------------+ +|23 |184 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|24 |192 |127 |0.969744 | ++---------------+---------------+---------------+-----------------+ +|25 |200 |191 |1.280246 | ++---------------+---------------+---------------+-----------------+ +|26 |208 |191 |1.230921 | ++---------------+---------------+---------------+-----------------+ +|27 |216 |0 |1.000000 | ++---------------+---------------+---------------+-----------------+ +|28 |224 |191 |1.143118 | ++---------------+---------------+---------------+-----------------+ + +If rmid > rmid threshold, MBM total and local values should be multiplied +by the correction factor. + +See: + +1. Erratum SKX99 in Intel Xeon Processor Scalable Family Specification Update: +http://web.archive.org/web/20200716124958/https://www.intel.com/content/www/us/en/processors/xeon/scalable/xeon-scalable-spec-update.html + +2. Erratum BDF102 in Intel Xeon E5-2600 v4 Processor Product Family Specification Update: +http://web.archive.org/web/20191125200531/https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/xeon-e5-v4-spec-update.pdf + +3. The errata in Intel Resource Director Technology (Intel RDT) on 2nd Generation Intel Xeon Scalable Processors Reference Manual: +https://software.intel.com/content/www/us/en/develop/articles/intel-resource-director-technology-rdt-reference-manual.html + +for further information. diff --git a/Documentation/x86/sgx.rst b/Documentation/x86/sgx.rst new file mode 100644 index 000000000000..eaee1368b4fd --- /dev/null +++ b/Documentation/x86/sgx.rst @@ -0,0 +1,211 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============================== +Software Guard eXtensions (SGX) +=============================== + +Overview +======== + +Software Guard eXtensions (SGX) hardware enables for user space applications +to set aside private memory regions of code and data: + +* Privileged (ring-0) ENCLS functions orchestrate the construction of the. + regions. +* Unprivileged (ring-3) ENCLU functions allow an application to enter and + execute inside the regions. + +These memory regions are called enclaves. An enclave can be only entered at a +fixed set of entry points. Each entry point can hold a single hardware thread +at a time. While the enclave is loaded from a regular binary file by using +ENCLS functions, only the threads inside the enclave can access its memory. The +region is denied from outside access by the CPU, and encrypted before it leaves +from LLC. + +The support can be determined by + + ``grep sgx /proc/cpuinfo`` + +SGX must both be supported in the processor and enabled by the BIOS. If SGX +appears to be unsupported on a system which has hardware support, ensure +support is enabled in the BIOS. If a BIOS presents a choice between "Enabled" +and "Software Enabled" modes for SGX, choose "Enabled". + +Enclave Page Cache +================== + +SGX utilizes an *Enclave Page Cache (EPC)* to store pages that are associated +with an enclave. It is contained in a BIOS-reserved region of physical memory. +Unlike pages used for regular memory, pages can only be accessed from outside of +the enclave during enclave construction with special, limited SGX instructions. + +Only a CPU executing inside an enclave can directly access enclave memory. +However, a CPU executing inside an enclave may access normal memory outside the +enclave. + +The kernel manages enclave memory similar to how it treats device memory. + +Enclave Page Types +------------------ + +**SGX Enclave Control Structure (SECS)** + Enclave's address range, attributes and other global data are defined + by this structure. + +**Regular (REG)** + Regular EPC pages contain the code and data of an enclave. + +**Thread Control Structure (TCS)** + Thread Control Structure pages define the entry points to an enclave and + track the execution state of an enclave thread. + +**Version Array (VA)** + Version Array pages contain 512 slots, each of which can contain a version + number for a page evicted from the EPC. + +Enclave Page Cache Map +---------------------- + +The processor tracks EPC pages in a hardware metadata structure called the +*Enclave Page Cache Map (EPCM)*. The EPCM contains an entry for each EPC page +which describes the owning enclave, access rights and page type among the other +things. + +EPCM permissions are separate from the normal page tables. This prevents the +kernel from, for instance, allowing writes to data which an enclave wishes to +remain read-only. EPCM permissions may only impose additional restrictions on +top of normal x86 page permissions. + +For all intents and purposes, the SGX architecture allows the processor to +invalidate all EPCM entries at will. This requires that software be prepared to +handle an EPCM fault at any time. In practice, this can happen on events like +power transitions when the ephemeral key that encrypts enclave memory is lost. + +Application interface +===================== + +Enclave build functions +----------------------- + +In addition to the traditional compiler and linker build process, SGX has a +separate enclave “build” process. Enclaves must be built before they can be +executed (entered). The first step in building an enclave is opening the +**/dev/sgx_enclave** device. Since enclave memory is protected from direct +access, special privileged instructions are Then used to copy data into enclave +pages and establish enclave page permissions. + +.. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c + :functions: sgx_ioc_enclave_create + sgx_ioc_enclave_add_pages + sgx_ioc_enclave_init + sgx_ioc_enclave_provision + +Enclave vDSO +------------ + +Entering an enclave can only be done through SGX-specific EENTER and ERESUME +functions, and is a non-trivial process. Because of the complexity of +transitioning to and from an enclave, enclaves typically utilize a library to +handle the actual transitions. This is roughly analogous to how glibc +implementations are used by most applications to wrap system calls. + +Another crucial characteristic of enclaves is that they can generate exceptions +as part of their normal operation that need to be handled in the enclave or are +unique to SGX. + +Instead of the traditional signal mechanism to handle these exceptions, SGX +can leverage special exception fixup provided by the vDSO. The kernel-provided +vDSO function wraps low-level transitions to/from the enclave like EENTER and +ERESUME. The vDSO function intercepts exceptions that would otherwise generate +a signal and return the fault information directly to its caller. This avoids +the need to juggle signal handlers. + +.. kernel-doc:: arch/x86/include/uapi/asm/sgx.h + :functions: vdso_sgx_enter_enclave_t + +ksgxd +===== + +SGX support includes a kernel thread called *ksgxwapd*. + +EPC sanitization +---------------- + +ksgxd is started when SGX initializes. Enclave memory is typically ready +For use when the processor powers on or resets. However, if SGX has been in +use since the reset, enclave pages may be in an inconsistent state. This might +occur after a crash and kexec() cycle, for instance. At boot, ksgxd +reinitializes all enclave pages so that they can be allocated and re-used. + +The sanitization is done by going through EPC address space and applying the +EREMOVE function to each physical page. Some enclave pages like SECS pages have +hardware dependencies on other pages which prevents EREMOVE from functioning. +Executing two EREMOVE passes removes the dependencies. + +Page reclaimer +-------------- + +Similar to the core kswapd, ksgxd, is responsible for managing the +overcommitment of enclave memory. If the system runs out of enclave memory, +*ksgxwapd* “swaps” enclave memory to normal memory. + +Launch Control +============== + +SGX provides a launch control mechanism. After all enclave pages have been +copied, kernel executes EINIT function, which initializes the enclave. Only after +this the CPU can execute inside the enclave. + +ENIT function takes an RSA-3072 signature of the enclave measurement. The function +checks that the measurement is correct and signature is signed with the key +hashed to the four **IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}** MSRs representing the +SHA256 of a public key. + +Those MSRs can be configured by the BIOS to be either readable or writable. +Linux supports only writable configuration in order to give full control to the +kernel on launch control policy. Before calling EINIT function, the driver sets +the MSRs to match the enclave's signing key. + +Encryption engines +================== + +In order to conceal the enclave data while it is out of the CPU package, the +memory controller has an encryption engine to transparently encrypt and decrypt +enclave memory. + +In CPUs prior to Ice Lake, the Memory Encryption Engine (MEE) is used to +encrypt pages leaving the CPU caches. MEE uses a n-ary Merkle tree with root in +SRAM to maintain integrity of the encrypted data. This provides integrity and +anti-replay protection but does not scale to large memory sizes because the time +required to update the Merkle tree grows logarithmically in relation to the +memory size. + +CPUs starting from Icelake use Total Memory Encryption (TME) in the place of +MEE. TME-based SGX implementations do not have an integrity Merkle tree, which +means integrity and replay-attacks are not mitigated. B, it includes +additional changes to prevent cipher text from being returned and SW memory +aliases from being Created. + +DMA to enclave memory is blocked by range registers on both MEE and TME systems +(SDM section 41.10). + +Usage Models +============ + +Shared Library +-------------- + +Sensitive data and the code that acts on it is partitioned from the application +into a separate library. The library is then linked as a DSO which can be loaded +into an enclave. The application can then make individual function calls into +the enclave through special SGX instructions. A run-time within the enclave is +configured to marshal function parameters into and out of the enclave and to +call the correct library function. + +Application Container +--------------------- + +An application may be loaded into a container enclave which is specially +configured with a library OS and run-time which permits the application to run. +The enclave run-time and library OS work together to execute the application +when a thread enters the enclave. diff --git a/Documentation/x86/topology.rst b/Documentation/x86/topology.rst index e29739904e37..7f58010ea86a 100644 --- a/Documentation/x86/topology.rst +++ b/Documentation/x86/topology.rst @@ -41,6 +41,8 @@ Package Packages contain a number of cores plus shared resources, e.g. DRAM controller, shared caches etc. +Modern systems may also use the term 'Die' for package. + AMD nomenclature for package is 'Node'. Package-related topology information in the kernel: @@ -53,11 +55,18 @@ Package-related topology information in the kernel: The number of dies in a package. This information is retrieved via CPUID. + - cpuinfo_x86.cpu_die_id: + + The physical ID of the die. This information is retrieved via CPUID. + - cpuinfo_x86.phys_proc_id: The physical ID of the package. This information is retrieved via CPUID and deduced from the APIC IDs of the cores in the package. + Modern systems use this value for the socket. There may be multiple + packages within a socket. This value may differ from cpu_die_id. + - cpuinfo_x86.logical_proc_id: The logical ID of the package. As we do not trust BIOSes to enumerate the diff --git a/MAINTAINERS b/MAINTAINERS index 7b073c41c3a0..2bdd87123060 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1073,6 +1073,7 @@ M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/adv7604* +F: Documentation/devicetree/bindings/media/i2c/adv7604.yaml ANALOG DEVICES INC ADV7842 DRIVER M: Hans Verkuil <hverkuil-cisco@xs4all.nl> @@ -2484,7 +2485,7 @@ F: drivers/clk/socfpga/ ARM/SOCFPGA EDAC SUPPORT M: Dinh Nguyen <dinguyen@kernel.org> S: Maintained -F: drivers/edac/altera_edac. +F: drivers/edac/altera_edac.[ch] ARM/SPREADTRUM SoC SUPPORT M: Orson Zhai <orsonzhai@gmail.com> @@ -3880,9 +3881,8 @@ T: git git://linuxtv.org/media_tree.git F: drivers/media/radio/radio-cadet* CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER -M: Jonathan Corbet <corbet@lwn.net> L: linux-media@vger.kernel.org -S: Maintained +S: Orphan T: git git://linuxtv.org/media_tree.git F: Documentation/admin-guide/media/cafe_ccic* F: drivers/media/platform/marvell-ccic/ @@ -4363,7 +4363,7 @@ CODA V4L2 MEM2MEM DRIVER M: Philipp Zabel <p.zabel@pengutronix.de> L: linux-media@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/media/coda.txt +F: Documentation/devicetree/bindings/media/coda.yaml F: drivers/media/platform/coda/ CODE OF CONDUCT @@ -6370,6 +6370,13 @@ L: linux-edac@vger.kernel.org S: Maintained F: drivers/edac/ie31200_edac.c +EDAC-IGEN6 +M: Tony Luck <tony.luck@intel.com> +R: Qiuxu Zhuo <qiuxu.zhuo@intel.com> +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/igen6_edac.c + EDAC-MPC85XX M: Johannes Thumshirn <morbidrsa@gmail.com> L: linux-edac@vger.kernel.org @@ -6419,7 +6426,7 @@ EDAC-SKYLAKE M: Tony Luck <tony.luck@intel.com> L: linux-edac@vger.kernel.org S: Maintained -F: drivers/edac/skx_*.c +F: drivers/edac/skx_*.[ch] EDAC-TI M: Tero Kristo <t-kristo@ti.com> @@ -8016,7 +8023,7 @@ F: drivers/staging/hikey9xx/ HISILICON TRUE RANDOM NUMBER GENERATOR V2 SUPPORT M: Zaibo Xu <xuzaibo@huawei.com> S: Maintained -F: drivers/char/hw_random/hisi-trng-v2.c +F: drivers/crypto/hisilicon/trng/trng.c HISILICON V3XX SPI NOR FLASH Controller Driver M: John Garry <john.garry@huawei.com> @@ -8975,13 +8982,23 @@ M: Deepak Saxena <dsaxena@plexity.net> S: Maintained F: drivers/char/hw_random/ixp4xx-rng.c -INTEL KEEMBAY DRM DRIVER +INTEL KEEM BAY DRM DRIVER M: Anitha Chrisanthus <anitha.chrisanthus@intel.com> M: Edmund Dea <edmund.j.dea@intel.com> S: Maintained F: Documentation/devicetree/bindings/display/intel,kmb_display.yaml F: drivers/gpu/drm/kmb/ +INTEL KEEM BAY OCS AES/SM4 CRYPTO DRIVER +M: Daniele Alessandrelli <daniele.alessandrelli@intel.com> +S: Maintained +F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-aes.yaml +F: drivers/crypto/keembay/Kconfig +F: drivers/crypto/keembay/Makefile +F: drivers/crypto/keembay/keembay-ocs-aes-core.c +F: drivers/crypto/keembay/ocs-aes.c +F: drivers/crypto/keembay/ocs-aes.h + INTEL MANAGEMENT ENGINE (mei) M: Tomas Winkler <tomas.winkler@intel.com> L: linux-kernel@vger.kernel.org @@ -9150,6 +9167,19 @@ F: Documentation/x86/intel_txt.rst F: arch/x86/kernel/tboot.c F: include/linux/tboot.h +INTEL SGX +M: Jarkko Sakkinen <jarkko@kernel.org> +L: linux-sgx@vger.kernel.org +S: Supported +Q: https://patchwork.kernel.org/project/intel-sgx/list/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-sgx.git +F: Documentation/x86/sgx.rst +F: arch/x86/entry/vdso/vsgx.S +F: arch/x86/include/uapi/asm/sgx.h +F: arch/x86/kernel/cpu/sgx/* +F: tools/testing/selftests/sgx/* +K: \bSGX_ + INTERCONNECT API M: Georgi Djakov <georgi.djakov@linaro.org> L: linux-pm@vger.kernel.org @@ -10854,8 +10884,8 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/admin-guide/media/imx7.rst -F: Documentation/devicetree/bindings/media/imx7-csi.txt -F: Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt +F: Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml +F: Documentation/devicetree/bindings/media/nxp,imx7-mipi-csi2.yaml F: drivers/staging/media/imx/imx7-media-csi.c F: drivers/staging/media/imx/imx7-mipi-csis.c @@ -11418,6 +11448,15 @@ F: Documentation/devicetree/bindings/media/amlogic,meson-gx-ao-cec.yaml F: drivers/media/cec/platform/meson/ao-cec-g12a.c F: drivers/media/cec/platform/meson/ao-cec.c +MESON GE2D DRIVER FOR AMLOGIC SOCS +M: Neil Armstrong <narmstrong@baylibre.com> +L: linux-media@vger.kernel.org +L: linux-amlogic@lists.infradead.org +S: Supported +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/amlogic,axg-ge2d.yaml +F: drivers/media/meson/ge2d/ + MESON NAND CONTROLLER DRIVER FOR AMLOGIC SOCS M: Liang Yang <liang.yang@amlogic.com> L: linux-mtd@lists.infradead.org @@ -11667,6 +11706,17 @@ M: Oliver Neukum <oliver@neukum.org> S: Maintained F: drivers/usb/image/microtek.* +MIPI CCS, SMIA AND SMIA++ IMAGE SENSOR DRIVER +M: Sakari Ailus <sakari.ailus@linux.intel.com> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/mipi-ccs.yaml +F: Documentation/driver-api/media/drivers/ccs/ +F: drivers/media/i2c/ccs-pll.c +F: drivers/media/i2c/ccs-pll.h +F: drivers/media/i2c/ccs/ +F: include/uapi/linux/smiapp.h + MIPS M: Thomas Bogendoerfer <tsbogend@alpha.franken.de> L: linux-mips@vger.kernel.org @@ -11937,7 +11987,7 @@ M: Jacopo Mondi <jacopo@jmondi.org> L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt +F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml F: drivers/media/i2c/mt9v111.c MULTIFUNCTION DEVICES (MFD) @@ -12882,6 +12932,14 @@ M: Harald Welte <laforge@gnumonks.org> S: Maintained F: drivers/char/pcmcia/cm4040_cs.* +OMNIVISION OV02A10 SENSOR DRIVER +M: Dongchun Zhu <dongchun.zhu@mediatek.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/ovti,ov02a10.yaml +F: drivers/media/i2c/ov02a10.c + OMNIVISION OV13858 SENSOR DRIVER M: Sakari Ailus <sakari.ailus@linux.intel.com> L: linux-media@vger.kernel.org @@ -12894,7 +12952,7 @@ M: Rui Miguel Silva <rmfrfs@gmail.com> L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/ov2680.txt +F: Documentation/devicetree/bindings/media/i2c/ov2680.yaml F: drivers/media/i2c/ov2680.c OMNIVISION OV2685 SENSOR DRIVER @@ -12952,9 +13010,8 @@ T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov5695.c OMNIVISION OV7670 SENSOR DRIVER -M: Jonathan Corbet <corbet@lwn.net> L: linux-media@vger.kernel.org -S: Maintained +S: Orphan T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ov7670.txt F: drivers/media/i2c/ov7670.c @@ -12964,7 +13021,7 @@ M: Jacopo Mondi <jacopo@jmondi.org> L: linux-media@vger.kernel.org S: Odd fixes T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/ov772x.txt +F: Documentation/devicetree/bindings/media/i2c/ovti,ov772x.yaml F: drivers/media/i2c/ov772x.c F: include/media/i2c/ov772x.h @@ -13000,6 +13057,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ov9650.txt F: drivers/media/i2c/ov9650.c +OMNIVISION OV9734 SENSOR DRIVER +M: Tianshu Qiu <tian.shu.qiu@intel.com> +R: Bingbu Cao <bingbu.cao@intel.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: drivers/media/i2c/ov9734.c + ONENAND FLASH DRIVER M: Kyungmin Park <kyungmin.park@samsung.com> L: linux-mtd@lists.infradead.org @@ -14472,6 +14537,7 @@ W: https://wireless.wiki.kernel.org/en/users/Drivers/ath9k F: drivers/net/wireless/ath/ath9k/ QUALCOMM CAMERA SUBSYSTEM DRIVER +M: Robert Foss <robert.foss@linaro.org> M: Todor Tomov <todor.too@gmail.com> L: linux-media@vger.kernel.org S: Maintained @@ -15045,10 +15111,13 @@ ROCKCHIP ISP V1 DRIVER M: Helen Koike <helen.koike@collabora.com> M: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> L: linux-media@vger.kernel.org +L: linux-rockchip@lists.infradead.org S: Maintained F: Documentation/admin-guide/media/rkisp1.rst +F: Documentation/devicetree/bindings/media/rockchip-isp1.yaml F: Documentation/userspace-api/media/v4l/pixfmt-meta-rkisp1.rst -F: drivers/staging/media/rkisp1/ +F: drivers/media/platform/rockchip/rkisp1 +F: include/uapi/linux/rkisp1-config.h ROCKCHIP RASTER 2D GRAPHIC ACCELERATION UNIT DRIVER M: Jacob Chen <jacob-chen@iotwrt.com> @@ -15836,13 +15905,14 @@ F: include/linux/sfp.h K: phylink\.h|struct\s+phylink|\.phylink|>phylink_|phylink_(autoneg|clear|connect|create|destroy|disconnect|ethtool|helper|mac|mii|of|set|start|stop|test|validate) SGI GRU DRIVER -M: Dimitri Sivanich <sivanich@sgi.com> +M: Dimitri Sivanich <dimitri.sivanich@hpe.com> S: Maintained F: drivers/misc/sgi-gru/ SGI XP/XPC/XPNET DRIVER -M: Cliff Whickman <cpw@sgi.com> M: Robin Holt <robinmholt@gmail.com> +M: Steve Wahl <steve.wahl@hpe.com> +R: Mike Travis <mike.travis@hpe.com> S: Maintained F: drivers/misc/sgi-xp/ @@ -16127,16 +16197,6 @@ S: Maintained F: drivers/firmware/smccc/ F: include/linux/arm-smccc.h -SMIA AND SMIA++ IMAGE SENSOR DRIVER -M: Sakari Ailus <sakari.ailus@linux.intel.com> -L: linux-media@vger.kernel.org -S: Maintained -F: Documentation/devicetree/bindings/media/i2c/nokia,smia.txt -F: drivers/media/i2c/smiapp-pll.c -F: drivers/media/i2c/smiapp-pll.h -F: drivers/media/i2c/smiapp/ -F: include/uapi/linux/smiapp.h - SMM665 HARDWARE MONITOR DRIVER M: Guenter Roeck <linux@roeck-us.net> L: linux-hwmon@vger.kernel.org @@ -16299,7 +16359,7 @@ M: Ricardo Ribalda <ribalda@kernel.org> L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt +F: Documentation/devicetree/bindings/media/i2c/sony,imx214.yaml F: drivers/media/i2c/imx214.c SONY IMX219 SENSOR DRIVER @@ -18395,6 +18455,12 @@ F: include/uapi/linux/uuid.h F: lib/test_uuid.c F: lib/uuid.c +UV SYSFS DRIVER +M: Justin Ernst <justin.ernst@hpe.com> +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/uv_sysfs.c + UVESAFB DRIVER M: Michal Januszewski <spock@gentoo.org> L: linux-fbdev@vger.kernel.org @@ -19101,6 +19167,7 @@ F: arch/x86/platform X86 PLATFORM UV HPE SUPERDOME FLEX M: Steve Wahl <steve.wahl@hpe.com> +R: Mike Travis <mike.travis@hpe.com> R: Dimitri Sivanich <dimitri.sivanich@hpe.com> R: Russ Anderson <russ.anderson@hpe.com> S: Supported @@ -19443,6 +19510,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/zonefs.git F: Documentation/filesystems/zonefs.rst F: fs/zonefs/ +ZPOOL COMPRESSED PAGE STORAGE API +M: Dan Streetman <ddstreet@ieee.org> +L: linux-mm@kvack.org +S: Maintained +F: include/linux/zpool.h +F: mm/zpool.c + ZR36067 VIDEO FOR LINUX DRIVER M: Corentin Labbe <clabbe@baylibre.com> L: mjpeg-users@lists.sourceforge.net @@ -19453,13 +19527,6 @@ Q: https://patchwork.linuxtv.org/project/linux-media/list/ F: Documentation/driver-api/media/drivers/zoran.rst F: drivers/staging/media/zoran/ -ZPOOL COMPRESSED PAGE STORAGE API -M: Dan Streetman <ddstreet@ieee.org> -L: linux-mm@kvack.org -S: Maintained -F: include/linux/zpool.h -F: mm/zpool.c - ZRAM COMPRESSED RAM BLOCK DEVICE DRVIER M: Minchan Kim <minchan@kernel.org> M: Nitin Gupta <ngupta@vflare.org> diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index b58220a49cbd..74367ee96f20 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -69,6 +69,12 @@ always-on; }; + edac: sdram@1e6e0000 { + compatible = "aspeed,ast2600-sdram-edac", "syscon"; + reg = <0x1e6e0000 0x174>; + interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>; + }; + ahb { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/crypto/aes-ce-core.S b/arch/arm/crypto/aes-ce-core.S index 4d1707388d94..312428d83eed 100644 --- a/arch/arm/crypto/aes-ce-core.S +++ b/arch/arm/crypto/aes-ce-core.S @@ -386,20 +386,32 @@ ENTRY(ce_aes_ctr_encrypt) .Lctrloop4x: subs r4, r4, #4 bmi .Lctr1x - add r6, r6, #1 + + /* + * NOTE: the sequence below has been carefully tweaked to avoid + * a silicon erratum that exists in Cortex-A57 (#1742098) and + * Cortex-A72 (#1655431) cores, where AESE/AESMC instruction pairs + * may produce an incorrect result if they take their input from a + * register of which a single 32-bit lane has been updated the last + * time it was modified. To work around this, the lanes of registers + * q0-q3 below are not manipulated individually, and the different + * counter values are prepared by successive manipulations of q7. + */ + add ip, r6, #1 vmov q0, q7 + rev ip, ip + add lr, r6, #2 + vmov s31, ip @ set lane 3 of q1 via q7 + add ip, r6, #3 + rev lr, lr vmov q1, q7 - rev ip, r6 - add r6, r6, #1 + vmov s31, lr @ set lane 3 of q2 via q7 + rev ip, ip vmov q2, q7 - vmov s7, ip - rev ip, r6 - add r6, r6, #1 + vmov s31, ip @ set lane 3 of q3 via q7 + add r6, r6, #4 vmov q3, q7 - vmov s11, ip - rev ip, r6 - add r6, r6, #1 - vmov s15, ip + vld1.8 {q4-q5}, [r1]! vld1.8 {q6}, [r1]! vld1.8 {q15}, [r1]! diff --git a/arch/arm/crypto/aes-neonbs-glue.c b/arch/arm/crypto/aes-neonbs-glue.c index bda8bf17631e..f70af1d0514b 100644 --- a/arch/arm/crypto/aes-neonbs-glue.c +++ b/arch/arm/crypto/aes-neonbs-glue.c @@ -19,7 +19,7 @@ MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS_CRYPTO("ecb(aes)"); -MODULE_ALIAS_CRYPTO("cbc(aes)"); +MODULE_ALIAS_CRYPTO("cbc(aes)-all"); MODULE_ALIAS_CRYPTO("ctr(aes)"); MODULE_ALIAS_CRYPTO("xts(aes)"); @@ -191,7 +191,8 @@ static int cbc_init(struct crypto_skcipher *tfm) struct aesbs_cbc_ctx *ctx = crypto_skcipher_ctx(tfm); unsigned int reqsize; - ctx->enc_tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); + ctx->enc_tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(ctx->enc_tfm)) return PTR_ERR(ctx->enc_tfm); @@ -441,7 +442,8 @@ static struct skcipher_alg aes_algs[] = { { .base.cra_blocksize = AES_BLOCK_SIZE, .base.cra_ctxsize = sizeof(struct aesbs_cbc_ctx), .base.cra_module = THIS_MODULE, - .base.cra_flags = CRYPTO_ALG_INTERNAL, + .base.cra_flags = CRYPTO_ALG_INTERNAL | + CRYPTO_ALG_NEED_FALLBACK, .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, diff --git a/arch/arm/crypto/chacha-glue.c b/arch/arm/crypto/chacha-glue.c index 59da6c0b63b6..7b5cf8430c6d 100644 --- a/arch/arm/crypto/chacha-glue.c +++ b/arch/arm/crypto/chacha-glue.c @@ -23,7 +23,7 @@ asmlinkage void chacha_block_xor_neon(const u32 *state, u8 *dst, const u8 *src, int nrounds); asmlinkage void chacha_4block_xor_neon(const u32 *state, u8 *dst, const u8 *src, - int nrounds); + int nrounds, unsigned int nbytes); asmlinkage void hchacha_block_arm(const u32 *state, u32 *out, int nrounds); asmlinkage void hchacha_block_neon(const u32 *state, u32 *out, int nrounds); @@ -42,24 +42,24 @@ static void chacha_doneon(u32 *state, u8 *dst, const u8 *src, { u8 buf[CHACHA_BLOCK_SIZE]; - while (bytes >= CHACHA_BLOCK_SIZE * 4) { - chacha_4block_xor_neon(state, dst, src, nrounds); - bytes -= CHACHA_BLOCK_SIZE * 4; - src += CHACHA_BLOCK_SIZE * 4; - dst += CHACHA_BLOCK_SIZE * 4; - state[12] += 4; - } - while (bytes >= CHACHA_BLOCK_SIZE) { - chacha_block_xor_neon(state, dst, src, nrounds); - bytes -= CHACHA_BLOCK_SIZE; - src += CHACHA_BLOCK_SIZE; - dst += CHACHA_BLOCK_SIZE; - state[12]++; + while (bytes > CHACHA_BLOCK_SIZE) { + unsigned int l = min(bytes, CHACHA_BLOCK_SIZE * 4U); + + chacha_4block_xor_neon(state, dst, src, nrounds, l); + bytes -= l; + src += l; + dst += l; + state[12] += DIV_ROUND_UP(l, CHACHA_BLOCK_SIZE); } if (bytes) { - memcpy(buf, src, bytes); - chacha_block_xor_neon(state, buf, buf, nrounds); - memcpy(dst, buf, bytes); + const u8 *s = src; + u8 *d = dst; + + if (bytes != CHACHA_BLOCK_SIZE) + s = d = memcpy(buf, src, bytes); + chacha_block_xor_neon(state, d, s, nrounds); + if (d != dst) + memcpy(dst, buf, bytes); } } diff --git a/arch/arm/crypto/chacha-neon-core.S b/arch/arm/crypto/chacha-neon-core.S index eb22926d4912..13d12f672656 100644 --- a/arch/arm/crypto/chacha-neon-core.S +++ b/arch/arm/crypto/chacha-neon-core.S @@ -47,6 +47,7 @@ */ #include <linux/linkage.h> +#include <asm/cache.h> .text .fpu neon @@ -205,7 +206,7 @@ ENDPROC(hchacha_block_neon) .align 5 ENTRY(chacha_4block_xor_neon) - push {r4-r5} + push {r4, lr} mov r4, sp // preserve the stack pointer sub ip, sp, #0x20 // allocate a 32 byte buffer bic ip, ip, #0x1f // aligned to 32 bytes @@ -229,10 +230,10 @@ ENTRY(chacha_4block_xor_neon) vld1.32 {q0-q1}, [r0] vld1.32 {q2-q3}, [ip] - adr r5, .Lctrinc + adr lr, .Lctrinc vdup.32 q15, d7[1] vdup.32 q14, d7[0] - vld1.32 {q4}, [r5, :128] + vld1.32 {q4}, [lr, :128] vdup.32 q13, d6[1] vdup.32 q12, d6[0] vdup.32 q11, d5[1] @@ -455,7 +456,7 @@ ENTRY(chacha_4block_xor_neon) // Re-interleave the words in the first two rows of each block (x0..7). // Also add the counter values 0-3 to x12[0-3]. - vld1.32 {q8}, [r5, :128] // load counter values 0-3 + vld1.32 {q8}, [lr, :128] // load counter values 0-3 vzip.32 q0, q1 // => (0 1 0 1) (0 1 0 1) vzip.32 q2, q3 // => (2 3 2 3) (2 3 2 3) vzip.32 q4, q5 // => (4 5 4 5) (4 5 4 5) @@ -493,6 +494,8 @@ ENTRY(chacha_4block_xor_neon) // Re-interleave the words in the last two rows of each block (x8..15). vld1.32 {q8-q9}, [sp, :256] + mov sp, r4 // restore original stack pointer + ldr r4, [r4, #8] // load number of bytes vzip.32 q12, q13 // => (12 13 12 13) (12 13 12 13) vzip.32 q14, q15 // => (14 15 14 15) (14 15 14 15) vzip.32 q8, q9 // => (8 9 8 9) (8 9 8 9) @@ -520,41 +523,121 @@ ENTRY(chacha_4block_xor_neon) // XOR the rest of the data with the keystream vld1.8 {q0-q1}, [r2]! + subs r4, r4, #96 veor q0, q0, q8 veor q1, q1, q12 + ble .Lle96 vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2]! + subs r4, r4, #32 veor q0, q0, q2 veor q1, q1, q6 + ble .Lle128 vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2]! + subs r4, r4, #32 veor q0, q0, q10 veor q1, q1, q14 + ble .Lle160 vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2]! + subs r4, r4, #32 veor q0, q0, q4 veor q1, q1, q5 + ble .Lle192 vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2]! + subs r4, r4, #32 veor q0, q0, q9 veor q1, q1, q13 + ble .Lle224 vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2]! + subs r4, r4, #32 veor q0, q0, q3 veor q1, q1, q7 + blt .Llt256 +.Lout: vst1.8 {q0-q1}, [r1]! vld1.8 {q0-q1}, [r2] - mov sp, r4 // restore original stack pointer veor q0, q0, q11 veor q1, q1, q15 vst1.8 {q0-q1}, [r1] - pop {r4-r5} - bx lr + pop {r4, pc} + +.Lle192: + vmov q4, q9 + vmov q5, q13 + +.Lle160: + // nothing to do + +.Lfinalblock: + // Process the final block if processing less than 4 full blocks. + // Entered with 32 bytes of ChaCha cipher stream in q4-q5, and the + // previous 32 byte output block that still needs to be written at + // [r1] in q0-q1. + beq .Lfullblock + +.Lpartialblock: + adr lr, .Lpermute + 32 + add r2, r2, r4 + add lr, lr, r4 + add r4, r4, r1 + + vld1.8 {q2-q3}, [lr] + vld1.8 {q6-q7}, [r2] + + add r4, r4, #32 + + vtbl.8 d4, {q4-q5}, d4 + vtbl.8 d5, {q4-q5}, d5 + vtbl.8 d6, {q4-q5}, d6 + vtbl.8 d7, {q4-q5}, d7 + + veor q6, q6, q2 + veor q7, q7, q3 + + vst1.8 {q6-q7}, [r4] // overlapping stores + vst1.8 {q0-q1}, [r1] + pop {r4, pc} + +.Lfullblock: + vmov q11, q4 + vmov q15, q5 + b .Lout +.Lle96: + vmov q4, q2 + vmov q5, q6 + b .Lfinalblock +.Lle128: + vmov q4, q10 + vmov q5, q14 + b .Lfinalblock +.Lle224: + vmov q4, q3 + vmov q5, q7 + b .Lfinalblock +.Llt256: + vmov q4, q11 + vmov q5, q15 + b .Lpartialblock ENDPROC(chacha_4block_xor_neon) + + .align L1_CACHE_SHIFT +.Lpermute: + .byte 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + .byte 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + .byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 + .byte 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + .byte 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + .byte 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + .byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 + .byte 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f diff --git a/arch/arm/crypto/sha1-ce-glue.c b/arch/arm/crypto/sha1-ce-glue.c index e79b1fb4b4dc..de9100c67b37 100644 --- a/arch/arm/crypto/sha1-ce-glue.c +++ b/arch/arm/crypto/sha1-ce-glue.c @@ -7,7 +7,7 @@ #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> diff --git a/arch/arm/crypto/sha1.h b/arch/arm/crypto/sha1.h index 758db3e9ff0a..b1b7e21da2c3 100644 --- a/arch/arm/crypto/sha1.h +++ b/arch/arm/crypto/sha1.h @@ -3,7 +3,7 @@ #define ASM_ARM_CRYPTO_SHA1_H #include <linux/crypto.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> extern int sha1_update_arm(struct shash_desc *desc, const u8 *data, unsigned int len); diff --git a/arch/arm/crypto/sha1_glue.c b/arch/arm/crypto/sha1_glue.c index 4e954b3f7ecd..6c2b849e459d 100644 --- a/arch/arm/crypto/sha1_glue.c +++ b/arch/arm/crypto/sha1_glue.c @@ -15,7 +15,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <asm/byteorder.h> diff --git a/arch/arm/crypto/sha1_neon_glue.c b/arch/arm/crypto/sha1_neon_glue.c index 0071e5e4411a..cfe36ae0f3f5 100644 --- a/arch/arm/crypto/sha1_neon_glue.c +++ b/arch/arm/crypto/sha1_neon_glue.c @@ -19,7 +19,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <asm/neon.h> #include <asm/simd.h> diff --git a/arch/arm/crypto/sha2-ce-glue.c b/arch/arm/crypto/sha2-ce-glue.c index 87f0b62386c6..c62ce89dd3e0 100644 --- a/arch/arm/crypto/sha2-ce-glue.c +++ b/arch/arm/crypto/sha2-ce-glue.c @@ -7,7 +7,7 @@ #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> diff --git a/arch/arm/crypto/sha256_glue.c b/arch/arm/crypto/sha256_glue.c index b8a4f79020cf..433ee4ddce6c 100644 --- a/arch/arm/crypto/sha256_glue.c +++ b/arch/arm/crypto/sha256_glue.c @@ -17,7 +17,7 @@ #include <linux/mm.h> #include <linux/types.h> #include <linux/string.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <asm/simd.h> #include <asm/neon.h> diff --git a/arch/arm/crypto/sha256_neon_glue.c b/arch/arm/crypto/sha256_neon_glue.c index 79820b9e2541..701706262ef3 100644 --- a/arch/arm/crypto/sha256_neon_glue.c +++ b/arch/arm/crypto/sha256_neon_glue.c @@ -13,7 +13,7 @@ #include <crypto/internal/simd.h> #include <linux/types.h> #include <linux/string.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <asm/byteorder.h> #include <asm/simd.h> diff --git a/arch/arm/crypto/sha512-glue.c b/arch/arm/crypto/sha512-glue.c index 8775aa42bbbe..0635a65aa488 100644 --- a/arch/arm/crypto/sha512-glue.c +++ b/arch/arm/crypto/sha512-glue.c @@ -6,7 +6,7 @@ */ #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <linux/crypto.h> #include <linux/module.h> diff --git a/arch/arm/crypto/sha512-neon-glue.c b/arch/arm/crypto/sha512-neon-glue.c index 96cb94403540..c879ad32db51 100644 --- a/arch/arm/crypto/sha512-neon-glue.c +++ b/arch/arm/crypto/sha512-neon-glue.c @@ -7,7 +7,7 @@ #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <linux/crypto.h> #include <linux/module.h> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 5cfe3cf6f2ac..5e7d86cf5dfa 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1082,6 +1082,7 @@ CONFIG_CRYPTO_DEV_CCREE=m CONFIG_CRYPTO_DEV_HISI_SEC2=m CONFIG_CRYPTO_DEV_HISI_ZIP=m CONFIG_CRYPTO_DEV_HISI_HPRE=m +CONFIG_CRYPTO_DEV_HISI_TRNG=m CONFIG_CMA_SIZE_MBYTES=32 CONFIG_PRINTK_TIME=y CONFIG_DEBUG_INFO=y diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c index 395bbf64b2ab..34b8a89197be 100644 --- a/arch/arm64/crypto/aes-glue.c +++ b/arch/arm64/crypto/aes-glue.c @@ -10,7 +10,7 @@ #include <asm/simd.h> #include <crypto/aes.h> #include <crypto/ctr.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> #include <crypto/internal/skcipher.h> diff --git a/arch/arm64/crypto/chacha-neon-core.S b/arch/arm64/crypto/chacha-neon-core.S index e90386a7db8e..b70ac76f2610 100644 --- a/arch/arm64/crypto/chacha-neon-core.S +++ b/arch/arm64/crypto/chacha-neon-core.S @@ -195,7 +195,6 @@ SYM_FUNC_START(chacha_4block_xor_neon) adr_l x10, .Lpermute and x5, x4, #63 add x10, x10, x5 - add x11, x10, #64 // // This function encrypts four consecutive ChaCha blocks by loading @@ -645,11 +644,11 @@ CPU_BE( rev a15, a15 ) zip2 v31.4s, v14.4s, v15.4s eor a15, a15, w9 - mov x3, #64 + add x3, x2, x4 + sub x3, x3, #128 // start of last block + subs x5, x4, #128 - add x6, x5, x2 - csel x3, x3, xzr, ge - csel x2, x2, x6, ge + csel x2, x2, x3, ge // interleave 64-bit words in state n, n+2 zip1 v0.2d, v16.2d, v18.2d @@ -658,13 +657,10 @@ CPU_BE( rev a15, a15 ) zip1 v8.2d, v17.2d, v19.2d zip2 v12.2d, v17.2d, v19.2d stp a2, a3, [x1, #-56] - ld1 {v16.16b-v19.16b}, [x2], x3 subs x6, x4, #192 - ccmp x3, xzr, #4, lt - add x7, x6, x2 - csel x3, x3, xzr, eq - csel x2, x2, x7, eq + ld1 {v16.16b-v19.16b}, [x2], #64 + csel x2, x2, x3, ge zip1 v1.2d, v20.2d, v22.2d zip2 v5.2d, v20.2d, v22.2d @@ -672,13 +668,10 @@ CPU_BE( rev a15, a15 ) zip1 v9.2d, v21.2d, v23.2d zip2 v13.2d, v21.2d, v23.2d stp a6, a7, [x1, #-40] - ld1 {v20.16b-v23.16b}, [x2], x3 subs x7, x4, #256 - ccmp x3, xzr, #4, lt - add x8, x7, x2 - csel x3, x3, xzr, eq - csel x2, x2, x8, eq + ld1 {v20.16b-v23.16b}, [x2], #64 + csel x2, x2, x3, ge zip1 v2.2d, v24.2d, v26.2d zip2 v6.2d, v24.2d, v26.2d @@ -686,12 +679,10 @@ CPU_BE( rev a15, a15 ) zip1 v10.2d, v25.2d, v27.2d zip2 v14.2d, v25.2d, v27.2d stp a10, a11, [x1, #-24] - ld1 {v24.16b-v27.16b}, [x2], x3 subs x8, x4, #320 - ccmp x3, xzr, #4, lt - add x9, x8, x2 - csel x2, x2, x9, eq + ld1 {v24.16b-v27.16b}, [x2], #64 + csel x2, x2, x3, ge zip1 v3.2d, v28.2d, v30.2d zip2 v7.2d, v28.2d, v30.2d @@ -699,151 +690,105 @@ CPU_BE( rev a15, a15 ) zip1 v11.2d, v29.2d, v31.2d zip2 v15.2d, v29.2d, v31.2d stp a14, a15, [x1, #-8] + + tbnz x5, #63, .Lt128 ld1 {v28.16b-v31.16b}, [x2] // xor with corresponding input, write to output - tbnz x5, #63, 0f eor v16.16b, v16.16b, v0.16b eor v17.16b, v17.16b, v1.16b eor v18.16b, v18.16b, v2.16b eor v19.16b, v19.16b, v3.16b - st1 {v16.16b-v19.16b}, [x1], #64 - cbz x5, .Lout - tbnz x6, #63, 1f + tbnz x6, #63, .Lt192 + eor v20.16b, v20.16b, v4.16b eor v21.16b, v21.16b, v5.16b eor v22.16b, v22.16b, v6.16b eor v23.16b, v23.16b, v7.16b - st1 {v20.16b-v23.16b}, [x1], #64 - cbz x6, .Lout - tbnz x7, #63, 2f + st1 {v16.16b-v19.16b}, [x1], #64 + tbnz x7, #63, .Lt256 + eor v24.16b, v24.16b, v8.16b eor v25.16b, v25.16b, v9.16b eor v26.16b, v26.16b, v10.16b eor v27.16b, v27.16b, v11.16b - st1 {v24.16b-v27.16b}, [x1], #64 - cbz x7, .Lout - tbnz x8, #63, 3f + st1 {v20.16b-v23.16b}, [x1], #64 + tbnz x8, #63, .Lt320 + eor v28.16b, v28.16b, v12.16b eor v29.16b, v29.16b, v13.16b eor v30.16b, v30.16b, v14.16b eor v31.16b, v31.16b, v15.16b + + st1 {v24.16b-v27.16b}, [x1], #64 st1 {v28.16b-v31.16b}, [x1] .Lout: frame_pop ret - // fewer than 128 bytes of in/output -0: ld1 {v8.16b}, [x10] - ld1 {v9.16b}, [x11] - movi v10.16b, #16 - sub x2, x1, #64 - add x1, x1, x5 - ld1 {v16.16b-v19.16b}, [x2] - tbl v4.16b, {v0.16b-v3.16b}, v8.16b - tbx v20.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v5.16b, {v0.16b-v3.16b}, v8.16b - tbx v21.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v6.16b, {v0.16b-v3.16b}, v8.16b - tbx v22.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v7.16b, {v0.16b-v3.16b}, v8.16b - tbx v23.16b, {v16.16b-v19.16b}, v9.16b - - eor v20.16b, v20.16b, v4.16b - eor v21.16b, v21.16b, v5.16b - eor v22.16b, v22.16b, v6.16b - eor v23.16b, v23.16b, v7.16b - st1 {v20.16b-v23.16b}, [x1] - b .Lout - // fewer than 192 bytes of in/output -1: ld1 {v8.16b}, [x10] - ld1 {v9.16b}, [x11] - movi v10.16b, #16 - add x1, x1, x6 - tbl v0.16b, {v4.16b-v7.16b}, v8.16b - tbx v20.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v1.16b, {v4.16b-v7.16b}, v8.16b - tbx v21.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v2.16b, {v4.16b-v7.16b}, v8.16b - tbx v22.16b, {v16.16b-v19.16b}, v9.16b - add v8.16b, v8.16b, v10.16b - add v9.16b, v9.16b, v10.16b - tbl v3.16b, {v4.16b-v7.16b}, v8.16b - tbx v23.16b, {v16.16b-v19.16b}, v9.16b - - eor v20.16b, v20.16b, v0.16b - eor v21.16b, v21.16b, v1.16b - eor v22.16b, v22.16b, v2.16b - eor v23.16b, v23.16b, v3.16b - st1 {v20.16b-v23.16b}, [x1] +.Lt192: cbz x5, 1f // exactly 128 bytes? + ld1 {v28.16b-v31.16b}, [x10] + add x5, x5, x1 + tbl v28.16b, {v4.16b-v7.16b}, v28.16b + tbl v29.16b, {v4.16b-v7.16b}, v29.16b + tbl v30.16b, {v4.16b-v7.16b}, v30.16b + tbl v31.16b, {v4.16b-v7.16b}, v31.16b + +0: eor v20.16b, v20.16b, v28.16b + eor v21.16b, v21.16b, v29.16b + eor v22.16b, v22.16b, v30.16b + eor v23.16b, v23.16b, v31.16b + st1 {v20.16b-v23.16b}, [x5] // overlapping stores +1: st1 {v16.16b-v19.16b}, [x1] b .Lout + // fewer than 128 bytes of in/output +.Lt128: ld1 {v28.16b-v31.16b}, [x10] + add x5, x5, x1 + sub x1, x1, #64 + tbl v28.16b, {v0.16b-v3.16b}, v28.16b + tbl v29.16b, {v0.16b-v3.16b}, v29.16b + tbl v30.16b, {v0.16b-v3.16b}, v30.16b + tbl v31.16b, {v0.16b-v3.16b}, v31.16b + ld1 {v16.16b-v19.16b}, [x1] // reload first output block + b 0b + // fewer than 256 bytes of in/output -2: ld1 {v4.16b}, [x10] - ld1 {v5.16b}, [x11] - movi v6.16b, #16 - add x1, x1, x7 +.Lt256: cbz x6, 2f // exactly 192 bytes? + ld1 {v4.16b-v7.16b}, [x10] + add x6, x6, x1 tbl v0.16b, {v8.16b-v11.16b}, v4.16b - tbx v24.16b, {v20.16b-v23.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v1.16b, {v8.16b-v11.16b}, v4.16b - tbx v25.16b, {v20.16b-v23.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v2.16b, {v8.16b-v11.16b}, v4.16b - tbx v26.16b, {v20.16b-v23.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v3.16b, {v8.16b-v11.16b}, v4.16b - tbx v27.16b, {v20.16b-v23.16b}, v5.16b - - eor v24.16b, v24.16b, v0.16b - eor v25.16b, v25.16b, v1.16b - eor v26.16b, v26.16b, v2.16b - eor v27.16b, v27.16b, v3.16b - st1 {v24.16b-v27.16b}, [x1] + tbl v1.16b, {v8.16b-v11.16b}, v5.16b + tbl v2.16b, {v8.16b-v11.16b}, v6.16b + tbl v3.16b, {v8.16b-v11.16b}, v7.16b + + eor v28.16b, v28.16b, v0.16b + eor v29.16b, v29.16b, v1.16b + eor v30.16b, v30.16b, v2.16b + eor v31.16b, v31.16b, v3.16b + st1 {v28.16b-v31.16b}, [x6] // overlapping stores +2: st1 {v20.16b-v23.16b}, [x1] b .Lout // fewer than 320 bytes of in/output -3: ld1 {v4.16b}, [x10] - ld1 {v5.16b}, [x11] - movi v6.16b, #16 - add x1, x1, x8 +.Lt320: cbz x7, 3f // exactly 256 bytes? + ld1 {v4.16b-v7.16b}, [x10] + add x7, x7, x1 tbl v0.16b, {v12.16b-v15.16b}, v4.16b - tbx v28.16b, {v24.16b-v27.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v1.16b, {v12.16b-v15.16b}, v4.16b - tbx v29.16b, {v24.16b-v27.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v2.16b, {v12.16b-v15.16b}, v4.16b - tbx v30.16b, {v24.16b-v27.16b}, v5.16b - add v4.16b, v4.16b, v6.16b - add v5.16b, v5.16b, v6.16b - tbl v3.16b, {v12.16b-v15.16b}, v4.16b - tbx v31.16b, {v24.16b-v27.16b}, v5.16b + tbl v1.16b, {v12.16b-v15.16b}, v5.16b + tbl v2.16b, {v12.16b-v15.16b}, v6.16b + tbl v3.16b, {v12.16b-v15.16b}, v7.16b eor v28.16b, v28.16b, v0.16b eor v29.16b, v29.16b, v1.16b eor v30.16b, v30.16b, v2.16b eor v31.16b, v31.16b, v3.16b - st1 {v28.16b-v31.16b}, [x1] + st1 {v28.16b-v31.16b}, [x7] // overlapping stores +3: st1 {v24.16b-v27.16b}, [x1] b .Lout SYM_FUNC_END(chacha_4block_xor_neon) @@ -851,7 +796,7 @@ SYM_FUNC_END(chacha_4block_xor_neon) .align L1_CACHE_SHIFT .Lpermute: .set .Li, 0 - .rept 192 + .rept 128 .byte (.Li - 64) .set .Li, .Li + 1 .endr diff --git a/arch/arm64/crypto/ghash-ce-core.S b/arch/arm64/crypto/ghash-ce-core.S index 6b958dcdf136..7868330dd54e 100644 --- a/arch/arm64/crypto/ghash-ce-core.S +++ b/arch/arm64/crypto/ghash-ce-core.S @@ -544,7 +544,22 @@ CPU_LE( rev w8, w8 ) ext XL.16b, XL.16b, XL.16b, #8 rev64 XL.16b, XL.16b eor XL.16b, XL.16b, KS0.16b + + .if \enc == 1 st1 {XL.16b}, [x10] // store tag + .else + ldp x11, x12, [sp, #40] // load tag pointer and authsize + adr_l x17, .Lpermute_table + ld1 {KS0.16b}, [x11] // load supplied tag + add x17, x17, x12 + ld1 {KS1.16b}, [x17] // load permute vector + + cmeq XL.16b, XL.16b, KS0.16b // compare tags + mvn XL.16b, XL.16b // -1 for fail, 0 for pass + tbl XL.16b, {XL.16b}, KS1.16b // keep authsize bytes only + sminv b0, XL.16b // signed minimum across XL + smov w0, v0.b[0] // return b0 + .endif 4: ldp x29, x30, [sp], #32 ret diff --git a/arch/arm64/crypto/ghash-ce-glue.c b/arch/arm64/crypto/ghash-ce-glue.c index 8536008e3e35..720cd3a58da3 100644 --- a/arch/arm64/crypto/ghash-ce-glue.c +++ b/arch/arm64/crypto/ghash-ce-glue.c @@ -55,10 +55,10 @@ asmlinkage void pmull_ghash_update_p8(int blocks, u64 dg[], const char *src, asmlinkage void pmull_gcm_encrypt(int bytes, u8 dst[], const u8 src[], u64 const h[][2], u64 dg[], u8 ctr[], u32 const rk[], int rounds, u8 tag[]); - -asmlinkage void pmull_gcm_decrypt(int bytes, u8 dst[], const u8 src[], - u64 const h[][2], u64 dg[], u8 ctr[], - u32 const rk[], int rounds, u8 tag[]); +asmlinkage int pmull_gcm_decrypt(int bytes, u8 dst[], const u8 src[], + u64 const h[][2], u64 dg[], u8 ctr[], + u32 const rk[], int rounds, const u8 l[], + const u8 tag[], u64 authsize); static int ghash_init(struct shash_desc *desc) { @@ -168,7 +168,7 @@ static int ghash_final(struct shash_desc *desc, u8 *dst) put_unaligned_be64(ctx->digest[1], dst); put_unaligned_be64(ctx->digest[0], dst + 8); - *ctx = (struct ghash_desc_ctx){}; + memzero_explicit(ctx, sizeof(*ctx)); return 0; } @@ -458,6 +458,7 @@ static int gcm_decrypt(struct aead_request *req) unsigned int authsize = crypto_aead_authsize(aead); int nrounds = num_rounds(&ctx->aes_key); struct skcipher_walk walk; + u8 otag[AES_BLOCK_SIZE]; u8 buf[AES_BLOCK_SIZE]; u8 iv[AES_BLOCK_SIZE]; u64 dg[2] = {}; @@ -474,9 +475,15 @@ static int gcm_decrypt(struct aead_request *req) memcpy(iv, req->iv, GCM_IV_SIZE); put_unaligned_be32(2, iv + GCM_IV_SIZE); + scatterwalk_map_and_copy(otag, req->src, + req->assoclen + req->cryptlen - authsize, + authsize, 0); + err = skcipher_walk_aead_decrypt(&walk, req, false); if (likely(crypto_simd_usable())) { + int ret; + do { const u8 *src = walk.src.virt.addr; u8 *dst = walk.dst.virt.addr; @@ -493,9 +500,10 @@ static int gcm_decrypt(struct aead_request *req) } kernel_neon_begin(); - pmull_gcm_decrypt(nbytes, dst, src, ctx->ghash_key.h, - dg, iv, ctx->aes_key.key_enc, nrounds, - tag); + ret = pmull_gcm_decrypt(nbytes, dst, src, + ctx->ghash_key.h, + dg, iv, ctx->aes_key.key_enc, + nrounds, tag, otag, authsize); kernel_neon_end(); if (unlikely(!nbytes)) @@ -507,6 +515,11 @@ static int gcm_decrypt(struct aead_request *req) err = skcipher_walk_done(&walk, walk.nbytes - nbytes); } while (walk.nbytes); + + if (err) + return err; + if (ret) + return -EBADMSG; } else { while (walk.nbytes >= AES_BLOCK_SIZE) { int blocks = walk.nbytes / AES_BLOCK_SIZE; @@ -548,23 +561,20 @@ static int gcm_decrypt(struct aead_request *req) err = skcipher_walk_done(&walk, 0); } + if (err) + return err; + put_unaligned_be64(dg[1], tag); put_unaligned_be64(dg[0], tag + 8); put_unaligned_be32(1, iv + GCM_IV_SIZE); aes_encrypt(&ctx->aes_key, iv, iv); crypto_xor(tag, iv, AES_BLOCK_SIZE); - } - - if (err) - return err; - /* compare calculated auth tag with the stored one */ - scatterwalk_map_and_copy(buf, req->src, - req->assoclen + req->cryptlen - authsize, - authsize, 0); - - if (crypto_memneq(tag, buf, authsize)) - return -EBADMSG; + if (crypto_memneq(tag, otag, authsize)) { + memzero_explicit(tag, AES_BLOCK_SIZE); + return -EBADMSG; + } + } return 0; } diff --git a/arch/arm64/crypto/poly1305-armv8.pl b/arch/arm64/crypto/poly1305-armv8.pl index 6e5576d19af8..cbc980fb02e3 100644 --- a/arch/arm64/crypto/poly1305-armv8.pl +++ b/arch/arm64/crypto/poly1305-armv8.pl @@ -840,7 +840,6 @@ poly1305_blocks_neon: ldp d14,d15,[sp,#64] addp $ACC2,$ACC2,$ACC2 ldr x30,[sp,#8] - .inst 0xd50323bf // autiasp //////////////////////////////////////////////////////////////// // lazy reduction, but without narrowing @@ -882,6 +881,7 @@ poly1305_blocks_neon: str x4,[$ctx,#8] // set is_base2_26 ldr x29,[sp],#80 + .inst 0xd50323bf // autiasp ret .size poly1305_blocks_neon,.-poly1305_blocks_neon diff --git a/arch/arm64/crypto/poly1305-core.S_shipped b/arch/arm64/crypto/poly1305-core.S_shipped index 8d1c4e420ccd..fb2822abf63a 100644 --- a/arch/arm64/crypto/poly1305-core.S_shipped +++ b/arch/arm64/crypto/poly1305-core.S_shipped @@ -779,7 +779,6 @@ poly1305_blocks_neon: ldp d14,d15,[sp,#64] addp v21.2d,v21.2d,v21.2d ldr x30,[sp,#8] - .inst 0xd50323bf // autiasp //////////////////////////////////////////////////////////////// // lazy reduction, but without narrowing @@ -821,6 +820,7 @@ poly1305_blocks_neon: str x4,[x0,#8] // set is_base2_26 ldr x29,[sp],#80 + .inst 0xd50323bf // autiasp ret .size poly1305_blocks_neon,.-poly1305_blocks_neon diff --git a/arch/arm64/crypto/poly1305-glue.c b/arch/arm64/crypto/poly1305-glue.c index f33ada70c4ed..683de671741a 100644 --- a/arch/arm64/crypto/poly1305-glue.c +++ b/arch/arm64/crypto/poly1305-glue.c @@ -177,7 +177,7 @@ void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst) } poly1305_emit(&dctx->h, dst, dctx->s); - *dctx = (struct poly1305_desc_ctx){}; + memzero_explicit(dctx, sizeof(*dctx)); } EXPORT_SYMBOL(poly1305_final_arch); diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c index c63b99211db3..c93121bcfdeb 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -10,7 +10,7 @@ #include <asm/unaligned.h> #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c index 5e956d7582a5..31ba3da5e61b 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -10,7 +10,7 @@ #include <asm/unaligned.h> #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> diff --git a/arch/arm64/crypto/sha256-glue.c b/arch/arm64/crypto/sha256-glue.c index 77bc6e72abae..9462f6088b3f 100644 --- a/arch/arm64/crypto/sha256-glue.c +++ b/arch/arm64/crypto/sha256-glue.c @@ -10,7 +10,7 @@ #include <asm/simd.h> #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <linux/types.h> #include <linux/string.h> diff --git a/arch/arm64/crypto/sha3-ce-glue.c b/arch/arm64/crypto/sha3-ce-glue.c index 9a4bbfc45f40..e5a2936f0886 100644 --- a/arch/arm64/crypto/sha3-ce-glue.c +++ b/arch/arm64/crypto/sha3-ce-glue.c @@ -94,7 +94,7 @@ static int sha3_final(struct shash_desc *desc, u8 *out) if (digest_size & 4) put_unaligned_le32(sctx->st[i], (__le32 *)digest); - *sctx = (struct sha3_state){}; + memzero_explicit(sctx, sizeof(*sctx)); return 0; } diff --git a/arch/arm64/crypto/sha512-ce-glue.c b/arch/arm64/crypto/sha512-ce-glue.c index dc890a719f54..faa83f6cf376 100644 --- a/arch/arm64/crypto/sha512-ce-glue.c +++ b/arch/arm64/crypto/sha512-ce-glue.c @@ -14,7 +14,7 @@ #include <asm/unaligned.h> #include <crypto/internal/hash.h> #include <crypto/internal/simd.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> diff --git a/arch/arm64/crypto/sha512-glue.c b/arch/arm64/crypto/sha512-glue.c index 370ccb29602f..2acff1c7df5d 100644 --- a/arch/arm64/crypto/sha512-glue.c +++ b/arch/arm64/crypto/sha512-glue.c @@ -8,7 +8,7 @@ #include <crypto/internal/hash.h> #include <linux/types.h> #include <linux/string.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <asm/neon.h> diff --git a/arch/mips/cavium-octeon/crypto/octeon-crypto.h b/arch/mips/cavium-octeon/crypto/octeon-crypto.h index 7315cc307397..cb68f9e284bb 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-crypto.h +++ b/arch/mips/cavium-octeon/crypto/octeon-crypto.h @@ -41,7 +41,7 @@ do { \ */ #define read_octeon_64bit_hash_dword(index) \ ({ \ - u64 __value; \ + __be64 __value; \ \ __asm__ __volatile__ ( \ "dmfc2 %[rt],0x0048+" STR(index) \ diff --git a/arch/mips/cavium-octeon/crypto/octeon-md5.c b/arch/mips/cavium-octeon/crypto/octeon-md5.c index 8c8ea139653e..5ee4ade99b99 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-md5.c +++ b/arch/mips/cavium-octeon/crypto/octeon-md5.c @@ -68,10 +68,11 @@ static int octeon_md5_init(struct shash_desc *desc) { struct md5_state *mctx = shash_desc_ctx(desc); - mctx->hash[0] = cpu_to_le32(MD5_H0); - mctx->hash[1] = cpu_to_le32(MD5_H1); - mctx->hash[2] = cpu_to_le32(MD5_H2); - mctx->hash[3] = cpu_to_le32(MD5_H3); + mctx->hash[0] = MD5_H0; + mctx->hash[1] = MD5_H1; + mctx->hash[2] = MD5_H2; + mctx->hash[3] = MD5_H3; + cpu_to_le32_array(mctx->hash, 4); mctx->byte_count = 0; return 0; @@ -139,8 +140,9 @@ static int octeon_md5_final(struct shash_desc *desc, u8 *out) } memset(p, 0, padding); - mctx->block[14] = cpu_to_le32(mctx->byte_count << 3); - mctx->block[15] = cpu_to_le32(mctx->byte_count >> 29); + mctx->block[14] = mctx->byte_count << 3; + mctx->block[15] = mctx->byte_count >> 29; + cpu_to_le32_array(mctx->block + 14, 2); octeon_md5_transform(mctx->block); octeon_md5_read_hash(mctx); diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha1.c b/arch/mips/cavium-octeon/crypto/octeon-sha1.c index 75e79b47abfe..30f1d75208a5 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-sha1.c +++ b/arch/mips/cavium-octeon/crypto/octeon-sha1.c @@ -14,7 +14,7 @@ */ #include <linux/mm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha256.c b/arch/mips/cavium-octeon/crypto/octeon-sha256.c index a682ce76716a..36cb92895d72 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-sha256.c +++ b/arch/mips/cavium-octeon/crypto/octeon-sha256.c @@ -15,7 +15,7 @@ */ #include <linux/mm.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha512.c b/arch/mips/cavium-octeon/crypto/octeon-sha512.c index 50722a0cfb53..359f039820d8 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-sha512.c +++ b/arch/mips/cavium-octeon/crypto/octeon-sha512.c @@ -14,7 +14,7 @@ */ #include <linux/mm.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> diff --git a/arch/powerpc/crypto/sha1-spe-glue.c b/arch/powerpc/crypto/sha1-spe-glue.c index cb57be4ada61..b1e577cbf00c 100644 --- a/arch/powerpc/crypto/sha1-spe-glue.c +++ b/arch/powerpc/crypto/sha1-spe-glue.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/byteorder.h> #include <asm/switch_to.h> #include <linux/hardirq.h> diff --git a/arch/powerpc/crypto/sha1.c b/arch/powerpc/crypto/sha1.c index b40dc50a6908..7a55d790cdb1 100644 --- a/arch/powerpc/crypto/sha1.c +++ b/arch/powerpc/crypto/sha1.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/byteorder.h> void powerpc_sha_transform(u32 *state, const u8 *src); diff --git a/arch/powerpc/crypto/sha256-spe-glue.c b/arch/powerpc/crypto/sha256-spe-glue.c index ceb0b6c980b3..a6e650a97d8f 100644 --- a/arch/powerpc/crypto/sha256-spe-glue.c +++ b/arch/powerpc/crypto/sha256-spe-glue.c @@ -13,7 +13,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/byteorder.h> #include <asm/switch_to.h> #include <linux/hardirq.h> @@ -177,7 +177,7 @@ static int ppc_spe_sha256_final(struct shash_desc *desc, u8 *out) static int ppc_spe_sha224_final(struct shash_desc *desc, u8 *out) { - u32 D[SHA256_DIGEST_SIZE >> 2]; + __be32 D[SHA256_DIGEST_SIZE >> 2]; __be32 *dst = (__be32 *)out; ppc_spe_sha256_final(desc, (u8 *)D); diff --git a/arch/s390/crypto/sha.h b/arch/s390/crypto/sha.h index ada2f98c27b7..65ea12fc87a1 100644 --- a/arch/s390/crypto/sha.h +++ b/arch/s390/crypto/sha.h @@ -11,7 +11,8 @@ #define _CRYPTO_ARCH_S390_SHA_H #include <linux/crypto.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sha3.h> /* must be big enough for the largest SHA variant */ diff --git a/arch/s390/crypto/sha1_s390.c b/arch/s390/crypto/sha1_s390.c index 698b1e6d3c14..a3fabf310a38 100644 --- a/arch/s390/crypto/sha1_s390.c +++ b/arch/s390/crypto/sha1_s390.c @@ -22,7 +22,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/cpufeature.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/cpacf.h> #include "sha.h" diff --git a/arch/s390/crypto/sha256_s390.c b/arch/s390/crypto/sha256_s390.c index b52c87e44939..24983f175676 100644 --- a/arch/s390/crypto/sha256_s390.c +++ b/arch/s390/crypto/sha256_s390.c @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/cpufeature.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/cpacf.h> #include "sha.h" diff --git a/arch/s390/crypto/sha3_256_s390.c b/arch/s390/crypto/sha3_256_s390.c index 460cbbbaa44a..30ac49b635bf 100644 --- a/arch/s390/crypto/sha3_256_s390.c +++ b/arch/s390/crypto/sha3_256_s390.c @@ -12,7 +12,6 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/cpufeature.h> -#include <crypto/sha.h> #include <crypto/sha3.h> #include <asm/cpacf.h> diff --git a/arch/s390/crypto/sha3_512_s390.c b/arch/s390/crypto/sha3_512_s390.c index 72cf460a53e5..e70d50f7620f 100644 --- a/arch/s390/crypto/sha3_512_s390.c +++ b/arch/s390/crypto/sha3_512_s390.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/cpufeature.h> -#include <crypto/sha.h> #include <crypto/sha3.h> #include <asm/cpacf.h> diff --git a/arch/s390/crypto/sha512_s390.c b/arch/s390/crypto/sha512_s390.c index ad29db085a18..29a6bd404c59 100644 --- a/arch/s390/crypto/sha512_s390.c +++ b/arch/s390/crypto/sha512_s390.c @@ -8,7 +8,7 @@ * Author(s): Jan Glauber (jang@de.ibm.com) */ #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/kernel.h> diff --git a/arch/s390/purgatory/purgatory.c b/arch/s390/purgatory/purgatory.c index 0a423bcf6746..030efda05dbe 100644 --- a/arch/s390/purgatory/purgatory.c +++ b/arch/s390/purgatory/purgatory.c @@ -9,7 +9,7 @@ #include <linux/kexec.h> #include <linux/string.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/purgatory.h> int verify_sha256_digest(void) diff --git a/arch/sparc/crypto/crc32c_glue.c b/arch/sparc/crypto/crc32c_glue.c index 4e9323229e71..82efb7f81c28 100644 --- a/arch/sparc/crypto/crc32c_glue.c +++ b/arch/sparc/crypto/crc32c_glue.c @@ -35,7 +35,7 @@ static int crc32c_sparc64_setkey(struct crypto_shash *hash, const u8 *key, if (keylen != sizeof(u32)) return -EINVAL; - *(__le32 *)mctx = le32_to_cpup((__le32 *)key); + *mctx = le32_to_cpup((__le32 *)key); return 0; } diff --git a/arch/sparc/crypto/md5_glue.c b/arch/sparc/crypto/md5_glue.c index 111283fe837e..511db98d590a 100644 --- a/arch/sparc/crypto/md5_glue.c +++ b/arch/sparc/crypto/md5_glue.c @@ -33,10 +33,11 @@ static int md5_sparc64_init(struct shash_desc *desc) { struct md5_state *mctx = shash_desc_ctx(desc); - mctx->hash[0] = cpu_to_le32(MD5_H0); - mctx->hash[1] = cpu_to_le32(MD5_H1); - mctx->hash[2] = cpu_to_le32(MD5_H2); - mctx->hash[3] = cpu_to_le32(MD5_H3); + mctx->hash[0] = MD5_H0; + mctx->hash[1] = MD5_H1; + mctx->hash[2] = MD5_H2; + mctx->hash[3] = MD5_H3; + le32_to_cpu_array(mctx->hash, 4); mctx->byte_count = 0; return 0; diff --git a/arch/sparc/crypto/sha1_glue.c b/arch/sparc/crypto/sha1_glue.c index dc017782be52..86a654cce5ab 100644 --- a/arch/sparc/crypto/sha1_glue.c +++ b/arch/sparc/crypto/sha1_glue.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/pstate.h> #include <asm/elf.h> diff --git a/arch/sparc/crypto/sha256_glue.c b/arch/sparc/crypto/sha256_glue.c index ca2547df9652..60ec524cf9ca 100644 --- a/arch/sparc/crypto/sha256_glue.c +++ b/arch/sparc/crypto/sha256_glue.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/pstate.h> #include <asm/elf.h> diff --git a/arch/sparc/crypto/sha512_glue.c b/arch/sparc/crypto/sha512_glue.c index 3b2ca732ff7a..273ce21918c1 100644 --- a/arch/sparc/crypto/sha512_glue.c +++ b/arch/sparc/crypto/sha512_glue.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/pstate.h> #include <asm/elf.h> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fbf26e0f7a6a..52e36adb5112 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1931,6 +1931,23 @@ config X86_INTEL_TSX_MODE_AUTO side channel attacks- equals the tsx=auto command line parameter. endchoice +config X86_SGX + bool "Software Guard eXtensions (SGX)" + depends on X86_64 && CPU_SUP_INTEL + depends on CRYPTO=y + depends on CRYPTO_SHA256=y + select SRCU + select MMU_NOTIFIER + help + Intel(R) Software Guard eXtensions (SGX) is a set of CPU instructions + that can be used by applications to set aside private regions of code + and data, referred to as enclaves. An enclave's private memory can + only be accessed by code running within the enclave. Accesses from + outside the enclave, including other enclaves, are disallowed by + hardware. + + If unsure, say N. + config EFI bool "EFI runtime service support" depends on ACPI diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 27b5e2bc6a01..80b57e7f4947 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -62,9 +62,6 @@ config EARLY_PRINTK_USB_XDBC You should normally say N here, unless you want to debug early crashes or need a very simple printk logging facility. -config COPY_MC_TEST - def_bool n - config EFI_PGT_DUMP bool "Dump the EFI pagetable" depends on EFI diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 1bf21746f4ce..7116da3980be 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -24,14 +24,7 @@ endif # How to compile the 16-bit code. Note we always compile for -march=i386; # that way we can complain to the user if the CPU is insufficient. -# -# The -m16 option is supported by GCC >= 4.9 and clang >= 3.5. For -# older versions of GCC, include an *assembly* header to make sure that -# gcc doesn't play any games behind our back. -CODE16GCC_CFLAGS := -m32 -Wa,$(srctree)/arch/x86/boot/code16gcc.h -M16_CFLAGS := $(call cc-option, -m16, $(CODE16GCC_CFLAGS)) - -REALMODE_CFLAGS := $(M16_CFLAGS) -g -Os -DDISABLE_BRANCH_PROFILING \ +REALMODE_CFLAGS := -m16 -g -Os -DDISABLE_BRANCH_PROFILING \ -Wall -Wstrict-prototypes -march=i386 -mregparm=3 \ -fno-strict-aliasing -fomit-frame-pointer -fno-pic \ -mno-mmx -mno-sse diff --git a/arch/x86/boot/code16gcc.h b/arch/x86/boot/code16gcc.h deleted file mode 100644 index e19fd7536307..000000000000 --- a/arch/x86/boot/code16gcc.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -# -# code16gcc.h -# -# This file is added to the assembler via -Wa when compiling 16-bit C code. -# This is done this way instead via asm() to make sure gcc does not reorder -# things around us. -# -# gcc 4.9+ has a real -m16 option so we can drop this hack long term. -# - - .code16gcc diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 017de6cc87dc..e94874f4bbc1 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -241,12 +241,12 @@ SYM_FUNC_START(startup_32) leal rva(startup_64)(%ebp), %eax #ifdef CONFIG_EFI_MIXED movl rva(efi32_boot_args)(%ebp), %edi - cmp $0, %edi + testl %edi, %edi jz 1f leal rva(efi64_stub_entry)(%ebp), %eax movl rva(efi32_boot_args+4)(%ebp), %esi movl rva(efi32_boot_args+8)(%ebp), %edx // saved bootparams pointer - cmpl $0, %edx + testl %edx, %edx jnz 1f /* * efi_pe_entry uses MS calling convention, which requires 32 bytes of @@ -592,7 +592,7 @@ SYM_CODE_START(trampoline_32bit_src) movl %eax, %cr0 /* Check what paging mode we want to be in after the trampoline */ - cmpl $0, %edx + testl %edx, %edx jz 1f /* We want 5-level paging: don't touch CR3 if it already points to 5-level page tables */ @@ -622,7 +622,7 @@ SYM_CODE_START(trampoline_32bit_src) /* Enable PAE and LA57 (if required) paging modes */ movl $X86_CR4_PAE, %eax - cmpl $0, %edx + testl %edx, %edx jz 1f orl $X86_CR4_LA57, %eax 1: diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c index 39b2eded7bc2..f7213d0943b8 100644 --- a/arch/x86/boot/compressed/ident_map_64.c +++ b/arch/x86/boot/compressed/ident_map_64.c @@ -168,16 +168,6 @@ void initialize_identity_maps(void *rmode) write_cr3(top_level_pgt); } -/* - * This switches the page tables to the new level4 that has been built - * via calls to add_identity_map() above. If booted via startup_32(), - * this is effectively a no-op. - */ -void finalize_identity_maps(void) -{ - write_cr3(top_level_pgt); -} - static pte_t *split_large_pmd(struct x86_mapping_info *info, pmd_t *pmdp, unsigned long __address) { diff --git a/arch/x86/crypto/aes_glue.c b/arch/x86/crypto/aes_glue.c deleted file mode 100644 index 7b7dc05fa1a4..000000000000 --- a/arch/x86/crypto/aes_glue.c +++ /dev/null @@ -1 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S index 1852b19a73a0..d1436c37008b 100644 --- a/arch/x86/crypto/aesni-intel_asm.S +++ b/arch/x86/crypto/aesni-intel_asm.S @@ -318,7 +318,7 @@ _initial_blocks_\@: # Main loop - Encrypt/Decrypt remaining blocks - cmp $0, %r13 + test %r13, %r13 je _zero_cipher_left_\@ sub $64, %r13 je _four_cipher_left_\@ @@ -437,7 +437,7 @@ _multiple_of_16_bytes_\@: mov PBlockLen(%arg2), %r12 - cmp $0, %r12 + test %r12, %r12 je _partial_done\@ GHASH_MUL %xmm8, %xmm13, %xmm9, %xmm10, %xmm11, %xmm5, %xmm6 @@ -474,7 +474,7 @@ _T_8_\@: add $8, %r10 sub $8, %r11 psrldq $8, %xmm0 - cmp $0, %r11 + test %r11, %r11 je _return_T_done_\@ _T_4_\@: movd %xmm0, %eax @@ -482,7 +482,7 @@ _T_4_\@: add $4, %r10 sub $4, %r11 psrldq $4, %xmm0 - cmp $0, %r11 + test %r11, %r11 je _return_T_done_\@ _T_123_\@: movd %xmm0, %eax @@ -619,7 +619,7 @@ _get_AAD_blocks\@: /* read the last <16B of AAD */ _get_AAD_rest\@: - cmp $0, %r11 + test %r11, %r11 je _get_AAD_done\@ READ_PARTIAL_BLOCK %r10, %r11, \TMP1, \TMP7 @@ -640,7 +640,7 @@ _get_AAD_done\@: .macro PARTIAL_BLOCK CYPH_PLAIN_OUT PLAIN_CYPH_IN PLAIN_CYPH_LEN DATA_OFFSET \ AAD_HASH operation mov PBlockLen(%arg2), %r13 - cmp $0, %r13 + test %r13, %r13 je _partial_block_done_\@ # Leave Macro if no partial blocks # Read in input data without over reading cmp $16, \PLAIN_CYPH_LEN @@ -692,7 +692,7 @@ _no_extra_mask_1_\@: pshufb %xmm2, %xmm3 pxor %xmm3, \AAD_HASH - cmp $0, %r10 + test %r10, %r10 jl _partial_incomplete_1_\@ # GHASH computation for the last <16 Byte block @@ -727,7 +727,7 @@ _no_extra_mask_2_\@: pshufb %xmm2, %xmm9 pxor %xmm9, \AAD_HASH - cmp $0, %r10 + test %r10, %r10 jl _partial_incomplete_2_\@ # GHASH computation for the last <16 Byte block @@ -747,7 +747,7 @@ _encode_done_\@: pshufb %xmm2, %xmm9 .endif # output encrypted Bytes - cmp $0, %r10 + test %r10, %r10 jl _partial_fill_\@ mov %r13, %r12 mov $16, %r13 @@ -2720,7 +2720,7 @@ SYM_FUNC_END(aesni_ctr_enc) */ SYM_FUNC_START(aesni_xts_crypt8) FRAME_BEGIN - cmpb $0, %cl + testb %cl, %cl movl $0, %ecx movl $240, %r10d leaq _aesni_enc4, %r11 diff --git a/arch/x86/crypto/aesni-intel_avx-x86_64.S b/arch/x86/crypto/aesni-intel_avx-x86_64.S index 5fee47956f3b..2cf8e94d986a 100644 --- a/arch/x86/crypto/aesni-intel_avx-x86_64.S +++ b/arch/x86/crypto/aesni-intel_avx-x86_64.S @@ -369,7 +369,7 @@ _initial_num_blocks_is_0\@: _initial_blocks_encrypted\@: - cmp $0, %r13 + test %r13, %r13 je _zero_cipher_left\@ sub $128, %r13 @@ -528,7 +528,7 @@ _multiple_of_16_bytes\@: vmovdqu HashKey(arg2), %xmm13 mov PBlockLen(arg2), %r12 - cmp $0, %r12 + test %r12, %r12 je _partial_done\@ #GHASH computation for the last <16 Byte block @@ -573,7 +573,7 @@ _T_8\@: add $8, %r10 sub $8, %r11 vpsrldq $8, %xmm9, %xmm9 - cmp $0, %r11 + test %r11, %r11 je _return_T_done\@ _T_4\@: vmovd %xmm9, %eax @@ -581,7 +581,7 @@ _T_4\@: add $4, %r10 sub $4, %r11 vpsrldq $4, %xmm9, %xmm9 - cmp $0, %r11 + test %r11, %r11 je _return_T_done\@ _T_123\@: vmovd %xmm9, %eax @@ -625,7 +625,7 @@ _get_AAD_blocks\@: cmp $16, %r11 jge _get_AAD_blocks\@ vmovdqu \T8, \T7 - cmp $0, %r11 + test %r11, %r11 je _get_AAD_done\@ vpxor \T7, \T7, \T7 @@ -644,7 +644,7 @@ _get_AAD_rest8\@: vpxor \T1, \T7, \T7 jmp _get_AAD_rest8\@ _get_AAD_rest4\@: - cmp $0, %r11 + test %r11, %r11 jle _get_AAD_rest0\@ mov (%r10), %eax movq %rax, \T1 @@ -749,7 +749,7 @@ _done_read_partial_block_\@: .macro PARTIAL_BLOCK GHASH_MUL CYPH_PLAIN_OUT PLAIN_CYPH_IN PLAIN_CYPH_LEN DATA_OFFSET \ AAD_HASH ENC_DEC mov PBlockLen(arg2), %r13 - cmp $0, %r13 + test %r13, %r13 je _partial_block_done_\@ # Leave Macro if no partial blocks # Read in input data without over reading cmp $16, \PLAIN_CYPH_LEN @@ -801,7 +801,7 @@ _no_extra_mask_1_\@: vpshufb %xmm2, %xmm3, %xmm3 vpxor %xmm3, \AAD_HASH, \AAD_HASH - cmp $0, %r10 + test %r10, %r10 jl _partial_incomplete_1_\@ # GHASH computation for the last <16 Byte block @@ -836,7 +836,7 @@ _no_extra_mask_2_\@: vpshufb %xmm2, %xmm9, %xmm9 vpxor %xmm9, \AAD_HASH, \AAD_HASH - cmp $0, %r10 + test %r10, %r10 jl _partial_incomplete_2_\@ # GHASH computation for the last <16 Byte block @@ -856,7 +856,7 @@ _encode_done_\@: vpshufb %xmm2, %xmm9, %xmm9 .endif # output encrypted Bytes - cmp $0, %r10 + test %r10, %r10 jl _partial_fill_\@ mov %r13, %r12 mov $16, %r13 diff --git a/arch/x86/crypto/poly1305-x86_64-cryptogams.pl b/arch/x86/crypto/poly1305-x86_64-cryptogams.pl index 7d568012cc15..71fae5a09e56 100644 --- a/arch/x86/crypto/poly1305-x86_64-cryptogams.pl +++ b/arch/x86/crypto/poly1305-x86_64-cryptogams.pl @@ -251,7 +251,7 @@ $code.=<<___; mov %rax,8($ctx) mov %rax,16($ctx) - cmp \$0,$inp + test $inp,$inp je .Lno_key ___ $code.=<<___ if (!$kernel); diff --git a/arch/x86/crypto/poly1305_glue.c b/arch/x86/crypto/poly1305_glue.c index c44aba290fbb..646da46e8d10 100644 --- a/arch/x86/crypto/poly1305_glue.c +++ b/arch/x86/crypto/poly1305_glue.c @@ -210,7 +210,7 @@ void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst) } poly1305_simd_emit(&dctx->h, dst, dctx->s); - *dctx = (struct poly1305_desc_ctx){}; + memzero_explicit(dctx, sizeof(*dctx)); } EXPORT_SYMBOL(poly1305_final_arch); diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c index 18200135603f..44340a1139e0 100644 --- a/arch/x86/crypto/sha1_ssse3_glue.c +++ b/arch/x86/crypto/sha1_ssse3_glue.c @@ -22,7 +22,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <asm/simd.h> diff --git a/arch/x86/crypto/sha256_ssse3_glue.c b/arch/x86/crypto/sha256_ssse3_glue.c index dd06249229e1..3a5f6be7dbba 100644 --- a/arch/x86/crypto/sha256_ssse3_glue.c +++ b/arch/x86/crypto/sha256_ssse3_glue.c @@ -35,7 +35,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <linux/string.h> #include <asm/simd.h> diff --git a/arch/x86/crypto/sha512-avx-asm.S b/arch/x86/crypto/sha512-avx-asm.S index 63470fd6ae32..684d58c8bc4f 100644 --- a/arch/x86/crypto/sha512-avx-asm.S +++ b/arch/x86/crypto/sha512-avx-asm.S @@ -278,7 +278,7 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE # "blocks" is the message length in SHA512 blocks ######################################################################## SYM_FUNC_START(sha512_transform_avx) - cmp $0, msglen + test msglen, msglen je nowork # Allocate Stack Space diff --git a/arch/x86/crypto/sha512-ssse3-asm.S b/arch/x86/crypto/sha512-ssse3-asm.S index 7946a1bee85b..50812af0b083 100644 --- a/arch/x86/crypto/sha512-ssse3-asm.S +++ b/arch/x86/crypto/sha512-ssse3-asm.S @@ -280,7 +280,7 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE ######################################################################## SYM_FUNC_START(sha512_transform_ssse3) - cmp $0, msglen + test msglen, msglen je nowork # Allocate Stack Space diff --git a/arch/x86/crypto/sha512_ssse3_glue.c b/arch/x86/crypto/sha512_ssse3_glue.c index b0b05c93409e..30e70f4fe2f7 100644 --- a/arch/x86/crypto/sha512_ssse3_glue.c +++ b/arch/x86/crypto/sha512_ssse3_glue.c @@ -34,7 +34,7 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <asm/simd.h> diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 21243747965d..02e3e42f380b 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -27,9 +27,10 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vobjs32-y := vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o vobjs32-y += vdso32/vclock_gettime.o +vobjs-$(CONFIG_X86_SGX) += vsgx.o # files to link into kernel -obj-y += vma.o +obj-y += vma.o extable.o KASAN_SANITIZE_vma.o := y UBSAN_SANITIZE_vma.o := y KCSAN_SANITIZE_vma.o := y @@ -98,6 +99,7 @@ $(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS CFLAGS_REMOVE_vclock_gettime.o = -pg CFLAGS_REMOVE_vdso32/vclock_gettime.o = -pg CFLAGS_REMOVE_vgetcpu.o = -pg +CFLAGS_REMOVE_vsgx.o = -pg # # X32 processes use x32 vDSO to access 64bit kernel data. @@ -128,8 +130,8 @@ $(obj)/%-x32.o: $(obj)/%.o FORCE targets += vdsox32.lds $(vobjx32s-y) -$(obj)/%.so: OBJCOPYFLAGS := -S -$(obj)/%.so: $(obj)/%.so.dbg FORCE +$(obj)/%.so: OBJCOPYFLAGS := -S --remove-section __ex_table +$(obj)/%.so: $(obj)/%.so.dbg $(call if_changed,objcopy) $(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE diff --git a/arch/x86/entry/vdso/extable.c b/arch/x86/entry/vdso/extable.c new file mode 100644 index 000000000000..afcf5b65beef --- /dev/null +++ b/arch/x86/entry/vdso/extable.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/err.h> +#include <linux/mm.h> +#include <asm/current.h> +#include <asm/traps.h> +#include <asm/vdso.h> + +struct vdso_exception_table_entry { + int insn, fixup; +}; + +bool fixup_vdso_exception(struct pt_regs *regs, int trapnr, + unsigned long error_code, unsigned long fault_addr) +{ + const struct vdso_image *image = current->mm->context.vdso_image; + const struct vdso_exception_table_entry *extable; + unsigned int nr_entries, i; + unsigned long base; + + /* + * Do not attempt to fixup #DB or #BP. It's impossible to identify + * whether or not a #DB/#BP originated from within an SGX enclave and + * SGX enclaves are currently the only use case for vDSO fixup. + */ + if (trapnr == X86_TRAP_DB || trapnr == X86_TRAP_BP) + return false; + + if (!current->mm->context.vdso) + return false; + + base = (unsigned long)current->mm->context.vdso + image->extable_base; + nr_entries = image->extable_len / (sizeof(*extable)); + extable = image->extable; + + for (i = 0; i < nr_entries; i++) { + if (regs->ip == base + extable[i].insn) { + regs->ip = base + extable[i].fixup; + regs->di = trapnr; + regs->si = error_code; + regs->dx = fault_addr; + return true; + } + } + + return false; +} diff --git a/arch/x86/entry/vdso/extable.h b/arch/x86/entry/vdso/extable.h new file mode 100644 index 000000000000..b56f6b012941 --- /dev/null +++ b/arch/x86/entry/vdso/extable.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __VDSO_EXTABLE_H +#define __VDSO_EXTABLE_H + +/* + * Inject exception fixup for vDSO code. Unlike normal exception fixup, + * vDSO uses a dedicated handler the addresses are relative to the overall + * exception table, not each individual entry. + */ +#ifdef __ASSEMBLY__ +#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \ + ASM_VDSO_EXTABLE_HANDLE from to + +.macro ASM_VDSO_EXTABLE_HANDLE from:req to:req + .pushsection __ex_table, "a" + .long (\from) - __ex_table + .long (\to) - __ex_table + .popsection +.endm +#else +#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \ + ".pushsection __ex_table, \"a\"\n" \ + ".long (" #from ") - __ex_table\n" \ + ".long (" #to ") - __ex_table\n" \ + ".popsection\n" +#endif + +#endif /* __VDSO_EXTABLE_H */ diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S index 4d152933547d..dc8da7695859 100644 --- a/arch/x86/entry/vdso/vdso-layout.lds.S +++ b/arch/x86/entry/vdso/vdso-layout.lds.S @@ -75,11 +75,18 @@ SECTIONS * stuff that isn't used at runtime in between. */ - .text : { *(.text*) } :text =0x90909090, + .text : { + *(.text*) + *(.fixup) + } :text =0x90909090, + + .altinstructions : { *(.altinstructions) } :text .altinstr_replacement : { *(.altinstr_replacement) } :text + __ex_table : { *(__ex_table) } :text + /DISCARD/ : { *(.discard) *(.discard.*) diff --git a/arch/x86/entry/vdso/vdso.lds.S b/arch/x86/entry/vdso/vdso.lds.S index 36b644e16272..4bf48462fca7 100644 --- a/arch/x86/entry/vdso/vdso.lds.S +++ b/arch/x86/entry/vdso/vdso.lds.S @@ -27,6 +27,7 @@ VERSION { __vdso_time; clock_getres; __vdso_clock_getres; + __vdso_sgx_enter_enclave; local: *; }; } diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h index 6f46e11ce539..1c7cfac7e64a 100644 --- a/arch/x86/entry/vdso/vdso2c.h +++ b/arch/x86/entry/vdso/vdso2c.h @@ -5,6 +5,41 @@ * are built for 32-bit userspace. */ +static void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (i % 10 == 0) + fprintf(outfile, "\n\t"); + fprintf(outfile, "0x%02X, ", (int)(data)[i]); + } +} + + +/* + * Extract a section from the input data into a standalone blob. Used to + * capture kernel-only data that needs to persist indefinitely, e.g. the + * exception fixup tables, but only in the kernel, i.e. the section can + * be stripped from the final vDSO image. + */ +static void BITSFUNC(extract)(const unsigned char *data, size_t data_len, + FILE *outfile, ELF(Shdr) *sec, const char *name) +{ + unsigned long offset; + size_t len; + + offset = (unsigned long)GET_LE(&sec->sh_offset); + len = (size_t)GET_LE(&sec->sh_size); + + if (offset + len > data_len) + fail("section to extract overruns input data"); + + fprintf(outfile, "static const unsigned char %s[%lu] = {", name, len); + BITSFUNC(copy)(outfile, data + offset, len); + fprintf(outfile, "\n};\n\n"); +} + static void BITSFUNC(go)(void *raw_addr, size_t raw_len, void *stripped_addr, size_t stripped_len, FILE *outfile, const char *image_name) @@ -15,7 +50,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; unsigned long i, syms_nr; ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, - *alt_sec = NULL; + *alt_sec = NULL, *extable_sec = NULL; ELF(Dyn) *dyn = 0, *dyn_end = 0; const char *secstrings; INT_BITS syms[NSYMS] = {}; @@ -77,6 +112,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, if (!strcmp(secstrings + GET_LE(&sh->sh_name), ".altinstructions")) alt_sec = sh; + if (!strcmp(secstrings + GET_LE(&sh->sh_name), "__ex_table")) + extable_sec = sh; } if (!symtab_hdr) @@ -155,6 +192,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, (int)((unsigned char *)stripped_addr)[i]); } fprintf(outfile, "\n};\n\n"); + if (extable_sec) + BITSFUNC(extract)(raw_addr, raw_len, outfile, + extable_sec, "extable"); fprintf(outfile, "const struct vdso_image %s = {\n", image_name); fprintf(outfile, "\t.data = raw_data,\n"); @@ -165,6 +205,14 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, fprintf(outfile, "\t.alt_len = %lu,\n", (unsigned long)GET_LE(&alt_sec->sh_size)); } + if (extable_sec) { + fprintf(outfile, "\t.extable_base = %lu,\n", + (unsigned long)GET_LE(&extable_sec->sh_offset)); + fprintf(outfile, "\t.extable_len = %lu,\n", + (unsigned long)GET_LE(&extable_sec->sh_size)); + fprintf(outfile, "\t.extable = extable,\n"); + } + for (i = 0; i < NSYMS; i++) { if (required_syms[i].export && syms[i]) fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n", diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c index 9185cb1d13b9..50e5d3a2e70a 100644 --- a/arch/x86/entry/vdso/vma.c +++ b/arch/x86/entry/vdso/vma.c @@ -413,10 +413,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) #ifdef CONFIG_COMPAT int compat_arch_setup_additional_pages(struct linux_binprm *bprm, - int uses_interp) + int uses_interp, bool x32) { #ifdef CONFIG_X86_X32_ABI - if (test_thread_flag(TIF_X32)) { + if (x32) { if (!vdso64_enabled) return 0; return map_vdso_randomized(&vdso_image_x32); diff --git a/arch/x86/entry/vdso/vsgx.S b/arch/x86/entry/vdso/vsgx.S new file mode 100644 index 000000000000..86a0e94f68df --- /dev/null +++ b/arch/x86/entry/vdso/vsgx.S @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <linux/linkage.h> +#include <asm/export.h> +#include <asm/errno.h> +#include <asm/enclu.h> + +#include "extable.h" + +/* Relative to %rbp. */ +#define SGX_ENCLAVE_OFFSET_OF_RUN 16 + +/* The offsets relative to struct sgx_enclave_run. */ +#define SGX_ENCLAVE_RUN_TCS 0 +#define SGX_ENCLAVE_RUN_LEAF 8 +#define SGX_ENCLAVE_RUN_EXCEPTION_VECTOR 12 +#define SGX_ENCLAVE_RUN_EXCEPTION_ERROR_CODE 14 +#define SGX_ENCLAVE_RUN_EXCEPTION_ADDR 16 +#define SGX_ENCLAVE_RUN_USER_HANDLER 24 +#define SGX_ENCLAVE_RUN_USER_DATA 32 /* not used */ +#define SGX_ENCLAVE_RUN_RESERVED_START 40 +#define SGX_ENCLAVE_RUN_RESERVED_END 256 + +.code64 +.section .text, "ax" + +SYM_FUNC_START(__vdso_sgx_enter_enclave) + /* Prolog */ + .cfi_startproc + push %rbp + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbp, 0 + mov %rsp, %rbp + .cfi_def_cfa_register %rbp + push %rbx + .cfi_rel_offset %rbx, -8 + + mov %ecx, %eax +.Lenter_enclave: + /* EENTER <= function <= ERESUME */ + cmp $EENTER, %eax + jb .Linvalid_input + cmp $ERESUME, %eax + ja .Linvalid_input + + mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rcx + + /* Validate that the reserved area contains only zeros. */ + mov $SGX_ENCLAVE_RUN_RESERVED_START, %rbx +1: + cmpq $0, (%rcx, %rbx) + jne .Linvalid_input + add $8, %rbx + cmpq $SGX_ENCLAVE_RUN_RESERVED_END, %rbx + jne 1b + + /* Load TCS and AEP */ + mov SGX_ENCLAVE_RUN_TCS(%rcx), %rbx + lea .Lasync_exit_pointer(%rip), %rcx + + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */ +.Lasync_exit_pointer: +.Lenclu_eenter_eresume: + enclu + + /* EEXIT jumps here unless the enclave is doing something fancy. */ + mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rbx + + /* Set exit_reason. */ + movl $EEXIT, SGX_ENCLAVE_RUN_LEAF(%rbx) + + /* Invoke userspace's exit handler if one was provided. */ +.Lhandle_exit: + cmpq $0, SGX_ENCLAVE_RUN_USER_HANDLER(%rbx) + jne .Linvoke_userspace_handler + + /* Success, in the sense that ENCLU was attempted. */ + xor %eax, %eax + +.Lout: + pop %rbx + leave + .cfi_def_cfa %rsp, 8 + ret + + /* The out-of-line code runs with the pre-leave stack frame. */ + .cfi_def_cfa %rbp, 16 + +.Linvalid_input: + mov $(-EINVAL), %eax + jmp .Lout + +.Lhandle_exception: + mov SGX_ENCLAVE_OFFSET_OF_RUN(%rbp), %rbx + + /* Set the exception info. */ + mov %eax, (SGX_ENCLAVE_RUN_LEAF)(%rbx) + mov %di, (SGX_ENCLAVE_RUN_EXCEPTION_VECTOR)(%rbx) + mov %si, (SGX_ENCLAVE_RUN_EXCEPTION_ERROR_CODE)(%rbx) + mov %rdx, (SGX_ENCLAVE_RUN_EXCEPTION_ADDR)(%rbx) + jmp .Lhandle_exit + +.Linvoke_userspace_handler: + /* Pass the untrusted RSP (at exit) to the callback via %rcx. */ + mov %rsp, %rcx + + /* Save struct sgx_enclave_exception %rbx is about to be clobbered. */ + mov %rbx, %rax + + /* Save the untrusted RSP offset in %rbx (non-volatile register). */ + mov %rsp, %rbx + and $0xf, %rbx + + /* + * Align stack per x86_64 ABI. Note, %rsp needs to be 16-byte aligned + * _after_ pushing the parameters on the stack, hence the bonus push. + */ + and $-0x10, %rsp + push %rax + + /* Push struct sgx_enclave_exception as a param to the callback. */ + push %rax + + /* Clear RFLAGS.DF per x86_64 ABI */ + cld + + /* + * Load the callback pointer to %rax and lfence for LVI (load value + * injection) protection before making the call. + */ + mov SGX_ENCLAVE_RUN_USER_HANDLER(%rax), %rax + lfence + call *%rax + + /* Undo the post-exit %rsp adjustment. */ + lea 0x10(%rsp, %rbx), %rsp + + /* + * If the return from callback is zero or negative, return immediately, + * else re-execute ENCLU with the postive return value interpreted as + * the requested ENCLU function. + */ + cmp $0, %eax + jle .Lout + jmp .Lenter_enclave + + .cfi_endproc + +_ASM_VDSO_EXTABLE_HANDLE(.Lenclu_eenter_eresume, .Lhandle_exception) + +SYM_FUNC_END(__vdso_sgx_enter_enclave) diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index 44c33103a955..1b40b9297083 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c @@ -316,7 +316,7 @@ static struct vm_area_struct gate_vma __ro_after_init = { struct vm_area_struct *get_gate_vma(struct mm_struct *mm) { #ifdef CONFIG_COMPAT - if (!mm || mm->context.ia32_compat) + if (!mm || !(mm->context.flags & MM_CONTEXT_HAS_VSYSCALL)) return NULL; #endif if (vsyscall_mode == NONE) diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index 39eb276d0277..2c1791c4a518 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -538,7 +538,7 @@ static void amd_pmu_cpu_starting(int cpu) if (!x86_pmu.amd_nb_constraints) return; - nb_id = amd_get_nb_id(cpu); + nb_id = topology_die_id(cpu); WARN_ON_ONCE(nb_id == BAD_APICID); for_each_online_cpu(i) { diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index a88c94d65693..77b963e5e70a 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -2602,7 +2602,7 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *ent struct stack_frame_ia32 frame; const struct stack_frame_ia32 __user *fp; - if (!test_thread_flag(TIF_IA32)) + if (user_64bit_mode(regs)) return 0; cs_base = get_segment_base(regs->cs); diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c index 485c5066f8b8..a5c6ff5f1b4c 100644 --- a/arch/x86/events/intel/ds.c +++ b/arch/x86/events/intel/ds.c @@ -1261,7 +1261,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs) old_to = to; #ifdef CONFIG_X86_64 - is_64bit = kernel_ip(to) || !test_thread_flag(TIF_IA32); + is_64bit = kernel_ip(to) || any_64bit_mode(regs); #endif insn_init(&insn, kaddr, size, is_64bit); insn_get_length(&insn); diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c index 8961653c5dd2..1aadb253d296 100644 --- a/arch/x86/events/intel/lbr.c +++ b/arch/x86/events/intel/lbr.c @@ -1221,7 +1221,7 @@ static int branch_type(unsigned long from, unsigned long to, int abort) * on 64-bit systems running 32-bit apps */ #ifdef CONFIG_X86_64 - is64 = kernel_ip((unsigned long)addr) || !test_thread_flag(TIF_IA32); + is64 = kernel_ip((unsigned long)addr) || any_64bit_mode(current_pt_regs()); #endif insn_init(&insn, addr, bytes_read, is64); insn_get_opcode(&insn); diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index 81cf22398cd1..5e3d9b7fd5fb 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -347,7 +347,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig, */ unsafe_put_user(*((u64 *)&code), (u64 __user *)frame->retcode, Efault); unsafe_put_sigcontext32(&frame->uc.uc_mcontext, fp, regs, set, Efault); - unsafe_put_user(*(__u64 *)set, (__u64 *)&frame->uc.uc_sigmask, Efault); + unsafe_put_user(*(__u64 *)set, (__u64 __user *)&frame->uc.uc_sigmask, Efault); user_access_end(); if (__copy_siginfo_to_user32(&frame->info, &ksig->info)) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 6d2df1ee427b..65064d9f7fa6 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -159,6 +159,8 @@ static inline u64 x86_default_get_root_pointer(void) extern int x86_acpi_numa_init(void); #endif /* CONFIG_ACPI_NUMA */ +struct cper_ia_proc_ctx; + #ifdef CONFIG_ACPI_APEI static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) { @@ -177,6 +179,15 @@ static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) */ return PAGE_KERNEL_NOENC; } + +int arch_apei_report_x86_error(struct cper_ia_proc_ctx *ctx_info, + u64 lapic_id); +#else +static inline int arch_apei_report_x86_error(struct cper_ia_proc_ctx *ctx_info, + u64 lapic_id) +{ + return -EINVAL; +} #endif #define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn_mapped << PAGE_SHIFT) diff --git a/arch/x86/include/asm/cacheinfo.h b/arch/x86/include/asm/cacheinfo.h index 86b63c7feab7..86b2e0dcc4bf 100644 --- a/arch/x86/include/asm/cacheinfo.h +++ b/arch/x86/include/asm/cacheinfo.h @@ -2,7 +2,7 @@ #ifndef _ASM_X86_CACHEINFO_H #define _ASM_X86_CACHEINFO_H -void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id); -void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id); +void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu); +void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c, int cpu); #endif /* _ASM_X86_CACHEINFO_H */ diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index 0e327a01f50f..f145e3326c6d 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -177,14 +177,13 @@ typedef struct user_regs_struct compat_elf_gregset_t; static inline void __user *arch_compat_alloc_user_space(long len) { - compat_uptr_t sp; - - if (test_thread_flag(TIF_IA32)) { - sp = task_pt_regs(current)->sp; - } else { - /* -128 for the x32 ABI redzone */ - sp = task_pt_regs(current)->sp - 128; - } + compat_uptr_t sp = task_pt_regs(current)->sp; + + /* + * -128 for the x32 ABI redzone. For IA32, it is not strictly + * necessary, but not harmful. + */ + sp -= 128; return (void __user *)round_down(sp - len, 16); } diff --git a/arch/x86/include/asm/copy_mc_test.h b/arch/x86/include/asm/copy_mc_test.h deleted file mode 100644 index e4991ba96726..000000000000 --- a/arch/x86/include/asm/copy_mc_test.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _COPY_MC_TEST_H_ -#define _COPY_MC_TEST_H_ - -#ifndef __ASSEMBLY__ -#ifdef CONFIG_COPY_MC_TEST -extern unsigned long copy_mc_test_src; -extern unsigned long copy_mc_test_dst; - -static inline void copy_mc_inject_src(void *addr) -{ - if (addr) - copy_mc_test_src = (unsigned long) addr; - else - copy_mc_test_src = ~0UL; -} - -static inline void copy_mc_inject_dst(void *addr) -{ - if (addr) - copy_mc_test_dst = (unsigned long) addr; - else - copy_mc_test_dst = ~0UL; -} -#else /* CONFIG_COPY_MC_TEST */ -static inline void copy_mc_inject_src(void *addr) -{ -} - -static inline void copy_mc_inject_dst(void *addr) -{ -} -#endif /* CONFIG_COPY_MC_TEST */ - -#else /* __ASSEMBLY__ */ -#include <asm/export.h> - -#ifdef CONFIG_COPY_MC_TEST -.macro COPY_MC_TEST_CTL - .pushsection .data - .align 8 - .globl copy_mc_test_src - copy_mc_test_src: - .quad 0 - EXPORT_SYMBOL_GPL(copy_mc_test_src) - .globl copy_mc_test_dst - copy_mc_test_dst: - .quad 0 - EXPORT_SYMBOL_GPL(copy_mc_test_dst) - .popsection -.endm - -.macro COPY_MC_TEST_SRC reg count target - leaq \count(\reg), %r9 - cmp copy_mc_test_src, %r9 - ja \target -.endm - -.macro COPY_MC_TEST_DST reg count target - leaq \count(\reg), %r9 - cmp copy_mc_test_dst, %r9 - ja \target -.endm -#else -.macro COPY_MC_TEST_CTL -.endm - -.macro COPY_MC_TEST_SRC reg count target -.endm - -.macro COPY_MC_TEST_DST reg count target -.endm -#endif /* CONFIG_COPY_MC_TEST */ -#endif /* __ASSEMBLY__ */ -#endif /* _COPY_MC_TEST_H_ */ diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index dad350d42ecf..f5ef2d5b9231 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -241,6 +241,7 @@ /* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */ #define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/ #define X86_FEATURE_TSC_ADJUST ( 9*32+ 1) /* TSC adjustment MSR 0x3B */ +#define X86_FEATURE_SGX ( 9*32+ 2) /* Software Guard Extensions */ #define X86_FEATURE_BMI1 ( 9*32+ 3) /* 1st group bit manipulation extensions */ #define X86_FEATURE_HLE ( 9*32+ 4) /* Hardware Lock Elision */ #define X86_FEATURE_AVX2 ( 9*32+ 5) /* AVX2 instructions */ @@ -356,6 +357,7 @@ #define X86_FEATURE_MOVDIRI (16*32+27) /* MOVDIRI instruction */ #define X86_FEATURE_MOVDIR64B (16*32+28) /* MOVDIR64B instruction */ #define X86_FEATURE_ENQCMD (16*32+29) /* ENQCMD and ENQCMDS instructions */ +#define X86_FEATURE_SGX_LC (16*32+30) /* Software Guard Extensions Launch Control */ /* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */ #define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */ diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h index 5861d34f9771..7947cb1782da 100644 --- a/arch/x86/include/asm/disabled-features.h +++ b/arch/x86/include/asm/disabled-features.h @@ -62,6 +62,12 @@ # define DISABLE_ENQCMD (1 << (X86_FEATURE_ENQCMD & 31)) #endif +#ifdef CONFIG_X86_SGX +# define DISABLE_SGX 0 +#else +# define DISABLE_SGX (1 << (X86_FEATURE_SGX & 31)) +#endif + /* * Make sure to add features to the correct mask */ @@ -74,7 +80,7 @@ #define DISABLED_MASK6 0 #define DISABLED_MASK7 (DISABLE_PTI) #define DISABLED_MASK8 0 -#define DISABLED_MASK9 (DISABLE_SMAP) +#define DISABLED_MASK9 (DISABLE_SMAP|DISABLE_SGX) #define DISABLED_MASK10 0 #define DISABLED_MASK11 0 #define DISABLED_MASK12 0 diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index b9a5d488f1a5..44a9b9940535 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -186,8 +186,9 @@ static inline void elf_common_init(struct thread_struct *t, #define COMPAT_ELF_PLAT_INIT(regs, load_addr) \ elf_common_init(¤t->thread, regs, __USER_DS) -void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp); -#define compat_start_thread compat_start_thread +void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp, bool x32); +#define COMPAT_START_THREAD(ex, regs, new_ip, new_sp) \ + compat_start_thread(regs, new_ip, new_sp, ex->e_machine == EM_X86_64) void set_personality_ia32(bool); #define COMPAT_SET_PERSONALITY(ex) \ @@ -361,7 +362,7 @@ do { \ #define AT_SYSINFO 32 #define COMPAT_ARCH_DLINFO \ -if (test_thread_flag(TIF_X32)) \ +if (exec->e_machine == EM_X86_64) \ ARCH_DLINFO_X32; \ else \ ARCH_DLINFO_IA32 @@ -382,8 +383,10 @@ struct linux_binprm; extern int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp); extern int compat_arch_setup_additional_pages(struct linux_binprm *bprm, - int uses_interp); -#define compat_arch_setup_additional_pages compat_arch_setup_additional_pages + int uses_interp, bool x32); +#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ + compat_arch_setup_additional_pages(bprm, interpreter, \ + (ex->e_machine == EM_X86_64)) /* Do not change the values. See get_align_mask() */ enum align_flags { diff --git a/arch/x86/include/asm/enclu.h b/arch/x86/include/asm/enclu.h new file mode 100644 index 000000000000..b1314e41a744 --- /dev/null +++ b/arch/x86/include/asm/enclu.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_ENCLU_H +#define _ASM_X86_ENCLU_H + +#define EENTER 0x02 +#define ERESUME 0x03 +#define EEXIT 0x04 + +#endif /* _ASM_X86_ENCLU_H */ diff --git a/arch/x86/include/asm/inst.h b/arch/x86/include/asm/inst.h index bd7f02480ca1..438ccd4f3cc4 100644 --- a/arch/x86/include/asm/inst.h +++ b/arch/x86/include/asm/inst.h @@ -143,21 +143,6 @@ .macro MODRM mod opd1 opd2 .byte \mod | (\opd1 & 7) | ((\opd2 & 7) << 3) .endm - -.macro RDPID opd - REG_TYPE rdpid_opd_type \opd - .if rdpid_opd_type == REG_TYPE_R64 - R64_NUM rdpid_opd \opd - .else - R32_NUM rdpid_opd \opd - .endif - .byte 0xf3 - .if rdpid_opd > 7 - PFX_REX rdpid_opd 0 - .endif - .byte 0x0f, 0xc7 - MODRM 0xc0 rdpid_opd 0x7 -.endm #endif #endif diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index a0f147893a04..56cdeaac76a0 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -177,7 +177,8 @@ enum mce_notifier_prios { MCE_PRIO_EXTLOG, MCE_PRIO_UC, MCE_PRIO_EARLY, - MCE_PRIO_CEC + MCE_PRIO_CEC, + MCE_PRIO_HIGHEST = MCE_PRIO_CEC }; struct notifier_block; @@ -198,16 +199,22 @@ static inline void enable_copy_mc_fragile(void) } #endif +struct cper_ia_proc_ctx; + #ifdef CONFIG_X86_MCE int mcheck_init(void); void mcheck_cpu_init(struct cpuinfo_x86 *c); void mcheck_cpu_clear(struct cpuinfo_x86 *c); void mcheck_vendor_init_severity(void); +int apei_smca_report_x86_error(struct cper_ia_proc_ctx *ctx_info, + u64 lapic_id); #else static inline int mcheck_init(void) { return 0; } static inline void mcheck_cpu_init(struct cpuinfo_x86 *c) {} static inline void mcheck_cpu_clear(struct cpuinfo_x86 *c) {} static inline void mcheck_vendor_init_severity(void) {} +static inline int apei_smca_report_x86_error(struct cper_ia_proc_ctx *ctx_info, + u64 lapic_id) { return -EINVAL; } #endif #ifdef CONFIG_X86_ANCIENT_MCE diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h index 9257667d13c5..5d7494631ea9 100644 --- a/arch/x86/include/asm/mmu.h +++ b/arch/x86/include/asm/mmu.h @@ -6,6 +6,12 @@ #include <linux/rwsem.h> #include <linux/mutex.h> #include <linux/atomic.h> +#include <linux/bits.h> + +/* Uprobes on this MM assume 32-bit code */ +#define MM_CONTEXT_UPROBE_IA32 BIT(0) +/* vsyscall page is accessible on this MM */ +#define MM_CONTEXT_HAS_VSYSCALL BIT(1) /* * x86 has arch-specific MMU state beyond what lives in mm_struct. @@ -33,8 +39,7 @@ typedef struct { #endif #ifdef CONFIG_X86_64 - /* True if mm supports a task running in 32 bit compatibility mode. */ - unsigned short ia32_compat; + unsigned short flags; #endif struct mutex lock; diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index d98016b83755..054a79157323 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -177,7 +177,7 @@ static inline void arch_exit_mmap(struct mm_struct *mm) static inline bool is_64bit_mm(struct mm_struct *mm) { return !IS_ENABLED(CONFIG_IA32_EMULATION) || - !(mm->context.ia32_compat == TIF_IA32); + !(mm->context.flags & MM_CONTEXT_UPROBE_IA32); } #else static inline bool is_64bit_mm(struct mm_struct *mm) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 972a34d93505..6a5085a5f9d8 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -139,6 +139,7 @@ #define MSR_IA32_MCG_CAP 0x00000179 #define MSR_IA32_MCG_STATUS 0x0000017a #define MSR_IA32_MCG_CTL 0x0000017b +#define MSR_ERROR_CONTROL 0x0000017f #define MSR_IA32_MCG_EXT_CTL 0x000004d0 #define MSR_OFFCORE_RSP_0 0x000001a6 @@ -609,6 +610,8 @@ #define FEAT_CTL_LOCKED BIT(0) #define FEAT_CTL_VMX_ENABLED_INSIDE_SMX BIT(1) #define FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX BIT(2) +#define FEAT_CTL_SGX_LC_ENABLED BIT(17) +#define FEAT_CTL_SGX_ENABLED BIT(18) #define FEAT_CTL_LMCE_ENABLED BIT(20) #define MSR_IA32_TSC_ADJUST 0x0000003b @@ -628,6 +631,12 @@ #define MSR_IA32_UCODE_WRITE 0x00000079 #define MSR_IA32_UCODE_REV 0x0000008b +/* Intel SGX Launch Enclave Public Key Hash MSRs */ +#define MSR_IA32_SGXLEPUBKEYHASH0 0x0000008C +#define MSR_IA32_SGXLEPUBKEYHASH1 0x0000008D +#define MSR_IA32_SGXLEPUBKEYHASH2 0x0000008E +#define MSR_IA32_SGXLEPUBKEYHASH3 0x0000008F + #define MSR_IA32_SMM_MONITOR_CTL 0x0000009b #define MSR_IA32_SMBASE 0x0000009e diff --git a/arch/x86/include/asm/page_32_types.h b/arch/x86/include/asm/page_32_types.h index f462895a33e4..faf9cc1c14bb 100644 --- a/arch/x86/include/asm/page_32_types.h +++ b/arch/x86/include/asm/page_32_types.h @@ -53,7 +53,13 @@ #define STACK_TOP_MAX STACK_TOP /* - * Kernel image size is limited to 512 MB (see in arch/x86/kernel/head_32.S) + * In spite of the name, KERNEL_IMAGE_SIZE is a limit on the maximum virtual + * address for the kernel image, rather than the limit on the size itself. On + * 32-bit, this is not a strict limit, but this value is used to limit the + * link-time virtual address range of the kernel, and by KASLR to limit the + * randomized address from which the kernel is executed. A relocatable kernel + * can be loaded somewhat higher than KERNEL_IMAGE_SIZE as long as enough space + * remains for the vmalloc area. */ #define KERNEL_IMAGE_SIZE (512 * 1024 * 1024) diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h index 3f49dac03617..645bd1d0ee07 100644 --- a/arch/x86/include/asm/page_64_types.h +++ b/arch/x86/include/asm/page_64_types.h @@ -98,8 +98,10 @@ #define STACK_TOP_MAX TASK_SIZE_MAX /* - * Maximum kernel image size is limited to 1 GiB, due to the fixmap living - * in the next 1 GiB (see level2_kernel_pgt in arch/x86/kernel/head_64.S). + * In spite of the name, KERNEL_IMAGE_SIZE is a limit on the maximum virtual + * address for the kernel image, rather than the limit on the size itself. + * This can be at most 1 GiB, due to the fixmap living in the next 1 GiB (see + * level2_kernel_pgt in arch/x86/kernel/head_64.S). * * On KASLR use 1 GiB by default, leaving 1 GiB for modules once the * page tables are fully set up. diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index d25cc6830e89..f8dce11d2bc1 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -812,17 +812,6 @@ extern void default_banner(void); #endif /* CONFIG_PARAVIRT_XXL */ #endif /* CONFIG_X86_64 */ -#ifdef CONFIG_PARAVIRT_XXL - -#define GET_CR2_INTO_AX \ - PARA_SITE(PARA_PATCH(PV_MMU_read_cr2), \ - ANNOTATE_RETPOLINE_SAFE; \ - call PARA_INDIRECT(pv_ops+PV_MMU_read_cr2); \ - ) - -#endif /* CONFIG_PARAVIRT_XXL */ - - #endif /* __ASSEMBLY__ */ #else /* CONFIG_PARAVIRT */ # define default_banner x86_init_noop diff --git a/arch/x86/include/asm/pgtable_32.h b/arch/x86/include/asm/pgtable_32.h index d7acae4120d5..7c9c968a42ef 100644 --- a/arch/x86/include/asm/pgtable_32.h +++ b/arch/x86/include/asm/pgtable_32.h @@ -57,19 +57,13 @@ do { \ #endif /* - * This is how much memory in addition to the memory covered up to - * and including _end we need mapped initially. - * We need: - * (KERNEL_IMAGE_SIZE/4096) / 1024 pages (worst case, non PAE) - * (KERNEL_IMAGE_SIZE/4096) / 512 + 4 pages (worst case for PAE) + * This is used to calculate the .brk reservation for initial pagetables. + * Enough space is reserved to allocate pagetables sufficient to cover all + * of LOWMEM_PAGES, which is an upper bound on the size of the direct map of + * lowmem. * - * Modulo rounding, each megabyte assigned here requires a kilobyte of - * memory, which is currently unreclaimed. - * - * This should be a multiple of a page. - * - * KERNEL_IMAGE_SIZE should be greater than pa(_end) - * and small than max_low_pfn, otherwise will waste some page table entries + * With PAE paging (PTRS_PER_PMD > 1), we allocate PTRS_PER_PGD == 4 pages for + * the PMD's in addition to the pages required for the last level pagetables. */ #if PTRS_PER_PMD > 1 #define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD) diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 82a08b585818..c20a52b5534b 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -813,10 +813,8 @@ extern int set_tsc_mode(unsigned int val); DECLARE_PER_CPU(u64, msr_misc_features_shadow); #ifdef CONFIG_CPU_SUP_AMD -extern u16 amd_get_nb_id(int cpu); extern u32 amd_get_nodes_per_socket(void); #else -static inline u16 amd_get_nb_id(int cpu) { return 0; } static inline u32 amd_get_nodes_per_socket(void) { return 0; } #endif diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h index 49600643faba..f248eb2ac2d4 100644 --- a/arch/x86/include/asm/stacktrace.h +++ b/arch/x86/include/asm/stacktrace.h @@ -88,9 +88,6 @@ get_stack_pointer(struct task_struct *task, struct pt_regs *regs) return (unsigned long *)task->thread.sp; } -void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, const char *log_lvl); - /* The form of the top of the frame on the stack */ struct stack_frame { struct stack_frame *next_frame; diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 44733a4bfc42..a12b9644193b 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -91,7 +91,6 @@ struct thread_info { #define TIF_NEED_FPU_LOAD 14 /* load FPU on return to userspace */ #define TIF_NOCPUID 15 /* CPUID is not accessible in userland */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ -#define TIF_IA32 17 /* IA32 compatibility process */ #define TIF_SLD 18 /* Restore split lock detection on context switch */ #define TIF_MEMDIE 20 /* is terminating due to OOM killer */ #define TIF_POLLING_NRFLAG 21 /* idle is polling for TIF_NEED_RESCHED */ @@ -101,7 +100,6 @@ struct thread_info { #define TIF_LAZY_MMU_UPDATES 27 /* task is updating the mmu lazily */ #define TIF_SYSCALL_TRACEPOINT 28 /* syscall tracepoint instrumentation */ #define TIF_ADDR32 29 /* 32-bit address space on 64 bits */ -#define TIF_X32 30 /* 32-bit native x86-64 binary */ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) @@ -120,7 +118,6 @@ struct thread_info { #define _TIF_NEED_FPU_LOAD (1 << TIF_NEED_FPU_LOAD) #define _TIF_NOCPUID (1 << TIF_NOCPUID) #define _TIF_NOTSC (1 << TIF_NOTSC) -#define _TIF_IA32 (1 << TIF_IA32) #define _TIF_SLD (1 << TIF_SLD) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) @@ -129,7 +126,6 @@ struct thread_info { #define _TIF_LAZY_MMU_UPDATES (1 << TIF_LAZY_MMU_UPDATES) #define _TIF_SYSCALL_TRACEPOINT (1 << TIF_SYSCALL_TRACEPOINT) #define _TIF_ADDR32 (1 << TIF_ADDR32) -#define _TIF_X32 (1 << TIF_X32) /* flags to check in __switch_to() */ #define _TIF_WORK_CTXSW_BASE \ diff --git a/arch/x86/include/asm/trap_pf.h b/arch/x86/include/asm/trap_pf.h index 305bc1214aef..10b1de500ab1 100644 --- a/arch/x86/include/asm/trap_pf.h +++ b/arch/x86/include/asm/trap_pf.h @@ -11,6 +11,7 @@ * bit 3 == 1: use of reserved bit detected * bit 4 == 1: fault was an instruction fetch * bit 5 == 1: protection keys block access + * bit 15 == 1: SGX MMU page-fault */ enum x86_pf_error_code { X86_PF_PROT = 1 << 0, @@ -19,6 +20,7 @@ enum x86_pf_error_code { X86_PF_RSVD = 1 << 3, X86_PF_INSTR = 1 << 4, X86_PF_PK = 1 << 5, + X86_PF_SGX = 1 << 15, }; #endif /* _ASM_X86_TRAP_PF_H */ diff --git a/arch/x86/include/asm/uv/bios.h b/arch/x86/include/asm/uv/bios.h index 08b3d810dfba..1b6455f881f9 100644 --- a/arch/x86/include/asm/uv/bios.h +++ b/arch/x86/include/asm/uv/bios.h @@ -28,6 +28,20 @@ enum uv_bios_cmd { UV_BIOS_SET_LEGACY_VGA_TARGET }; +#define UV_BIOS_EXTRA 0x10000 +#define UV_BIOS_GET_PCI_TOPOLOGY 0x10001 +#define UV_BIOS_GET_GEOINFO 0x10003 + +#define UV_BIOS_EXTRA_OP_MEM_COPYIN 0x1000 +#define UV_BIOS_EXTRA_OP_MEM_COPYOUT 0x2000 +#define UV_BIOS_EXTRA_OP_MASK 0x0fff +#define UV_BIOS_EXTRA_GET_HEAPSIZE 1 +#define UV_BIOS_EXTRA_INSTALL_HEAP 2 +#define UV_BIOS_EXTRA_MASTER_NASID 3 +#define UV_BIOS_EXTRA_OBJECT_COUNT (10|UV_BIOS_EXTRA_OP_MEM_COPYOUT) +#define UV_BIOS_EXTRA_ENUM_OBJECTS (12|UV_BIOS_EXTRA_OP_MEM_COPYOUT) +#define UV_BIOS_EXTRA_ENUM_PORTS (13|UV_BIOS_EXTRA_OP_MEM_COPYOUT) + /* * Status values returned from a BIOS call. */ @@ -109,6 +123,32 @@ struct uv_systab { } entry[1]; /* additional entries follow */ }; extern struct uv_systab *uv_systab; + +#define UV_BIOS_MAXSTRING 128 +struct uv_bios_hub_info { + unsigned int id; + union { + struct { + unsigned long long this_part:1; + unsigned long long is_shared:1; + unsigned long long is_disabled:1; + } fields; + struct { + unsigned long long flags; + unsigned long long reserved; + } b; + } f; + char name[UV_BIOS_MAXSTRING]; + char location[UV_BIOS_MAXSTRING]; + unsigned int ports; +}; + +struct uv_bios_port_info { + unsigned int port; + unsigned int conn_id; + unsigned int conn_port; +}; + /* (... end of definitions from UV BIOS ...) */ enum { @@ -142,6 +182,15 @@ extern s64 uv_bios_change_memprotect(u64, u64, enum uv_memprotect); extern s64 uv_bios_reserved_page_pa(u64, u64 *, u64 *, u64 *); extern int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus); +extern s64 uv_bios_get_master_nasid(u64 sz, u64 *nasid); +extern s64 uv_bios_get_heapsize(u64 nasid, u64 sz, u64 *heap_sz); +extern s64 uv_bios_install_heap(u64 nasid, u64 sz, u64 *heap); +extern s64 uv_bios_obj_count(u64 nasid, u64 sz, u64 *objcnt); +extern s64 uv_bios_enum_objs(u64 nasid, u64 sz, u64 *objbuf); +extern s64 uv_bios_enum_ports(u64 nasid, u64 obj_id, u64 sz, u64 *portbuf); +extern s64 uv_bios_get_geoinfo(u64 nasid, u64 sz, u64 *geo); +extern s64 uv_bios_get_pci_topology(u64 sz, u64 *buf); + extern int uv_bios_init(void); extern unsigned long get_uv_systab_phys(bool msg); @@ -151,6 +200,8 @@ extern long sn_partition_id; extern long sn_coherency_id; extern long sn_region_size; extern long system_serial_number; +extern ssize_t uv_get_archtype(char *buf, int len); +extern int uv_get_hubless_system(void); extern struct kobject *sgi_uv_kobj; /* /sys/firmware/sgi_uv */ diff --git a/arch/x86/include/asm/uv/uv_geo.h b/arch/x86/include/asm/uv/uv_geo.h new file mode 100644 index 000000000000..f241451035fb --- /dev/null +++ b/arch/x86/include/asm/uv/uv_geo.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2020 Hewlett Packard Enterprise Development LP. All rights reserved. + */ + +#ifndef _ASM_UV_GEO_H +#define _ASM_UV_GEO_H + +/* Type declaractions */ + +/* Size of a geoid_s structure (must be before decl. of geoid_u) */ +#define GEOID_SIZE 8 + +/* Fields common to all substructures */ +struct geo_common_s { + unsigned char type; /* What type of h/w is named by this geoid_s */ + unsigned char blade; + unsigned char slot; /* slot is IRU */ + unsigned char upos; + unsigned char rack; +}; + +/* Additional fields for particular types of hardware */ +struct geo_node_s { + struct geo_common_s common; /* No additional fields needed */ +}; + +struct geo_rtr_s { + struct geo_common_s common; /* No additional fields needed */ +}; + +struct geo_iocntl_s { + struct geo_common_s common; /* No additional fields needed */ +}; + +struct geo_pcicard_s { + struct geo_iocntl_s common; + char bus; /* Bus/widget number */ + char slot; /* PCI slot number */ +}; + +/* Subcomponents of a node */ +struct geo_cpu_s { + struct geo_node_s node; + unsigned char socket:4, /* Which CPU on the node */ + thread:4; + unsigned char core; +}; + +struct geo_mem_s { + struct geo_node_s node; + char membus; /* The memory bus on the node */ + char memslot; /* The memory slot on the bus */ +}; + +union geoid_u { + struct geo_common_s common; + struct geo_node_s node; + struct geo_iocntl_s iocntl; + struct geo_pcicard_s pcicard; + struct geo_rtr_s rtr; + struct geo_cpu_s cpu; + struct geo_mem_s mem; + char padsize[GEOID_SIZE]; +}; + +/* Defined constants */ + +#define GEO_MAX_LEN 48 + +#define GEO_TYPE_INVALID 0 +#define GEO_TYPE_MODULE 1 +#define GEO_TYPE_NODE 2 +#define GEO_TYPE_RTR 3 +#define GEO_TYPE_IOCNTL 4 +#define GEO_TYPE_IOCARD 5 +#define GEO_TYPE_CPU 6 +#define GEO_TYPE_MEM 7 +#define GEO_TYPE_MAX (GEO_TYPE_MEM+1) + +static inline int geo_rack(union geoid_u g) +{ + return (g.common.type == GEO_TYPE_INVALID) ? + -1 : g.common.rack; +} + +static inline int geo_slot(union geoid_u g) +{ + return (g.common.type == GEO_TYPE_INVALID) ? + -1 : g.common.upos; +} + +static inline int geo_blade(union geoid_u g) +{ + return (g.common.type == GEO_TYPE_INVALID) ? + -1 : g.common.blade * 2 + g.common.slot; +} + +#endif /* _ASM_UV_GEO_H */ diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index bbcdc7b8f963..b5d23470f56b 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h @@ -15,6 +15,8 @@ struct vdso_image { unsigned long size; /* Always a multiple of PAGE_SIZE */ unsigned long alt, alt_len; + unsigned long extable_base, extable_len; + const void *extable; long sym_vvar_start; /* Negative offset to the vvar area */ @@ -45,6 +47,9 @@ extern void __init init_vdso_image(const struct vdso_image *image); extern int map_vdso_once(const struct vdso_image *image, unsigned long addr); +extern bool fixup_vdso_exception(struct pt_regs *regs, int trapnr, + unsigned long error_code, + unsigned long fault_addr); #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_VDSO_H */ diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h new file mode 100644 index 000000000000..9034f3007c4e --- /dev/null +++ b/arch/x86/include/uapi/asm/sgx.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright(c) 2016-20 Intel Corporation. + */ +#ifndef _UAPI_ASM_X86_SGX_H +#define _UAPI_ASM_X86_SGX_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +/** + * enum sgx_page_flags - page control flags + * %SGX_PAGE_MEASURE: Measure the page contents with a sequence of + * ENCLS[EEXTEND] operations. + */ +enum sgx_page_flags { + SGX_PAGE_MEASURE = 0x01, +}; + +#define SGX_MAGIC 0xA4 + +#define SGX_IOC_ENCLAVE_CREATE \ + _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create) +#define SGX_IOC_ENCLAVE_ADD_PAGES \ + _IOWR(SGX_MAGIC, 0x01, struct sgx_enclave_add_pages) +#define SGX_IOC_ENCLAVE_INIT \ + _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) +#define SGX_IOC_ENCLAVE_PROVISION \ + _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_provision) + +/** + * struct sgx_enclave_create - parameter structure for the + * %SGX_IOC_ENCLAVE_CREATE ioctl + * @src: address for the SECS page data + */ +struct sgx_enclave_create { + __u64 src; +}; + +/** + * struct sgx_enclave_add_pages - parameter structure for the + * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl + * @src: start address for the page data + * @offset: starting page offset + * @length: length of the data (multiple of the page size) + * @secinfo: address for the SECINFO data + * @flags: page control flags + * @count: number of bytes added (multiple of the page size) + */ +struct sgx_enclave_add_pages { + __u64 src; + __u64 offset; + __u64 length; + __u64 secinfo; + __u64 flags; + __u64 count; +}; + +/** + * struct sgx_enclave_init - parameter structure for the + * %SGX_IOC_ENCLAVE_INIT ioctl + * @sigstruct: address for the SIGSTRUCT data + */ +struct sgx_enclave_init { + __u64 sigstruct; +}; + +/** + * struct sgx_enclave_provision - parameter structure for the + * %SGX_IOC_ENCLAVE_PROVISION ioctl + * @fd: file handle of /dev/sgx_provision + */ +struct sgx_enclave_provision { + __u64 fd; +}; + +struct sgx_enclave_run; + +/** + * typedef sgx_enclave_user_handler_t - Exit handler function accepted by + * __vdso_sgx_enter_enclave() + * @run: The run instance given by the caller + * + * The register parameters contain the snapshot of their values at enclave + * exit. An invalid ENCLU function number will cause -EINVAL to be returned + * to the caller. + * + * Return: + * - <= 0: The given value is returned back to the caller. + * - > 0: ENCLU function to invoke, either EENTER or ERESUME. + */ +typedef int (*sgx_enclave_user_handler_t)(long rdi, long rsi, long rdx, + long rsp, long r8, long r9, + struct sgx_enclave_run *run); + +/** + * struct sgx_enclave_run - the execution context of __vdso_sgx_enter_enclave() + * @tcs: TCS used to enter the enclave + * @function: The last seen ENCLU function (EENTER, ERESUME or EEXIT) + * @exception_vector: The interrupt vector of the exception + * @exception_error_code: The exception error code pulled out of the stack + * @exception_addr: The address that triggered the exception + * @user_handler: User provided callback run on exception + * @user_data: Data passed to the user handler + * @reserved Reserved for future extensions + * + * If @user_handler is provided, the handler will be invoked on all return paths + * of the normal flow. The user handler may transfer control, e.g. via a + * longjmp() call or a C++ exception, without returning to + * __vdso_sgx_enter_enclave(). + */ +struct sgx_enclave_run { + __u64 tcs; + __u32 function; + __u16 exception_vector; + __u16 exception_error_code; + __u64 exception_addr; + __u64 user_handler; + __u64 user_data; + __u8 reserved[216]; +}; + +/** + * typedef vdso_sgx_enter_enclave_t - Prototype for __vdso_sgx_enter_enclave(), + * a vDSO function to enter an SGX enclave. + * @rdi: Pass-through value for RDI + * @rsi: Pass-through value for RSI + * @rdx: Pass-through value for RDX + * @function: ENCLU function, must be EENTER or ERESUME + * @r8: Pass-through value for R8 + * @r9: Pass-through value for R9 + * @run: struct sgx_enclave_run, must be non-NULL + * + * NOTE: __vdso_sgx_enter_enclave() does not ensure full compliance with the + * x86-64 ABI, e.g. doesn't handle XSAVE state. Except for non-volatile + * general purpose registers, EFLAGS.DF, and RSP alignment, preserving/setting + * state in accordance with the x86-64 ABI is the responsibility of the enclave + * and its runtime, i.e. __vdso_sgx_enter_enclave() cannot be called from C + * code without careful consideration by both the enclave and its runtime. + * + * All general purpose registers except RAX, RBX and RCX are passed as-is to the + * enclave. RAX, RBX and RCX are consumed by EENTER and ERESUME and are loaded + * with @function, asynchronous exit pointer, and @run.tcs respectively. + * + * RBP and the stack are used to anchor __vdso_sgx_enter_enclave() to the + * pre-enclave state, e.g. to retrieve @run.exception and @run.user_handler + * after an enclave exit. All other registers are available for use by the + * enclave and its runtime, e.g. an enclave can push additional data onto the + * stack (and modify RSP) to pass information to the optional user handler (see + * below). + * + * Most exceptions reported on ENCLU, including those that occur within the + * enclave, are fixed up and reported synchronously instead of being delivered + * via a standard signal. Debug Exceptions (#DB) and Breakpoints (#BP) are + * never fixed up and are always delivered via standard signals. On synchrously + * reported exceptions, -EFAULT is returned and details about the exception are + * recorded in @run.exception, the optional sgx_enclave_exception struct. + * + * Return: + * - 0: ENCLU function was successfully executed. + * - -EINVAL: Invalid ENCL number (neither EENTER nor ERESUME). + */ +typedef int (*vdso_sgx_enter_enclave_t)(unsigned long rdi, unsigned long rsi, + unsigned long rdx, unsigned int function, + unsigned long r8, unsigned long r9, + struct sgx_enclave_run *run); + +#endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c index c22fb55abcfd..0916f00a992e 100644 --- a/arch/x86/kernel/acpi/apei.c +++ b/arch/x86/kernel/acpi/apei.c @@ -43,3 +43,8 @@ void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) apei_mce_report_mem_error(sev, mem_err); #endif } + +int arch_apei_report_x86_error(struct cper_ia_proc_ctx *ctx_info, u64 lapic_id) +{ + return apei_smca_report_x86_error(ctx_info, lapic_id); +} diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 2400ad62f330..8d778e46725d 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -1374,7 +1374,7 @@ void __ref text_poke_queue(void *addr, const void *opcode, size_t len, const voi * @addr: address to patch * @opcode: opcode of new instruction * @len: length to copy - * @handler: address to jump to when the temporary breakpoint is hit + * @emulate: instruction to be emulated * * Update a single instruction with the vector in the stack, avoiding * dynamically allocated memory. This function should be used when it is diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 18f6b7c4bd79..b4396952c9a6 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -384,7 +384,7 @@ struct resource *amd_get_mmconfig_range(struct resource *res) int amd_get_subcaches(int cpu) { - struct pci_dev *link = node_to_amd_nb(amd_get_nb_id(cpu))->link; + struct pci_dev *link = node_to_amd_nb(topology_die_id(cpu))->link; unsigned int mask; if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) @@ -398,7 +398,7 @@ int amd_get_subcaches(int cpu) int amd_set_subcaches(int cpu, unsigned long mask) { static unsigned int reset, ban; - struct amd_northbridge *nb = node_to_amd_nb(amd_get_nb_id(cpu)); + struct amd_northbridge *nb = node_to_amd_nb(topology_die_id(cpu)); unsigned int reg; int cuid; diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index 235f5cde06fc..4bde125a5bf1 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -502,6 +502,18 @@ enum uv_system_type get_uv_system_type(void) return uv_system_type; } +int uv_get_hubless_system(void) +{ + return uv_hubless_system; +} +EXPORT_SYMBOL_GPL(uv_get_hubless_system); + +ssize_t uv_get_archtype(char *buf, int len) +{ + return scnprintf(buf, len, "%s/%s", uv_archtype, oem_table_id); +} +EXPORT_SYMBOL_GPL(uv_get_archtype); + int is_uv_system(void) { return uv_system_type != UV_NONE; @@ -1603,21 +1615,30 @@ static void check_efi_reboot(void) reboot_type = BOOT_ACPI; } -/* Setup user proc fs files */ +/* + * User proc fs file handling now deprecated. + * Recommend using /sys/firmware/sgi_uv/... instead. + */ static int __maybe_unused proc_hubbed_show(struct seq_file *file, void *data) { + pr_notice_once("%s: using deprecated /proc/sgi_uv/hubbed, use /sys/firmware/sgi_uv/hub_type\n", + current->comm); seq_printf(file, "0x%x\n", uv_hubbed_system); return 0; } static int __maybe_unused proc_hubless_show(struct seq_file *file, void *data) { + pr_notice_once("%s: using deprecated /proc/sgi_uv/hubless, use /sys/firmware/sgi_uv/hubless\n", + current->comm); seq_printf(file, "0x%x\n", uv_hubless_system); return 0; } static int __maybe_unused proc_archtype_show(struct seq_file *file, void *data) { + pr_notice_once("%s: using deprecated /proc/sgi_uv/archtype, use /sys/firmware/sgi_uv/archtype\n", + current->comm); seq_printf(file, "%s/%s\n", uv_archtype, oem_table_id); return 0; } diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c index 70b7154f4bdd..60b9f42ce3c1 100644 --- a/arch/x86/kernel/asm-offsets.c +++ b/arch/x86/kernel/asm-offsets.c @@ -66,7 +66,6 @@ static void __used common(void) OFFSET(PV_IRQ_irq_disable, paravirt_patch_template, irq.irq_disable); OFFSET(PV_IRQ_irq_enable, paravirt_patch_template, irq.irq_enable); OFFSET(PV_CPU_iret, paravirt_patch_template, cpu.iret); - OFFSET(PV_MMU_read_cr2, paravirt_patch_template, mmu.read_cr2); #endif #ifdef CONFIG_XEN diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 93792b457b81..637b499450d1 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_X86_MCE) += mce/ obj-$(CONFIG_MTRR) += mtrr/ obj-$(CONFIG_MICROCODE) += microcode/ obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/ +obj-$(CONFIG_X86_SGX) += sgx/ obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 6062ce586b95..f8ca66f3d861 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -23,7 +23,6 @@ #ifdef CONFIG_X86_64 # include <asm/mmconfig.h> -# include <asm/set_memory.h> #endif #include "cpu.h" @@ -330,7 +329,6 @@ static void legacy_fixup_core_id(struct cpuinfo_x86 *c) */ static void amd_get_topology(struct cpuinfo_x86 *c) { - u8 node_id; int cpu = smp_processor_id(); /* get information required for multi-node processors */ @@ -340,7 +338,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); - node_id = ecx & 0xff; + c->cpu_die_id = ecx & 0xff; if (c->x86 == 0x15) c->cu_id = ebx & 0xff; @@ -360,15 +358,15 @@ static void amd_get_topology(struct cpuinfo_x86 *c) if (!err) c->x86_coreid_bits = get_count_order(c->x86_max_cores); - cacheinfo_amd_init_llc_id(c, cpu, node_id); + cacheinfo_amd_init_llc_id(c, cpu); } else if (cpu_has(c, X86_FEATURE_NODEID_MSR)) { u64 value; rdmsrl(MSR_FAM10H_NODE_ID, value); - node_id = value & 7; + c->cpu_die_id = value & 7; - per_cpu(cpu_llc_id, cpu) = node_id; + per_cpu(cpu_llc_id, cpu) = c->cpu_die_id; } else return; @@ -393,7 +391,7 @@ static void amd_detect_cmp(struct cpuinfo_x86 *c) /* Convert the initial APIC ID into the socket ID */ c->phys_proc_id = c->initial_apicid >> bits; /* use socket ID also for last level cache */ - per_cpu(cpu_llc_id, cpu) = c->phys_proc_id; + per_cpu(cpu_llc_id, cpu) = c->cpu_die_id = c->phys_proc_id; } static void amd_detect_ppin(struct cpuinfo_x86 *c) @@ -425,12 +423,6 @@ clear_ppin: clear_cpu_cap(c, X86_FEATURE_AMD_PPIN); } -u16 amd_get_nb_id(int cpu) -{ - return per_cpu(cpu_llc_id, cpu); -} -EXPORT_SYMBOL_GPL(amd_get_nb_id); - u32 amd_get_nodes_per_socket(void) { return nodes_per_socket; @@ -516,26 +508,6 @@ static void early_init_amd_mc(struct cpuinfo_x86 *c) static void bsp_init_amd(struct cpuinfo_x86 *c) { - -#ifdef CONFIG_X86_64 - if (c->x86 >= 0xf) { - unsigned long long tseg; - - /* - * Split up direct mapping around the TSEG SMM area. - * Don't do it for gbpages because there seems very little - * benefit in doing so. - */ - if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { - unsigned long pfn = tseg >> PAGE_SHIFT; - - pr_debug("tseg: %010llx\n", tseg); - if (pfn_range_is_mapped(pfn, pfn + 1)) - set_memory_4k((unsigned long)__va(tseg), 1); - } - } -#endif - if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { if (c->x86 > 0x10 || diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c index 57074cf3ad7c..3ca9be482a9e 100644 --- a/arch/x86/kernel/cpu/cacheinfo.c +++ b/arch/x86/kernel/cpu/cacheinfo.c @@ -580,7 +580,7 @@ static void amd_init_l3_cache(struct _cpuid4_info_regs *this_leaf, int index) if (index < 3) return; - node = amd_get_nb_id(smp_processor_id()); + node = topology_die_id(smp_processor_id()); this_leaf->nb = node_to_amd_nb(node); if (this_leaf->nb && !this_leaf->nb->l3_cache.indices) amd_calc_l3_indices(this_leaf->nb); @@ -646,7 +646,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c) return i; } -void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id) +void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu) { /* * We may have multiple LLCs if L3 caches exist, so check if we @@ -657,7 +657,7 @@ void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id) if (c->x86 < 0x17) { /* LLC is at the node level. */ - per_cpu(cpu_llc_id, cpu) = node_id; + per_cpu(cpu_llc_id, cpu) = c->cpu_die_id; } else if (c->x86 == 0x17 && c->x86_model <= 0x1F) { /* * LLC is at the core complex level. @@ -684,7 +684,7 @@ void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id) } } -void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c, int cpu, u8 node_id) +void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c, int cpu) { /* * We may have multiple LLCs if L3 caches exist, so check if we diff --git a/arch/x86/kernel/cpu/feat_ctl.c b/arch/x86/kernel/cpu/feat_ctl.c index 29a3bedabd06..3b1b01f2b248 100644 --- a/arch/x86/kernel/cpu/feat_ctl.c +++ b/arch/x86/kernel/cpu/feat_ctl.c @@ -93,16 +93,41 @@ static void init_vmx_capabilities(struct cpuinfo_x86 *c) } #endif /* CONFIG_X86_VMX_FEATURE_NAMES */ +static void clear_sgx_caps(void) +{ + setup_clear_cpu_cap(X86_FEATURE_SGX); + setup_clear_cpu_cap(X86_FEATURE_SGX_LC); +} + +static int __init nosgx(char *str) +{ + clear_sgx_caps(); + + return 0; +} + +early_param("nosgx", nosgx); + void init_ia32_feat_ctl(struct cpuinfo_x86 *c) { bool tboot = tboot_enabled(); + bool enable_sgx; u64 msr; if (rdmsrl_safe(MSR_IA32_FEAT_CTL, &msr)) { clear_cpu_cap(c, X86_FEATURE_VMX); + clear_sgx_caps(); return; } + /* + * Enable SGX if and only if the kernel supports SGX and Launch Control + * is supported, i.e. disable SGX if the LE hash MSRs can't be written. + */ + enable_sgx = cpu_has(c, X86_FEATURE_SGX) && + cpu_has(c, X86_FEATURE_SGX_LC) && + IS_ENABLED(CONFIG_X86_SGX); + if (msr & FEAT_CTL_LOCKED) goto update_caps; @@ -124,13 +149,16 @@ void init_ia32_feat_ctl(struct cpuinfo_x86 *c) msr |= FEAT_CTL_VMX_ENABLED_INSIDE_SMX; } + if (enable_sgx) + msr |= FEAT_CTL_SGX_ENABLED | FEAT_CTL_SGX_LC_ENABLED; + wrmsrl(MSR_IA32_FEAT_CTL, msr); update_caps: set_cpu_cap(c, X86_FEATURE_MSR_IA32_FEAT_CTL); if (!cpu_has(c, X86_FEATURE_VMX)) - return; + goto update_sgx; if ( (tboot && !(msr & FEAT_CTL_VMX_ENABLED_INSIDE_SMX)) || (!tboot && !(msr & FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX))) { @@ -143,4 +171,12 @@ update_caps: init_vmx_capabilities(c); #endif } + +update_sgx: + if (!(msr & FEAT_CTL_SGX_ENABLED) || + !(msr & FEAT_CTL_SGX_LC_ENABLED) || !enable_sgx) { + if (enable_sgx) + pr_err_once("SGX disabled by BIOS\n"); + clear_sgx_caps(); + } } diff --git a/arch/x86/kernel/cpu/hygon.c b/arch/x86/kernel/cpu/hygon.c index ac6c30e5801d..ae59115d18f9 100644 --- a/arch/x86/kernel/cpu/hygon.c +++ b/arch/x86/kernel/cpu/hygon.c @@ -14,9 +14,6 @@ #include <asm/cacheinfo.h> #include <asm/spec-ctrl.h> #include <asm/delay.h> -#ifdef CONFIG_X86_64 -# include <asm/set_memory.h> -#endif #include "cpu.h" @@ -65,7 +62,6 @@ static void hygon_get_topology_early(struct cpuinfo_x86 *c) */ static void hygon_get_topology(struct cpuinfo_x86 *c) { - u8 node_id; int cpu = smp_processor_id(); /* get information required for multi-node processors */ @@ -75,7 +71,7 @@ static void hygon_get_topology(struct cpuinfo_x86 *c) cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); - node_id = ecx & 0xff; + c->cpu_die_id = ecx & 0xff; c->cpu_core_id = ebx & 0xff; @@ -93,14 +89,14 @@ static void hygon_get_topology(struct cpuinfo_x86 *c) /* Socket ID is ApicId[6] for these processors. */ c->phys_proc_id = c->apicid >> APICID_SOCKET_ID_BIT; - cacheinfo_hygon_init_llc_id(c, cpu, node_id); + cacheinfo_hygon_init_llc_id(c, cpu); } else if (cpu_has(c, X86_FEATURE_NODEID_MSR)) { u64 value; rdmsrl(MSR_FAM10H_NODE_ID, value); - node_id = value & 7; + c->cpu_die_id = value & 7; - per_cpu(cpu_llc_id, cpu) = node_id; + per_cpu(cpu_llc_id, cpu) = c->cpu_die_id; } else return; @@ -123,7 +119,7 @@ static void hygon_detect_cmp(struct cpuinfo_x86 *c) /* Convert the initial APIC ID into the socket ID */ c->phys_proc_id = c->initial_apicid >> bits; /* use socket ID also for last level cache */ - per_cpu(cpu_llc_id, cpu) = c->phys_proc_id; + per_cpu(cpu_llc_id, cpu) = c->cpu_die_id = c->phys_proc_id; } static void srat_detect_node(struct cpuinfo_x86 *c) @@ -204,23 +200,6 @@ static void early_init_hygon_mc(struct cpuinfo_x86 *c) static void bsp_init_hygon(struct cpuinfo_x86 *c) { -#ifdef CONFIG_X86_64 - unsigned long long tseg; - - /* - * Split up direct mapping around the TSEG SMM area. - * Don't do it for gbpages because there seems very little - * benefit in doing so. - */ - if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { - unsigned long pfn = tseg >> PAGE_SHIFT; - - pr_debug("tseg: %010llx\n", tseg); - if (pfn_range_is_mapped(pfn, pfn + 1)) - set_memory_4k((unsigned long)__va(tseg), 1); - } -#endif - if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) { u64 val; diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index 0c6b02dd744c..e486f96b3cb3 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -1341,7 +1341,7 @@ static int threshold_create_bank(struct threshold_bank **bp, unsigned int cpu, return -ENODEV; if (is_shared_bank(bank)) { - nb = node_to_amd_nb(amd_get_nb_id(cpu)); + nb = node_to_amd_nb(topology_die_id(cpu)); /* threshold descriptor already initialized on this node? */ if (nb && nb->bank4) { @@ -1445,7 +1445,7 @@ static void threshold_remove_bank(struct threshold_bank *bank) * The last CPU on this node using the shared bank is going * away, remove that bank now. */ - nb = node_to_amd_nb(amd_get_nb_id(smp_processor_id())); + nb = node_to_amd_nb(topology_die_id(smp_processor_id())); nb->bank4 = NULL; } diff --git a/arch/x86/kernel/cpu/mce/apei.c b/arch/x86/kernel/cpu/mce/apei.c index af8d37962586..b58b85380ddb 100644 --- a/arch/x86/kernel/cpu/mce/apei.c +++ b/arch/x86/kernel/cpu/mce/apei.c @@ -51,6 +51,67 @@ void apei_mce_report_mem_error(int severity, struct cper_sec_mem_err *mem_err) } EXPORT_SYMBOL_GPL(apei_mce_report_mem_error); +int apei_smca_report_x86_error(struct cper_ia_proc_ctx *ctx_info, u64 lapic_id) +{ + const u64 *i_mce = ((const u64 *) (ctx_info + 1)); + unsigned int cpu; + struct mce m; + + if (!boot_cpu_has(X86_FEATURE_SMCA)) + return -EINVAL; + + /* + * The starting address of the register array extracted from BERT must + * match with the first expected register in the register layout of + * SMCA address space. This address corresponds to banks's MCA_STATUS + * register. + * + * Match any MCi_STATUS register by turning off bank numbers. + */ + if ((ctx_info->msr_addr & MSR_AMD64_SMCA_MC0_STATUS) != + MSR_AMD64_SMCA_MC0_STATUS) + return -EINVAL; + + /* + * The register array size must be large enough to include all the + * SMCA registers which need to be extracted. + * + * The number of registers in the register array is determined by + * Register Array Size/8 as defined in UEFI spec v2.8, sec N.2.4.2.2. + * The register layout is fixed and currently the raw data in the + * register array includes 6 SMCA registers which the kernel can + * extract. + */ + if (ctx_info->reg_arr_size < 48) + return -EINVAL; + + mce_setup(&m); + + m.extcpu = -1; + m.socketid = -1; + + for_each_possible_cpu(cpu) { + if (cpu_data(cpu).initial_apicid == lapic_id) { + m.extcpu = cpu; + m.socketid = cpu_data(m.extcpu).phys_proc_id; + break; + } + } + + m.apicid = lapic_id; + m.bank = (ctx_info->msr_addr >> 4) & 0xFF; + m.status = *i_mce; + m.addr = *(i_mce + 1); + m.misc = *(i_mce + 2); + /* Skipping MCA_CONFIG */ + m.ipid = *(i_mce + 4); + m.synd = *(i_mce + 5); + + mce_log(&m); + + return 0; +} + #define CPER_CREATOR_MCE \ GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ 0x64, 0x90, 0xb8, 0x9d) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 32b7099e3511..6af6a3c0698f 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -162,7 +162,8 @@ EXPORT_SYMBOL_GPL(mce_log); void mce_register_decode_chain(struct notifier_block *nb) { - if (WARN_ON(nb->priority > MCE_PRIO_MCELOG && nb->priority < MCE_PRIO_EDAC)) + if (WARN_ON(nb->priority < MCE_PRIO_LOWEST || + nb->priority > MCE_PRIO_HIGHEST)) return; blocking_notifier_chain_register(&x86_mce_decoder_chain, nb); @@ -1265,14 +1266,14 @@ static void kill_me_maybe(struct callback_head *cb) } } -static void queue_task_work(struct mce *m, int kill_it) +static void queue_task_work(struct mce *m, int kill_current_task) { current->mce_addr = m->addr; current->mce_kflags = m->kflags; current->mce_ripv = !!(m->mcgstatus & MCG_STATUS_RIPV); current->mce_whole_page = whole_page(m); - if (kill_it) + if (kill_current_task) current->mce_kill_me.func = kill_me_now; else current->mce_kill_me.func = kill_me_maybe; @@ -1320,10 +1321,10 @@ noinstr void do_machine_check(struct pt_regs *regs) int no_way_out = 0; /* - * If kill_it gets set, there might be a way to recover from this + * If kill_current_task is not set, there might be a way to recover from this * error. */ - int kill_it = 0; + int kill_current_task = 0; /* * MCEs are always local on AMD. Same is determined by MCG_STATUS_LMCES @@ -1350,8 +1351,7 @@ noinstr void do_machine_check(struct pt_regs *regs) * severity is MCE_AR_SEVERITY we have other options. */ if (!(m.mcgstatus & MCG_STATUS_RIPV)) - kill_it = 1; - + kill_current_task = (cfg->tolerant == 3) ? 0 : 1; /* * Check if this MCE is signaled to only this logical processor, * on Intel, Zhaoxin only. @@ -1368,7 +1368,7 @@ noinstr void do_machine_check(struct pt_regs *regs) * to see it will clear it. */ if (lmce) { - if (no_way_out) + if (no_way_out && cfg->tolerant < 3) mce_panic("Fatal local machine check", &m, msg); } else { order = mce_start(&no_way_out); @@ -1387,6 +1387,9 @@ noinstr void do_machine_check(struct pt_regs *regs) if (mce_end(order) < 0) { if (!no_way_out) no_way_out = worst >= MCE_PANIC_SEVERITY; + + if (no_way_out && cfg->tolerant < 3) + mce_panic("Fatal machine check on current CPU", &m, msg); } } else { /* @@ -1403,19 +1406,7 @@ noinstr void do_machine_check(struct pt_regs *regs) } } - /* - * If tolerant is at an insane level we drop requests to kill - * processes and continue even when there is no way out. - */ - if (cfg->tolerant == 3) - kill_it = 0; - else if (no_way_out) - mce_panic("Fatal machine check on current CPU", &m, msg); - - if (worst > 0) - irq_work_queue(&mce_irq_work); - - if (worst != MCE_AR_SEVERITY && !kill_it) + if (worst != MCE_AR_SEVERITY && !kill_current_task) goto out; /* Fault was in user mode and we need to take some action */ @@ -1423,7 +1414,7 @@ noinstr void do_machine_check(struct pt_regs *regs) /* If this triggers there is no way to recover. Die hard. */ BUG_ON(!on_thread_stack() || !user_mode(regs)); - queue_task_work(&m, kill_it); + queue_task_work(&m, kill_current_task); } else { /* @@ -1441,7 +1432,7 @@ noinstr void do_machine_check(struct pt_regs *regs) } if (m.kflags & MCE_IN_KERNEL_COPYIN) - queue_task_work(&m, kill_it); + queue_task_work(&m, kill_current_task); } out: mce_wrmsrl(MSR_IA32_MCG_STATUS, 0); @@ -1583,7 +1574,7 @@ static void __mcheck_cpu_mce_banks_init(void) * __mcheck_cpu_init_clear_banks() does the final bank setup. */ b->ctl = -1ULL; - b->init = 1; + b->init = true; } } @@ -1764,7 +1755,7 @@ static int __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c) */ if (c->x86 == 6 && c->x86_model < 0x1A && this_cpu_read(mce_num_banks) > 0) - mce_banks[0].init = 0; + mce_banks[0].init = false; /* * All newer Intel systems support MCE broadcasting. Enable @@ -1813,11 +1804,9 @@ static int __mcheck_cpu_ancient_init(struct cpuinfo_x86 *c) case X86_VENDOR_INTEL: intel_p5_mcheck_init(c); return 1; - break; case X86_VENDOR_CENTAUR: winchip_mcheck_init(c); return 1; - break; default: return 0; } diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c index 3a44346f2276..7b360731fc2d 100644 --- a/arch/x86/kernel/cpu/mce/inject.c +++ b/arch/x86/kernel/cpu/mce/inject.c @@ -522,8 +522,8 @@ static void do_inject(void) if (boot_cpu_has(X86_FEATURE_AMD_DCM) && b == 4 && boot_cpu_data.x86 < 0x17) { - toggle_nb_mca_mst_cpu(amd_get_nb_id(cpu)); - cpu = get_nbc_for_node(amd_get_nb_id(cpu)); + toggle_nb_mca_mst_cpu(topology_die_id(cpu)); + cpu = get_nbc_for_node(topology_die_id(cpu)); } get_online_cpus(); diff --git a/arch/x86/kernel/cpu/mce/intel.c b/arch/x86/kernel/cpu/mce/intel.c index abe9fe0fb851..c2476fe0682e 100644 --- a/arch/x86/kernel/cpu/mce/intel.c +++ b/arch/x86/kernel/cpu/mce/intel.c @@ -509,12 +509,33 @@ static void intel_ppin_init(struct cpuinfo_x86 *c) } } +/* + * Enable additional error logs from the integrated + * memory controller on processors that support this. + */ +static void intel_imc_init(struct cpuinfo_x86 *c) +{ + u64 error_control; + + switch (c->x86_model) { + case INTEL_FAM6_SANDYBRIDGE_X: + case INTEL_FAM6_IVYBRIDGE_X: + case INTEL_FAM6_HASWELL_X: + if (rdmsrl_safe(MSR_ERROR_CONTROL, &error_control)) + return; + error_control |= 2; + wrmsrl_safe(MSR_ERROR_CONTROL, error_control); + break; + } +} + void mce_intel_feature_init(struct cpuinfo_x86 *c) { intel_init_thermal(c); intel_init_cmci(); intel_init_lmce(); intel_ppin_init(c); + intel_imc_init(c); } void mce_intel_feature_clear(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 3f6b137ef4e6..3d4a48336084 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -215,7 +215,6 @@ static unsigned int __verify_patch_size(u8 family, u32 sh_psize, size_t buf_size default: WARN(1, "%s: WTF family: 0x%x\n", __func__, family); return 0; - break; } if (sh_psize > min_t(u32, buf_size, max_size)) diff --git a/arch/x86/kernel/cpu/mtrr/mtrr.c b/arch/x86/kernel/cpu/mtrr/mtrr.c index 6a80f36b5d59..08a30c8e9431 100644 --- a/arch/x86/kernel/cpu/mtrr/mtrr.c +++ b/arch/x86/kernel/cpu/mtrr/mtrr.c @@ -813,7 +813,8 @@ void mtrr_ap_init(void) } /** - * Save current fixed-range MTRR state of the first cpu in cpu_online_mask. + * mtrr_save_state - Save current fixed-range MTRR state of the first + * cpu in cpu_online_mask. */ void mtrr_save_state(void) { diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c index e8b5f1cf1ae8..698bb26aeb6e 100644 --- a/arch/x86/kernel/cpu/resctrl/core.c +++ b/arch/x86/kernel/cpu/resctrl/core.c @@ -895,6 +895,10 @@ static __init void __check_quirks_intel(void) set_rdt_options("!cmt,!mbmtotal,!mbmlocal,!l3cat"); else set_rdt_options("!l3cat"); + fallthrough; + case INTEL_FAM6_BROADWELL_X: + intel_rdt_mbm_apply_quirk(); + break; } } diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h index f65d3c0dbc41..ee71c47844cb 100644 --- a/arch/x86/kernel/cpu/resctrl/internal.h +++ b/arch/x86/kernel/cpu/resctrl/internal.h @@ -264,7 +264,7 @@ void __exit rdtgroup_exit(void); struct rftype { char *name; umode_t mode; - struct kernfs_ops *kf_ops; + const struct kernfs_ops *kf_ops; unsigned long flags; unsigned long fflags; @@ -619,6 +619,7 @@ void mon_event_read(struct rmid_read *rr, struct rdt_resource *r, void mbm_setup_overflow_handler(struct rdt_domain *dom, unsigned long delay_ms); void mbm_handle_overflow(struct work_struct *work); +void __init intel_rdt_mbm_apply_quirk(void); bool is_mba_sc(struct rdt_resource *r); void setup_default_ctrlval(struct rdt_resource *r, u32 *dc, u32 *dm); u32 delay_bw_map(unsigned long bw, struct rdt_resource *r); diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index a98519a3a2e6..7ac31210e452 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -64,6 +64,69 @@ unsigned int rdt_mon_features; */ unsigned int resctrl_cqm_threshold; +#define CF(cf) ((unsigned long)(1048576 * (cf) + 0.5)) + +/* + * The correction factor table is documented in Documentation/x86/resctrl.rst. + * If rmid > rmid threshold, MBM total and local values should be multiplied + * by the correction factor. + * + * The original table is modified for better code: + * + * 1. The threshold 0 is changed to rmid count - 1 so don't do correction + * for the case. + * 2. MBM total and local correction table indexed by core counter which is + * equal to (x86_cache_max_rmid + 1) / 8 - 1 and is from 0 up to 27. + * 3. The correction factor is normalized to 2^20 (1048576) so it's faster + * to calculate corrected value by shifting: + * corrected_value = (original_value * correction_factor) >> 20 + */ +static const struct mbm_correction_factor_table { + u32 rmidthreshold; + u64 cf; +} mbm_cf_table[] __initdata = { + {7, CF(1.000000)}, + {15, CF(1.000000)}, + {15, CF(0.969650)}, + {31, CF(1.000000)}, + {31, CF(1.066667)}, + {31, CF(0.969650)}, + {47, CF(1.142857)}, + {63, CF(1.000000)}, + {63, CF(1.185115)}, + {63, CF(1.066553)}, + {79, CF(1.454545)}, + {95, CF(1.000000)}, + {95, CF(1.230769)}, + {95, CF(1.142857)}, + {95, CF(1.066667)}, + {127, CF(1.000000)}, + {127, CF(1.254863)}, + {127, CF(1.185255)}, + {151, CF(1.000000)}, + {127, CF(1.066667)}, + {167, CF(1.000000)}, + {159, CF(1.454334)}, + {183, CF(1.000000)}, + {127, CF(0.969744)}, + {191, CF(1.280246)}, + {191, CF(1.230921)}, + {215, CF(1.000000)}, + {191, CF(1.143118)}, +}; + +static u32 mbm_cf_rmidthreshold __read_mostly = UINT_MAX; +static u64 mbm_cf __read_mostly; + +static inline u64 get_corrected_mbm_count(u32 rmid, unsigned long val) +{ + /* Correct MBM value. */ + if (rmid > mbm_cf_rmidthreshold) + val = (val * mbm_cf) >> 20; + + return val; +} + static inline struct rmid_entry *__rmid_entry(u32 rmid) { struct rmid_entry *entry; @@ -260,7 +323,8 @@ static int __mon_event_count(u32 rmid, struct rmid_read *rr) m->chunks += chunks; m->prev_msr = tval; - rr->val += m->chunks; + rr->val += get_corrected_mbm_count(rmid, m->chunks); + return 0; } @@ -279,7 +343,7 @@ static void mbm_bw_count(u32 rmid, struct rmid_read *rr) return; chunks = mbm_overflow_count(m->prev_bw_msr, tval, rr->r->mbm_width); - cur_bw = (chunks * r->mon_scale) >> 20; + cur_bw = (get_corrected_mbm_count(rmid, chunks) * r->mon_scale) >> 20; if (m->delta_comp) m->delta_bw = abs(cur_bw - m->prev_bw); @@ -642,3 +706,17 @@ int rdt_get_mon_l3_config(struct rdt_resource *r) return 0; } + +void __init intel_rdt_mbm_apply_quirk(void) +{ + int cf_index; + + cf_index = (boot_cpu_data.x86_cache_max_rmid + 1) / 8 - 1; + if (cf_index >= ARRAY_SIZE(mbm_cf_table)) { + pr_info("No MBM correction factor available\n"); + return; + } + + mbm_cf_rmidthreshold = mbm_cf_table[cf_index].rmidthreshold; + mbm_cf = mbm_cf_table[cf_index].cf; +} diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c index f3418428682b..29ffb95b25ff 100644 --- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c +++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c @@ -240,13 +240,13 @@ static ssize_t rdtgroup_file_write(struct kernfs_open_file *of, char *buf, return -EINVAL; } -static struct kernfs_ops rdtgroup_kf_single_ops = { +static const struct kernfs_ops rdtgroup_kf_single_ops = { .atomic_write_len = PAGE_SIZE, .write = rdtgroup_file_write, .seq_show = rdtgroup_seqfile_show, }; -static struct kernfs_ops kf_mondata_ops = { +static const struct kernfs_ops kf_mondata_ops = { .atomic_write_len = PAGE_SIZE, .seq_show = rdtgroup_mondata_show, }; @@ -3023,8 +3023,7 @@ static int rdtgroup_mkdir(struct kernfs_node *parent_kn, const char *name, return -EPERM; } -static int rdtgroup_rmdir_mon(struct kernfs_node *kn, struct rdtgroup *rdtgrp, - cpumask_var_t tmpmask) +static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) { struct rdtgroup *prdtgrp = rdtgrp->mon.parent; int cpu; @@ -3056,8 +3055,7 @@ static int rdtgroup_rmdir_mon(struct kernfs_node *kn, struct rdtgroup *rdtgrp, return 0; } -static int rdtgroup_ctrl_remove(struct kernfs_node *kn, - struct rdtgroup *rdtgrp) +static int rdtgroup_ctrl_remove(struct rdtgroup *rdtgrp) { rdtgrp->flags = RDT_DELETED; list_del(&rdtgrp->rdtgroup_list); @@ -3066,8 +3064,7 @@ static int rdtgroup_ctrl_remove(struct kernfs_node *kn, return 0; } -static int rdtgroup_rmdir_ctrl(struct kernfs_node *kn, struct rdtgroup *rdtgrp, - cpumask_var_t tmpmask) +static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) { int cpu; @@ -3094,7 +3091,7 @@ static int rdtgroup_rmdir_ctrl(struct kernfs_node *kn, struct rdtgroup *rdtgrp, closid_free(rdtgrp->closid); free_rmid(rdtgrp->mon.rmid); - rdtgroup_ctrl_remove(kn, rdtgrp); + rdtgroup_ctrl_remove(rdtgrp); /* * Free all the child monitor group rmids. @@ -3131,13 +3128,13 @@ static int rdtgroup_rmdir(struct kernfs_node *kn) rdtgrp != &rdtgroup_default) { if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { - ret = rdtgroup_ctrl_remove(kn, rdtgrp); + ret = rdtgroup_ctrl_remove(rdtgrp); } else { - ret = rdtgroup_rmdir_ctrl(kn, rdtgrp, tmpmask); + ret = rdtgroup_rmdir_ctrl(rdtgrp, tmpmask); } } else if (rdtgrp->type == RDTMON_GROUP && is_mon_groups(parent_kn, kn->name)) { - ret = rdtgroup_rmdir_mon(kn, rdtgrp, tmpmask); + ret = rdtgroup_rmdir_mon(rdtgrp, tmpmask); } else { ret = -EPERM; } diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile new file mode 100644 index 000000000000..91d3dc784a29 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/Makefile @@ -0,0 +1,5 @@ +obj-y += \ + driver.o \ + encl.o \ + ioctl.o \ + main.o diff --git a/arch/x86/kernel/cpu/sgx/arch.h b/arch/x86/kernel/cpu/sgx/arch.h new file mode 100644 index 000000000000..dd7602c44c72 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/arch.h @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright(c) 2016-20 Intel Corporation. + * + * Contains data structures defined by the SGX architecture. Data structures + * defined by the Linux software stack should not be placed here. + */ +#ifndef _ASM_X86_SGX_ARCH_H +#define _ASM_X86_SGX_ARCH_H + +#include <linux/bits.h> +#include <linux/types.h> + +/* The SGX specific CPUID function. */ +#define SGX_CPUID 0x12 +/* EPC enumeration. */ +#define SGX_CPUID_EPC 2 +/* An invalid EPC section, i.e. the end marker. */ +#define SGX_CPUID_EPC_INVALID 0x0 +/* A valid EPC section. */ +#define SGX_CPUID_EPC_SECTION 0x1 +/* The bitmask for the EPC section type. */ +#define SGX_CPUID_EPC_MASK GENMASK(3, 0) + +/** + * enum sgx_return_code - The return code type for ENCLS, ENCLU and ENCLV + * %SGX_NOT_TRACKED: Previous ETRACK's shootdown sequence has not + * been completed yet. + * %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's + * public key does not match IA32_SGXLEPUBKEYHASH. + * %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received + */ +enum sgx_return_code { + SGX_NOT_TRACKED = 11, + SGX_INVALID_EINITTOKEN = 16, + SGX_UNMASKED_EVENT = 128, +}; + +/* The modulus size for 3072-bit RSA keys. */ +#define SGX_MODULUS_SIZE 384 + +/** + * enum sgx_miscselect - additional information to an SSA frame + * %SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame. + * + * Save State Area (SSA) is a stack inside the enclave used to store processor + * state when an exception or interrupt occurs. This enum defines additional + * information stored to an SSA frame. + */ +enum sgx_miscselect { + SGX_MISC_EXINFO = BIT(0), +}; + +#define SGX_MISC_RESERVED_MASK GENMASK_ULL(63, 1) + +#define SGX_SSA_GPRS_SIZE 184 +#define SGX_SSA_MISC_EXINFO_SIZE 16 + +/** + * enum sgx_attributes - the attributes field in &struct sgx_secs + * %SGX_ATTR_INIT: Enclave can be entered (is initialized). + * %SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR). + * %SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave. + * %SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote + * attestation. + * %SGX_ATTR_KSS: Allow to use key separation and sharing (KSS). + * %SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to + * sign cryptographic tokens that can be passed to + * EINIT as an authorization to run an enclave. + */ +enum sgx_attribute { + SGX_ATTR_INIT = BIT(0), + SGX_ATTR_DEBUG = BIT(1), + SGX_ATTR_MODE64BIT = BIT(2), + SGX_ATTR_PROVISIONKEY = BIT(4), + SGX_ATTR_EINITTOKENKEY = BIT(5), + SGX_ATTR_KSS = BIT(7), +}; + +#define SGX_ATTR_RESERVED_MASK (BIT_ULL(3) | BIT_ULL(6) | GENMASK_ULL(63, 8)) + +/** + * struct sgx_secs - SGX Enclave Control Structure (SECS) + * @size: size of the address space + * @base: base address of the address space + * @ssa_frame_size: size of an SSA frame + * @miscselect: additional information stored to an SSA frame + * @attributes: attributes for enclave + * @xfrm: XSave-Feature Request Mask (subset of XCR0) + * @mrenclave: SHA256-hash of the enclave contents + * @mrsigner: SHA256-hash of the public key used to sign the SIGSTRUCT + * @config_id: a user-defined value that is used in key derivation + * @isv_prod_id: a user-defined value that is used in key derivation + * @isv_svn: a user-defined value that is used in key derivation + * @config_svn: a user-defined value that is used in key derivation + * + * SGX Enclave Control Structure (SECS) is a special enclave page that is not + * visible in the address space. In fact, this structure defines the address + * range and other global attributes for the enclave and it is the first EPC + * page created for any enclave. It is moved from a temporary buffer to an EPC + * by the means of ENCLS[ECREATE] function. + */ +struct sgx_secs { + u64 size; + u64 base; + u32 ssa_frame_size; + u32 miscselect; + u8 reserved1[24]; + u64 attributes; + u64 xfrm; + u32 mrenclave[8]; + u8 reserved2[32]; + u32 mrsigner[8]; + u8 reserved3[32]; + u32 config_id[16]; + u16 isv_prod_id; + u16 isv_svn; + u16 config_svn; + u8 reserved4[3834]; +} __packed; + +/** + * enum sgx_tcs_flags - execution flags for TCS + * %SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints + * inside an enclave. It is cleared by EADD but can + * be set later with EDBGWR. + */ +enum sgx_tcs_flags { + SGX_TCS_DBGOPTIN = 0x01, +}; + +#define SGX_TCS_RESERVED_MASK GENMASK_ULL(63, 1) +#define SGX_TCS_RESERVED_SIZE 4024 + +/** + * struct sgx_tcs - Thread Control Structure (TCS) + * @state: used to mark an entered TCS + * @flags: execution flags (cleared by EADD) + * @ssa_offset: SSA stack offset relative to the enclave base + * @ssa_index: the current SSA frame index (cleard by EADD) + * @nr_ssa_frames: the number of frame in the SSA stack + * @entry_offset: entry point offset relative to the enclave base + * @exit_addr: address outside the enclave to exit on an exception or + * interrupt + * @fs_offset: offset relative to the enclave base to become FS + * segment inside the enclave + * @gs_offset: offset relative to the enclave base to become GS + * segment inside the enclave + * @fs_limit: size to become a new FS-limit (only 32-bit enclaves) + * @gs_limit: size to become a new GS-limit (only 32-bit enclaves) + * + * Thread Control Structure (TCS) is an enclave page visible in its address + * space that defines an entry point inside the enclave. A thread enters inside + * an enclave by supplying address of TCS to ENCLU(EENTER). A TCS can be entered + * by only one thread at a time. + */ +struct sgx_tcs { + u64 state; + u64 flags; + u64 ssa_offset; + u32 ssa_index; + u32 nr_ssa_frames; + u64 entry_offset; + u64 exit_addr; + u64 fs_offset; + u64 gs_offset; + u32 fs_limit; + u32 gs_limit; + u8 reserved[SGX_TCS_RESERVED_SIZE]; +} __packed; + +/** + * struct sgx_pageinfo - an enclave page descriptor + * @addr: address of the enclave page + * @contents: pointer to the page contents + * @metadata: pointer either to a SECINFO or PCMD instance + * @secs: address of the SECS page + */ +struct sgx_pageinfo { + u64 addr; + u64 contents; + u64 metadata; + u64 secs; +} __packed __aligned(32); + + +/** + * enum sgx_page_type - bits in the SECINFO flags defining the page type + * %SGX_PAGE_TYPE_SECS: a SECS page + * %SGX_PAGE_TYPE_TCS: a TCS page + * %SGX_PAGE_TYPE_REG: a regular page + * %SGX_PAGE_TYPE_VA: a VA page + * %SGX_PAGE_TYPE_TRIM: a page in trimmed state + */ +enum sgx_page_type { + SGX_PAGE_TYPE_SECS, + SGX_PAGE_TYPE_TCS, + SGX_PAGE_TYPE_REG, + SGX_PAGE_TYPE_VA, + SGX_PAGE_TYPE_TRIM, +}; + +#define SGX_NR_PAGE_TYPES 5 +#define SGX_PAGE_TYPE_MASK GENMASK(7, 0) + +/** + * enum sgx_secinfo_flags - the flags field in &struct sgx_secinfo + * %SGX_SECINFO_R: allow read + * %SGX_SECINFO_W: allow write + * %SGX_SECINFO_X: allow execution + * %SGX_SECINFO_SECS: a SECS page + * %SGX_SECINFO_TCS: a TCS page + * %SGX_SECINFO_REG: a regular page + * %SGX_SECINFO_VA: a VA page + * %SGX_SECINFO_TRIM: a page in trimmed state + */ +enum sgx_secinfo_flags { + SGX_SECINFO_R = BIT(0), + SGX_SECINFO_W = BIT(1), + SGX_SECINFO_X = BIT(2), + SGX_SECINFO_SECS = (SGX_PAGE_TYPE_SECS << 8), + SGX_SECINFO_TCS = (SGX_PAGE_TYPE_TCS << 8), + SGX_SECINFO_REG = (SGX_PAGE_TYPE_REG << 8), + SGX_SECINFO_VA = (SGX_PAGE_TYPE_VA << 8), + SGX_SECINFO_TRIM = (SGX_PAGE_TYPE_TRIM << 8), +}; + +#define SGX_SECINFO_PERMISSION_MASK GENMASK_ULL(2, 0) +#define SGX_SECINFO_PAGE_TYPE_MASK (SGX_PAGE_TYPE_MASK << 8) +#define SGX_SECINFO_RESERVED_MASK ~(SGX_SECINFO_PERMISSION_MASK | \ + SGX_SECINFO_PAGE_TYPE_MASK) + +/** + * struct sgx_secinfo - describes attributes of an EPC page + * @flags: permissions and type + * + * Used together with ENCLS leaves that add or modify an EPC page to an + * enclave to define page permissions and type. + */ +struct sgx_secinfo { + u64 flags; + u8 reserved[56]; +} __packed __aligned(64); + +#define SGX_PCMD_RESERVED_SIZE 40 + +/** + * struct sgx_pcmd - Paging Crypto Metadata (PCMD) + * @enclave_id: enclave identifier + * @mac: MAC over PCMD, page contents and isvsvn + * + * PCMD is stored for every swapped page to the regular memory. When ELDU loads + * the page back it recalculates the MAC by using a isvsvn number stored in a + * VA page. Together these two structures bring integrity and rollback + * protection. + */ +struct sgx_pcmd { + struct sgx_secinfo secinfo; + u64 enclave_id; + u8 reserved[SGX_PCMD_RESERVED_SIZE]; + u8 mac[16]; +} __packed __aligned(128); + +#define SGX_SIGSTRUCT_RESERVED1_SIZE 84 +#define SGX_SIGSTRUCT_RESERVED2_SIZE 20 +#define SGX_SIGSTRUCT_RESERVED3_SIZE 32 +#define SGX_SIGSTRUCT_RESERVED4_SIZE 12 + +/** + * struct sgx_sigstruct_header - defines author of the enclave + * @header1: constant byte string + * @vendor: must be either 0x0000 or 0x8086 + * @date: YYYYMMDD in BCD + * @header2: costant byte string + * @swdefined: software defined value + */ +struct sgx_sigstruct_header { + u64 header1[2]; + u32 vendor; + u32 date; + u64 header2[2]; + u32 swdefined; + u8 reserved1[84]; +} __packed; + +/** + * struct sgx_sigstruct_body - defines contents of the enclave + * @miscselect: additional information stored to an SSA frame + * @misc_mask: required miscselect in SECS + * @attributes: attributes for enclave + * @xfrm: XSave-Feature Request Mask (subset of XCR0) + * @attributes_mask: required attributes in SECS + * @xfrm_mask: required XFRM in SECS + * @mrenclave: SHA256-hash of the enclave contents + * @isvprodid: a user-defined value that is used in key derivation + * @isvsvn: a user-defined value that is used in key derivation + */ +struct sgx_sigstruct_body { + u32 miscselect; + u32 misc_mask; + u8 reserved2[20]; + u64 attributes; + u64 xfrm; + u64 attributes_mask; + u64 xfrm_mask; + u8 mrenclave[32]; + u8 reserved3[32]; + u16 isvprodid; + u16 isvsvn; +} __packed; + +/** + * struct sgx_sigstruct - an enclave signature + * @header: defines author of the enclave + * @modulus: the modulus of the public key + * @exponent: the exponent of the public key + * @signature: the signature calculated over the fields except modulus, + * @body: defines contents of the enclave + * @q1: a value used in RSA signature verification + * @q2: a value used in RSA signature verification + * + * Header and body are the parts that are actual signed. The remaining fields + * define the signature of the enclave. + */ +struct sgx_sigstruct { + struct sgx_sigstruct_header header; + u8 modulus[SGX_MODULUS_SIZE]; + u32 exponent; + u8 signature[SGX_MODULUS_SIZE]; + struct sgx_sigstruct_body body; + u8 reserved4[12]; + u8 q1[SGX_MODULUS_SIZE]; + u8 q2[SGX_MODULUS_SIZE]; +} __packed; + +#define SGX_LAUNCH_TOKEN_SIZE 304 + +#endif /* _ASM_X86_SGX_ARCH_H */ diff --git a/arch/x86/kernel/cpu/sgx/driver.c b/arch/x86/kernel/cpu/sgx/driver.c new file mode 100644 index 000000000000..f2eac41bb4ff --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <linux/acpi.h> +#include <linux/miscdevice.h> +#include <linux/mman.h> +#include <linux/security.h> +#include <linux/suspend.h> +#include <asm/traps.h> +#include "driver.h" +#include "encl.h" + +u64 sgx_attributes_reserved_mask; +u64 sgx_xfrm_reserved_mask = ~0x3; +u32 sgx_misc_reserved_mask; + +static int sgx_open(struct inode *inode, struct file *file) +{ + struct sgx_encl *encl; + int ret; + + encl = kzalloc(sizeof(*encl), GFP_KERNEL); + if (!encl) + return -ENOMEM; + + kref_init(&encl->refcount); + xa_init(&encl->page_array); + mutex_init(&encl->lock); + INIT_LIST_HEAD(&encl->va_pages); + INIT_LIST_HEAD(&encl->mm_list); + spin_lock_init(&encl->mm_lock); + + ret = init_srcu_struct(&encl->srcu); + if (ret) { + kfree(encl); + return ret; + } + + file->private_data = encl; + + return 0; +} + +static int sgx_release(struct inode *inode, struct file *file) +{ + struct sgx_encl *encl = file->private_data; + struct sgx_encl_mm *encl_mm; + + /* + * Drain the remaining mm_list entries. At this point the list contains + * entries for processes, which have closed the enclave file but have + * not exited yet. The processes, which have exited, are gone from the + * list by sgx_mmu_notifier_release(). + */ + for ( ; ; ) { + spin_lock(&encl->mm_lock); + + if (list_empty(&encl->mm_list)) { + encl_mm = NULL; + } else { + encl_mm = list_first_entry(&encl->mm_list, + struct sgx_encl_mm, list); + list_del_rcu(&encl_mm->list); + } + + spin_unlock(&encl->mm_lock); + + /* The enclave is no longer mapped by any mm. */ + if (!encl_mm) + break; + + synchronize_srcu(&encl->srcu); + mmu_notifier_unregister(&encl_mm->mmu_notifier, encl_mm->mm); + kfree(encl_mm); + } + + kref_put(&encl->refcount, sgx_encl_release); + return 0; +} + +static int sgx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sgx_encl *encl = file->private_data; + int ret; + + ret = sgx_encl_may_map(encl, vma->vm_start, vma->vm_end, vma->vm_flags); + if (ret) + return ret; + + ret = sgx_encl_mm_add(encl, vma->vm_mm); + if (ret) + return ret; + + vma->vm_ops = &sgx_vm_ops; + vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO; + vma->vm_private_data = encl; + + return 0; +} + +static unsigned long sgx_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + if ((flags & MAP_TYPE) == MAP_PRIVATE) + return -EINVAL; + + if (flags & MAP_FIXED) + return addr; + + return current->mm->get_unmapped_area(file, addr, len, pgoff, flags); +} + +#ifdef CONFIG_COMPAT +static long sgx_compat_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return sgx_ioctl(filep, cmd, arg); +} +#endif + +static const struct file_operations sgx_encl_fops = { + .owner = THIS_MODULE, + .open = sgx_open, + .release = sgx_release, + .unlocked_ioctl = sgx_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sgx_compat_ioctl, +#endif + .mmap = sgx_mmap, + .get_unmapped_area = sgx_get_unmapped_area, +}; + +const struct file_operations sgx_provision_fops = { + .owner = THIS_MODULE, +}; + +static struct miscdevice sgx_dev_enclave = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sgx_enclave", + .nodename = "sgx_enclave", + .fops = &sgx_encl_fops, +}; + +static struct miscdevice sgx_dev_provision = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sgx_provision", + .nodename = "sgx_provision", + .fops = &sgx_provision_fops, +}; + +int __init sgx_drv_init(void) +{ + unsigned int eax, ebx, ecx, edx; + u64 attr_mask; + u64 xfrm_mask; + int ret; + + if (!cpu_feature_enabled(X86_FEATURE_SGX_LC)) + return -ENODEV; + + cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx); + + if (!(eax & 1)) { + pr_err("SGX disabled: SGX1 instruction support not available.\n"); + return -ENODEV; + } + + sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK; + + cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx); + + attr_mask = (((u64)ebx) << 32) + (u64)eax; + sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK; + + if (cpu_feature_enabled(X86_FEATURE_OSXSAVE)) { + xfrm_mask = (((u64)edx) << 32) + (u64)ecx; + sgx_xfrm_reserved_mask = ~xfrm_mask; + } + + ret = misc_register(&sgx_dev_enclave); + if (ret) + return ret; + + ret = misc_register(&sgx_dev_provision); + if (ret) { + misc_deregister(&sgx_dev_enclave); + return ret; + } + + return 0; +} diff --git a/arch/x86/kernel/cpu/sgx/driver.h b/arch/x86/kernel/cpu/sgx/driver.h new file mode 100644 index 000000000000..4eddb4d571ef --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ARCH_SGX_DRIVER_H__ +#define __ARCH_SGX_DRIVER_H__ + +#include <crypto/hash.h> +#include <linux/kref.h> +#include <linux/mmu_notifier.h> +#include <linux/radix-tree.h> +#include <linux/rwsem.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <uapi/asm/sgx.h> +#include "sgx.h" + +#define SGX_EINIT_SPIN_COUNT 20 +#define SGX_EINIT_SLEEP_COUNT 50 +#define SGX_EINIT_SLEEP_TIME 20 + +extern u64 sgx_attributes_reserved_mask; +extern u64 sgx_xfrm_reserved_mask; +extern u32 sgx_misc_reserved_mask; + +extern const struct file_operations sgx_provision_fops; + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); + +int sgx_drv_init(void); + +#endif /* __ARCH_X86_SGX_DRIVER_H__ */ diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c new file mode 100644 index 000000000000..ee50a5010277 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <linux/lockdep.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/shmem_fs.h> +#include <linux/suspend.h> +#include <linux/sched/mm.h> +#include "arch.h" +#include "encl.h" +#include "encls.h" +#include "sgx.h" + +/* + * ELDU: Load an EPC page as unblocked. For more info, see "OS Management of EPC + * Pages" in the SDM. + */ +static int __sgx_encl_eldu(struct sgx_encl_page *encl_page, + struct sgx_epc_page *epc_page, + struct sgx_epc_page *secs_page) +{ + unsigned long va_offset = encl_page->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK; + struct sgx_encl *encl = encl_page->encl; + struct sgx_pageinfo pginfo; + struct sgx_backing b; + pgoff_t page_index; + int ret; + + if (secs_page) + page_index = PFN_DOWN(encl_page->desc - encl_page->encl->base); + else + page_index = PFN_DOWN(encl->size); + + ret = sgx_encl_get_backing(encl, page_index, &b); + if (ret) + return ret; + + pginfo.addr = encl_page->desc & PAGE_MASK; + pginfo.contents = (unsigned long)kmap_atomic(b.contents); + pginfo.metadata = (unsigned long)kmap_atomic(b.pcmd) + + b.pcmd_offset; + + if (secs_page) + pginfo.secs = (u64)sgx_get_epc_virt_addr(secs_page); + else + pginfo.secs = 0; + + ret = __eldu(&pginfo, sgx_get_epc_virt_addr(epc_page), + sgx_get_epc_virt_addr(encl_page->va_page->epc_page) + va_offset); + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "ELDU"); + + ret = -EFAULT; + } + + kunmap_atomic((void *)(unsigned long)(pginfo.metadata - b.pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.contents); + + sgx_encl_put_backing(&b, false); + + return ret; +} + +static struct sgx_epc_page *sgx_encl_eldu(struct sgx_encl_page *encl_page, + struct sgx_epc_page *secs_page) +{ + + unsigned long va_offset = encl_page->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK; + struct sgx_encl *encl = encl_page->encl; + struct sgx_epc_page *epc_page; + int ret; + + epc_page = sgx_alloc_epc_page(encl_page, false); + if (IS_ERR(epc_page)) + return epc_page; + + ret = __sgx_encl_eldu(encl_page, epc_page, secs_page); + if (ret) { + sgx_free_epc_page(epc_page); + return ERR_PTR(ret); + } + + sgx_free_va_slot(encl_page->va_page, va_offset); + list_move(&encl_page->va_page->list, &encl->va_pages); + encl_page->desc &= ~SGX_ENCL_PAGE_VA_OFFSET_MASK; + encl_page->epc_page = epc_page; + + return epc_page; +} + +static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl, + unsigned long addr, + unsigned long vm_flags) +{ + unsigned long vm_prot_bits = vm_flags & (VM_READ | VM_WRITE | VM_EXEC); + struct sgx_epc_page *epc_page; + struct sgx_encl_page *entry; + + entry = xa_load(&encl->page_array, PFN_DOWN(addr)); + if (!entry) + return ERR_PTR(-EFAULT); + + /* + * Verify that the faulted page has equal or higher build time + * permissions than the VMA permissions (i.e. the subset of {VM_READ, + * VM_WRITE, VM_EXECUTE} in vma->vm_flags). + */ + if ((entry->vm_max_prot_bits & vm_prot_bits) != vm_prot_bits) + return ERR_PTR(-EFAULT); + + /* Entry successfully located. */ + if (entry->epc_page) { + if (entry->desc & SGX_ENCL_PAGE_BEING_RECLAIMED) + return ERR_PTR(-EBUSY); + + return entry; + } + + if (!(encl->secs.epc_page)) { + epc_page = sgx_encl_eldu(&encl->secs, NULL); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); + } + + epc_page = sgx_encl_eldu(entry, encl->secs.epc_page); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); + + encl->secs_child_cnt++; + sgx_mark_page_reclaimable(entry->epc_page); + + return entry; +} + +static vm_fault_t sgx_vma_fault(struct vm_fault *vmf) +{ + unsigned long addr = (unsigned long)vmf->address; + struct vm_area_struct *vma = vmf->vma; + struct sgx_encl_page *entry; + unsigned long phys_addr; + struct sgx_encl *encl; + unsigned long pfn; + vm_fault_t ret; + + encl = vma->vm_private_data; + + /* + * It's very unlikely but possible that allocating memory for the + * mm_list entry of a forked process failed in sgx_vma_open(). When + * this happens, vm_private_data is set to NULL. + */ + if (unlikely(!encl)) + return VM_FAULT_SIGBUS; + + mutex_lock(&encl->lock); + + entry = sgx_encl_load_page(encl, addr, vma->vm_flags); + if (IS_ERR(entry)) { + mutex_unlock(&encl->lock); + + if (PTR_ERR(entry) == -EBUSY) + return VM_FAULT_NOPAGE; + + return VM_FAULT_SIGBUS; + } + + phys_addr = sgx_get_epc_phys_addr(entry->epc_page); + + /* Check if another thread got here first to insert the PTE. */ + if (!follow_pfn(vma, addr, &pfn)) { + mutex_unlock(&encl->lock); + + return VM_FAULT_NOPAGE; + } + + ret = vmf_insert_pfn(vma, addr, PFN_DOWN(phys_addr)); + if (ret != VM_FAULT_NOPAGE) { + mutex_unlock(&encl->lock); + + return VM_FAULT_SIGBUS; + } + + sgx_encl_test_and_clear_young(vma->vm_mm, entry); + mutex_unlock(&encl->lock); + + return VM_FAULT_NOPAGE; +} + +static void sgx_vma_open(struct vm_area_struct *vma) +{ + struct sgx_encl *encl = vma->vm_private_data; + + /* + * It's possible but unlikely that vm_private_data is NULL. This can + * happen in a grandchild of a process, when sgx_encl_mm_add() had + * failed to allocate memory in this callback. + */ + if (unlikely(!encl)) + return; + + if (sgx_encl_mm_add(encl, vma->vm_mm)) + vma->vm_private_data = NULL; +} + + +/** + * sgx_encl_may_map() - Check if a requested VMA mapping is allowed + * @encl: an enclave pointer + * @start: lower bound of the address range, inclusive + * @end: upper bound of the address range, exclusive + * @vm_flags: VMA flags + * + * Iterate through the enclave pages contained within [@start, @end) to verify + * that the permissions requested by a subset of {VM_READ, VM_WRITE, VM_EXEC} + * do not contain any permissions that are not contained in the build time + * permissions of any of the enclave pages within the given address range. + * + * An enclave creator must declare the strongest permissions that will be + * needed for each enclave page. This ensures that mappings have the identical + * or weaker permissions than the earlier declared permissions. + * + * Return: 0 on success, -EACCES otherwise + */ +int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start, + unsigned long end, unsigned long vm_flags) +{ + unsigned long vm_prot_bits = vm_flags & (VM_READ | VM_WRITE | VM_EXEC); + struct sgx_encl_page *page; + unsigned long count = 0; + int ret = 0; + + XA_STATE(xas, &encl->page_array, PFN_DOWN(start)); + + /* + * Disallow READ_IMPLIES_EXEC tasks as their VMA permissions might + * conflict with the enclave page permissions. + */ + if (current->personality & READ_IMPLIES_EXEC) + return -EACCES; + + mutex_lock(&encl->lock); + xas_lock(&xas); + xas_for_each(&xas, page, PFN_DOWN(end - 1)) { + if (~page->vm_max_prot_bits & vm_prot_bits) { + ret = -EACCES; + break; + } + + /* Reschedule on every XA_CHECK_SCHED iteration. */ + if (!(++count % XA_CHECK_SCHED)) { + xas_pause(&xas); + xas_unlock(&xas); + mutex_unlock(&encl->lock); + + cond_resched(); + + mutex_lock(&encl->lock); + xas_lock(&xas); + } + } + xas_unlock(&xas); + mutex_unlock(&encl->lock); + + return ret; +} + +static int sgx_vma_mprotect(struct vm_area_struct *vma, unsigned long start, + unsigned long end, unsigned long newflags) +{ + return sgx_encl_may_map(vma->vm_private_data, start, end, newflags); +} + +static int sgx_encl_debug_read(struct sgx_encl *encl, struct sgx_encl_page *page, + unsigned long addr, void *data) +{ + unsigned long offset = addr & ~PAGE_MASK; + int ret; + + + ret = __edbgrd(sgx_get_epc_virt_addr(page->epc_page) + offset, data); + if (ret) + return -EIO; + + return 0; +} + +static int sgx_encl_debug_write(struct sgx_encl *encl, struct sgx_encl_page *page, + unsigned long addr, void *data) +{ + unsigned long offset = addr & ~PAGE_MASK; + int ret; + + ret = __edbgwr(sgx_get_epc_virt_addr(page->epc_page) + offset, data); + if (ret) + return -EIO; + + return 0; +} + +/* + * Load an enclave page to EPC if required, and take encl->lock. + */ +static struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl, + unsigned long addr, + unsigned long vm_flags) +{ + struct sgx_encl_page *entry; + + for ( ; ; ) { + mutex_lock(&encl->lock); + + entry = sgx_encl_load_page(encl, addr, vm_flags); + if (PTR_ERR(entry) != -EBUSY) + break; + + mutex_unlock(&encl->lock); + } + + if (IS_ERR(entry)) + mutex_unlock(&encl->lock); + + return entry; +} + +static int sgx_vma_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_page *entry = NULL; + char data[sizeof(unsigned long)]; + unsigned long align; + int offset; + int cnt; + int ret = 0; + int i; + + /* + * If process was forked, VMA is still there but vm_private_data is set + * to NULL. + */ + if (!encl) + return -EFAULT; + + if (!test_bit(SGX_ENCL_DEBUG, &encl->flags)) + return -EFAULT; + + for (i = 0; i < len; i += cnt) { + entry = sgx_encl_reserve_page(encl, (addr + i) & PAGE_MASK, + vma->vm_flags); + if (IS_ERR(entry)) { + ret = PTR_ERR(entry); + break; + } + + align = ALIGN_DOWN(addr + i, sizeof(unsigned long)); + offset = (addr + i) & (sizeof(unsigned long) - 1); + cnt = sizeof(unsigned long) - offset; + cnt = min(cnt, len - i); + + ret = sgx_encl_debug_read(encl, entry, align, data); + if (ret) + goto out; + + if (write) { + memcpy(data + offset, buf + i, cnt); + ret = sgx_encl_debug_write(encl, entry, align, data); + if (ret) + goto out; + } else { + memcpy(buf + i, data + offset, cnt); + } + +out: + mutex_unlock(&encl->lock); + + if (ret) + break; + } + + return ret < 0 ? ret : i; +} + +const struct vm_operations_struct sgx_vm_ops = { + .fault = sgx_vma_fault, + .mprotect = sgx_vma_mprotect, + .open = sgx_vma_open, + .access = sgx_vma_access, +}; + +/** + * sgx_encl_release - Destroy an enclave instance + * @kref: address of a kref inside &sgx_encl + * + * Used together with kref_put(). Frees all the resources associated with the + * enclave and the instance itself. + */ +void sgx_encl_release(struct kref *ref) +{ + struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount); + struct sgx_va_page *va_page; + struct sgx_encl_page *entry; + unsigned long index; + + xa_for_each(&encl->page_array, index, entry) { + if (entry->epc_page) { + /* + * The page and its radix tree entry cannot be freed + * if the page is being held by the reclaimer. + */ + if (sgx_unmark_page_reclaimable(entry->epc_page)) + continue; + + sgx_free_epc_page(entry->epc_page); + encl->secs_child_cnt--; + entry->epc_page = NULL; + } + + kfree(entry); + } + + xa_destroy(&encl->page_array); + + if (!encl->secs_child_cnt && encl->secs.epc_page) { + sgx_free_epc_page(encl->secs.epc_page); + encl->secs.epc_page = NULL; + } + + while (!list_empty(&encl->va_pages)) { + va_page = list_first_entry(&encl->va_pages, struct sgx_va_page, + list); + list_del(&va_page->list); + sgx_free_epc_page(va_page->epc_page); + kfree(va_page); + } + + if (encl->backing) + fput(encl->backing); + + cleanup_srcu_struct(&encl->srcu); + + WARN_ON_ONCE(!list_empty(&encl->mm_list)); + + /* Detect EPC page leak's. */ + WARN_ON_ONCE(encl->secs_child_cnt); + WARN_ON_ONCE(encl->secs.epc_page); + + kfree(encl); +} + +/* + * 'mm' is exiting and no longer needs mmu notifications. + */ +static void sgx_mmu_notifier_release(struct mmu_notifier *mn, + struct mm_struct *mm) +{ + struct sgx_encl_mm *encl_mm = container_of(mn, struct sgx_encl_mm, mmu_notifier); + struct sgx_encl_mm *tmp = NULL; + + /* + * The enclave itself can remove encl_mm. Note, objects can't be moved + * off an RCU protected list, but deletion is ok. + */ + spin_lock(&encl_mm->encl->mm_lock); + list_for_each_entry(tmp, &encl_mm->encl->mm_list, list) { + if (tmp == encl_mm) { + list_del_rcu(&encl_mm->list); + break; + } + } + spin_unlock(&encl_mm->encl->mm_lock); + + if (tmp == encl_mm) { + synchronize_srcu(&encl_mm->encl->srcu); + mmu_notifier_put(mn); + } +} + +static void sgx_mmu_notifier_free(struct mmu_notifier *mn) +{ + struct sgx_encl_mm *encl_mm = container_of(mn, struct sgx_encl_mm, mmu_notifier); + + kfree(encl_mm); +} + +static const struct mmu_notifier_ops sgx_mmu_notifier_ops = { + .release = sgx_mmu_notifier_release, + .free_notifier = sgx_mmu_notifier_free, +}; + +static struct sgx_encl_mm *sgx_encl_find_mm(struct sgx_encl *encl, + struct mm_struct *mm) +{ + struct sgx_encl_mm *encl_mm = NULL; + struct sgx_encl_mm *tmp; + int idx; + + idx = srcu_read_lock(&encl->srcu); + + list_for_each_entry_rcu(tmp, &encl->mm_list, list) { + if (tmp->mm == mm) { + encl_mm = tmp; + break; + } + } + + srcu_read_unlock(&encl->srcu, idx); + + return encl_mm; +} + +int sgx_encl_mm_add(struct sgx_encl *encl, struct mm_struct *mm) +{ + struct sgx_encl_mm *encl_mm; + int ret; + + /* + * Even though a single enclave may be mapped into an mm more than once, + * each 'mm' only appears once on encl->mm_list. This is guaranteed by + * holding the mm's mmap lock for write before an mm can be added or + * remove to an encl->mm_list. + */ + mmap_assert_write_locked(mm); + + /* + * It's possible that an entry already exists in the mm_list, because it + * is removed only on VFS release or process exit. + */ + if (sgx_encl_find_mm(encl, mm)) + return 0; + + encl_mm = kzalloc(sizeof(*encl_mm), GFP_KERNEL); + if (!encl_mm) + return -ENOMEM; + + encl_mm->encl = encl; + encl_mm->mm = mm; + encl_mm->mmu_notifier.ops = &sgx_mmu_notifier_ops; + + ret = __mmu_notifier_register(&encl_mm->mmu_notifier, mm); + if (ret) { + kfree(encl_mm); + return ret; + } + + spin_lock(&encl->mm_lock); + list_add_rcu(&encl_mm->list, &encl->mm_list); + /* Pairs with smp_rmb() in sgx_reclaimer_block(). */ + smp_wmb(); + encl->mm_list_version++; + spin_unlock(&encl->mm_lock); + + return 0; +} + +static struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, + pgoff_t index) +{ + struct inode *inode = encl->backing->f_path.dentry->d_inode; + struct address_space *mapping = inode->i_mapping; + gfp_t gfpmask = mapping_gfp_mask(mapping); + + return shmem_read_mapping_page_gfp(mapping, index, gfpmask); +} + +/** + * sgx_encl_get_backing() - Pin the backing storage + * @encl: an enclave pointer + * @page_index: enclave page index + * @backing: data for accessing backing storage for the page + * + * Pin the backing storage pages for storing the encrypted contents and Paging + * Crypto MetaData (PCMD) of an enclave page. + * + * Return: + * 0 on success, + * -errno otherwise. + */ +int sgx_encl_get_backing(struct sgx_encl *encl, unsigned long page_index, + struct sgx_backing *backing) +{ + pgoff_t pcmd_index = PFN_DOWN(encl->size) + 1 + (page_index >> 5); + struct page *contents; + struct page *pcmd; + + contents = sgx_encl_get_backing_page(encl, page_index); + if (IS_ERR(contents)) + return PTR_ERR(contents); + + pcmd = sgx_encl_get_backing_page(encl, pcmd_index); + if (IS_ERR(pcmd)) { + put_page(contents); + return PTR_ERR(pcmd); + } + + backing->page_index = page_index; + backing->contents = contents; + backing->pcmd = pcmd; + backing->pcmd_offset = + (page_index & (PAGE_SIZE / sizeof(struct sgx_pcmd) - 1)) * + sizeof(struct sgx_pcmd); + + return 0; +} + +/** + * sgx_encl_put_backing() - Unpin the backing storage + * @backing: data for accessing backing storage for the page + * @do_write: mark pages dirty + */ +void sgx_encl_put_backing(struct sgx_backing *backing, bool do_write) +{ + if (do_write) { + set_page_dirty(backing->pcmd); + set_page_dirty(backing->contents); + } + + put_page(backing->pcmd); + put_page(backing->contents); +} + +static int sgx_encl_test_and_clear_young_cb(pte_t *ptep, unsigned long addr, + void *data) +{ + pte_t pte; + int ret; + + ret = pte_young(*ptep); + if (ret) { + pte = pte_mkold(*ptep); + set_pte_at((struct mm_struct *)data, addr, ptep, pte); + } + + return ret; +} + +/** + * sgx_encl_test_and_clear_young() - Test and reset the accessed bit + * @mm: mm_struct that is checked + * @page: enclave page to be tested for recent access + * + * Checks the Access (A) bit from the PTE corresponding to the enclave page and + * clears it. + * + * Return: 1 if the page has been recently accessed and 0 if not. + */ +int sgx_encl_test_and_clear_young(struct mm_struct *mm, + struct sgx_encl_page *page) +{ + unsigned long addr = page->desc & PAGE_MASK; + struct sgx_encl *encl = page->encl; + struct vm_area_struct *vma; + int ret; + + ret = sgx_encl_find(mm, addr, &vma); + if (ret) + return 0; + + if (encl != vma->vm_private_data) + return 0; + + ret = apply_to_page_range(vma->vm_mm, addr, PAGE_SIZE, + sgx_encl_test_and_clear_young_cb, vma->vm_mm); + if (ret < 0) + return 0; + + return ret; +} + +/** + * sgx_alloc_va_page() - Allocate a Version Array (VA) page + * + * Allocate a free EPC page and convert it to a Version Array (VA) page. + * + * Return: + * a VA page, + * -errno otherwise + */ +struct sgx_epc_page *sgx_alloc_va_page(void) +{ + struct sgx_epc_page *epc_page; + int ret; + + epc_page = sgx_alloc_epc_page(NULL, true); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); + + ret = __epa(sgx_get_epc_virt_addr(epc_page)); + if (ret) { + WARN_ONCE(1, "EPA returned %d (0x%x)", ret, ret); + sgx_free_epc_page(epc_page); + return ERR_PTR(-EFAULT); + } + + return epc_page; +} + +/** + * sgx_alloc_va_slot - allocate a VA slot + * @va_page: a &struct sgx_va_page instance + * + * Allocates a slot from a &struct sgx_va_page instance. + * + * Return: offset of the slot inside the VA page + */ +unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page) +{ + int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT); + + if (slot < SGX_VA_SLOT_COUNT) + set_bit(slot, va_page->slots); + + return slot << 3; +} + +/** + * sgx_free_va_slot - free a VA slot + * @va_page: a &struct sgx_va_page instance + * @offset: offset of the slot inside the VA page + * + * Frees a slot from a &struct sgx_va_page instance. + */ +void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset) +{ + clear_bit(offset >> 3, va_page->slots); +} + +/** + * sgx_va_page_full - is the VA page full? + * @va_page: a &struct sgx_va_page instance + * + * Return: true if all slots have been taken + */ +bool sgx_va_page_full(struct sgx_va_page *va_page) +{ + int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT); + + return slot == SGX_VA_SLOT_COUNT; +} diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h new file mode 100644 index 000000000000..d8d30ccbef4c --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encl.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright(c) 2016-20 Intel Corporation. + * + * Contains the software defined data structures for enclaves. + */ +#ifndef _X86_ENCL_H +#define _X86_ENCL_H + +#include <linux/cpumask.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/mm_types.h> +#include <linux/mmu_notifier.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/srcu.h> +#include <linux/workqueue.h> +#include <linux/xarray.h> +#include "sgx.h" + +/* 'desc' bits holding the offset in the VA (version array) page. */ +#define SGX_ENCL_PAGE_VA_OFFSET_MASK GENMASK_ULL(11, 3) + +/* 'desc' bit marking that the page is being reclaimed. */ +#define SGX_ENCL_PAGE_BEING_RECLAIMED BIT(3) + +struct sgx_encl_page { + unsigned long desc; + unsigned long vm_max_prot_bits; + struct sgx_epc_page *epc_page; + struct sgx_encl *encl; + struct sgx_va_page *va_page; +}; + +enum sgx_encl_flags { + SGX_ENCL_IOCTL = BIT(0), + SGX_ENCL_DEBUG = BIT(1), + SGX_ENCL_CREATED = BIT(2), + SGX_ENCL_INITIALIZED = BIT(3), +}; + +struct sgx_encl_mm { + struct sgx_encl *encl; + struct mm_struct *mm; + struct list_head list; + struct mmu_notifier mmu_notifier; +}; + +struct sgx_encl { + unsigned long base; + unsigned long size; + unsigned long flags; + unsigned int page_cnt; + unsigned int secs_child_cnt; + struct mutex lock; + struct xarray page_array; + struct sgx_encl_page secs; + unsigned long attributes; + unsigned long attributes_mask; + + cpumask_t cpumask; + struct file *backing; + struct kref refcount; + struct list_head va_pages; + unsigned long mm_list_version; + struct list_head mm_list; + spinlock_t mm_lock; + struct srcu_struct srcu; +}; + +#define SGX_VA_SLOT_COUNT 512 + +struct sgx_va_page { + struct sgx_epc_page *epc_page; + DECLARE_BITMAP(slots, SGX_VA_SLOT_COUNT); + struct list_head list; +}; + +struct sgx_backing { + pgoff_t page_index; + struct page *contents; + struct page *pcmd; + unsigned long pcmd_offset; +}; + +extern const struct vm_operations_struct sgx_vm_ops; + +static inline int sgx_encl_find(struct mm_struct *mm, unsigned long addr, + struct vm_area_struct **vma) +{ + struct vm_area_struct *result; + + result = find_vma(mm, addr); + if (!result || result->vm_ops != &sgx_vm_ops || addr < result->vm_start) + return -EINVAL; + + *vma = result; + + return 0; +} + +int sgx_encl_may_map(struct sgx_encl *encl, unsigned long start, + unsigned long end, unsigned long vm_flags); + +void sgx_encl_release(struct kref *ref); +int sgx_encl_mm_add(struct sgx_encl *encl, struct mm_struct *mm); +int sgx_encl_get_backing(struct sgx_encl *encl, unsigned long page_index, + struct sgx_backing *backing); +void sgx_encl_put_backing(struct sgx_backing *backing, bool do_write); +int sgx_encl_test_and_clear_young(struct mm_struct *mm, + struct sgx_encl_page *page); + +struct sgx_epc_page *sgx_alloc_va_page(void); +unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page); +void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset); +bool sgx_va_page_full(struct sgx_va_page *va_page); + +#endif /* _X86_ENCL_H */ diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h new file mode 100644 index 000000000000..443188fe7e70 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encls.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _X86_ENCLS_H +#define _X86_ENCLS_H + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/rwsem.h> +#include <linux/types.h> +#include <asm/asm.h> +#include <asm/traps.h> +#include "sgx.h" + +enum sgx_encls_function { + ECREATE = 0x00, + EADD = 0x01, + EINIT = 0x02, + EREMOVE = 0x03, + EDGBRD = 0x04, + EDGBWR = 0x05, + EEXTEND = 0x06, + ELDU = 0x08, + EBLOCK = 0x09, + EPA = 0x0A, + EWB = 0x0B, + ETRACK = 0x0C, +}; + +/** + * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr + * + * ENCLS has its own (positive value) error codes and also generates + * ENCLS specific #GP and #PF faults. And the ENCLS values get munged + * with system error codes as everything percolates back up the stack. + * Unfortunately (for us), we need to precisely identify each unique + * error code, e.g. the action taken if EWB fails varies based on the + * type of fault and on the exact SGX error code, i.e. we can't simply + * convert all faults to -EFAULT. + * + * To make all three error types coexist, we set bit 30 to identify an + * ENCLS fault. Bit 31 (technically bits N:31) is used to differentiate + * between positive (faults and SGX error codes) and negative (system + * error codes) values. + */ +#define ENCLS_FAULT_FLAG 0x40000000 + +/* Retrieve the encoded trapnr from the specified return code. */ +#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG) + +/* Issue a WARN() about an ENCLS function. */ +#define ENCLS_WARN(r, name) { \ + do { \ + int _r = (r); \ + WARN_ONCE(_r, "%s returned %d (0x%x)\n", (name), _r, _r); \ + } while (0); \ +} + +/** + * encls_failed() - Check if an ENCLS function failed + * @ret: the return value of an ENCLS function call + * + * Check if an ENCLS function failed. This happens when the function causes a + * fault that is not caused by an EPCM conflict or when the function returns a + * non-zero value. + */ +static inline bool encls_failed(int ret) +{ + if (ret & ENCLS_FAULT_FLAG) + return ENCLS_TRAPNR(ret) != X86_TRAP_PF; + + return !!ret; +} + +/** + * __encls_ret_N - encode an ENCLS function that returns an error code in EAX + * @rax: function number + * @inputs: asm inputs for the function + * + * Emit assembly for an ENCLS function that returns an error code, e.g. EREMOVE. + * And because SGX isn't complex enough as it is, function that return an error + * code also modify flags. + * + * Return: + * 0 on success, + * SGX error code on failure + */ +#define __encls_ret_N(rax, inputs...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE_FAULT(1b, 3b) \ + : "=a"(ret) \ + : "a"(rax), inputs \ + : "memory", "cc"); \ + ret; \ + }) + +#define __encls_ret_1(rax, rcx) \ + ({ \ + __encls_ret_N(rax, "c"(rcx)); \ + }) + +#define __encls_ret_2(rax, rbx, rcx) \ + ({ \ + __encls_ret_N(rax, "b"(rbx), "c"(rcx)); \ + }) + +#define __encls_ret_3(rax, rbx, rcx, rdx) \ + ({ \ + __encls_ret_N(rax, "b"(rbx), "c"(rcx), "d"(rdx)); \ + }) + +/** + * __encls_N - encode an ENCLS function that doesn't return an error code + * @rax: function number + * @rbx_out: optional output variable + * @inputs: asm inputs for the function + * + * Emit assembly for an ENCLS function that does not return an error code, e.g. + * ECREATE. Leaves without error codes either succeed or fault. @rbx_out is an + * optional parameter for use by EDGBRD, which returns the requested value in + * RBX. + * + * Return: + * 0 on success, + * trapnr with ENCLS_FAULT_FLAG set on fault + */ +#define __encls_N(rax, rbx_out, inputs...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + " xor %%eax,%%eax;\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE_FAULT(1b, 3b) \ + : "=a"(ret), "=b"(rbx_out) \ + : "a"(rax), inputs \ + : "memory"); \ + ret; \ + }) + +#define __encls_2(rax, rbx, rcx) \ + ({ \ + unsigned long ign_rbx_out; \ + __encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \ + }) + +#define __encls_1_1(rax, data, rcx) \ + ({ \ + unsigned long rbx_out; \ + int ret = __encls_N(rax, rbx_out, "c"(rcx)); \ + if (!ret) \ + data = rbx_out; \ + ret; \ + }) + +static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs) +{ + return __encls_2(ECREATE, pginfo, secs); +} + +static inline int __eextend(void *secs, void *addr) +{ + return __encls_2(EEXTEND, secs, addr); +} + +static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr) +{ + return __encls_2(EADD, pginfo, addr); +} + +static inline int __einit(void *sigstruct, void *token, void *secs) +{ + return __encls_ret_3(EINIT, sigstruct, secs, token); +} + +static inline int __eremove(void *addr) +{ + return __encls_ret_1(EREMOVE, addr); +} + +static inline int __edbgwr(void *addr, unsigned long *data) +{ + return __encls_2(EDGBWR, *data, addr); +} + +static inline int __edbgrd(void *addr, unsigned long *data) +{ + return __encls_1_1(EDGBRD, *data, addr); +} + +static inline int __etrack(void *addr) +{ + return __encls_ret_1(ETRACK, addr); +} + +static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr, + void *va) +{ + return __encls_ret_3(ELDU, pginfo, addr, va); +} + +static inline int __eblock(void *addr) +{ + return __encls_ret_1(EBLOCK, addr); +} + +static inline int __epa(void *addr) +{ + unsigned long rbx = SGX_PAGE_TYPE_VA; + + return __encls_2(EPA, rbx, addr); +} + +static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr, + void *va) +{ + return __encls_ret_3(EWB, pginfo, addr, va); +} + +#endif /* _X86_ENCLS_H */ diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c new file mode 100644 index 000000000000..90a5caf76939 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/ioctl.c @@ -0,0 +1,716 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <asm/mman.h> +#include <linux/mman.h> +#include <linux/delay.h> +#include <linux/file.h> +#include <linux/hashtable.h> +#include <linux/highmem.h> +#include <linux/ratelimit.h> +#include <linux/sched/signal.h> +#include <linux/shmem_fs.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include "driver.h" +#include "encl.h" +#include "encls.h" + +static struct sgx_va_page *sgx_encl_grow(struct sgx_encl *encl) +{ + struct sgx_va_page *va_page = NULL; + void *err; + + BUILD_BUG_ON(SGX_VA_SLOT_COUNT != + (SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1); + + if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) { + va_page = kzalloc(sizeof(*va_page), GFP_KERNEL); + if (!va_page) + return ERR_PTR(-ENOMEM); + + va_page->epc_page = sgx_alloc_va_page(); + if (IS_ERR(va_page->epc_page)) { + err = ERR_CAST(va_page->epc_page); + kfree(va_page); + return err; + } + + WARN_ON_ONCE(encl->page_cnt % SGX_VA_SLOT_COUNT); + } + encl->page_cnt++; + return va_page; +} + +static void sgx_encl_shrink(struct sgx_encl *encl, struct sgx_va_page *va_page) +{ + encl->page_cnt--; + + if (va_page) { + sgx_free_epc_page(va_page->epc_page); + list_del(&va_page->list); + kfree(va_page); + } +} + +static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) +{ + struct sgx_epc_page *secs_epc; + struct sgx_va_page *va_page; + struct sgx_pageinfo pginfo; + struct sgx_secinfo secinfo; + unsigned long encl_size; + struct file *backing; + long ret; + + va_page = sgx_encl_grow(encl); + if (IS_ERR(va_page)) + return PTR_ERR(va_page); + else if (va_page) + list_add(&va_page->list, &encl->va_pages); + /* else the tail page of the VA page list had free slots. */ + + /* The extra page goes to SECS. */ + encl_size = secs->size + PAGE_SIZE; + + backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5), + VM_NORESERVE); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + goto err_out_shrink; + } + + encl->backing = backing; + + secs_epc = sgx_alloc_epc_page(&encl->secs, true); + if (IS_ERR(secs_epc)) { + ret = PTR_ERR(secs_epc); + goto err_out_backing; + } + + encl->secs.epc_page = secs_epc; + + pginfo.addr = 0; + pginfo.contents = (unsigned long)secs; + pginfo.metadata = (unsigned long)&secinfo; + pginfo.secs = 0; + memset(&secinfo, 0, sizeof(secinfo)); + + ret = __ecreate((void *)&pginfo, sgx_get_epc_virt_addr(secs_epc)); + if (ret) { + ret = -EIO; + goto err_out; + } + + if (secs->attributes & SGX_ATTR_DEBUG) + set_bit(SGX_ENCL_DEBUG, &encl->flags); + + encl->secs.encl = encl; + encl->base = secs->base; + encl->size = secs->size; + encl->attributes = secs->attributes; + encl->attributes_mask = SGX_ATTR_DEBUG | SGX_ATTR_MODE64BIT | SGX_ATTR_KSS; + + /* Set only after completion, as encl->lock has not been taken. */ + set_bit(SGX_ENCL_CREATED, &encl->flags); + + return 0; + +err_out: + sgx_free_epc_page(encl->secs.epc_page); + encl->secs.epc_page = NULL; + +err_out_backing: + fput(encl->backing); + encl->backing = NULL; + +err_out_shrink: + sgx_encl_shrink(encl, va_page); + + return ret; +} + +/** + * sgx_ioc_enclave_create() - handler for %SGX_IOC_ENCLAVE_CREATE + * @encl: An enclave pointer. + * @arg: The ioctl argument. + * + * Allocate kernel data structures for the enclave and invoke ECREATE. + * + * Return: + * - 0: Success. + * - -EIO: ECREATE failed. + * - -errno: POSIX error. + */ +static long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg) +{ + struct sgx_enclave_create create_arg; + void *secs; + int ret; + + if (test_bit(SGX_ENCL_CREATED, &encl->flags)) + return -EINVAL; + + if (copy_from_user(&create_arg, arg, sizeof(create_arg))) + return -EFAULT; + + secs = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!secs) + return -ENOMEM; + + if (copy_from_user(secs, (void __user *)create_arg.src, PAGE_SIZE)) + ret = -EFAULT; + else + ret = sgx_encl_create(encl, secs); + + kfree(secs); + return ret; +} + +static struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl, + unsigned long offset, + u64 secinfo_flags) +{ + struct sgx_encl_page *encl_page; + unsigned long prot; + + encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL); + if (!encl_page) + return ERR_PTR(-ENOMEM); + + encl_page->desc = encl->base + offset; + encl_page->encl = encl; + + prot = _calc_vm_trans(secinfo_flags, SGX_SECINFO_R, PROT_READ) | + _calc_vm_trans(secinfo_flags, SGX_SECINFO_W, PROT_WRITE) | + _calc_vm_trans(secinfo_flags, SGX_SECINFO_X, PROT_EXEC); + + /* + * TCS pages must always RW set for CPU access while the SECINFO + * permissions are *always* zero - the CPU ignores the user provided + * values and silently overwrites them with zero permissions. + */ + if ((secinfo_flags & SGX_SECINFO_PAGE_TYPE_MASK) == SGX_SECINFO_TCS) + prot |= PROT_READ | PROT_WRITE; + + /* Calculate maximum of the VM flags for the page. */ + encl_page->vm_max_prot_bits = calc_vm_prot_bits(prot, 0); + + return encl_page; +} + +static int sgx_validate_secinfo(struct sgx_secinfo *secinfo) +{ + u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK; + u64 pt = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + + if (pt != SGX_SECINFO_REG && pt != SGX_SECINFO_TCS) + return -EINVAL; + + if ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) + return -EINVAL; + + /* + * CPU will silently overwrite the permissions as zero, which means + * that we need to validate it ourselves. + */ + if (pt == SGX_SECINFO_TCS && perm) + return -EINVAL; + + if (secinfo->flags & SGX_SECINFO_RESERVED_MASK) + return -EINVAL; + + if (memchr_inv(secinfo->reserved, 0, sizeof(secinfo->reserved))) + return -EINVAL; + + return 0; +} + +static int __sgx_encl_add_page(struct sgx_encl *encl, + struct sgx_encl_page *encl_page, + struct sgx_epc_page *epc_page, + struct sgx_secinfo *secinfo, unsigned long src) +{ + struct sgx_pageinfo pginfo; + struct vm_area_struct *vma; + struct page *src_page; + int ret; + + /* Deny noexec. */ + vma = find_vma(current->mm, src); + if (!vma) + return -EFAULT; + + if (!(vma->vm_flags & VM_MAYEXEC)) + return -EACCES; + + ret = get_user_pages(src, 1, 0, &src_page, NULL); + if (ret < 1) + return -EFAULT; + + pginfo.secs = (unsigned long)sgx_get_epc_virt_addr(encl->secs.epc_page); + pginfo.addr = encl_page->desc & PAGE_MASK; + pginfo.metadata = (unsigned long)secinfo; + pginfo.contents = (unsigned long)kmap_atomic(src_page); + + ret = __eadd(&pginfo, sgx_get_epc_virt_addr(epc_page)); + + kunmap_atomic((void *)pginfo.contents); + put_page(src_page); + + return ret ? -EIO : 0; +} + +/* + * If the caller requires measurement of the page as a proof for the content, + * use EEXTEND to add a measurement for 256 bytes of the page. Repeat this + * operation until the entire page is measured." + */ +static int __sgx_encl_extend(struct sgx_encl *encl, + struct sgx_epc_page *epc_page) +{ + unsigned long offset; + int ret; + + for (offset = 0; offset < PAGE_SIZE; offset += SGX_EEXTEND_BLOCK_SIZE) { + ret = __eextend(sgx_get_epc_virt_addr(encl->secs.epc_page), + sgx_get_epc_virt_addr(epc_page) + offset); + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EEXTEND"); + + return -EIO; + } + } + + return 0; +} + +static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long src, + unsigned long offset, struct sgx_secinfo *secinfo, + unsigned long flags) +{ + struct sgx_encl_page *encl_page; + struct sgx_epc_page *epc_page; + struct sgx_va_page *va_page; + int ret; + + encl_page = sgx_encl_page_alloc(encl, offset, secinfo->flags); + if (IS_ERR(encl_page)) + return PTR_ERR(encl_page); + + epc_page = sgx_alloc_epc_page(encl_page, true); + if (IS_ERR(epc_page)) { + kfree(encl_page); + return PTR_ERR(epc_page); + } + + va_page = sgx_encl_grow(encl); + if (IS_ERR(va_page)) { + ret = PTR_ERR(va_page); + goto err_out_free; + } + + mmap_read_lock(current->mm); + mutex_lock(&encl->lock); + + /* + * Adding to encl->va_pages must be done under encl->lock. Ditto for + * deleting (via sgx_encl_shrink()) in the error path. + */ + if (va_page) + list_add(&va_page->list, &encl->va_pages); + + /* + * Insert prior to EADD in case of OOM. EADD modifies MRENCLAVE, i.e. + * can't be gracefully unwound, while failure on EADD/EXTEND is limited + * to userspace errors (or kernel/hardware bugs). + */ + ret = xa_insert(&encl->page_array, PFN_DOWN(encl_page->desc), + encl_page, GFP_KERNEL); + if (ret) + goto err_out_unlock; + + ret = __sgx_encl_add_page(encl, encl_page, epc_page, secinfo, + src); + if (ret) + goto err_out; + + /* + * Complete the "add" before doing the "extend" so that the "add" + * isn't in a half-baked state in the extremely unlikely scenario + * the enclave will be destroyed in response to EEXTEND failure. + */ + encl_page->encl = encl; + encl_page->epc_page = epc_page; + encl->secs_child_cnt++; + + if (flags & SGX_PAGE_MEASURE) { + ret = __sgx_encl_extend(encl, epc_page); + if (ret) + goto err_out; + } + + sgx_mark_page_reclaimable(encl_page->epc_page); + mutex_unlock(&encl->lock); + mmap_read_unlock(current->mm); + return ret; + +err_out: + xa_erase(&encl->page_array, PFN_DOWN(encl_page->desc)); + +err_out_unlock: + sgx_encl_shrink(encl, va_page); + mutex_unlock(&encl->lock); + mmap_read_unlock(current->mm); + +err_out_free: + sgx_free_epc_page(epc_page); + kfree(encl_page); + + return ret; +} + +/** + * sgx_ioc_enclave_add_pages() - The handler for %SGX_IOC_ENCLAVE_ADD_PAGES + * @encl: an enclave pointer + * @arg: a user pointer to a struct sgx_enclave_add_pages instance + * + * Add one or more pages to an uninitialized enclave, and optionally extend the + * measurement with the contents of the page. The SECINFO and measurement mask + * are applied to all pages. + * + * A SECINFO for a TCS is required to always contain zero permissions because + * CPU silently zeros them. Allowing anything else would cause a mismatch in + * the measurement. + * + * mmap()'s protection bits are capped by the page permissions. For each page + * address, the maximum protection bits are computed with the following + * heuristics: + * + * 1. A regular page: PROT_R, PROT_W and PROT_X match the SECINFO permissions. + * 2. A TCS page: PROT_R | PROT_W. + * + * mmap() is not allowed to surpass the minimum of the maximum protection bits + * within the given address range. + * + * The function deinitializes kernel data structures for enclave and returns + * -EIO in any of the following conditions: + * + * - Enclave Page Cache (EPC), the physical memory holding enclaves, has + * been invalidated. This will cause EADD and EEXTEND to fail. + * - If the source address is corrupted somehow when executing EADD. + * + * Return: + * - 0: Success. + * - -EACCES: The source page is located in a noexec partition. + * - -ENOMEM: Out of EPC pages. + * - -EINTR: The call was interrupted before data was processed. + * - -EIO: Either EADD or EEXTEND failed because invalid source address + * or power cycle. + * - -errno: POSIX error. + */ +static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg) +{ + struct sgx_enclave_add_pages add_arg; + struct sgx_secinfo secinfo; + unsigned long c; + int ret; + + if (!test_bit(SGX_ENCL_CREATED, &encl->flags) || + test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) + return -EINVAL; + + if (copy_from_user(&add_arg, arg, sizeof(add_arg))) + return -EFAULT; + + if (!IS_ALIGNED(add_arg.offset, PAGE_SIZE) || + !IS_ALIGNED(add_arg.src, PAGE_SIZE)) + return -EINVAL; + + if (!add_arg.length || add_arg.length & (PAGE_SIZE - 1)) + return -EINVAL; + + if (add_arg.offset + add_arg.length - PAGE_SIZE >= encl->size) + return -EINVAL; + + if (copy_from_user(&secinfo, (void __user *)add_arg.secinfo, + sizeof(secinfo))) + return -EFAULT; + + if (sgx_validate_secinfo(&secinfo)) + return -EINVAL; + + for (c = 0 ; c < add_arg.length; c += PAGE_SIZE) { + if (signal_pending(current)) { + if (!c) + ret = -ERESTARTSYS; + + break; + } + + if (need_resched()) + cond_resched(); + + ret = sgx_encl_add_page(encl, add_arg.src + c, add_arg.offset + c, + &secinfo, add_arg.flags); + if (ret) + break; + } + + add_arg.count = c; + + if (copy_to_user(arg, &add_arg, sizeof(add_arg))) + return -EFAULT; + + return ret; +} + +static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus, + void *hash) +{ + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tfm; + + return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash); +} + +static int sgx_get_key_hash(const void *modulus, void *hash) +{ + struct crypto_shash *tfm; + int ret; + + tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ret = __sgx_get_key_hash(tfm, modulus, hash); + + crypto_free_shash(tfm); + return ret; +} + +static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct, + void *token) +{ + u64 mrsigner[4]; + int i, j, k; + void *addr; + int ret; + + /* + * Deny initializing enclaves with attributes (namely provisioning) + * that have not been explicitly allowed. + */ + if (encl->attributes & ~encl->attributes_mask) + return -EACCES; + + /* + * Attributes should not be enforced *only* against what's available on + * platform (done in sgx_encl_create) but checked and enforced against + * the mask for enforcement in sigstruct. For example an enclave could + * opt to sign with AVX bit in xfrm, but still be loadable on a platform + * without it if the sigstruct->body.attributes_mask does not turn that + * bit on. + */ + if (sigstruct->body.attributes & sigstruct->body.attributes_mask & + sgx_attributes_reserved_mask) + return -EINVAL; + + if (sigstruct->body.miscselect & sigstruct->body.misc_mask & + sgx_misc_reserved_mask) + return -EINVAL; + + if (sigstruct->body.xfrm & sigstruct->body.xfrm_mask & + sgx_xfrm_reserved_mask) + return -EINVAL; + + ret = sgx_get_key_hash(sigstruct->modulus, mrsigner); + if (ret) + return ret; + + mutex_lock(&encl->lock); + + /* + * ENCLS[EINIT] is interruptible because it has such a high latency, + * e.g. 50k+ cycles on success. If an IRQ/NMI/SMI becomes pending, + * EINIT may fail with SGX_UNMASKED_EVENT so that the event can be + * serviced. + */ + for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) { + for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) { + addr = sgx_get_epc_virt_addr(encl->secs.epc_page); + + preempt_disable(); + + for (k = 0; k < 4; k++) + wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + k, mrsigner[k]); + + ret = __einit(sigstruct, token, addr); + + preempt_enable(); + + if (ret == SGX_UNMASKED_EVENT) + continue; + else + break; + } + + if (ret != SGX_UNMASKED_EVENT) + break; + + msleep_interruptible(SGX_EINIT_SLEEP_TIME); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto err_out; + } + } + + if (ret & ENCLS_FAULT_FLAG) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EINIT"); + + ret = -EIO; + } else if (ret) { + pr_debug("EINIT returned %d\n", ret); + ret = -EPERM; + } else { + set_bit(SGX_ENCL_INITIALIZED, &encl->flags); + } + +err_out: + mutex_unlock(&encl->lock); + return ret; +} + +/** + * sgx_ioc_enclave_init() - handler for %SGX_IOC_ENCLAVE_INIT + * @encl: an enclave pointer + * @arg: userspace pointer to a struct sgx_enclave_init instance + * + * Flush any outstanding enqueued EADD operations and perform EINIT. The + * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match + * the enclave's MRSIGNER, which is caculated from the provided sigstruct. + * + * Return: + * - 0: Success. + * - -EPERM: Invalid SIGSTRUCT. + * - -EIO: EINIT failed because of a power cycle. + * - -errno: POSIX error. + */ +static long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg) +{ + struct sgx_sigstruct *sigstruct; + struct sgx_enclave_init init_arg; + struct page *initp_page; + void *token; + int ret; + + if (!test_bit(SGX_ENCL_CREATED, &encl->flags) || + test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) + return -EINVAL; + + if (copy_from_user(&init_arg, arg, sizeof(init_arg))) + return -EFAULT; + + initp_page = alloc_page(GFP_KERNEL); + if (!initp_page) + return -ENOMEM; + + sigstruct = kmap(initp_page); + token = (void *)((unsigned long)sigstruct + PAGE_SIZE / 2); + memset(token, 0, SGX_LAUNCH_TOKEN_SIZE); + + if (copy_from_user(sigstruct, (void __user *)init_arg.sigstruct, + sizeof(*sigstruct))) { + ret = -EFAULT; + goto out; + } + + /* + * A legacy field used with Intel signed enclaves. These used to mean + * regular and architectural enclaves. The CPU only accepts these values + * but they do not have any other meaning. + * + * Thus, reject any other values. + */ + if (sigstruct->header.vendor != 0x0000 && + sigstruct->header.vendor != 0x8086) { + ret = -EINVAL; + goto out; + } + + ret = sgx_encl_init(encl, sigstruct, token); + +out: + kunmap(initp_page); + __free_page(initp_page); + return ret; +} + +/** + * sgx_ioc_enclave_provision() - handler for %SGX_IOC_ENCLAVE_PROVISION + * @encl: an enclave pointer + * @arg: userspace pointer to a struct sgx_enclave_provision instance + * + * Allow ATTRIBUTE.PROVISION_KEY for an enclave by providing a file handle to + * /dev/sgx_provision. + * + * Return: + * - 0: Success. + * - -errno: Otherwise. + */ +static long sgx_ioc_enclave_provision(struct sgx_encl *encl, void __user *arg) +{ + struct sgx_enclave_provision params; + struct file *file; + + if (copy_from_user(¶ms, arg, sizeof(params))) + return -EFAULT; + + file = fget(params.fd); + if (!file) + return -EINVAL; + + if (file->f_op != &sgx_provision_fops) { + fput(file); + return -EINVAL; + } + + encl->attributes_mask |= SGX_ATTR_PROVISIONKEY; + + fput(file); + return 0; +} + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct sgx_encl *encl = filep->private_data; + int ret; + + if (test_and_set_bit(SGX_ENCL_IOCTL, &encl->flags)) + return -EBUSY; + + switch (cmd) { + case SGX_IOC_ENCLAVE_CREATE: + ret = sgx_ioc_enclave_create(encl, (void __user *)arg); + break; + case SGX_IOC_ENCLAVE_ADD_PAGES: + ret = sgx_ioc_enclave_add_pages(encl, (void __user *)arg); + break; + case SGX_IOC_ENCLAVE_INIT: + ret = sgx_ioc_enclave_init(encl, (void __user *)arg); + break; + case SGX_IOC_ENCLAVE_PROVISION: + ret = sgx_ioc_enclave_provision(encl, (void __user *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + clear_bit(SGX_ENCL_IOCTL, &encl->flags); + return ret; +} diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c new file mode 100644 index 000000000000..c519fc5f6948 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <linux/freezer.h> +#include <linux/highmem.h> +#include <linux/kthread.h> +#include <linux/pagemap.h> +#include <linux/ratelimit.h> +#include <linux/sched/mm.h> +#include <linux/sched/signal.h> +#include <linux/slab.h> +#include "driver.h" +#include "encl.h" +#include "encls.h" + +struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; +static int sgx_nr_epc_sections; +static struct task_struct *ksgxd_tsk; +static DECLARE_WAIT_QUEUE_HEAD(ksgxd_waitq); + +/* + * These variables are part of the state of the reclaimer, and must be accessed + * with sgx_reclaimer_lock acquired. + */ +static LIST_HEAD(sgx_active_page_list); + +static DEFINE_SPINLOCK(sgx_reclaimer_lock); + +/* + * Reset dirty EPC pages to uninitialized state. Laundry can be left with SECS + * pages whose child pages blocked EREMOVE. + */ +static void sgx_sanitize_section(struct sgx_epc_section *section) +{ + struct sgx_epc_page *page; + LIST_HEAD(dirty); + int ret; + + /* init_laundry_list is thread-local, no need for a lock: */ + while (!list_empty(§ion->init_laundry_list)) { + if (kthread_should_stop()) + return; + + /* needed for access to ->page_list: */ + spin_lock(§ion->lock); + + page = list_first_entry(§ion->init_laundry_list, + struct sgx_epc_page, list); + + ret = __eremove(sgx_get_epc_virt_addr(page)); + if (!ret) + list_move(&page->list, §ion->page_list); + else + list_move_tail(&page->list, &dirty); + + spin_unlock(§ion->lock); + + cond_resched(); + } + + list_splice(&dirty, §ion->init_laundry_list); +} + +static bool sgx_reclaimer_age(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *page = epc_page->owner; + struct sgx_encl *encl = page->encl; + struct sgx_encl_mm *encl_mm; + bool ret = true; + int idx; + + idx = srcu_read_lock(&encl->srcu); + + list_for_each_entry_rcu(encl_mm, &encl->mm_list, list) { + if (!mmget_not_zero(encl_mm->mm)) + continue; + + mmap_read_lock(encl_mm->mm); + ret = !sgx_encl_test_and_clear_young(encl_mm->mm, page); + mmap_read_unlock(encl_mm->mm); + + mmput_async(encl_mm->mm); + + if (!ret) + break; + } + + srcu_read_unlock(&encl->srcu, idx); + + if (!ret) + return false; + + return true; +} + +static void sgx_reclaimer_block(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *page = epc_page->owner; + unsigned long addr = page->desc & PAGE_MASK; + struct sgx_encl *encl = page->encl; + unsigned long mm_list_version; + struct sgx_encl_mm *encl_mm; + struct vm_area_struct *vma; + int idx, ret; + + do { + mm_list_version = encl->mm_list_version; + + /* Pairs with smp_rmb() in sgx_encl_mm_add(). */ + smp_rmb(); + + idx = srcu_read_lock(&encl->srcu); + + list_for_each_entry_rcu(encl_mm, &encl->mm_list, list) { + if (!mmget_not_zero(encl_mm->mm)) + continue; + + mmap_read_lock(encl_mm->mm); + + ret = sgx_encl_find(encl_mm->mm, addr, &vma); + if (!ret && encl == vma->vm_private_data) + zap_vma_ptes(vma, addr, PAGE_SIZE); + + mmap_read_unlock(encl_mm->mm); + + mmput_async(encl_mm->mm); + } + + srcu_read_unlock(&encl->srcu, idx); + } while (unlikely(encl->mm_list_version != mm_list_version)); + + mutex_lock(&encl->lock); + + ret = __eblock(sgx_get_epc_virt_addr(epc_page)); + if (encls_failed(ret)) + ENCLS_WARN(ret, "EBLOCK"); + + mutex_unlock(&encl->lock); +} + +static int __sgx_encl_ewb(struct sgx_epc_page *epc_page, void *va_slot, + struct sgx_backing *backing) +{ + struct sgx_pageinfo pginfo; + int ret; + + pginfo.addr = 0; + pginfo.secs = 0; + + pginfo.contents = (unsigned long)kmap_atomic(backing->contents); + pginfo.metadata = (unsigned long)kmap_atomic(backing->pcmd) + + backing->pcmd_offset; + + ret = __ewb(&pginfo, sgx_get_epc_virt_addr(epc_page), va_slot); + + kunmap_atomic((void *)(unsigned long)(pginfo.metadata - + backing->pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.contents); + + return ret; +} + +static void sgx_ipi_cb(void *info) +{ +} + +static const cpumask_t *sgx_encl_ewb_cpumask(struct sgx_encl *encl) +{ + cpumask_t *cpumask = &encl->cpumask; + struct sgx_encl_mm *encl_mm; + int idx; + + /* + * Can race with sgx_encl_mm_add(), but ETRACK has already been + * executed, which means that the CPUs running in the new mm will enter + * into the enclave with a fresh epoch. + */ + cpumask_clear(cpumask); + + idx = srcu_read_lock(&encl->srcu); + + list_for_each_entry_rcu(encl_mm, &encl->mm_list, list) { + if (!mmget_not_zero(encl_mm->mm)) + continue; + + cpumask_or(cpumask, cpumask, mm_cpumask(encl_mm->mm)); + + mmput_async(encl_mm->mm); + } + + srcu_read_unlock(&encl->srcu, idx); + + return cpumask; +} + +/* + * Swap page to the regular memory transformed to the blocked state by using + * EBLOCK, which means that it can no loger be referenced (no new TLB entries). + * + * The first trial just tries to write the page assuming that some other thread + * has reset the count for threads inside the enlave by using ETRACK, and + * previous thread count has been zeroed out. The second trial calls ETRACK + * before EWB. If that fails we kick all the HW threads out, and then do EWB, + * which should be guaranteed the succeed. + */ +static void sgx_encl_ewb(struct sgx_epc_page *epc_page, + struct sgx_backing *backing) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + struct sgx_va_page *va_page; + unsigned int va_offset; + void *va_slot; + int ret; + + encl_page->desc &= ~SGX_ENCL_PAGE_BEING_RECLAIMED; + + va_page = list_first_entry(&encl->va_pages, struct sgx_va_page, + list); + va_offset = sgx_alloc_va_slot(va_page); + va_slot = sgx_get_epc_virt_addr(va_page->epc_page) + va_offset; + if (sgx_va_page_full(va_page)) + list_move_tail(&va_page->list, &encl->va_pages); + + ret = __sgx_encl_ewb(epc_page, va_slot, backing); + if (ret == SGX_NOT_TRACKED) { + ret = __etrack(sgx_get_epc_virt_addr(encl->secs.epc_page)); + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "ETRACK"); + } + + ret = __sgx_encl_ewb(epc_page, va_slot, backing); + if (ret == SGX_NOT_TRACKED) { + /* + * Slow path, send IPIs to kick cpus out of the + * enclave. Note, it's imperative that the cpu + * mask is generated *after* ETRACK, else we'll + * miss cpus that entered the enclave between + * generating the mask and incrementing epoch. + */ + on_each_cpu_mask(sgx_encl_ewb_cpumask(encl), + sgx_ipi_cb, NULL, 1); + ret = __sgx_encl_ewb(epc_page, va_slot, backing); + } + } + + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EWB"); + + sgx_free_va_slot(va_page, va_offset); + } else { + encl_page->desc |= va_offset; + encl_page->va_page = va_page; + } +} + +static void sgx_reclaimer_write(struct sgx_epc_page *epc_page, + struct sgx_backing *backing) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + struct sgx_backing secs_backing; + int ret; + + mutex_lock(&encl->lock); + + sgx_encl_ewb(epc_page, backing); + encl_page->epc_page = NULL; + encl->secs_child_cnt--; + + if (!encl->secs_child_cnt && test_bit(SGX_ENCL_INITIALIZED, &encl->flags)) { + ret = sgx_encl_get_backing(encl, PFN_DOWN(encl->size), + &secs_backing); + if (ret) + goto out; + + sgx_encl_ewb(encl->secs.epc_page, &secs_backing); + + sgx_free_epc_page(encl->secs.epc_page); + encl->secs.epc_page = NULL; + + sgx_encl_put_backing(&secs_backing, true); + } + +out: + mutex_unlock(&encl->lock); +} + +/* + * Take a fixed number of pages from the head of the active page pool and + * reclaim them to the enclave's private shmem files. Skip the pages, which have + * been accessed since the last scan. Move those pages to the tail of active + * page pool so that the pages get scanned in LRU like fashion. + * + * Batch process a chunk of pages (at the moment 16) in order to degrade amount + * of IPI's and ETRACK's potentially required. sgx_encl_ewb() does degrade a bit + * among the HW threads with three stage EWB pipeline (EWB, ETRACK + EWB and IPI + * + EWB) but not sufficiently. Reclaiming one page at a time would also be + * problematic as it would increase the lock contention too much, which would + * halt forward progress. + */ +static void sgx_reclaim_pages(void) +{ + struct sgx_epc_page *chunk[SGX_NR_TO_SCAN]; + struct sgx_backing backing[SGX_NR_TO_SCAN]; + struct sgx_epc_section *section; + struct sgx_encl_page *encl_page; + struct sgx_epc_page *epc_page; + pgoff_t page_index; + int cnt = 0; + int ret; + int i; + + spin_lock(&sgx_reclaimer_lock); + for (i = 0; i < SGX_NR_TO_SCAN; i++) { + if (list_empty(&sgx_active_page_list)) + break; + + epc_page = list_first_entry(&sgx_active_page_list, + struct sgx_epc_page, list); + list_del_init(&epc_page->list); + encl_page = epc_page->owner; + + if (kref_get_unless_zero(&encl_page->encl->refcount) != 0) + chunk[cnt++] = epc_page; + else + /* The owner is freeing the page. No need to add the + * page back to the list of reclaimable pages. + */ + epc_page->flags &= ~SGX_EPC_PAGE_RECLAIMER_TRACKED; + } + spin_unlock(&sgx_reclaimer_lock); + + for (i = 0; i < cnt; i++) { + epc_page = chunk[i]; + encl_page = epc_page->owner; + + if (!sgx_reclaimer_age(epc_page)) + goto skip; + + page_index = PFN_DOWN(encl_page->desc - encl_page->encl->base); + ret = sgx_encl_get_backing(encl_page->encl, page_index, &backing[i]); + if (ret) + goto skip; + + mutex_lock(&encl_page->encl->lock); + encl_page->desc |= SGX_ENCL_PAGE_BEING_RECLAIMED; + mutex_unlock(&encl_page->encl->lock); + continue; + +skip: + spin_lock(&sgx_reclaimer_lock); + list_add_tail(&epc_page->list, &sgx_active_page_list); + spin_unlock(&sgx_reclaimer_lock); + + kref_put(&encl_page->encl->refcount, sgx_encl_release); + + chunk[i] = NULL; + } + + for (i = 0; i < cnt; i++) { + epc_page = chunk[i]; + if (epc_page) + sgx_reclaimer_block(epc_page); + } + + for (i = 0; i < cnt; i++) { + epc_page = chunk[i]; + if (!epc_page) + continue; + + encl_page = epc_page->owner; + sgx_reclaimer_write(epc_page, &backing[i]); + sgx_encl_put_backing(&backing[i], true); + + kref_put(&encl_page->encl->refcount, sgx_encl_release); + epc_page->flags &= ~SGX_EPC_PAGE_RECLAIMER_TRACKED; + + section = &sgx_epc_sections[epc_page->section]; + spin_lock(§ion->lock); + list_add_tail(&epc_page->list, §ion->page_list); + section->free_cnt++; + spin_unlock(§ion->lock); + } +} + +static unsigned long sgx_nr_free_pages(void) +{ + unsigned long cnt = 0; + int i; + + for (i = 0; i < sgx_nr_epc_sections; i++) + cnt += sgx_epc_sections[i].free_cnt; + + return cnt; +} + +static bool sgx_should_reclaim(unsigned long watermark) +{ + return sgx_nr_free_pages() < watermark && + !list_empty(&sgx_active_page_list); +} + +static int ksgxd(void *p) +{ + int i; + + set_freezable(); + + /* + * Sanitize pages in order to recover from kexec(). The 2nd pass is + * required for SECS pages, whose child pages blocked EREMOVE. + */ + for (i = 0; i < sgx_nr_epc_sections; i++) + sgx_sanitize_section(&sgx_epc_sections[i]); + + for (i = 0; i < sgx_nr_epc_sections; i++) { + sgx_sanitize_section(&sgx_epc_sections[i]); + + /* Should never happen. */ + if (!list_empty(&sgx_epc_sections[i].init_laundry_list)) + WARN(1, "EPC section %d has unsanitized pages.\n", i); + } + + while (!kthread_should_stop()) { + if (try_to_freeze()) + continue; + + wait_event_freezable(ksgxd_waitq, + kthread_should_stop() || + sgx_should_reclaim(SGX_NR_HIGH_PAGES)); + + if (sgx_should_reclaim(SGX_NR_HIGH_PAGES)) + sgx_reclaim_pages(); + + cond_resched(); + } + + return 0; +} + +static bool __init sgx_page_reclaimer_init(void) +{ + struct task_struct *tsk; + + tsk = kthread_run(ksgxd, NULL, "ksgxd"); + if (IS_ERR(tsk)) + return false; + + ksgxd_tsk = tsk; + + return true; +} + +static struct sgx_epc_page *__sgx_alloc_epc_page_from_section(struct sgx_epc_section *section) +{ + struct sgx_epc_page *page; + + spin_lock(§ion->lock); + + if (list_empty(§ion->page_list)) { + spin_unlock(§ion->lock); + return NULL; + } + + page = list_first_entry(§ion->page_list, struct sgx_epc_page, list); + list_del_init(&page->list); + section->free_cnt--; + + spin_unlock(§ion->lock); + return page; +} + +/** + * __sgx_alloc_epc_page() - Allocate an EPC page + * + * Iterate through EPC sections and borrow a free EPC page to the caller. When a + * page is no longer needed it must be released with sgx_free_epc_page(). + * + * Return: + * an EPC page, + * -errno on error + */ +struct sgx_epc_page *__sgx_alloc_epc_page(void) +{ + struct sgx_epc_section *section; + struct sgx_epc_page *page; + int i; + + for (i = 0; i < sgx_nr_epc_sections; i++) { + section = &sgx_epc_sections[i]; + + page = __sgx_alloc_epc_page_from_section(section); + if (page) + return page; + } + + return ERR_PTR(-ENOMEM); +} + +/** + * sgx_mark_page_reclaimable() - Mark a page as reclaimable + * @page: EPC page + * + * Mark a page as reclaimable and add it to the active page list. Pages + * are automatically removed from the active list when freed. + */ +void sgx_mark_page_reclaimable(struct sgx_epc_page *page) +{ + spin_lock(&sgx_reclaimer_lock); + page->flags |= SGX_EPC_PAGE_RECLAIMER_TRACKED; + list_add_tail(&page->list, &sgx_active_page_list); + spin_unlock(&sgx_reclaimer_lock); +} + +/** + * sgx_unmark_page_reclaimable() - Remove a page from the reclaim list + * @page: EPC page + * + * Clear the reclaimable flag and remove the page from the active page list. + * + * Return: + * 0 on success, + * -EBUSY if the page is in the process of being reclaimed + */ +int sgx_unmark_page_reclaimable(struct sgx_epc_page *page) +{ + spin_lock(&sgx_reclaimer_lock); + if (page->flags & SGX_EPC_PAGE_RECLAIMER_TRACKED) { + /* The page is being reclaimed. */ + if (list_empty(&page->list)) { + spin_unlock(&sgx_reclaimer_lock); + return -EBUSY; + } + + list_del(&page->list); + page->flags &= ~SGX_EPC_PAGE_RECLAIMER_TRACKED; + } + spin_unlock(&sgx_reclaimer_lock); + + return 0; +} + +/** + * sgx_alloc_epc_page() - Allocate an EPC page + * @owner: the owner of the EPC page + * @reclaim: reclaim pages if necessary + * + * Iterate through EPC sections and borrow a free EPC page to the caller. When a + * page is no longer needed it must be released with sgx_free_epc_page(). If + * @reclaim is set to true, directly reclaim pages when we are out of pages. No + * mm's can be locked when @reclaim is set to true. + * + * Finally, wake up ksgxd when the number of pages goes below the watermark + * before returning back to the caller. + * + * Return: + * an EPC page, + * -errno on error + */ +struct sgx_epc_page *sgx_alloc_epc_page(void *owner, bool reclaim) +{ + struct sgx_epc_page *page; + + for ( ; ; ) { + page = __sgx_alloc_epc_page(); + if (!IS_ERR(page)) { + page->owner = owner; + break; + } + + if (list_empty(&sgx_active_page_list)) + return ERR_PTR(-ENOMEM); + + if (!reclaim) { + page = ERR_PTR(-EBUSY); + break; + } + + if (signal_pending(current)) { + page = ERR_PTR(-ERESTARTSYS); + break; + } + + sgx_reclaim_pages(); + cond_resched(); + } + + if (sgx_should_reclaim(SGX_NR_LOW_PAGES)) + wake_up(&ksgxd_waitq); + + return page; +} + +/** + * sgx_free_epc_page() - Free an EPC page + * @page: an EPC page + * + * Call EREMOVE for an EPC page and insert it back to the list of free pages. + */ +void sgx_free_epc_page(struct sgx_epc_page *page) +{ + struct sgx_epc_section *section = &sgx_epc_sections[page->section]; + int ret; + + WARN_ON_ONCE(page->flags & SGX_EPC_PAGE_RECLAIMER_TRACKED); + + ret = __eremove(sgx_get_epc_virt_addr(page)); + if (WARN_ONCE(ret, "EREMOVE returned %d (0x%x)", ret, ret)) + return; + + spin_lock(§ion->lock); + list_add_tail(&page->list, §ion->page_list); + section->free_cnt++; + spin_unlock(§ion->lock); +} + +static bool __init sgx_setup_epc_section(u64 phys_addr, u64 size, + unsigned long index, + struct sgx_epc_section *section) +{ + unsigned long nr_pages = size >> PAGE_SHIFT; + unsigned long i; + + section->virt_addr = memremap(phys_addr, size, MEMREMAP_WB); + if (!section->virt_addr) + return false; + + section->pages = vmalloc(nr_pages * sizeof(struct sgx_epc_page)); + if (!section->pages) { + memunmap(section->virt_addr); + return false; + } + + section->phys_addr = phys_addr; + spin_lock_init(§ion->lock); + INIT_LIST_HEAD(§ion->page_list); + INIT_LIST_HEAD(§ion->init_laundry_list); + + for (i = 0; i < nr_pages; i++) { + section->pages[i].section = index; + section->pages[i].flags = 0; + section->pages[i].owner = NULL; + list_add_tail(§ion->pages[i].list, §ion->init_laundry_list); + } + + section->free_cnt = nr_pages; + return true; +} + +/** + * A section metric is concatenated in a way that @low bits 12-31 define the + * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the + * metric. + */ +static inline u64 __init sgx_calc_section_metric(u64 low, u64 high) +{ + return (low & GENMASK_ULL(31, 12)) + + ((high & GENMASK_ULL(19, 0)) << 32); +} + +static bool __init sgx_page_cache_init(void) +{ + u32 eax, ebx, ecx, edx, type; + u64 pa, size; + int i; + + for (i = 0; i < ARRAY_SIZE(sgx_epc_sections); i++) { + cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC, &eax, &ebx, &ecx, &edx); + + type = eax & SGX_CPUID_EPC_MASK; + if (type == SGX_CPUID_EPC_INVALID) + break; + + if (type != SGX_CPUID_EPC_SECTION) { + pr_err_once("Unknown EPC section type: %u\n", type); + break; + } + + pa = sgx_calc_section_metric(eax, ebx); + size = sgx_calc_section_metric(ecx, edx); + + pr_info("EPC section 0x%llx-0x%llx\n", pa, pa + size - 1); + + if (!sgx_setup_epc_section(pa, size, i, &sgx_epc_sections[i])) { + pr_err("No free memory for an EPC section\n"); + break; + } + + sgx_nr_epc_sections++; + } + + if (!sgx_nr_epc_sections) { + pr_err("There are zero EPC sections.\n"); + return false; + } + + return true; +} + +static void __init sgx_init(void) +{ + int ret; + int i; + + if (!cpu_feature_enabled(X86_FEATURE_SGX)) + return; + + if (!sgx_page_cache_init()) + return; + + if (!sgx_page_reclaimer_init()) + goto err_page_cache; + + ret = sgx_drv_init(); + if (ret) + goto err_kthread; + + return; + +err_kthread: + kthread_stop(ksgxd_tsk); + +err_page_cache: + for (i = 0; i < sgx_nr_epc_sections; i++) { + vfree(sgx_epc_sections[i].pages); + memunmap(sgx_epc_sections[i].virt_addr); + } +} + +device_initcall(sgx_init); diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h new file mode 100644 index 000000000000..5fa42d143feb --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _X86_SGX_H +#define _X86_SGX_H + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/rwsem.h> +#include <linux/types.h> +#include <asm/asm.h> +#include "arch.h" + +#undef pr_fmt +#define pr_fmt(fmt) "sgx: " fmt + +#define SGX_MAX_EPC_SECTIONS 8 +#define SGX_EEXTEND_BLOCK_SIZE 256 +#define SGX_NR_TO_SCAN 16 +#define SGX_NR_LOW_PAGES 32 +#define SGX_NR_HIGH_PAGES 64 + +/* Pages, which are being tracked by the page reclaimer. */ +#define SGX_EPC_PAGE_RECLAIMER_TRACKED BIT(0) + +struct sgx_epc_page { + unsigned int section; + unsigned int flags; + struct sgx_encl_page *owner; + struct list_head list; +}; + +/* + * The firmware can define multiple chunks of EPC to the different areas of the + * physical memory e.g. for memory areas of the each node. This structure is + * used to store EPC pages for one EPC section and virtual memory area where + * the pages have been mapped. + * + * 'lock' must be held before accessing 'page_list' or 'free_cnt'. + */ +struct sgx_epc_section { + unsigned long phys_addr; + void *virt_addr; + struct sgx_epc_page *pages; + + spinlock_t lock; + struct list_head page_list; + unsigned long free_cnt; + + /* + * Pages which need EREMOVE run on them before they can be + * used. Only safe to be accessed in ksgxd and init code. + * Not protected by locks. + */ + struct list_head init_laundry_list; +}; + +extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; + +static inline unsigned long sgx_get_epc_phys_addr(struct sgx_epc_page *page) +{ + struct sgx_epc_section *section = &sgx_epc_sections[page->section]; + unsigned long index; + + index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page); + + return section->phys_addr + index * PAGE_SIZE; +} + +static inline void *sgx_get_epc_virt_addr(struct sgx_epc_page *page) +{ + struct sgx_epc_section *section = &sgx_epc_sections[page->section]; + unsigned long index; + + index = ((unsigned long)page - (unsigned long)section->pages) / sizeof(*page); + + return section->virt_addr + index * PAGE_SIZE; +} + +struct sgx_epc_page *__sgx_alloc_epc_page(void); +void sgx_free_epc_page(struct sgx_epc_page *page); + +void sgx_mark_page_reclaimable(struct sgx_epc_page *page); +int sgx_unmark_page_reclaimable(struct sgx_epc_page *page); +struct sgx_epc_page *sgx_alloc_epc_page(void *owner, bool reclaim); + +#endif /* _X86_SGX_H */ diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c index d3a0791bc052..1068002c8532 100644 --- a/arch/x86/kernel/cpu/topology.c +++ b/arch/x86/kernel/cpu/topology.c @@ -96,6 +96,7 @@ int detect_extended_topology(struct cpuinfo_x86 *c) unsigned int ht_mask_width, core_plus_mask_width, die_plus_mask_width; unsigned int core_select_mask, core_level_siblings; unsigned int die_select_mask, die_level_siblings; + bool die_level_present = false; int leaf; leaf = detect_extended_topology_leaf(c); @@ -126,6 +127,7 @@ int detect_extended_topology(struct cpuinfo_x86 *c) die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); } if (LEAFB_SUBTYPE(ecx) == DIE_TYPE) { + die_level_present = true; die_level_siblings = LEVEL_MAX_SIBLINGS(ebx); die_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); } @@ -139,8 +141,12 @@ int detect_extended_topology(struct cpuinfo_x86 *c) c->cpu_core_id = apic->phys_pkg_id(c->initial_apicid, ht_mask_width) & core_select_mask; - c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, - core_plus_mask_width) & die_select_mask; + + if (die_level_present) { + c->cpu_die_id = apic->phys_pkg_id(c->initial_apicid, + core_plus_mask_width) & die_select_mask; + } + c->phys_proc_id = apic->phys_pkg_id(c->initial_apicid, die_plus_mask_width); /* diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 97aa900386cb..299c20f0a38b 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -183,7 +183,7 @@ static void show_regs_if_on_stack(struct stack_info *info, struct pt_regs *regs, } } -void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, +static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, unsigned long *stack, const char *log_lvl) { struct unwind_state state; diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 05e117137b45..5e9beb77cafd 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -37,7 +37,6 @@ #include <asm/kasan.h> #include <asm/fixmap.h> #include <asm/realmode.h> -#include <asm/desc.h> #include <asm/extable.h> #include <asm/trapnr.h> #include <asm/sev-es.h> diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 3c417734790f..04bddaaba8e2 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -26,15 +26,6 @@ #include <asm/nospec-branch.h> #include <asm/fixmap.h> -#ifdef CONFIG_PARAVIRT_XXL -#include <asm/asm-offsets.h> -#include <asm/paravirt.h> -#define GET_CR2_INTO(reg) GET_CR2_INTO_AX ; _ASM_MOV %_ASM_AX, reg -#else -#define INTERRUPT_RETURN iretq -#define GET_CR2_INTO(reg) _ASM_MOV %cr2, reg -#endif - /* * We are not able to switch in one step to the final KERNEL ADDRESS SPACE * because we need identity-mapped pages. @@ -540,21 +531,19 @@ SYM_DATA_END(level3_kernel_pgt) SYM_DATA_START_PAGE_ALIGNED(level2_kernel_pgt) /* - * 512 MB kernel mapping. We spend a full page on this pagetable - * anyway. + * Kernel high mapping. * - * The kernel code+data+bss must not be bigger than that. + * The kernel code+data+bss must be located below KERNEL_IMAGE_SIZE in + * virtual address space, which is 1 GiB if RANDOMIZE_BASE is enabled, + * 512 MiB otherwise. * - * (NOTE: at +512MB starts the module area, see MODULES_VADDR. - * If you want to increase this then increase MODULES_VADDR - * too.) + * (NOTE: after that starts the module area, see MODULES_VADDR.) * - * This table is eventually used by the kernel during normal - * runtime. Care must be taken to clear out undesired bits - * later, like _PAGE_RW or _PAGE_GLOBAL in some cases. + * This table is eventually used by the kernel during normal runtime. + * Care must be taken to clear out undesired bits later, like _PAGE_RW + * or _PAGE_GLOBAL in some cases. */ - PMDS(0, __PAGE_KERNEL_LARGE_EXEC, - KERNEL_IMAGE_SIZE/PMD_SIZE) + PMDS(0, __PAGE_KERNEL_LARGE_EXEC, KERNEL_IMAGE_SIZE/PMD_SIZE) SYM_DATA_END(level2_kernel_pgt) SYM_DATA_START_PAGE_ALIGNED(level2_fixmap_pgt) diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index c0d409810658..8a67d1fa8dc5 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -99,11 +99,9 @@ static int filter_write(u32 reg) if (!__ratelimit(&fw_rs)) return 0; - if (reg == MSR_IA32_ENERGY_PERF_BIAS) - return 0; - - pr_err("Write to unrecognized MSR 0x%x by %s (pid: %d). Please report to x86@kernel.org.\n", - reg, current->comm, current->pid); + pr_warn("Write to unrecognized MSR 0x%x by %s (pid: %d).\n", + reg, current->comm, current->pid); + pr_warn("See https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/about for details.\n"); return 0; } diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c index f9e5352b3bef..624703af80a1 100644 --- a/arch/x86/kernel/perf_regs.c +++ b/arch/x86/kernel/perf_regs.c @@ -122,7 +122,7 @@ int perf_reg_validate(u64 mask) u64 perf_reg_abi(struct task_struct *task) { - if (test_tsk_thread_flag(task, TIF_IA32)) + if (!user_64bit_mode(task_pt_regs(task))) return PERF_SAMPLE_REGS_ABI_32; else return PERF_SAMPLE_REGS_ABI_64; diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index df342bedea88..ad582f9ac5a6 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -511,11 +511,10 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp) EXPORT_SYMBOL_GPL(start_thread); #ifdef CONFIG_COMPAT -void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp) +void compat_start_thread(struct pt_regs *regs, u32 new_ip, u32 new_sp, bool x32) { start_thread_common(regs, new_ip, new_sp, - test_thread_flag(TIF_X32) - ? __USER_CS : __USER32_CS, + x32 ? __USER_CS : __USER32_CS, __USER_DS, __USER_DS); } #endif @@ -641,16 +640,12 @@ void set_personality_64bit(void) /* inherit personality from parent */ /* Make sure to be in 64bit mode */ - clear_thread_flag(TIF_IA32); clear_thread_flag(TIF_ADDR32); - clear_thread_flag(TIF_X32); /* Pretend that this comes from a 64bit execve */ task_pt_regs(current)->orig_ax = __NR_execve; current_thread_info()->status &= ~TS_COMPAT; - - /* Ensure the corresponding mm is not marked. */ if (current->mm) - current->mm->context.ia32_compat = 0; + current->mm->context.flags = MM_CONTEXT_HAS_VSYSCALL; /* TBD: overwrites user setup. Should have two bits. But 64bit processes have always behaved this way, @@ -662,10 +657,9 @@ void set_personality_64bit(void) static void __set_personality_x32(void) { #ifdef CONFIG_X86_X32 - clear_thread_flag(TIF_IA32); - set_thread_flag(TIF_X32); if (current->mm) - current->mm->context.ia32_compat = TIF_X32; + current->mm->context.flags = 0; + current->personality &= ~READ_IMPLIES_EXEC; /* * in_32bit_syscall() uses the presence of the x32 syscall bit @@ -683,10 +677,14 @@ static void __set_personality_x32(void) static void __set_personality_ia32(void) { #ifdef CONFIG_IA32_EMULATION - set_thread_flag(TIF_IA32); - clear_thread_flag(TIF_X32); - if (current->mm) - current->mm->context.ia32_compat = TIF_IA32; + if (current->mm) { + /* + * uprobes applied to this MM need to know this and + * cannot use user_64bit_mode() at that time. + */ + current->mm->context.flags = MM_CONTEXT_UPROBE_IA32; + } + current->personality |= force_personality32; /* Prepare the first "return" to user space */ task_pt_regs(current)->orig_ax = __NR_ia32_execve; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 84f581c91db4..a23130c86bdd 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -119,11 +119,6 @@ EXPORT_SYMBOL(boot_cpu_data); unsigned int def_to_bigsmp; -/* For MCA, but anyone else can use it if they want */ -unsigned int machine_id; -unsigned int machine_submodel_id; -unsigned int BIOS_revision; - struct apm_info apm_info; EXPORT_SYMBOL(apm_info); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index e19df6cde35d..7798d862983f 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -60,6 +60,7 @@ #include <asm/umip.h> #include <asm/insn.h> #include <asm/insn-eval.h> +#include <asm/vdso.h> #ifdef CONFIG_X86_64 #include <asm/x86_init.h> @@ -117,6 +118,9 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str, tsk->thread.error_code = error_code; tsk->thread.trap_nr = trapnr; die(str, regs, error_code); + } else { + if (fixup_vdso_exception(regs, trapnr, error_code, 0)) + return 0; } /* @@ -550,6 +554,9 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection) tsk->thread.error_code = error_code; tsk->thread.trap_nr = X86_TRAP_GP; + if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0)) + return; + show_signal(tsk, SIGSEGV, "", desc, regs, error_code); force_sig(SIGSEGV); goto exit; @@ -1048,6 +1055,9 @@ static void math_error(struct pt_regs *regs, int trapnr) if (!si_code) goto exit; + if (fixup_vdso_exception(regs, trapnr, 0, 0)) + return; + force_sig_fault(SIGFPE, si_code, (void __user *)uprobe_get_trap_addr(regs)); exit: diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index bf9e0adb5b7e..efd9e9ea17f2 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -454,13 +454,13 @@ SECTIONS ASSERT(SIZEOF(.rela.dyn) == 0, "Unexpected run-time relocations (.rela) detected!") } -#ifdef CONFIG_X86_32 /* * The ASSERT() sink to . is intentional, for binutils 2.14 compatibility: */ . = ASSERT((_end - LOAD_OFFSET <= KERNEL_IMAGE_SIZE), "kernel image bigger than KERNEL_IMAGE_SIZE"); -#else + +#ifdef CONFIG_X86_64 /* * Per-cpu symbols which need to be offset from __per_cpu_load * for the boot processor. @@ -470,18 +470,12 @@ INIT_PER_CPU(gdt_page); INIT_PER_CPU(fixed_percpu_data); INIT_PER_CPU(irq_stack_backing_store); -/* - * Build-time check on the image size: - */ -. = ASSERT((_end - _text <= KERNEL_IMAGE_SIZE), - "kernel image bigger than KERNEL_IMAGE_SIZE"); - #ifdef CONFIG_SMP . = ASSERT((fixed_percpu_data == 0), "fixed_percpu_data is not at start of per-cpu area"); #endif -#endif /* CONFIG_X86_32 */ +#endif /* CONFIG_X86_64 */ #ifdef CONFIG_KEXEC_CORE #include <asm/kexec.h> diff --git a/arch/x86/lib/copy_mc.c b/arch/x86/lib/copy_mc.c index c13e8c9ee926..80efd45a7761 100644 --- a/arch/x86/lib/copy_mc.c +++ b/arch/x86/lib/copy_mc.c @@ -10,10 +10,6 @@ #include <asm/mce.h> #ifdef CONFIG_X86_MCE -/* - * See COPY_MC_TEST for self-test of the copy_mc_fragile() - * implementation. - */ static DEFINE_STATIC_KEY_FALSE(copy_mc_fragile_key); void enable_copy_mc_fragile(void) diff --git a/arch/x86/lib/copy_mc_64.S b/arch/x86/lib/copy_mc_64.S index 892d8915f609..e5f77e293034 100644 --- a/arch/x86/lib/copy_mc_64.S +++ b/arch/x86/lib/copy_mc_64.S @@ -2,14 +2,11 @@ /* Copyright(c) 2016-2020 Intel Corporation. All rights reserved. */ #include <linux/linkage.h> -#include <asm/copy_mc_test.h> -#include <asm/export.h> #include <asm/asm.h> #ifndef CONFIG_UML #ifdef CONFIG_X86_MCE -COPY_MC_TEST_CTL /* * copy_mc_fragile - copy memory with indication if an exception / fault happened @@ -38,8 +35,6 @@ SYM_FUNC_START(copy_mc_fragile) subl %ecx, %edx .L_read_leading_bytes: movb (%rsi), %al - COPY_MC_TEST_SRC %rsi 1 .E_leading_bytes - COPY_MC_TEST_DST %rdi 1 .E_leading_bytes .L_write_leading_bytes: movb %al, (%rdi) incq %rsi @@ -55,8 +50,6 @@ SYM_FUNC_START(copy_mc_fragile) .L_read_words: movq (%rsi), %r8 - COPY_MC_TEST_SRC %rsi 8 .E_read_words - COPY_MC_TEST_DST %rdi 8 .E_write_words .L_write_words: movq %r8, (%rdi) addq $8, %rsi @@ -73,8 +66,6 @@ SYM_FUNC_START(copy_mc_fragile) movl %edx, %ecx .L_read_trailing_bytes: movb (%rsi), %al - COPY_MC_TEST_SRC %rsi 1 .E_trailing_bytes - COPY_MC_TEST_DST %rdi 1 .E_trailing_bytes .L_write_trailing_bytes: movb %al, (%rdi) incq %rsi @@ -88,7 +79,6 @@ SYM_FUNC_START(copy_mc_fragile) .L_done: ret SYM_FUNC_END(copy_mc_fragile) -EXPORT_SYMBOL_GPL(copy_mc_fragile) .section .fixup, "ax" /* diff --git a/arch/x86/lib/usercopy.c b/arch/x86/lib/usercopy.c index 3f435d7fca5e..c3e8a62ca561 100644 --- a/arch/x86/lib/usercopy.c +++ b/arch/x86/lib/usercopy.c @@ -9,9 +9,23 @@ #include <asm/tlbflush.h> -/* - * We rely on the nested NMI work to allow atomic faults from the NMI path; the - * nested NMI paths are careful to preserve CR2. +/** + * copy_from_user_nmi - NMI safe copy from user + * @to: Pointer to the destination buffer + * @from: Pointer to a user space address of the current task + * @n: Number of bytes to copy + * + * Returns: The number of not copied bytes. 0 is success, i.e. all bytes copied + * + * Contrary to other copy_from_user() variants this function can be called + * from NMI context. Despite the name it is not restricted to be called + * from NMI context. It is safe to be called from any other context as + * well. It disables pagefaults across the copy which means a fault will + * abort the copy. + * + * For NMI context invocations this relies on the nested NMI work to allow + * atomic faults from the NMI path; the nested NMI paths are careful to + * preserve CR2. */ unsigned long copy_from_user_nmi(void *to, const void __user *from, unsigned long n) @@ -27,7 +41,7 @@ copy_from_user_nmi(void *to, const void __user *from, unsigned long n) /* * Even though this function is typically called from NMI/IRQ context * disable pagefaults so that its behaviour is consistent even when - * called form other contexts. + * called from other contexts. */ pagefault_disable(); ret = __copy_from_user_inatomic(to, from, n); diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 82bf37a5c9ec..f1f1b5a0956a 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -30,6 +30,7 @@ #include <asm/cpu_entry_area.h> /* exception stack */ #include <asm/pgtable_areas.h> /* VMALLOC_START, ... */ #include <asm/kvm_para.h> /* kvm_handle_async_pf */ +#include <asm/vdso.h> /* fixup_vdso_exception() */ #define CREATE_TRACE_POINTS #include <asm/trace/exceptions.h> @@ -602,11 +603,9 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code, oops_end(flags, regs, sig); } -static void set_signal_archinfo(unsigned long address, - unsigned long error_code) +static void sanitize_error_code(unsigned long address, + unsigned long *error_code) { - struct task_struct *tsk = current; - /* * To avoid leaking information about the kernel page * table layout, pretend that user-mode accesses to @@ -617,7 +616,13 @@ static void set_signal_archinfo(unsigned long address, * information and does not appear to cause any problems. */ if (address >= TASK_SIZE_MAX) - error_code |= X86_PF_PROT; + *error_code |= X86_PF_PROT; +} + +static void set_signal_archinfo(unsigned long address, + unsigned long error_code) +{ + struct task_struct *tsk = current; tsk->thread.trap_nr = X86_TRAP_PF; tsk->thread.error_code = error_code | X86_PF_USER; @@ -658,6 +663,8 @@ no_context(struct pt_regs *regs, unsigned long error_code, * faulting through the emulate_vsyscall() logic. */ if (current->thread.sig_on_uaccess_err && signal) { + sanitize_error_code(address, &error_code); + set_signal_archinfo(address, error_code); /* XXX: hwpoison faults will set the wrong code. */ @@ -806,13 +813,10 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, if (is_errata100(regs, address)) return; - /* - * To avoid leaking information about the kernel page table - * layout, pretend that user-mode accesses to kernel addresses - * are always protection faults. - */ - if (address >= TASK_SIZE_MAX) - error_code |= X86_PF_PROT; + sanitize_error_code(address, &error_code); + + if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address)) + return; if (likely(show_unhandled_signals)) show_signal_msg(regs, error_code, address, tsk); @@ -931,6 +935,11 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, if (is_prefetch(regs, error_code, address)) return; + sanitize_error_code(address, &error_code); + + if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address)) + return; + set_signal_archinfo(address, error_code); #ifdef CONFIG_MEMORY_FAILURE @@ -1102,6 +1111,18 @@ access_error(unsigned long error_code, struct vm_area_struct *vma) return 1; /* + * SGX hardware blocked the access. This usually happens + * when the enclave memory contents have been destroyed, like + * after a suspend/resume cycle. In any case, the kernel can't + * fix the cause of the fault. Handle the fault as an access + * error even in cases where no actual access violation + * occurred. This allows userspace to rebuild the enclave in + * response to the signal. + */ + if (unlikely(error_code & X86_PF_SGX)) + return 1; + + /* * Make sure to check the VMA so that we do not perform * faults just to hit a X86_PF_PK as soon as we fill in a * page. diff --git a/arch/x86/mm/ident_map.c b/arch/x86/mm/ident_map.c index fe7a12599d8e..968d7005f4a7 100644 --- a/arch/x86/mm/ident_map.c +++ b/arch/x86/mm/ident_map.c @@ -62,6 +62,7 @@ static int ident_p4d_init(struct x86_mapping_info *info, p4d_t *p4d_page, unsigned long addr, unsigned long end) { unsigned long next; + int result; for (; addr < end; addr = next) { p4d_t *p4d = p4d_page + p4d_index(addr); @@ -73,13 +74,20 @@ static int ident_p4d_init(struct x86_mapping_info *info, p4d_t *p4d_page, if (p4d_present(*p4d)) { pud = pud_offset(p4d, 0); - ident_pud_init(info, pud, addr, next); + result = ident_pud_init(info, pud, addr, next); + if (result) + return result; + continue; } pud = (pud_t *)info->alloc_pgt_page(info->context); if (!pud) return -ENOMEM; - ident_pud_init(info, pud, addr, next); + + result = ident_pud_init(info, pud, addr, next); + if (result) + return result; + set_p4d(p4d, __p4d(__pa(pud) | info->kernpg_flag)); } diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index c7a47603537f..e26f5c5c6565 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -596,7 +596,7 @@ static unsigned long __init get_new_step_size(unsigned long step_size) static void __init memory_map_top_down(unsigned long map_start, unsigned long map_end) { - unsigned long real_end, start, last_start; + unsigned long real_end, last_start; unsigned long step_size; unsigned long addr; unsigned long mapped_ram_size = 0; @@ -609,7 +609,7 @@ static void __init memory_map_top_down(unsigned long map_start, step_size = PMD_SIZE; max_pfn_mapped = 0; /* will get exact value next */ min_pfn_mapped = real_end >> PAGE_SHIFT; - last_start = start = real_end; + last_start = real_end; /* * We start from the top (end of memory) and go to the bottom. @@ -618,6 +618,8 @@ static void __init memory_map_top_down(unsigned long map_start, * for page table. */ while (last_start > map_start) { + unsigned long start; + if (last_start > step_size) { start = round_down(last_start - 1, step_size); if (start < map_start) diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index a2488b6e27d6..1d8391fcca68 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c @@ -49,7 +49,7 @@ x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) struct stack_frame_ia32 *head; /* User process is IA32 */ - if (!current || !test_thread_flag(TIF_IA32)) + if (!current || user_64bit_mode(regs)) return 0; head = (struct stack_frame_ia32 *) regs->bp; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index fa855bbaebaf..f2f4a5d50b27 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -366,9 +366,9 @@ static int __init pcibios_assign_resources(void) return 0; } -/** - * called in fs_initcall (one below subsys_initcall), - * give a chance for motherboard reserve resources +/* + * This is an fs_initcall (one below subsys_initcall) in order to reserve + * resources properly. */ fs_initcall(pcibios_assign_resources); diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 6fa42e9c4e6f..234998f196d4 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -425,7 +425,7 @@ static acpi_status find_mboard_resource(acpi_handle handle, u32 lvl, return AE_OK; } -static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) +static bool is_acpi_reserved(u64 start, u64 end, enum e820_type not_used) { struct resource mcfg_res; @@ -442,7 +442,7 @@ static bool is_acpi_reserved(u64 start, u64 end, unsigned not_used) return mcfg_res.flags; } -typedef bool (*check_reserved_t)(u64 start, u64 end, unsigned type); +typedef bool (*check_reserved_t)(u64 start, u64 end, enum e820_type type); static bool __ref is_mmconf_reserved(check_reserved_t is_reserved, struct pci_mmcfg_region *cfg, diff --git a/arch/x86/platform/uv/Makefile b/arch/x86/platform/uv/Makefile index 224ff0504890..1441dda8edf7 100644 --- a/arch/x86/platform/uv/Makefile +++ b/arch/x86/platform/uv/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_X86_UV) += bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o +obj-$(CONFIG_X86_UV) += bios_uv.o uv_irq.o uv_time.o uv_nmi.o diff --git a/arch/x86/platform/uv/bios_uv.c b/arch/x86/platform/uv/bios_uv.c index 54511eaccf4d..bf31af3d32d6 100644 --- a/arch/x86/platform/uv/bios_uv.c +++ b/arch/x86/platform/uv/bios_uv.c @@ -72,6 +72,7 @@ static s64 uv_bios_call_irqsave(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, long sn_partition_id; EXPORT_SYMBOL_GPL(sn_partition_id); long sn_coherency_id; +EXPORT_SYMBOL_GPL(sn_coherency_id); long sn_region_size; EXPORT_SYMBOL_GPL(sn_region_size); long system_serial_number; @@ -171,6 +172,60 @@ int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus) (u64)decode, (u64)domain, (u64)bus, 0, 0); } +extern s64 uv_bios_get_master_nasid(u64 size, u64 *master_nasid) +{ + return uv_bios_call(UV_BIOS_EXTRA, 0, UV_BIOS_EXTRA_MASTER_NASID, 0, + size, (u64)master_nasid); +} +EXPORT_SYMBOL_GPL(uv_bios_get_master_nasid); + +extern s64 uv_bios_get_heapsize(u64 nasid, u64 size, u64 *heap_size) +{ + return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_GET_HEAPSIZE, + 0, size, (u64)heap_size); +} +EXPORT_SYMBOL_GPL(uv_bios_get_heapsize); + +extern s64 uv_bios_install_heap(u64 nasid, u64 heap_size, u64 *bios_heap) +{ + return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_INSTALL_HEAP, + 0, heap_size, (u64)bios_heap); +} +EXPORT_SYMBOL_GPL(uv_bios_install_heap); + +extern s64 uv_bios_obj_count(u64 nasid, u64 size, u64 *objcnt) +{ + return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_OBJECT_COUNT, + 0, size, (u64)objcnt); +} +EXPORT_SYMBOL_GPL(uv_bios_obj_count); + +extern s64 uv_bios_enum_objs(u64 nasid, u64 size, u64 *objbuf) +{ + return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_ENUM_OBJECTS, + 0, size, (u64)objbuf); +} +EXPORT_SYMBOL_GPL(uv_bios_enum_objs); + +extern s64 uv_bios_enum_ports(u64 nasid, u64 obj_id, u64 size, u64 *portbuf) +{ + return uv_bios_call(UV_BIOS_EXTRA, nasid, UV_BIOS_EXTRA_ENUM_PORTS, + obj_id, size, (u64)portbuf); +} +EXPORT_SYMBOL_GPL(uv_bios_enum_ports); + +extern s64 uv_bios_get_geoinfo(u64 nasid, u64 size, u64 *buf) +{ + return uv_bios_call(UV_BIOS_GET_GEOINFO, nasid, (u64)buf, size, 0, 0); +} +EXPORT_SYMBOL_GPL(uv_bios_get_geoinfo); + +extern s64 uv_bios_get_pci_topology(u64 size, u64 *buf) +{ + return uv_bios_call(UV_BIOS_GET_PCI_TOPOLOGY, (u64)buf, size, 0, 0, 0); +} +EXPORT_SYMBOL_GPL(uv_bios_get_pci_topology); + unsigned long get_uv_systab_phys(bool msg) { if ((uv_systab_phys == EFI_INVALID_TABLE_ADDR) || diff --git a/arch/x86/platform/uv/uv_sysfs.c b/arch/x86/platform/uv/uv_sysfs.c deleted file mode 100644 index 266773e2fb37..000000000000 --- a/arch/x86/platform/uv/uv_sysfs.c +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * This file supports the /sys/firmware/sgi_uv interfaces for SGI UV. - * - * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. - * Copyright (c) Russ Anderson - */ - -#include <linux/device.h> -#include <asm/uv/bios.h> -#include <asm/uv/uv.h> - -struct kobject *sgi_uv_kobj; - -static ssize_t partition_id_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%ld\n", sn_partition_id); -} - -static ssize_t coherence_id_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%ld\n", sn_coherency_id); -} - -static struct kobj_attribute partition_id_attr = - __ATTR(partition_id, S_IRUGO, partition_id_show, NULL); - -static struct kobj_attribute coherence_id_attr = - __ATTR(coherence_id, S_IRUGO, coherence_id_show, NULL); - - -static int __init sgi_uv_sysfs_init(void) -{ - unsigned long ret; - - if (!is_uv_system()) - return -ENODEV; - - if (!sgi_uv_kobj) - sgi_uv_kobj = kobject_create_and_add("sgi_uv", firmware_kobj); - if (!sgi_uv_kobj) { - printk(KERN_WARNING "kobject_create_and_add sgi_uv failed\n"); - return -EINVAL; - } - - ret = sysfs_create_file(sgi_uv_kobj, &partition_id_attr.attr); - if (ret) { - printk(KERN_WARNING "sysfs_create_file partition_id failed\n"); - return ret; - } - - ret = sysfs_create_file(sgi_uv_kobj, &coherence_id_attr.attr); - if (ret) { - printk(KERN_WARNING "sysfs_create_file coherence_id failed\n"); - return ret; - } - - return 0; -} - -device_initcall(sgi_uv_sysfs_init); diff --git a/arch/x86/purgatory/purgatory.c b/arch/x86/purgatory/purgatory.c index 7b37a412f829..f03b64d9cb51 100644 --- a/arch/x86/purgatory/purgatory.c +++ b/arch/x86/purgatory/purgatory.c @@ -9,7 +9,7 @@ */ #include <linux/bug.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/purgatory.h> #include "../boot/string.h" diff --git a/crypto/Kconfig b/crypto/Kconfig index 094ef56ab7b4..a367fcfeb5d4 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -145,7 +145,7 @@ config CRYPTO_MANAGER_DISABLE_TESTS config CRYPTO_MANAGER_EXTRA_TESTS bool "Enable extra run-time crypto self tests" - depends on DEBUG_KERNEL && !CRYPTO_MANAGER_DISABLE_TESTS + depends on DEBUG_KERNEL && !CRYPTO_MANAGER_DISABLE_TESTS && CRYPTO_MANAGER help Enable extra run-time self tests of registered crypto algorithms, including randomized fuzz tests. @@ -201,7 +201,7 @@ config CRYPTO_AUTHENC config CRYPTO_TEST tristate "Testing module" - depends on m + depends on m || EXPERT select CRYPTO_MANAGER help Quick & dirty crypto test module. diff --git a/crypto/aegis128-core.c b/crypto/aegis128-core.c index 44fb4956f0dd..89dc1c559689 100644 --- a/crypto/aegis128-core.c +++ b/crypto/aegis128-core.c @@ -67,9 +67,11 @@ void crypto_aegis128_encrypt_chunk_simd(struct aegis_state *state, u8 *dst, const u8 *src, unsigned int size); void crypto_aegis128_decrypt_chunk_simd(struct aegis_state *state, u8 *dst, const u8 *src, unsigned int size); -void crypto_aegis128_final_simd(struct aegis_state *state, - union aegis_block *tag_xor, - u64 assoclen, u64 cryptlen); +int crypto_aegis128_final_simd(struct aegis_state *state, + union aegis_block *tag_xor, + unsigned int assoclen, + unsigned int cryptlen, + unsigned int authsize); static void crypto_aegis128_update(struct aegis_state *state) { @@ -84,9 +86,10 @@ static void crypto_aegis128_update(struct aegis_state *state) } static void crypto_aegis128_update_a(struct aegis_state *state, - const union aegis_block *msg) + const union aegis_block *msg, + bool do_simd) { - if (aegis128_do_simd()) { + if (IS_ENABLED(CONFIG_CRYPTO_AEGIS128_SIMD) && do_simd) { crypto_aegis128_update_simd(state, msg); return; } @@ -95,9 +98,10 @@ static void crypto_aegis128_update_a(struct aegis_state *state, crypto_aegis_block_xor(&state->blocks[0], msg); } -static void crypto_aegis128_update_u(struct aegis_state *state, const void *msg) +static void crypto_aegis128_update_u(struct aegis_state *state, const void *msg, + bool do_simd) { - if (aegis128_do_simd()) { + if (IS_ENABLED(CONFIG_CRYPTO_AEGIS128_SIMD) && do_simd) { crypto_aegis128_update_simd(state, msg); return; } @@ -126,27 +130,28 @@ static void crypto_aegis128_init(struct aegis_state *state, crypto_aegis_block_xor(&state->blocks[4], &crypto_aegis_const[1]); for (i = 0; i < 5; i++) { - crypto_aegis128_update_a(state, key); - crypto_aegis128_update_a(state, &key_iv); + crypto_aegis128_update_a(state, key, false); + crypto_aegis128_update_a(state, &key_iv, false); } } static void crypto_aegis128_ad(struct aegis_state *state, - const u8 *src, unsigned int size) + const u8 *src, unsigned int size, + bool do_simd) { if (AEGIS_ALIGNED(src)) { const union aegis_block *src_blk = (const union aegis_block *)src; while (size >= AEGIS_BLOCK_SIZE) { - crypto_aegis128_update_a(state, src_blk); + crypto_aegis128_update_a(state, src_blk, do_simd); size -= AEGIS_BLOCK_SIZE; src_blk++; } } else { while (size >= AEGIS_BLOCK_SIZE) { - crypto_aegis128_update_u(state, src); + crypto_aegis128_update_u(state, src, do_simd); size -= AEGIS_BLOCK_SIZE; src += AEGIS_BLOCK_SIZE; @@ -154,6 +159,12 @@ static void crypto_aegis128_ad(struct aegis_state *state, } } +static void crypto_aegis128_wipe_chunk(struct aegis_state *state, u8 *dst, + const u8 *src, unsigned int size) +{ + memzero_explicit(dst, size); +} + static void crypto_aegis128_encrypt_chunk(struct aegis_state *state, u8 *dst, const u8 *src, unsigned int size) { @@ -172,7 +183,7 @@ static void crypto_aegis128_encrypt_chunk(struct aegis_state *state, u8 *dst, crypto_aegis_block_xor(&tmp, &state->blocks[1]); crypto_aegis_block_xor(&tmp, src_blk); - crypto_aegis128_update_a(state, src_blk); + crypto_aegis128_update_a(state, src_blk, false); *dst_blk = tmp; @@ -188,7 +199,7 @@ static void crypto_aegis128_encrypt_chunk(struct aegis_state *state, u8 *dst, crypto_aegis_block_xor(&tmp, &state->blocks[1]); crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); - crypto_aegis128_update_u(state, src); + crypto_aegis128_update_u(state, src, false); memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); @@ -207,7 +218,7 @@ static void crypto_aegis128_encrypt_chunk(struct aegis_state *state, u8 *dst, crypto_aegis_block_xor(&tmp, &state->blocks[4]); crypto_aegis_block_xor(&tmp, &state->blocks[1]); - crypto_aegis128_update_a(state, &msg); + crypto_aegis128_update_a(state, &msg, false); crypto_aegis_block_xor(&msg, &tmp); @@ -233,7 +244,7 @@ static void crypto_aegis128_decrypt_chunk(struct aegis_state *state, u8 *dst, crypto_aegis_block_xor(&tmp, &state->blocks[1]); crypto_aegis_block_xor(&tmp, src_blk); - crypto_aegis128_update_a(state, &tmp); + crypto_aegis128_update_a(state, &tmp, false); *dst_blk = tmp; @@ -249,7 +260,7 @@ static void crypto_aegis128_decrypt_chunk(struct aegis_state *state, u8 *dst, crypto_aegis_block_xor(&tmp, &state->blocks[1]); crypto_xor(tmp.bytes, src, AEGIS_BLOCK_SIZE); - crypto_aegis128_update_a(state, &tmp); + crypto_aegis128_update_a(state, &tmp, false); memcpy(dst, tmp.bytes, AEGIS_BLOCK_SIZE); @@ -271,7 +282,7 @@ static void crypto_aegis128_decrypt_chunk(struct aegis_state *state, u8 *dst, memset(msg.bytes + size, 0, AEGIS_BLOCK_SIZE - size); - crypto_aegis128_update_a(state, &msg); + crypto_aegis128_update_a(state, &msg, false); memcpy(dst, msg.bytes, size); } @@ -279,7 +290,8 @@ static void crypto_aegis128_decrypt_chunk(struct aegis_state *state, u8 *dst, static void crypto_aegis128_process_ad(struct aegis_state *state, struct scatterlist *sg_src, - unsigned int assoclen) + unsigned int assoclen, + bool do_simd) { struct scatter_walk walk; union aegis_block buf; @@ -296,13 +308,13 @@ static void crypto_aegis128_process_ad(struct aegis_state *state, if (pos > 0) { unsigned int fill = AEGIS_BLOCK_SIZE - pos; memcpy(buf.bytes + pos, src, fill); - crypto_aegis128_update_a(state, &buf); + crypto_aegis128_update_a(state, &buf, do_simd); pos = 0; left -= fill; src += fill; } - crypto_aegis128_ad(state, src, left); + crypto_aegis128_ad(state, src, left, do_simd); src += left & ~(AEGIS_BLOCK_SIZE - 1); left &= AEGIS_BLOCK_SIZE - 1; } @@ -318,13 +330,12 @@ static void crypto_aegis128_process_ad(struct aegis_state *state, if (pos > 0) { memset(buf.bytes + pos, 0, AEGIS_BLOCK_SIZE - pos); - crypto_aegis128_update_a(state, &buf); + crypto_aegis128_update_a(state, &buf, do_simd); } } static __always_inline int crypto_aegis128_process_crypt(struct aegis_state *state, - struct aead_request *req, struct skcipher_walk *walk, void (*crypt)(struct aegis_state *state, u8 *dst, const u8 *src, @@ -361,7 +372,7 @@ static void crypto_aegis128_final(struct aegis_state *state, crypto_aegis_block_xor(&tmp, &state->blocks[3]); for (i = 0; i < 7; i++) - crypto_aegis128_update_a(state, &tmp); + crypto_aegis128_update_a(state, &tmp, false); for (i = 0; i < AEGIS128_STATE_BLOCKS; i++) crypto_aegis_block_xor(tag_xor, &state->blocks[i]); @@ -389,7 +400,7 @@ static int crypto_aegis128_setauthsize(struct crypto_aead *tfm, return 0; } -static int crypto_aegis128_encrypt(struct aead_request *req) +static int crypto_aegis128_encrypt_generic(struct aead_request *req) { struct crypto_aead *tfm = crypto_aead_reqtfm(req); union aegis_block tag = {}; @@ -400,27 +411,18 @@ static int crypto_aegis128_encrypt(struct aead_request *req) struct aegis_state state; skcipher_walk_aead_encrypt(&walk, req, false); - if (aegis128_do_simd()) { - crypto_aegis128_init_simd(&state, &ctx->key, req->iv); - crypto_aegis128_process_ad(&state, req->src, req->assoclen); - crypto_aegis128_process_crypt(&state, req, &walk, - crypto_aegis128_encrypt_chunk_simd); - crypto_aegis128_final_simd(&state, &tag, req->assoclen, - cryptlen); - } else { - crypto_aegis128_init(&state, &ctx->key, req->iv); - crypto_aegis128_process_ad(&state, req->src, req->assoclen); - crypto_aegis128_process_crypt(&state, req, &walk, - crypto_aegis128_encrypt_chunk); - crypto_aegis128_final(&state, &tag, req->assoclen, cryptlen); - } + crypto_aegis128_init(&state, &ctx->key, req->iv); + crypto_aegis128_process_ad(&state, req->src, req->assoclen, false); + crypto_aegis128_process_crypt(&state, &walk, + crypto_aegis128_encrypt_chunk); + crypto_aegis128_final(&state, &tag, req->assoclen, cryptlen); scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, authsize, 1); return 0; } -static int crypto_aegis128_decrypt(struct aead_request *req) +static int crypto_aegis128_decrypt_generic(struct aead_request *req) { static const u8 zeros[AEGIS128_MAX_AUTH_SIZE] = {}; struct crypto_aead *tfm = crypto_aead_reqtfm(req); @@ -435,60 +437,152 @@ static int crypto_aegis128_decrypt(struct aead_request *req) authsize, 0); skcipher_walk_aead_decrypt(&walk, req, false); - if (aegis128_do_simd()) { - crypto_aegis128_init_simd(&state, &ctx->key, req->iv); - crypto_aegis128_process_ad(&state, req->src, req->assoclen); - crypto_aegis128_process_crypt(&state, req, &walk, - crypto_aegis128_decrypt_chunk_simd); - crypto_aegis128_final_simd(&state, &tag, req->assoclen, - cryptlen); - } else { - crypto_aegis128_init(&state, &ctx->key, req->iv); - crypto_aegis128_process_ad(&state, req->src, req->assoclen); - crypto_aegis128_process_crypt(&state, req, &walk, - crypto_aegis128_decrypt_chunk); - crypto_aegis128_final(&state, &tag, req->assoclen, cryptlen); + crypto_aegis128_init(&state, &ctx->key, req->iv); + crypto_aegis128_process_ad(&state, req->src, req->assoclen, false); + crypto_aegis128_process_crypt(&state, &walk, + crypto_aegis128_decrypt_chunk); + crypto_aegis128_final(&state, &tag, req->assoclen, cryptlen); + + if (unlikely(crypto_memneq(tag.bytes, zeros, authsize))) { + /* + * From Chapter 4. 'Security Analysis' of the AEGIS spec [0] + * + * "3. If verification fails, the decrypted plaintext and the + * wrong authentication tag should not be given as output." + * + * [0] https://competitions.cr.yp.to/round3/aegisv11.pdf + */ + skcipher_walk_aead_decrypt(&walk, req, false); + crypto_aegis128_process_crypt(NULL, &walk, + crypto_aegis128_wipe_chunk); + memzero_explicit(&tag, sizeof(tag)); + return -EBADMSG; } - - return crypto_memneq(tag.bytes, zeros, authsize) ? -EBADMSG : 0; + return 0; } -static struct aead_alg crypto_aegis128_alg = { - .setkey = crypto_aegis128_setkey, - .setauthsize = crypto_aegis128_setauthsize, - .encrypt = crypto_aegis128_encrypt, - .decrypt = crypto_aegis128_decrypt, +static int crypto_aegis128_encrypt_simd(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + union aegis_block tag = {}; + unsigned int authsize = crypto_aead_authsize(tfm); + struct aegis_ctx *ctx = crypto_aead_ctx(tfm); + unsigned int cryptlen = req->cryptlen; + struct skcipher_walk walk; + struct aegis_state state; - .ivsize = AEGIS128_NONCE_SIZE, - .maxauthsize = AEGIS128_MAX_AUTH_SIZE, - .chunksize = AEGIS_BLOCK_SIZE, + if (!aegis128_do_simd()) + return crypto_aegis128_encrypt_generic(req); - .base = { - .cra_blocksize = 1, - .cra_ctxsize = sizeof(struct aegis_ctx), - .cra_alignmask = 0, + skcipher_walk_aead_encrypt(&walk, req, false); + crypto_aegis128_init_simd(&state, &ctx->key, req->iv); + crypto_aegis128_process_ad(&state, req->src, req->assoclen, true); + crypto_aegis128_process_crypt(&state, &walk, + crypto_aegis128_encrypt_chunk_simd); + crypto_aegis128_final_simd(&state, &tag, req->assoclen, cryptlen, 0); - .cra_priority = 100, + scatterwalk_map_and_copy(tag.bytes, req->dst, req->assoclen + cryptlen, + authsize, 1); + return 0; +} - .cra_name = "aegis128", - .cra_driver_name = "aegis128-generic", +static int crypto_aegis128_decrypt_simd(struct aead_request *req) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + union aegis_block tag; + unsigned int authsize = crypto_aead_authsize(tfm); + unsigned int cryptlen = req->cryptlen - authsize; + struct aegis_ctx *ctx = crypto_aead_ctx(tfm); + struct skcipher_walk walk; + struct aegis_state state; + + if (!aegis128_do_simd()) + return crypto_aegis128_decrypt_generic(req); + + scatterwalk_map_and_copy(tag.bytes, req->src, req->assoclen + cryptlen, + authsize, 0); - .cra_module = THIS_MODULE, + skcipher_walk_aead_decrypt(&walk, req, false); + crypto_aegis128_init_simd(&state, &ctx->key, req->iv); + crypto_aegis128_process_ad(&state, req->src, req->assoclen, true); + crypto_aegis128_process_crypt(&state, &walk, + crypto_aegis128_decrypt_chunk_simd); + + if (unlikely(crypto_aegis128_final_simd(&state, &tag, req->assoclen, + cryptlen, authsize))) { + skcipher_walk_aead_decrypt(&walk, req, false); + crypto_aegis128_process_crypt(NULL, &walk, + crypto_aegis128_wipe_chunk); + return -EBADMSG; } + return 0; +} + +static struct aead_alg crypto_aegis128_alg_generic = { + .setkey = crypto_aegis128_setkey, + .setauthsize = crypto_aegis128_setauthsize, + .encrypt = crypto_aegis128_encrypt_generic, + .decrypt = crypto_aegis128_decrypt_generic, + + .ivsize = AEGIS128_NONCE_SIZE, + .maxauthsize = AEGIS128_MAX_AUTH_SIZE, + .chunksize = AEGIS_BLOCK_SIZE, + + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct aegis_ctx), + .base.cra_alignmask = 0, + .base.cra_priority = 100, + .base.cra_name = "aegis128", + .base.cra_driver_name = "aegis128-generic", + .base.cra_module = THIS_MODULE, +}; + +static struct aead_alg crypto_aegis128_alg_simd = { + .setkey = crypto_aegis128_setkey, + .setauthsize = crypto_aegis128_setauthsize, + .encrypt = crypto_aegis128_encrypt_simd, + .decrypt = crypto_aegis128_decrypt_simd, + + .ivsize = AEGIS128_NONCE_SIZE, + .maxauthsize = AEGIS128_MAX_AUTH_SIZE, + .chunksize = AEGIS_BLOCK_SIZE, + + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct aegis_ctx), + .base.cra_alignmask = 0, + .base.cra_priority = 200, + .base.cra_name = "aegis128", + .base.cra_driver_name = "aegis128-simd", + .base.cra_module = THIS_MODULE, }; static int __init crypto_aegis128_module_init(void) { + int ret; + + ret = crypto_register_aead(&crypto_aegis128_alg_generic); + if (ret) + return ret; + if (IS_ENABLED(CONFIG_CRYPTO_AEGIS128_SIMD) && - crypto_aegis128_have_simd()) + crypto_aegis128_have_simd()) { + ret = crypto_register_aead(&crypto_aegis128_alg_simd); + if (ret) { + crypto_unregister_aead(&crypto_aegis128_alg_generic); + return ret; + } static_branch_enable(&have_simd); - - return crypto_register_aead(&crypto_aegis128_alg); + } + return 0; } static void __exit crypto_aegis128_module_exit(void) { - crypto_unregister_aead(&crypto_aegis128_alg); + if (IS_ENABLED(CONFIG_CRYPTO_AEGIS128_SIMD) && + crypto_aegis128_have_simd()) + crypto_unregister_aead(&crypto_aegis128_alg_simd); + + crypto_unregister_aead(&crypto_aegis128_alg_generic); } subsys_initcall(crypto_aegis128_module_init); @@ -499,3 +593,4 @@ MODULE_AUTHOR("Ondrej Mosnacek <omosnacek@gmail.com>"); MODULE_DESCRIPTION("AEGIS-128 AEAD algorithm"); MODULE_ALIAS_CRYPTO("aegis128"); MODULE_ALIAS_CRYPTO("aegis128-generic"); +MODULE_ALIAS_CRYPTO("aegis128-simd"); diff --git a/crypto/aegis128-neon-inner.c b/crypto/aegis128-neon-inner.c index 2a660ac1bc3a..7de485907d81 100644 --- a/crypto/aegis128-neon-inner.c +++ b/crypto/aegis128-neon-inner.c @@ -20,7 +20,6 @@ extern int aegis128_have_aes_insn; void *memcpy(void *dest, const void *src, size_t n); -void *memset(void *s, int c, size_t n); struct aegis128_state { uint8x16_t v[5]; @@ -173,10 +172,57 @@ void crypto_aegis128_update_neon(void *state, const void *msg) aegis128_save_state_neon(st, state); } +#ifdef CONFIG_ARM +/* + * AArch32 does not provide these intrinsics natively because it does not + * implement the underlying instructions. AArch32 only provides 64-bit + * wide vtbl.8/vtbx.8 instruction, so use those instead. + */ +static uint8x16_t vqtbl1q_u8(uint8x16_t a, uint8x16_t b) +{ + union { + uint8x16_t val; + uint8x8x2_t pair; + } __a = { a }; + + return vcombine_u8(vtbl2_u8(__a.pair, vget_low_u8(b)), + vtbl2_u8(__a.pair, vget_high_u8(b))); +} + +static uint8x16_t vqtbx1q_u8(uint8x16_t v, uint8x16_t a, uint8x16_t b) +{ + union { + uint8x16_t val; + uint8x8x2_t pair; + } __a = { a }; + + return vcombine_u8(vtbx2_u8(vget_low_u8(v), __a.pair, vget_low_u8(b)), + vtbx2_u8(vget_high_u8(v), __a.pair, vget_high_u8(b))); +} + +static int8_t vminvq_s8(int8x16_t v) +{ + int8x8_t s = vpmin_s8(vget_low_s8(v), vget_high_s8(v)); + + s = vpmin_s8(s, s); + s = vpmin_s8(s, s); + s = vpmin_s8(s, s); + + return vget_lane_s8(s, 0); +} +#endif + +static const uint8_t permute[] __aligned(64) = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + void crypto_aegis128_encrypt_chunk_neon(void *state, void *dst, const void *src, unsigned int size) { struct aegis128_state st = aegis128_load_state_neon(state); + const int short_input = size < AEGIS_BLOCK_SIZE; uint8x16_t msg; preload_sbox(); @@ -186,7 +232,8 @@ void crypto_aegis128_encrypt_chunk_neon(void *state, void *dst, const void *src, msg = vld1q_u8(src); st = aegis128_update_neon(st, msg); - vst1q_u8(dst, msg ^ s); + msg ^= s; + vst1q_u8(dst, msg); size -= AEGIS_BLOCK_SIZE; src += AEGIS_BLOCK_SIZE; @@ -195,13 +242,26 @@ void crypto_aegis128_encrypt_chunk_neon(void *state, void *dst, const void *src, if (size > 0) { uint8x16_t s = st.v[1] ^ (st.v[2] & st.v[3]) ^ st.v[4]; - uint8_t buf[AEGIS_BLOCK_SIZE] = {}; + uint8_t buf[AEGIS_BLOCK_SIZE]; + const void *in = src; + void *out = dst; + uint8x16_t m; - memcpy(buf, src, size); - msg = vld1q_u8(buf); - st = aegis128_update_neon(st, msg); - vst1q_u8(buf, msg ^ s); - memcpy(dst, buf, size); + if (__builtin_expect(short_input, 0)) + in = out = memcpy(buf + AEGIS_BLOCK_SIZE - size, src, size); + + m = vqtbl1q_u8(vld1q_u8(in + size - AEGIS_BLOCK_SIZE), + vld1q_u8(permute + 32 - size)); + + st = aegis128_update_neon(st, m); + + vst1q_u8(out + size - AEGIS_BLOCK_SIZE, + vqtbl1q_u8(m ^ s, vld1q_u8(permute + size))); + + if (__builtin_expect(short_input, 0)) + memcpy(dst, out, size); + else + vst1q_u8(out - AEGIS_BLOCK_SIZE, msg); } aegis128_save_state_neon(st, state); @@ -211,6 +271,7 @@ void crypto_aegis128_decrypt_chunk_neon(void *state, void *dst, const void *src, unsigned int size) { struct aegis128_state st = aegis128_load_state_neon(state); + const int short_input = size < AEGIS_BLOCK_SIZE; uint8x16_t msg; preload_sbox(); @@ -228,21 +289,34 @@ void crypto_aegis128_decrypt_chunk_neon(void *state, void *dst, const void *src, if (size > 0) { uint8x16_t s = st.v[1] ^ (st.v[2] & st.v[3]) ^ st.v[4]; uint8_t buf[AEGIS_BLOCK_SIZE]; + const void *in = src; + void *out = dst; + uint8x16_t m; - vst1q_u8(buf, s); - memcpy(buf, src, size); - msg = vld1q_u8(buf) ^ s; - vst1q_u8(buf, msg); - memcpy(dst, buf, size); + if (__builtin_expect(short_input, 0)) + in = out = memcpy(buf + AEGIS_BLOCK_SIZE - size, src, size); - st = aegis128_update_neon(st, msg); + m = s ^ vqtbx1q_u8(s, vld1q_u8(in + size - AEGIS_BLOCK_SIZE), + vld1q_u8(permute + 32 - size)); + + st = aegis128_update_neon(st, m); + + vst1q_u8(out + size - AEGIS_BLOCK_SIZE, + vqtbl1q_u8(m, vld1q_u8(permute + size))); + + if (__builtin_expect(short_input, 0)) + memcpy(dst, out, size); + else + vst1q_u8(out - AEGIS_BLOCK_SIZE, msg); } aegis128_save_state_neon(st, state); } -void crypto_aegis128_final_neon(void *state, void *tag_xor, uint64_t assoclen, - uint64_t cryptlen) +int crypto_aegis128_final_neon(void *state, void *tag_xor, + unsigned int assoclen, + unsigned int cryptlen, + unsigned int authsize) { struct aegis128_state st = aegis128_load_state_neon(state); uint8x16_t v; @@ -250,13 +324,21 @@ void crypto_aegis128_final_neon(void *state, void *tag_xor, uint64_t assoclen, preload_sbox(); - v = st.v[3] ^ (uint8x16_t)vcombine_u64(vmov_n_u64(8 * assoclen), - vmov_n_u64(8 * cryptlen)); + v = st.v[3] ^ (uint8x16_t)vcombine_u64(vmov_n_u64(8ULL * assoclen), + vmov_n_u64(8ULL * cryptlen)); for (i = 0; i < 7; i++) st = aegis128_update_neon(st, v); - v = vld1q_u8(tag_xor); - v ^= st.v[0] ^ st.v[1] ^ st.v[2] ^ st.v[3] ^ st.v[4]; + v = st.v[0] ^ st.v[1] ^ st.v[2] ^ st.v[3] ^ st.v[4]; + + if (authsize > 0) { + v = vqtbl1q_u8(~vceqq_u8(v, vld1q_u8(tag_xor)), + vld1q_u8(permute + authsize)); + + return vminvq_s8((int8x16_t)v); + } + vst1q_u8(tag_xor, v); + return 0; } diff --git a/crypto/aegis128-neon.c b/crypto/aegis128-neon.c index 8271b1fa0fbc..94d591a002a4 100644 --- a/crypto/aegis128-neon.c +++ b/crypto/aegis128-neon.c @@ -14,8 +14,10 @@ void crypto_aegis128_encrypt_chunk_neon(void *state, void *dst, const void *src, unsigned int size); void crypto_aegis128_decrypt_chunk_neon(void *state, void *dst, const void *src, unsigned int size); -void crypto_aegis128_final_neon(void *state, void *tag_xor, uint64_t assoclen, - uint64_t cryptlen); +int crypto_aegis128_final_neon(void *state, void *tag_xor, + unsigned int assoclen, + unsigned int cryptlen, + unsigned int authsize); int aegis128_have_aes_insn __ro_after_init; @@ -60,11 +62,18 @@ void crypto_aegis128_decrypt_chunk_simd(union aegis_block *state, u8 *dst, kernel_neon_end(); } -void crypto_aegis128_final_simd(union aegis_block *state, - union aegis_block *tag_xor, - u64 assoclen, u64 cryptlen) +int crypto_aegis128_final_simd(union aegis_block *state, + union aegis_block *tag_xor, + unsigned int assoclen, + unsigned int cryptlen, + unsigned int authsize) { + int ret; + kernel_neon_begin(); - crypto_aegis128_final_neon(state, tag_xor, assoclen, cryptlen); + ret = crypto_aegis128_final_neon(state, tag_xor, assoclen, cryptlen, + authsize); kernel_neon_end(); + + return ret; } diff --git a/crypto/af_alg.c b/crypto/af_alg.c index d11db80d24cd..9acb9d2c4bcf 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -147,7 +147,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY; struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); - struct sockaddr_alg *sa = (void *)uaddr; + struct sockaddr_alg_new *sa = (void *)uaddr; const struct af_alg_type *type; void *private; int err; @@ -155,7 +155,11 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (sock->state == SS_CONNECTED) return -EINVAL; - if (addr_len < sizeof(*sa)) + BUILD_BUG_ON(offsetof(struct sockaddr_alg_new, salg_name) != + offsetof(struct sockaddr_alg, salg_name)); + BUILD_BUG_ON(offsetof(struct sockaddr_alg, salg_name) != sizeof(*sa)); + + if (addr_len < sizeof(*sa) + 1) return -EINVAL; /* If caller uses non-allowed flag, return error. */ @@ -163,7 +167,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return -EINVAL; sa->salg_type[sizeof(sa->salg_type) - 1] = 0; - sa->salg_name[sizeof(sa->salg_name) + addr_len - sizeof(*sa) - 1] = 0; + sa->salg_name[addr_len - sizeof(*sa) - 1] = 0; type = alg_get_type(sa->salg_type); if (PTR_ERR(type) == -ENOENT) { diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c index 378b18b9bc34..511932aa94a6 100644 --- a/crypto/asymmetric_keys/asym_tpm.c +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -10,7 +10,7 @@ #include <linux/tpm_command.h> #include <crypto/akcipher.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/unaligned.h> #include <keys/asymmetric-subtype.h> #include <keys/trusted_tpm.h> diff --git a/crypto/ecdh.c b/crypto/ecdh.c index b0232d6ab4ce..d56b8603dec9 100644 --- a/crypto/ecdh.c +++ b/crypto/ecdh.c @@ -53,12 +53,13 @@ static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf, return ecc_gen_privkey(ctx->curve_id, ctx->ndigits, ctx->private_key); - if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits, - (const u64 *)params.key, params.key_size) < 0) - return -EINVAL; - memcpy(ctx->private_key, params.key, params.key_size); + if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits, + ctx->private_key, params.key_size) < 0) { + memzero_explicit(ctx->private_key, params.key_size); + return -EINVAL; + } return 0; } diff --git a/crypto/seed.c b/crypto/seed.c index 5e3bef3a617d..27720140820e 100644 --- a/crypto/seed.c +++ b/crypto/seed.c @@ -322,7 +322,7 @@ static const u32 KC[SEED_NUM_KCONSTANTS] = { SS2[byte(t1, 2)] ^ SS3[byte(t1, 3)]; \ t0 += t1; \ X1 ^= t0; \ - X2 ^= t1; + X2 ^= t1 static int seed_set_key(struct crypto_tfm *tfm, const u8 *in_key, unsigned int key_len) diff --git a/crypto/sha1_generic.c b/crypto/sha1_generic.c index 1d43472fecbd..325b57fe28dc 100644 --- a/crypto/sha1_generic.c +++ b/crypto/sha1_generic.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/sha1_base.h> #include <asm/byteorder.h> diff --git a/crypto/sha256_generic.c b/crypto/sha256_generic.c index 88156e3e2a33..3b377197236e 100644 --- a/crypto/sha256_generic.c +++ b/crypto/sha256_generic.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha256_base.h> #include <asm/byteorder.h> #include <asm/unaligned.h> diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index e34d09dd9971..c72d72ad828e 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/crypto.h> #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/sha512_base.h> #include <linux/percpu.h> #include <asm/byteorder.h> diff --git a/crypto/sm2.c b/crypto/sm2.c index 767e160333f6..b21addc3ac06 100644 --- a/crypto/sm2.c +++ b/crypto/sm2.c @@ -119,12 +119,6 @@ static void sm2_ec_ctx_deinit(struct mpi_ec_ctx *ec) memset(ec, 0, sizeof(*ec)); } -static int sm2_ec_ctx_reset(struct mpi_ec_ctx *ec) -{ - sm2_ec_ctx_deinit(ec); - return sm2_ec_ctx_init(ec); -} - /* RESULT must have been initialized and is set on success to the * point given by VALUE. */ @@ -132,55 +126,48 @@ static int sm2_ecc_os2ec(MPI_POINT result, MPI value) { int rc; size_t n; - const unsigned char *buf; - unsigned char *buf_memory; + unsigned char *buf; MPI x, y; - n = (mpi_get_nbits(value)+7)/8; - buf_memory = kmalloc(n, GFP_KERNEL); - rc = mpi_print(GCRYMPI_FMT_USG, buf_memory, n, &n, value); - if (rc) { - kfree(buf_memory); - return rc; - } - buf = buf_memory; + n = MPI_NBYTES(value); + buf = kmalloc(n, GFP_KERNEL); + if (!buf) + return -ENOMEM; - if (n < 1) { - kfree(buf_memory); - return -EINVAL; - } - if (*buf != 4) { - kfree(buf_memory); - return -EINVAL; /* No support for point compression. */ - } - if (((n-1)%2)) { - kfree(buf_memory); - return -EINVAL; - } - n = (n-1)/2; + rc = mpi_print(GCRYMPI_FMT_USG, buf, n, &n, value); + if (rc) + goto err_freebuf; + + rc = -EINVAL; + if (n < 1 || ((n - 1) % 2)) + goto err_freebuf; + /* No support for point compression */ + if (*buf != 0x4) + goto err_freebuf; + + rc = -ENOMEM; + n = (n - 1) / 2; x = mpi_read_raw_data(buf + 1, n); - if (!x) { - kfree(buf_memory); - return -ENOMEM; - } + if (!x) + goto err_freebuf; y = mpi_read_raw_data(buf + 1 + n, n); - kfree(buf_memory); - if (!y) { - mpi_free(x); - return -ENOMEM; - } + if (!y) + goto err_freex; mpi_normalize(x); mpi_normalize(y); - mpi_set(result->x, x); mpi_set(result->y, y); mpi_set_ui(result->z, 1); - mpi_free(x); - mpi_free(y); + rc = 0; - return 0; + mpi_free(y); +err_freex: + mpi_free(x); +err_freebuf: + kfree(buf); + return rc; } struct sm2_signature_ctx { @@ -399,10 +386,6 @@ static int sm2_set_pub_key(struct crypto_akcipher *tfm, MPI a; int rc; - rc = sm2_ec_ctx_reset(ec); - if (rc) - return rc; - ec->Q = mpi_point_new(0); if (!ec->Q) return -ENOMEM; diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index eea0f453cfb6..a647bb298fbc 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -77,8 +77,8 @@ static const char *check[] = { NULL }; -static u32 block_sizes[] = { 16, 64, 256, 1024, 1472, 8192, 0 }; -static u32 aead_sizes[] = { 16, 64, 256, 512, 1024, 2048, 4096, 8192, 0 }; +static const int block_sizes[] = { 16, 64, 256, 1024, 1420, 4096, 0 }; +static const int aead_sizes[] = { 16, 64, 256, 512, 1024, 1420, 4096, 8192, 0 }; #define XBUFSIZE 8 #define MAX_IVLEN 32 @@ -256,10 +256,10 @@ static void test_mb_aead_speed(const char *algo, int enc, int secs, struct test_mb_aead_data *data; struct crypto_aead *tfm; unsigned int i, j, iv_len; + const int *b_size; const char *key; const char *e; void *assoc; - u32 *b_size; char *iv; int ret; @@ -337,15 +337,17 @@ static void test_mb_aead_speed(const char *algo, int enc, int secs, do { b_size = aead_sizes; do { - if (*b_size + authsize > XBUFSIZE * PAGE_SIZE) { + int bs = round_up(*b_size, crypto_aead_blocksize(tfm)); + + if (bs + authsize > XBUFSIZE * PAGE_SIZE) { pr_err("template (%u) too big for buffer (%lu)\n", - authsize + *b_size, + authsize + bs, XBUFSIZE * PAGE_SIZE); goto out; } pr_info("test %u (%d bit key, %d byte blocks): ", i, - *keysize * 8, *b_size); + *keysize * 8, bs); /* Set up tfm global state, i.e. the key */ @@ -380,11 +382,11 @@ static void test_mb_aead_speed(const char *algo, int enc, int secs, memset(assoc, 0xff, aad_size); sg_init_aead(cur->sg, cur->xbuf, - *b_size + (enc ? 0 : authsize), + bs + (enc ? 0 : authsize), assoc, aad_size); sg_init_aead(cur->sgout, cur->xoutbuf, - *b_size + (enc ? authsize : 0), + bs + (enc ? authsize : 0), assoc, aad_size); aead_request_set_ad(cur->req, aad_size); @@ -394,7 +396,7 @@ static void test_mb_aead_speed(const char *algo, int enc, int secs, aead_request_set_crypt(cur->req, cur->sgout, cur->sg, - *b_size, iv); + bs, iv); ret = crypto_aead_encrypt(cur->req); ret = do_one_aead_op(cur->req, ret); @@ -406,18 +408,18 @@ static void test_mb_aead_speed(const char *algo, int enc, int secs, } aead_request_set_crypt(cur->req, cur->sg, - cur->sgout, *b_size + + cur->sgout, bs + (enc ? 0 : authsize), iv); } if (secs) { - ret = test_mb_aead_jiffies(data, enc, *b_size, + ret = test_mb_aead_jiffies(data, enc, bs, secs, num_mb); cond_resched(); } else { - ret = test_mb_aead_cycles(data, enc, *b_size, + ret = test_mb_aead_cycles(data, enc, bs, num_mb); } @@ -534,7 +536,7 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, char *xbuf[XBUFSIZE]; char *xoutbuf[XBUFSIZE]; char *axbuf[XBUFSIZE]; - unsigned int *b_size; + const int *b_size; unsigned int iv_len; struct crypto_wait wait; @@ -590,12 +592,14 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, do { b_size = aead_sizes; do { + u32 bs = round_up(*b_size, crypto_aead_blocksize(tfm)); + assoc = axbuf[0]; memset(assoc, 0xff, aad_size); - if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) { + if ((*keysize + bs) > TVMEMSIZE * PAGE_SIZE) { pr_err("template (%u) too big for tvmem (%lu)\n", - *keysize + *b_size, + *keysize + bs, TVMEMSIZE * PAGE_SIZE); goto out; } @@ -616,7 +620,7 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, crypto_aead_clear_flags(tfm, ~0); printk(KERN_INFO "test %u (%d bit key, %d byte blocks): ", - i, *keysize * 8, *b_size); + i, *keysize * 8, bs); memset(tvmem[0], 0xff, PAGE_SIZE); @@ -627,11 +631,11 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, goto out; } - sg_init_aead(sg, xbuf, *b_size + (enc ? 0 : authsize), + sg_init_aead(sg, xbuf, bs + (enc ? 0 : authsize), assoc, aad_size); sg_init_aead(sgout, xoutbuf, - *b_size + (enc ? authsize : 0), assoc, + bs + (enc ? authsize : 0), assoc, aad_size); aead_request_set_ad(req, aad_size); @@ -644,7 +648,7 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, * reversed (input <-> output) to calculate it */ aead_request_set_crypt(req, sgout, sg, - *b_size, iv); + bs, iv); ret = do_one_aead_op(req, crypto_aead_encrypt(req)); @@ -656,15 +660,15 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs, } aead_request_set_crypt(req, sg, sgout, - *b_size + (enc ? 0 : authsize), + bs + (enc ? 0 : authsize), iv); if (secs) { - ret = test_aead_jiffies(req, enc, *b_size, + ret = test_aead_jiffies(req, enc, bs, secs); cond_resched(); } else { - ret = test_aead_cycles(req, enc, *b_size); + ret = test_aead_cycles(req, enc, bs); } if (ret) { @@ -1253,9 +1257,9 @@ static void test_mb_skcipher_speed(const char *algo, int enc, int secs, struct test_mb_skcipher_data *data; struct crypto_skcipher *tfm; unsigned int i, j, iv_len; + const int *b_size; const char *key; const char *e; - u32 *b_size; char iv[128]; int ret; @@ -1316,14 +1320,16 @@ static void test_mb_skcipher_speed(const char *algo, int enc, int secs, do { b_size = block_sizes; do { - if (*b_size > XBUFSIZE * PAGE_SIZE) { + u32 bs = round_up(*b_size, crypto_skcipher_blocksize(tfm)); + + if (bs > XBUFSIZE * PAGE_SIZE) { pr_err("template (%u) too big for buffer (%lu)\n", *b_size, XBUFSIZE * PAGE_SIZE); goto out; } pr_info("test %u (%d bit key, %d byte blocks): ", i, - *keysize * 8, *b_size); + *keysize * 8, bs); /* Set up tfm global state, i.e. the key */ @@ -1353,7 +1359,7 @@ static void test_mb_skcipher_speed(const char *algo, int enc, int secs, for (j = 0; j < num_mb; ++j) { struct test_mb_skcipher_data *cur = &data[j]; - unsigned int k = *b_size; + unsigned int k = bs; unsigned int pages = DIV_ROUND_UP(k, PAGE_SIZE); unsigned int p = 0; @@ -1377,12 +1383,12 @@ static void test_mb_skcipher_speed(const char *algo, int enc, int secs, if (secs) { ret = test_mb_acipher_jiffies(data, enc, - *b_size, secs, + bs, secs, num_mb); cond_resched(); } else { ret = test_mb_acipher_cycles(data, enc, - *b_size, num_mb); + bs, num_mb); } if (ret) { @@ -1497,8 +1503,8 @@ static void test_skcipher_speed(const char *algo, int enc, unsigned int secs, char iv[128]; struct skcipher_request *req; struct crypto_skcipher *tfm; + const int *b_size; const char *e; - u32 *b_size; if (enc == ENCRYPT) e = "encryption"; @@ -1533,17 +1539,18 @@ static void test_skcipher_speed(const char *algo, int enc, unsigned int secs, b_size = block_sizes; do { + u32 bs = round_up(*b_size, crypto_skcipher_blocksize(tfm)); struct scatterlist sg[TVMEMSIZE]; - if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) { + if ((*keysize + bs) > TVMEMSIZE * PAGE_SIZE) { pr_err("template (%u) too big for " - "tvmem (%lu)\n", *keysize + *b_size, + "tvmem (%lu)\n", *keysize + bs, TVMEMSIZE * PAGE_SIZE); goto out_free_req; } pr_info("test %u (%d bit key, %d byte blocks): ", i, - *keysize * 8, *b_size); + *keysize * 8, bs); memset(tvmem[0], 0xff, PAGE_SIZE); @@ -1565,7 +1572,7 @@ static void test_skcipher_speed(const char *algo, int enc, unsigned int secs, goto out_free_req; } - k = *keysize + *b_size; + k = *keysize + bs; sg_init_table(sg, DIV_ROUND_UP(k, PAGE_SIZE)); if (k > PAGE_SIZE) { @@ -1582,22 +1589,22 @@ static void test_skcipher_speed(const char *algo, int enc, unsigned int secs, sg_set_buf(sg + j, tvmem[j], k); memset(tvmem[j], 0xff, k); } else { - sg_set_buf(sg, tvmem[0] + *keysize, *b_size); + sg_set_buf(sg, tvmem[0] + *keysize, bs); } iv_len = crypto_skcipher_ivsize(tfm); if (iv_len) memset(&iv, 0xff, iv_len); - skcipher_request_set_crypt(req, sg, sg, *b_size, iv); + skcipher_request_set_crypt(req, sg, sg, bs, iv); if (secs) { ret = test_acipher_jiffies(req, enc, - *b_size, secs); + bs, secs); cond_resched(); } else { ret = test_acipher_cycles(req, enc, - *b_size); + bs); } if (ret) { @@ -3066,7 +3073,7 @@ err_free_tv: */ static void __exit tcrypt_mod_fini(void) { } -subsys_initcall(tcrypt_mod_init); +late_initcall(tcrypt_mod_init); module_exit(tcrypt_mod_fini); module_param(alg, charp, 0); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index a64a639eddfa..321e38eef51b 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1171,8 +1171,7 @@ static inline const void *sg_data(struct scatterlist *sg) } /* Test one hash test vector in one configuration, using the shash API */ -static int test_shash_vec_cfg(const char *driver, - const struct hash_testvec *vec, +static int test_shash_vec_cfg(const struct hash_testvec *vec, const char *vec_name, const struct testvec_config *cfg, struct shash_desc *desc, @@ -1183,6 +1182,7 @@ static int test_shash_vec_cfg(const char *driver, const unsigned int alignmask = crypto_shash_alignmask(tfm); const unsigned int digestsize = crypto_shash_digestsize(tfm); const unsigned int statesize = crypto_shash_statesize(tfm); + const char *driver = crypto_shash_driver_name(tfm); const struct test_sg_division *divs[XBUFSIZE]; unsigned int i; u8 result[HASH_MAX_DIGESTSIZE + TESTMGR_POISON_LEN]; @@ -1355,8 +1355,7 @@ static int check_nonfinal_ahash_op(const char *op, int err, } /* Test one hash test vector in one configuration, using the ahash API */ -static int test_ahash_vec_cfg(const char *driver, - const struct hash_testvec *vec, +static int test_ahash_vec_cfg(const struct hash_testvec *vec, const char *vec_name, const struct testvec_config *cfg, struct ahash_request *req, @@ -1367,6 +1366,7 @@ static int test_ahash_vec_cfg(const char *driver, const unsigned int alignmask = crypto_ahash_alignmask(tfm); const unsigned int digestsize = crypto_ahash_digestsize(tfm); const unsigned int statesize = crypto_ahash_statesize(tfm); + const char *driver = crypto_ahash_driver_name(tfm); const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags; const struct test_sg_division *divs[XBUFSIZE]; DECLARE_CRYPTO_WAIT(wait); @@ -1521,8 +1521,7 @@ result_ready: driver, cfg); } -static int test_hash_vec_cfg(const char *driver, - const struct hash_testvec *vec, +static int test_hash_vec_cfg(const struct hash_testvec *vec, const char *vec_name, const struct testvec_config *cfg, struct ahash_request *req, @@ -1539,20 +1538,18 @@ static int test_hash_vec_cfg(const char *driver, */ if (desc) { - err = test_shash_vec_cfg(driver, vec, vec_name, cfg, desc, tsgl, + err = test_shash_vec_cfg(vec, vec_name, cfg, desc, tsgl, hashstate); if (err) return err; } - return test_ahash_vec_cfg(driver, vec, vec_name, cfg, req, tsgl, - hashstate); + return test_ahash_vec_cfg(vec, vec_name, cfg, req, tsgl, hashstate); } -static int test_hash_vec(const char *driver, const struct hash_testvec *vec, - unsigned int vec_num, struct ahash_request *req, - struct shash_desc *desc, struct test_sglist *tsgl, - u8 *hashstate) +static int test_hash_vec(const struct hash_testvec *vec, unsigned int vec_num, + struct ahash_request *req, struct shash_desc *desc, + struct test_sglist *tsgl, u8 *hashstate) { char vec_name[16]; unsigned int i; @@ -1561,7 +1558,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec, sprintf(vec_name, "%u", vec_num); for (i = 0; i < ARRAY_SIZE(default_hash_testvec_configs); i++) { - err = test_hash_vec_cfg(driver, vec, vec_name, + err = test_hash_vec_cfg(vec, vec_name, &default_hash_testvec_configs[i], req, desc, tsgl, hashstate); if (err) @@ -1576,7 +1573,7 @@ static int test_hash_vec(const char *driver, const struct hash_testvec *vec, for (i = 0; i < fuzz_iterations; i++) { generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname)); - err = test_hash_vec_cfg(driver, vec, vec_name, &cfg, + err = test_hash_vec_cfg(vec, vec_name, &cfg, req, desc, tsgl, hashstate); if (err) return err; @@ -1633,8 +1630,7 @@ done: * Test the hash algorithm represented by @req against the corresponding generic * implementation, if one is available. */ -static int test_hash_vs_generic_impl(const char *driver, - const char *generic_driver, +static int test_hash_vs_generic_impl(const char *generic_driver, unsigned int maxkeysize, struct ahash_request *req, struct shash_desc *desc, @@ -1646,6 +1642,7 @@ static int test_hash_vs_generic_impl(const char *driver, const unsigned int blocksize = crypto_ahash_blocksize(tfm); const unsigned int maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN; const char *algname = crypto_hash_alg_common(tfm)->base.cra_name; + const char *driver = crypto_ahash_driver_name(tfm); char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_shash *generic_tfm = NULL; struct shash_desc *generic_desc = NULL; @@ -1732,7 +1729,7 @@ static int test_hash_vs_generic_impl(const char *driver, vec_name, sizeof(vec_name)); generate_random_testvec_config(cfg, cfgname, sizeof(cfgname)); - err = test_hash_vec_cfg(driver, &vec, vec_name, cfg, + err = test_hash_vec_cfg(&vec, vec_name, cfg, req, desc, tsgl, hashstate); if (err) goto out; @@ -1749,8 +1746,7 @@ out: return err; } #else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ -static int test_hash_vs_generic_impl(const char *driver, - const char *generic_driver, +static int test_hash_vs_generic_impl(const char *generic_driver, unsigned int maxkeysize, struct ahash_request *req, struct shash_desc *desc, @@ -1820,6 +1816,7 @@ static int __alg_test_hash(const struct hash_testvec *vecs, driver, PTR_ERR(atfm)); return PTR_ERR(atfm); } + driver = crypto_ahash_driver_name(atfm); req = ahash_request_alloc(atfm, GFP_KERNEL); if (!req) { @@ -1859,13 +1856,12 @@ static int __alg_test_hash(const struct hash_testvec *vecs, } for (i = 0; i < num_vecs; i++) { - err = test_hash_vec(driver, &vecs[i], i, req, desc, tsgl, - hashstate); + err = test_hash_vec(&vecs[i], i, req, desc, tsgl, hashstate); if (err) goto out; cond_resched(); } - err = test_hash_vs_generic_impl(driver, generic_driver, maxkeysize, req, + err = test_hash_vs_generic_impl(generic_driver, maxkeysize, req, desc, tsgl, hashstate); out: kfree(hashstate); @@ -1923,8 +1919,7 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver, return err; } -static int test_aead_vec_cfg(const char *driver, int enc, - const struct aead_testvec *vec, +static int test_aead_vec_cfg(int enc, const struct aead_testvec *vec, const char *vec_name, const struct testvec_config *cfg, struct aead_request *req, @@ -1934,6 +1929,7 @@ static int test_aead_vec_cfg(const char *driver, int enc, const unsigned int alignmask = crypto_aead_alignmask(tfm); const unsigned int ivsize = crypto_aead_ivsize(tfm); const unsigned int authsize = vec->clen - vec->plen; + const char *driver = crypto_aead_driver_name(tfm); const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags; const char *op = enc ? "encryption" : "decryption"; DECLARE_CRYPTO_WAIT(wait); @@ -2106,9 +2102,8 @@ static int test_aead_vec_cfg(const char *driver, int enc, return 0; } -static int test_aead_vec(const char *driver, int enc, - const struct aead_testvec *vec, unsigned int vec_num, - struct aead_request *req, +static int test_aead_vec(int enc, const struct aead_testvec *vec, + unsigned int vec_num, struct aead_request *req, struct cipher_test_sglists *tsgls) { char vec_name[16]; @@ -2121,7 +2116,7 @@ static int test_aead_vec(const char *driver, int enc, sprintf(vec_name, "%u", vec_num); for (i = 0; i < ARRAY_SIZE(default_cipher_testvec_configs); i++) { - err = test_aead_vec_cfg(driver, enc, vec, vec_name, + err = test_aead_vec_cfg(enc, vec, vec_name, &default_cipher_testvec_configs[i], req, tsgls); if (err) @@ -2136,7 +2131,7 @@ static int test_aead_vec(const char *driver, int enc, for (i = 0; i < fuzz_iterations; i++) { generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname)); - err = test_aead_vec_cfg(driver, enc, vec, vec_name, + err = test_aead_vec_cfg(enc, vec, vec_name, &cfg, req, tsgls); if (err) return err; @@ -2152,7 +2147,6 @@ static int test_aead_vec(const char *driver, int enc, struct aead_extra_tests_ctx { struct aead_request *req; struct crypto_aead *tfm; - const char *driver; const struct alg_test_desc *test_desc; struct cipher_test_sglists *tsgls; unsigned int maxdatasize; @@ -2358,7 +2352,7 @@ static int test_aead_inauthentic_inputs(struct aead_extra_tests_ctx *ctx) if (ctx->vec.novrfy) { generate_random_testvec_config(&ctx->cfg, ctx->cfgname, sizeof(ctx->cfgname)); - err = test_aead_vec_cfg(ctx->driver, DECRYPT, &ctx->vec, + err = test_aead_vec_cfg(DECRYPT, &ctx->vec, ctx->vec_name, &ctx->cfg, ctx->req, ctx->tsgls); if (err) @@ -2377,7 +2371,7 @@ static int test_aead_vs_generic_impl(struct aead_extra_tests_ctx *ctx) { struct crypto_aead *tfm = ctx->tfm; const char *algname = crypto_aead_alg(tfm)->base.cra_name; - const char *driver = ctx->driver; + const char *driver = crypto_aead_driver_name(tfm); const char *generic_driver = ctx->test_desc->generic_driver; char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_aead *generic_tfm = NULL; @@ -2454,14 +2448,14 @@ static int test_aead_vs_generic_impl(struct aead_extra_tests_ctx *ctx) generate_random_testvec_config(&ctx->cfg, ctx->cfgname, sizeof(ctx->cfgname)); if (!ctx->vec.novrfy) { - err = test_aead_vec_cfg(driver, ENCRYPT, &ctx->vec, + err = test_aead_vec_cfg(ENCRYPT, &ctx->vec, ctx->vec_name, &ctx->cfg, ctx->req, ctx->tsgls); if (err) goto out; } if (ctx->vec.crypt_error == 0 || ctx->vec.novrfy) { - err = test_aead_vec_cfg(driver, DECRYPT, &ctx->vec, + err = test_aead_vec_cfg(DECRYPT, &ctx->vec, ctx->vec_name, &ctx->cfg, ctx->req, ctx->tsgls); if (err) @@ -2476,8 +2470,7 @@ out: return err; } -static int test_aead_extra(const char *driver, - const struct alg_test_desc *test_desc, +static int test_aead_extra(const struct alg_test_desc *test_desc, struct aead_request *req, struct cipher_test_sglists *tsgls) { @@ -2493,7 +2486,6 @@ static int test_aead_extra(const char *driver, return -ENOMEM; ctx->req = req; ctx->tfm = crypto_aead_reqtfm(req); - ctx->driver = driver; ctx->test_desc = test_desc; ctx->tsgls = tsgls; ctx->maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN; @@ -2528,8 +2520,7 @@ out: return err; } #else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ -static int test_aead_extra(const char *driver, - const struct alg_test_desc *test_desc, +static int test_aead_extra(const struct alg_test_desc *test_desc, struct aead_request *req, struct cipher_test_sglists *tsgls) { @@ -2537,8 +2528,7 @@ static int test_aead_extra(const char *driver, } #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ -static int test_aead(const char *driver, int enc, - const struct aead_test_suite *suite, +static int test_aead(int enc, const struct aead_test_suite *suite, struct aead_request *req, struct cipher_test_sglists *tsgls) { @@ -2546,8 +2536,7 @@ static int test_aead(const char *driver, int enc, int err; for (i = 0; i < suite->count; i++) { - err = test_aead_vec(driver, enc, &suite->vecs[i], i, req, - tsgls); + err = test_aead_vec(enc, &suite->vecs[i], i, req, tsgls); if (err) return err; cond_resched(); @@ -2575,6 +2564,7 @@ static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, driver, PTR_ERR(tfm)); return PTR_ERR(tfm); } + driver = crypto_aead_driver_name(tfm); req = aead_request_alloc(tfm, GFP_KERNEL); if (!req) { @@ -2592,15 +2582,15 @@ static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, goto out; } - err = test_aead(driver, ENCRYPT, suite, req, tsgls); + err = test_aead(ENCRYPT, suite, req, tsgls); if (err) goto out; - err = test_aead(driver, DECRYPT, suite, req, tsgls); + err = test_aead(DECRYPT, suite, req, tsgls); if (err) goto out; - err = test_aead_extra(driver, desc, req, tsgls); + err = test_aead_extra(desc, req, tsgls); out: free_cipher_test_sglists(tsgls); aead_request_free(req); @@ -2695,8 +2685,7 @@ out_nobuf: return ret; } -static int test_skcipher_vec_cfg(const char *driver, int enc, - const struct cipher_testvec *vec, +static int test_skcipher_vec_cfg(int enc, const struct cipher_testvec *vec, const char *vec_name, const struct testvec_config *cfg, struct skcipher_request *req, @@ -2705,6 +2694,7 @@ static int test_skcipher_vec_cfg(const char *driver, int enc, struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); const unsigned int alignmask = crypto_skcipher_alignmask(tfm); const unsigned int ivsize = crypto_skcipher_ivsize(tfm); + const char *driver = crypto_skcipher_driver_name(tfm); const u32 req_flags = CRYPTO_TFM_REQ_MAY_BACKLOG | cfg->req_flags; const char *op = enc ? "encryption" : "decryption"; DECLARE_CRYPTO_WAIT(wait); @@ -2859,8 +2849,7 @@ static int test_skcipher_vec_cfg(const char *driver, int enc, return 0; } -static int test_skcipher_vec(const char *driver, int enc, - const struct cipher_testvec *vec, +static int test_skcipher_vec(int enc, const struct cipher_testvec *vec, unsigned int vec_num, struct skcipher_request *req, struct cipher_test_sglists *tsgls) @@ -2875,7 +2864,7 @@ static int test_skcipher_vec(const char *driver, int enc, sprintf(vec_name, "%u", vec_num); for (i = 0; i < ARRAY_SIZE(default_cipher_testvec_configs); i++) { - err = test_skcipher_vec_cfg(driver, enc, vec, vec_name, + err = test_skcipher_vec_cfg(enc, vec, vec_name, &default_cipher_testvec_configs[i], req, tsgls); if (err) @@ -2890,7 +2879,7 @@ static int test_skcipher_vec(const char *driver, int enc, for (i = 0; i < fuzz_iterations; i++) { generate_random_testvec_config(&cfg, cfgname, sizeof(cfgname)); - err = test_skcipher_vec_cfg(driver, enc, vec, vec_name, + err = test_skcipher_vec_cfg(enc, vec, vec_name, &cfg, req, tsgls); if (err) return err; @@ -2961,8 +2950,7 @@ done: * Test the skcipher algorithm represented by @req against the corresponding * generic implementation, if one is available. */ -static int test_skcipher_vs_generic_impl(const char *driver, - const char *generic_driver, +static int test_skcipher_vs_generic_impl(const char *generic_driver, struct skcipher_request *req, struct cipher_test_sglists *tsgls) { @@ -2972,6 +2960,7 @@ static int test_skcipher_vs_generic_impl(const char *driver, const unsigned int blocksize = crypto_skcipher_blocksize(tfm); const unsigned int maxdatasize = (2 * PAGE_SIZE) - TESTMGR_POISON_LEN; const char *algname = crypto_skcipher_alg(tfm)->base.cra_name; + const char *driver = crypto_skcipher_driver_name(tfm); char _generic_driver[CRYPTO_MAX_ALG_NAME]; struct crypto_skcipher *generic_tfm = NULL; struct skcipher_request *generic_req = NULL; @@ -3077,11 +3066,11 @@ static int test_skcipher_vs_generic_impl(const char *driver, vec_name, sizeof(vec_name)); generate_random_testvec_config(cfg, cfgname, sizeof(cfgname)); - err = test_skcipher_vec_cfg(driver, ENCRYPT, &vec, vec_name, + err = test_skcipher_vec_cfg(ENCRYPT, &vec, vec_name, cfg, req, tsgls); if (err) goto out; - err = test_skcipher_vec_cfg(driver, DECRYPT, &vec, vec_name, + err = test_skcipher_vec_cfg(DECRYPT, &vec, vec_name, cfg, req, tsgls); if (err) goto out; @@ -3099,8 +3088,7 @@ out: return err; } #else /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ -static int test_skcipher_vs_generic_impl(const char *driver, - const char *generic_driver, +static int test_skcipher_vs_generic_impl(const char *generic_driver, struct skcipher_request *req, struct cipher_test_sglists *tsgls) { @@ -3108,8 +3096,7 @@ static int test_skcipher_vs_generic_impl(const char *driver, } #endif /* !CONFIG_CRYPTO_MANAGER_EXTRA_TESTS */ -static int test_skcipher(const char *driver, int enc, - const struct cipher_test_suite *suite, +static int test_skcipher(int enc, const struct cipher_test_suite *suite, struct skcipher_request *req, struct cipher_test_sglists *tsgls) { @@ -3117,8 +3104,7 @@ static int test_skcipher(const char *driver, int enc, int err; for (i = 0; i < suite->count; i++) { - err = test_skcipher_vec(driver, enc, &suite->vecs[i], i, req, - tsgls); + err = test_skcipher_vec(enc, &suite->vecs[i], i, req, tsgls); if (err) return err; cond_resched(); @@ -3146,6 +3132,7 @@ static int alg_test_skcipher(const struct alg_test_desc *desc, driver, PTR_ERR(tfm)); return PTR_ERR(tfm); } + driver = crypto_skcipher_driver_name(tfm); req = skcipher_request_alloc(tfm, GFP_KERNEL); if (!req) { @@ -3163,16 +3150,15 @@ static int alg_test_skcipher(const struct alg_test_desc *desc, goto out; } - err = test_skcipher(driver, ENCRYPT, suite, req, tsgls); + err = test_skcipher(ENCRYPT, suite, req, tsgls); if (err) goto out; - err = test_skcipher(driver, DECRYPT, suite, req, tsgls); + err = test_skcipher(DECRYPT, suite, req, tsgls); if (err) goto out; - err = test_skcipher_vs_generic_impl(driver, desc->generic_driver, req, - tsgls); + err = test_skcipher_vs_generic_impl(desc->generic_driver, req, tsgls); out: free_cipher_test_sglists(tsgls); skcipher_request_free(req); @@ -3602,6 +3588,7 @@ static int alg_test_crc32c(const struct alg_test_desc *desc, "%ld\n", driver, PTR_ERR(tfm)); return PTR_ERR(tfm); } + driver = crypto_shash_driver_name(tfm); do { SHASH_DESC_ON_STACK(shash, tfm); @@ -5677,15 +5664,21 @@ int alg_test(const char *driver, const char *alg, u32 type, u32 mask) type, mask); test_done: - if (rc && (fips_enabled || panic_on_fail)) { - fips_fail_notify(); - panic("alg: self-tests for %s (%s) failed in %s mode!\n", - driver, alg, fips_enabled ? "fips" : "panic_on_fail"); + if (rc) { + if (fips_enabled || panic_on_fail) { + fips_fail_notify(); + panic("alg: self-tests for %s (%s) failed in %s mode!\n", + driver, alg, + fips_enabled ? "fips" : "panic_on_fail"); + } + WARN(1, "alg: self-tests for %s (%s) failed (rc=%d)", + driver, alg, rc); + } else { + if (fips_enabled) + pr_info("alg: self-tests for %s (%s) passed\n", + driver, alg); } - if (fips_enabled && !rc) - pr_info("alg: self-tests for %s (%s) passed\n", driver, alg); - return rc; notest: diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 81757eeded68..a2b59b84bb88 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -16,10 +16,29 @@ menuconfig AUXDISPLAY if AUXDISPLAY +config CHARLCD + tristate "Character LCD core support" if COMPILE_TEST + help + This is the base system for character-based LCD displays. + It makes no sense to have this alone, you select your display driver + and if it needs the charlcd core, it will select it automatically. + This is some character LCD core interface that multiple drivers can + use. + +config HD44780_COMMON + tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST + select CHARLCD + help + This is a module with the common symbols for HD44780 (and compatibles) + displays. This is the code that multiple other modules use. It is not + useful alone. If you have some sort of HD44780 compatible display, + you very likely use this. It is selected automatically by selecting + your concrete display. + config HD44780 tristate "HD44780 Character LCD support" depends on GPIOLIB || COMPILE_TEST - select CHARLCD + select HD44780_COMMON help Enable support for Character LCDs using a HD44780 controller. The LCD is accessible through the /dev/lcd char device (10, 156). @@ -154,6 +173,16 @@ config HT16K33 Say yes here to add support for Holtek HT16K33, RAM mapping 16*8 LED controller driver with keyscan. +config LCD2S + tristate "lcd2s 20x4 character display over I2C console" + depends on I2C + select CHARLCD + help + This is a driver that lets you use the lcd2s 20x4 character display + from Modtronix engineering as a console output device. The display + is a simple single color character display. You have to connect it + to an I2C bus. + config ARM_CHARLCD bool "ARM Ltd. Character LCD Driver" depends on PLAT_VERSATILE @@ -167,7 +196,7 @@ config ARM_CHARLCD menuconfig PARPORT_PANEL tristate "Parallel port LCD/Keypad Panel support" depends on PARPORT - select CHARLCD + select HD44780_COMMON help Say Y here if you have an HD44780 or KS-0074 LCD connected to your parallel port. This driver also features 4 and 6-key keypads. The LCD diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index cf54b5efb07e..307771027c89 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_CHARLCD) += charlcd.o +obj-$(CONFIG_HD44780_COMMON) += hd44780_common.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o obj-$(CONFIG_KS0108) += ks0108.o obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o @@ -11,3 +12,4 @@ obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o obj-$(CONFIG_HD44780) += hd44780.o obj-$(CONFIG_HT16K33) += ht16k33.o obj-$(CONFIG_PARPORT_PANEL) += panel.o +obj-$(CONFIG_LCD2S) += lcd2s.o diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 5aee0f546351..f43430e9dcee 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -8,7 +8,6 @@ #include <linux/atomic.h> #include <linux/ctype.h> -#include <linux/delay.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/module.h> @@ -22,43 +21,9 @@ #include "charlcd.h" -#define DEFAULT_LCD_BWIDTH 40 -#define DEFAULT_LCD_HWIDTH 64 - /* Keep the backlight on this many seconds for each flash */ #define LCD_BL_TEMPO_PERIOD 4 -#define LCD_FLAG_B 0x0004 /* Blink on */ -#define LCD_FLAG_C 0x0008 /* Cursor on */ -#define LCD_FLAG_D 0x0010 /* Display on */ -#define LCD_FLAG_F 0x0020 /* Large font mode */ -#define LCD_FLAG_N 0x0040 /* 2-rows mode */ -#define LCD_FLAG_L 0x0080 /* Backlight enabled */ - -/* LCD commands */ -#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */ - -#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */ -#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */ - -#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */ -#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */ -#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */ -#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */ - -#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */ -#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */ -#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */ - -#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */ -#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */ -#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */ -#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */ - -#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */ - -#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */ - #define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */ #define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */ @@ -74,12 +39,6 @@ struct charlcd_priv { /* contains the LCD config state */ unsigned long int flags; - /* Contains the LCD X and Y offset */ - struct { - unsigned long int x; - unsigned long int y; - } addr; - /* Current escape sequence and it's length or -1 if outside */ struct { char buf[LCD_ESCAPE_LEN + 1]; @@ -94,14 +53,8 @@ struct charlcd_priv { /* Device single-open policy control */ static atomic_t charlcd_available = ATOMIC_INIT(1); -/* sleeps that many milliseconds with a reschedule */ -static void long_sleep(int ms) -{ - schedule_timeout_interruptible(msecs_to_jiffies(ms)); -} - /* turn the backlight on or off */ -static void charlcd_backlight(struct charlcd *lcd, int on) +void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on) { struct charlcd_priv *priv = charlcd_to_priv(lcd); @@ -113,6 +66,7 @@ static void charlcd_backlight(struct charlcd *lcd, int on) lcd->ops->backlight(lcd, on); mutex_unlock(&priv->bl_tempo_lock); } +EXPORT_SYMBOL_GPL(charlcd_backlight); static void charlcd_bl_off(struct work_struct *work) { @@ -124,7 +78,7 @@ static void charlcd_bl_off(struct work_struct *work) if (priv->bl_tempo) { priv->bl_tempo = false; if (!(priv->flags & LCD_FLAG_L)) - priv->lcd.ops->backlight(&priv->lcd, 0); + priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF); } mutex_unlock(&priv->bl_tempo_lock); } @@ -141,148 +95,41 @@ void charlcd_poke(struct charlcd *lcd) mutex_lock(&priv->bl_tempo_lock); if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L)) - lcd->ops->backlight(lcd, 1); + lcd->ops->backlight(lcd, CHARLCD_ON); priv->bl_tempo = true; schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ); mutex_unlock(&priv->bl_tempo_lock); } EXPORT_SYMBOL_GPL(charlcd_poke); -static void charlcd_gotoxy(struct charlcd *lcd) -{ - struct charlcd_priv *priv = charlcd_to_priv(lcd); - unsigned int addr; - - /* - * we force the cursor to stay at the end of the - * line if it wants to go farther - */ - addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1) - : lcd->bwidth - 1; - if (priv->addr.y & 1) - addr += lcd->hwidth; - if (priv->addr.y & 2) - addr += lcd->bwidth; - lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr); -} - static void charlcd_home(struct charlcd *lcd) { - struct charlcd_priv *priv = charlcd_to_priv(lcd); - - priv->addr.x = 0; - priv->addr.y = 0; - charlcd_gotoxy(lcd); + lcd->addr.x = 0; + lcd->addr.y = 0; + lcd->ops->home(lcd); } static void charlcd_print(struct charlcd *lcd, char c) { - struct charlcd_priv *priv = charlcd_to_priv(lcd); - - if (priv->addr.x < lcd->bwidth) { - if (lcd->char_conv) - c = lcd->char_conv[(unsigned char)c]; - lcd->ops->write_data(lcd, c); - priv->addr.x++; - - /* prevents the cursor from wrapping onto the next line */ - if (priv->addr.x == lcd->bwidth) - charlcd_gotoxy(lcd); - } -} - -static void charlcd_clear_fast(struct charlcd *lcd) -{ - int pos; + if (lcd->addr.x >= lcd->width) + return; - charlcd_home(lcd); + if (lcd->char_conv) + c = lcd->char_conv[(unsigned char)c]; - if (lcd->ops->clear_fast) - lcd->ops->clear_fast(lcd); - else - for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++) - lcd->ops->write_data(lcd, ' '); + if (!lcd->ops->print(lcd, c)) + lcd->addr.x++; - charlcd_home(lcd); + /* prevents the cursor from wrapping onto the next line */ + if (lcd->addr.x == lcd->width) + lcd->ops->gotoxy(lcd, lcd->addr.x - 1, lcd->addr.y); } -/* clears the display and resets X/Y */ static void charlcd_clear_display(struct charlcd *lcd) { - struct charlcd_priv *priv = charlcd_to_priv(lcd); - - lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR); - priv->addr.x = 0; - priv->addr.y = 0; - /* we must wait a few milliseconds (15) */ - long_sleep(15); -} - -static int charlcd_init_display(struct charlcd *lcd) -{ - void (*write_cmd_raw)(struct charlcd *lcd, int cmd); - struct charlcd_priv *priv = charlcd_to_priv(lcd); - u8 init; - - if (lcd->ifwidth != 4 && lcd->ifwidth != 8) - return -EINVAL; - - priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | - LCD_FLAG_C | LCD_FLAG_B; - - long_sleep(20); /* wait 20 ms after power-up for the paranoid */ - - /* - * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure - * the LCD is in 8-bit mode afterwards - */ - init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS; - if (lcd->ifwidth == 4) { - init >>= 4; - write_cmd_raw = lcd->ops->write_cmd_raw4; - } else { - write_cmd_raw = lcd->ops->write_cmd; - } - write_cmd_raw(lcd, init); - long_sleep(10); - write_cmd_raw(lcd, init); - long_sleep(10); - write_cmd_raw(lcd, init); - long_sleep(10); - - if (lcd->ifwidth == 4) { - /* Switch to 4-bit mode, 1 line, small fonts */ - lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4); - long_sleep(10); - } - - /* set font height and lines number */ - lcd->ops->write_cmd(lcd, - LCD_CMD_FUNCTION_SET | - ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | - ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) | - ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)); - long_sleep(10); - - /* display off, cursor off, blink off */ - lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL); - long_sleep(10); - - lcd->ops->write_cmd(lcd, - LCD_CMD_DISPLAY_CTRL | /* set display mode */ - ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) | - ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) | - ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)); - - charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0); - - long_sleep(10); - - /* entry mode set : increment, cursor shifting */ - lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); - - charlcd_clear_display(lcd); - return 0; + lcd->ops->clear_display(lcd); + lcd->addr.x = 0; + lcd->addr.y = 0; } /* @@ -360,34 +207,58 @@ static inline int handle_lcd_special_code(struct charlcd *lcd) switch (*esc) { case 'D': /* Display ON */ priv->flags |= LCD_FLAG_D; + if (priv->flags != oldflags) + lcd->ops->display(lcd, CHARLCD_ON); + processed = 1; break; case 'd': /* Display OFF */ priv->flags &= ~LCD_FLAG_D; + if (priv->flags != oldflags) + lcd->ops->display(lcd, CHARLCD_OFF); + processed = 1; break; case 'C': /* Cursor ON */ priv->flags |= LCD_FLAG_C; + if (priv->flags != oldflags) + lcd->ops->cursor(lcd, CHARLCD_ON); + processed = 1; break; case 'c': /* Cursor OFF */ priv->flags &= ~LCD_FLAG_C; + if (priv->flags != oldflags) + lcd->ops->cursor(lcd, CHARLCD_OFF); + processed = 1; break; case 'B': /* Blink ON */ priv->flags |= LCD_FLAG_B; + if (priv->flags != oldflags) + lcd->ops->blink(lcd, CHARLCD_ON); + processed = 1; break; case 'b': /* Blink OFF */ priv->flags &= ~LCD_FLAG_B; + if (priv->flags != oldflags) + lcd->ops->blink(lcd, CHARLCD_OFF); + processed = 1; break; case '+': /* Back light ON */ priv->flags |= LCD_FLAG_L; + if (priv->flags != oldflags) + charlcd_backlight(lcd, CHARLCD_ON); + processed = 1; break; case '-': /* Back light OFF */ priv->flags &= ~LCD_FLAG_L; + if (priv->flags != oldflags) + charlcd_backlight(lcd, CHARLCD_OFF); + processed = 1; break; case '*': /* Flash back light */ @@ -396,158 +267,98 @@ static inline int handle_lcd_special_code(struct charlcd *lcd) break; case 'f': /* Small Font */ priv->flags &= ~LCD_FLAG_F; + if (priv->flags != oldflags) + lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_SMALL); + processed = 1; break; case 'F': /* Large Font */ priv->flags |= LCD_FLAG_F; + if (priv->flags != oldflags) + lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_LARGE); + processed = 1; break; case 'n': /* One Line */ priv->flags &= ~LCD_FLAG_N; + if (priv->flags != oldflags) + lcd->ops->lines(lcd, CHARLCD_LINES_1); + processed = 1; break; case 'N': /* Two Lines */ priv->flags |= LCD_FLAG_N; + if (priv->flags != oldflags) + lcd->ops->lines(lcd, CHARLCD_LINES_2); + processed = 1; break; case 'l': /* Shift Cursor Left */ - if (priv->addr.x > 0) { - /* back one char if not at end of line */ - if (priv->addr.x < lcd->bwidth) - lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); - priv->addr.x--; + if (lcd->addr.x > 0) { + if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT)) + lcd->addr.x--; } + processed = 1; break; case 'r': /* shift cursor right */ - if (priv->addr.x < lcd->width) { - /* allow the cursor to pass the end of the line */ - if (priv->addr.x < (lcd->bwidth - 1)) - lcd->ops->write_cmd(lcd, - LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT); - priv->addr.x++; + if (lcd->addr.x < lcd->width) { + if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT)) + lcd->addr.x++; } + processed = 1; break; case 'L': /* shift display left */ - lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT); + lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT); processed = 1; break; case 'R': /* shift display right */ - lcd->ops->write_cmd(lcd, - LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT | - LCD_CMD_SHIFT_RIGHT); + lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT); processed = 1; break; case 'k': { /* kill end of line */ - int x; + int x, xs, ys; - for (x = priv->addr.x; x < lcd->bwidth; x++) - lcd->ops->write_data(lcd, ' '); + xs = lcd->addr.x; + ys = lcd->addr.y; + for (x = lcd->addr.x; x < lcd->width; x++) + lcd->ops->print(lcd, ' '); /* restore cursor position */ - charlcd_gotoxy(lcd); + lcd->addr.x = xs; + lcd->addr.y = ys; + lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); processed = 1; break; } case 'I': /* reinitialize display */ - charlcd_init_display(lcd); + lcd->ops->init_display(lcd); + priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | + LCD_FLAG_C | LCD_FLAG_B; processed = 1; break; - case 'G': { - /* Generator : LGcxxxxx...xx; must have <c> between '0' - * and '7', representing the numerical ASCII code of the - * redefined character, and <xx...xx> a sequence of 16 - * hex digits representing 8 bytes for each character. - * Most LCDs will only use 5 lower bits of the 7 first - * bytes. - */ - - unsigned char cgbytes[8]; - unsigned char cgaddr; - int cgoffset; - int shift; - char value; - int addr; - - if (!strchr(esc, ';')) - break; - - esc++; - - cgaddr = *(esc++) - '0'; - if (cgaddr > 7) { + case 'G': + if (lcd->ops->redefine_char) + processed = lcd->ops->redefine_char(lcd, esc); + else processed = 1; - break; - } - - cgoffset = 0; - shift = 0; - value = 0; - while (*esc && cgoffset < 8) { - int half; - - shift ^= 4; - - half = hex_to_bin(*esc++); - if (half < 0) - continue; - - value |= half << shift; - if (shift == 0) { - cgbytes[cgoffset++] = value; - value = 0; - } - } - - lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8)); - for (addr = 0; addr < cgoffset; addr++) - lcd->ops->write_data(lcd, cgbytes[addr]); - - /* ensures that we stop writing to CGRAM */ - charlcd_gotoxy(lcd); - processed = 1; break; - } + case 'x': /* gotoxy : LxXXX[yYYY]; */ case 'y': /* gotoxy : LyYYY[xXXX]; */ if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';') break; /* If the command is valid, move to the new address */ - if (parse_xy(esc, &priv->addr.x, &priv->addr.y)) - charlcd_gotoxy(lcd); + if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y)) + lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); /* Regardless of its validity, mark as processed */ processed = 1; break; } - /* TODO: This indent party here got ugly, clean it! */ - /* Check whether one flag was changed */ - if (oldflags == priv->flags) - return processed; - - /* check whether one of B,C,D flags were changed */ - if ((oldflags ^ priv->flags) & - (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D)) - /* set display mode */ - lcd->ops->write_cmd(lcd, - LCD_CMD_DISPLAY_CTRL | - ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) | - ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) | - ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)); - /* check whether one of F,N flags was changed */ - else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N)) - lcd->ops->write_cmd(lcd, - LCD_CMD_FUNCTION_SET | - ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | - ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) | - ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)); - /* check whether L flag was changed */ - else if ((oldflags ^ priv->flags) & LCD_FLAG_L) - charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L)); - return processed; } @@ -572,40 +383,39 @@ static void charlcd_write_char(struct charlcd *lcd, char c) break; case '\b': /* go back one char and clear it */ - if (priv->addr.x > 0) { - /* - * check if we're not at the - * end of the line - */ - if (priv->addr.x < lcd->bwidth) - /* back one char */ - lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); - priv->addr.x--; + if (lcd->addr.x > 0) { + /* back one char */ + if (!lcd->ops->shift_cursor(lcd, + CHARLCD_SHIFT_LEFT)) + lcd->addr.x--; } /* replace with a space */ - lcd->ops->write_data(lcd, ' '); + charlcd_print(lcd, ' '); /* back one char again */ - lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); + if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT)) + lcd->addr.x--; + break; case '\f': /* quickly clear the display */ - charlcd_clear_fast(lcd); + charlcd_clear_display(lcd); break; case '\n': /* * flush the remainder of the current line and * go to the beginning of the next line */ - for (; priv->addr.x < lcd->bwidth; priv->addr.x++) - lcd->ops->write_data(lcd, ' '); - priv->addr.x = 0; - priv->addr.y = (priv->addr.y + 1) % lcd->height; - charlcd_gotoxy(lcd); + for (; lcd->addr.x < lcd->width; lcd->addr.x++) + lcd->ops->print(lcd, ' '); + + lcd->addr.x = 0; + lcd->addr.y = (lcd->addr.y + 1) % lcd->height; + lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); break; case '\r': /* go to the beginning of the same line */ - priv->addr.x = 0; - charlcd_gotoxy(lcd); + lcd->addr.x = 0; + lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); break; case '\t': /* print a space instead of the tab */ @@ -627,7 +437,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c) if (!strcmp(priv->esc_seq.buf, "[2J")) { /* clear the display */ - charlcd_clear_fast(lcd); + charlcd_clear_display(lcd); processed = 1; } else if (!strcmp(priv->esc_seq.buf, "[H")) { /* cursor to home */ @@ -690,8 +500,10 @@ static int charlcd_open(struct inode *inode, struct file *file) goto fail; if (priv->must_clear) { - charlcd_clear_display(&priv->lcd); + priv->lcd.ops->clear_display(&priv->lcd); priv->must_clear = false; + priv->lcd.addr.x = 0; + priv->lcd.addr.y = 0; } return nonseekable_open(inode, file); @@ -756,6 +568,8 @@ static int charlcd_init(struct charlcd *lcd) struct charlcd_priv *priv = charlcd_to_priv(lcd); int ret; + priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | + LCD_FLAG_C | LCD_FLAG_B; if (lcd->ops->backlight) { mutex_init(&priv->bl_tempo_lock); INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off); @@ -766,7 +580,7 @@ static int charlcd_init(struct charlcd *lcd) * Since charlcd_init_display() needs to write data, we have to * enable mark the LCD initialized just before. */ - ret = charlcd_init_display(lcd); + ret = lcd->ops->init_display(lcd); if (ret) return ret; @@ -779,22 +593,18 @@ static int charlcd_init(struct charlcd *lcd) return 0; } -struct charlcd *charlcd_alloc(unsigned int drvdata_size) +struct charlcd *charlcd_alloc(void) { struct charlcd_priv *priv; struct charlcd *lcd; - priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return NULL; priv->esc_seq.len = -1; lcd = &priv->lcd; - lcd->ifwidth = 8; - lcd->bwidth = DEFAULT_LCD_BWIDTH; - lcd->hwidth = DEFAULT_LCD_HWIDTH; - lcd->drvdata = priv->drvdata; return lcd; } @@ -862,7 +672,7 @@ int charlcd_unregister(struct charlcd *lcd) the_charlcd = NULL; if (lcd->ops->backlight) { cancel_delayed_work_sync(&priv->bl_work); - priv->lcd.ops->backlight(&priv->lcd, 0); + priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF); } return 0; diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h index 00911ad0f3de..eed80063a6d2 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -9,31 +9,91 @@ #ifndef _CHARLCD_H #define _CHARLCD_H +#define LCD_FLAG_B 0x0004 /* Blink on */ +#define LCD_FLAG_C 0x0008 /* Cursor on */ +#define LCD_FLAG_D 0x0010 /* Display on */ +#define LCD_FLAG_F 0x0020 /* Large font mode */ +#define LCD_FLAG_N 0x0040 /* 2-rows mode */ +#define LCD_FLAG_L 0x0080 /* Backlight enabled */ + +enum charlcd_onoff { + CHARLCD_OFF = 0, + CHARLCD_ON, +}; + +enum charlcd_shift_dir { + CHARLCD_SHIFT_LEFT, + CHARLCD_SHIFT_RIGHT, +}; + +enum charlcd_fontsize { + CHARLCD_FONTSIZE_SMALL, + CHARLCD_FONTSIZE_LARGE, +}; + +enum charlcd_lines { + CHARLCD_LINES_1, + CHARLCD_LINES_2, +}; + struct charlcd { const struct charlcd_ops *ops; const unsigned char *char_conv; /* Optional */ - int ifwidth; /* 4-bit or 8-bit (default) */ int height; int width; - int bwidth; /* Default set by charlcd_alloc() */ - int hwidth; /* Default set by charlcd_alloc() */ - void *drvdata; /* Set by charlcd_alloc() */ + /* Contains the LCD X and Y offset */ + struct { + unsigned long x; + unsigned long y; + } addr; + + void *drvdata; }; +/** + * struct charlcd_ops - Functions used by charlcd. Drivers have to implement + * these. + * @backlight: Turn backlight on or off. Optional. + * @print: Print one character to the display at current cursor position. + * The buffered cursor position is advanced by charlcd. The cursor should not + * wrap to the next line at the end of a line. + * @gotoxy: Set cursor to x, y. The x and y values to set the cursor to are + * previously set in addr.x and addr.y by charlcd. + * @home: Set cursor to 0, 0. The values in addr.x and addr.y are set to 0, 0 by + * charlcd prior to calling this function. + * @clear_display: Clear the whole display and set the cursor to 0, 0. The + * values in addr.x and addr.y are set to 0, 0 by charlcd after to calling this + * function. + * @init_display: Initialize the display. + * @shift_cursor: Shift cursor left or right one position. + * @shift_display: Shift whole display content left or right. + * @display: Turn display on or off. + * @cursor: Turn cursor on or off. + * @blink: Turn cursor blink on or off. + * @lines: One or two lines. + * @redefine_char: Redefine the actual pixel matrix of character. + */ struct charlcd_ops { - /* Required */ - void (*write_cmd)(struct charlcd *lcd, int cmd); - void (*write_data)(struct charlcd *lcd, int data); - - /* Optional */ - void (*write_cmd_raw4)(struct charlcd *lcd, int cmd); /* 4-bit only */ - void (*clear_fast)(struct charlcd *lcd); - void (*backlight)(struct charlcd *lcd, int on); + void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on); + int (*print)(struct charlcd *lcd, int c); + int (*gotoxy)(struct charlcd *lcd, unsigned int x, unsigned int y); + int (*home)(struct charlcd *lcd); + int (*clear_display)(struct charlcd *lcd); + int (*init_display)(struct charlcd *lcd); + int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir); + int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir); + int (*display)(struct charlcd *lcd, enum charlcd_onoff on); + int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on); + int (*blink)(struct charlcd *lcd, enum charlcd_onoff on); + int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size); + int (*lines)(struct charlcd *lcd, enum charlcd_lines lines); + int (*redefine_char)(struct charlcd *lcd, char *esc); }; -struct charlcd *charlcd_alloc(unsigned int drvdata_size); +void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on); +struct charlcd *charlcd_alloc(void); void charlcd_free(struct charlcd *lcd); int charlcd_register(struct charlcd *lcd); diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c index bcbe13092327..2e5e7c993933 100644 --- a/drivers/auxdisplay/hd44780.c +++ b/drivers/auxdisplay/hd44780.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include "charlcd.h" +#include "hd44780_common.h" enum hd44780_pin { /* Order does matter due to writing to GPIO array subsets! */ @@ -37,9 +38,10 @@ struct hd44780 { struct gpio_desc *pins[PIN_NUM]; }; -static void hd44780_backlight(struct charlcd *lcd, int on) +static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on) { - struct hd44780 *hd = lcd->drvdata; + struct hd44780_common *hdc = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; if (hd->pins[PIN_CTRL_BL]) gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on); @@ -101,9 +103,9 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs) } /* Send a command to the LCD panel in 8 bit GPIO mode */ -static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd) +static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd) { - struct hd44780 *hd = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; hd44780_write_gpio8(hd, cmd, 0); @@ -112,9 +114,9 @@ static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd) } /* Send data to the LCD panel in 8 bit GPIO mode */ -static void hd44780_write_data_gpio8(struct charlcd *lcd, int data) +static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data) { - struct hd44780 *hd = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; hd44780_write_gpio8(hd, data, 1); @@ -123,15 +125,26 @@ static void hd44780_write_data_gpio8(struct charlcd *lcd, int data) } static const struct charlcd_ops hd44780_ops_gpio8 = { - .write_cmd = hd44780_write_cmd_gpio8, - .write_data = hd44780_write_data_gpio8, .backlight = hd44780_backlight, + .print = hd44780_common_print, + .gotoxy = hd44780_common_gotoxy, + .home = hd44780_common_home, + .clear_display = hd44780_common_clear_display, + .init_display = hd44780_common_init_display, + .shift_cursor = hd44780_common_shift_cursor, + .shift_display = hd44780_common_shift_display, + .display = hd44780_common_display, + .cursor = hd44780_common_cursor, + .blink = hd44780_common_blink, + .fontsize = hd44780_common_fontsize, + .lines = hd44780_common_lines, + .redefine_char = hd44780_common_redefine_char, }; /* Send a command to the LCD panel in 4 bit GPIO mode */ -static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd) +static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd) { - struct hd44780 *hd = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; hd44780_write_gpio4(hd, cmd, 0); @@ -140,10 +153,10 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd) } /* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */ -static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd) +static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd) { DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */ - struct hd44780 *hd = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; unsigned int n; /* Command nibble + RS, RW */ @@ -157,9 +170,9 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd) } /* Send data to the LCD panel in 4 bit GPIO mode */ -static void hd44780_write_data_gpio4(struct charlcd *lcd, int data) +static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data) { - struct hd44780 *hd = lcd->drvdata; + struct hd44780 *hd = hdc->hd44780; hd44780_write_gpio4(hd, data, 1); @@ -168,10 +181,20 @@ static void hd44780_write_data_gpio4(struct charlcd *lcd, int data) } static const struct charlcd_ops hd44780_ops_gpio4 = { - .write_cmd = hd44780_write_cmd_gpio4, - .write_cmd_raw4 = hd44780_write_cmd_raw_gpio4, - .write_data = hd44780_write_data_gpio4, .backlight = hd44780_backlight, + .print = hd44780_common_print, + .gotoxy = hd44780_common_gotoxy, + .home = hd44780_common_home, + .clear_display = hd44780_common_clear_display, + .init_display = hd44780_common_init_display, + .shift_cursor = hd44780_common_shift_cursor, + .shift_display = hd44780_common_shift_display, + .display = hd44780_common_display, + .cursor = hd44780_common_cursor, + .blink = hd44780_common_blink, + .fontsize = hd44780_common_fontsize, + .lines = hd44780_common_lines, + .redefine_char = hd44780_common_redefine_char, }; static int hd44780_probe(struct platform_device *pdev) @@ -179,8 +202,9 @@ static int hd44780_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; unsigned int i, base; struct charlcd *lcd; + struct hd44780_common *hdc; struct hd44780 *hd; - int ifwidth, ret; + int ifwidth, ret = -ENOMEM; /* Required pins */ ifwidth = gpiod_count(dev, "data"); @@ -198,31 +222,39 @@ static int hd44780_probe(struct platform_device *pdev) return -EINVAL; } - lcd = charlcd_alloc(sizeof(struct hd44780)); - if (!lcd) + hdc = hd44780_common_alloc(); + if (!hdc) return -ENOMEM; - hd = lcd->drvdata; + lcd = charlcd_alloc(); + if (!lcd) + goto fail1; + + hd = kzalloc(sizeof(struct hd44780), GFP_KERNEL); + if (!hd) + goto fail2; + hdc->hd44780 = hd; + lcd->drvdata = hdc; for (i = 0; i < ifwidth; i++) { hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i, GPIOD_OUT_LOW); if (IS_ERR(hd->pins[base + i])) { ret = PTR_ERR(hd->pins[base + i]); - goto fail; + goto fail3; } } hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(hd->pins[PIN_CTRL_E])) { ret = PTR_ERR(hd->pins[PIN_CTRL_E]); - goto fail; + goto fail3; } hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH); if (IS_ERR(hd->pins[PIN_CTRL_RS])) { ret = PTR_ERR(hd->pins[PIN_CTRL_RS]); - goto fail; + goto fail3; } /* Optional pins */ @@ -230,47 +262,60 @@ static int hd44780_probe(struct platform_device *pdev) GPIOD_OUT_LOW); if (IS_ERR(hd->pins[PIN_CTRL_RW])) { ret = PTR_ERR(hd->pins[PIN_CTRL_RW]); - goto fail; + goto fail3; } hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight", GPIOD_OUT_LOW); if (IS_ERR(hd->pins[PIN_CTRL_BL])) { ret = PTR_ERR(hd->pins[PIN_CTRL_BL]); - goto fail; + goto fail3; } /* Required properties */ ret = device_property_read_u32(dev, "display-height-chars", &lcd->height); if (ret) - goto fail; + goto fail3; ret = device_property_read_u32(dev, "display-width-chars", &lcd->width); if (ret) - goto fail; + goto fail3; /* * On displays with more than two rows, the internal buffer width is * usually equal to the display width */ if (lcd->height > 2) - lcd->bwidth = lcd->width; + hdc->bwidth = lcd->width; /* Optional properties */ - device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth); - - lcd->ifwidth = ifwidth; - lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4; + device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth); + + hdc->ifwidth = ifwidth; + if (ifwidth == 8) { + lcd->ops = &hd44780_ops_gpio8; + hdc->write_data = hd44780_write_data_gpio8; + hdc->write_cmd = hd44780_write_cmd_gpio8; + } else { + lcd->ops = &hd44780_ops_gpio4; + hdc->write_data = hd44780_write_data_gpio4; + hdc->write_cmd = hd44780_write_cmd_gpio4; + hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4; + } ret = charlcd_register(lcd); if (ret) - goto fail; + goto fail3; platform_set_drvdata(pdev, lcd); return 0; -fail: - charlcd_free(lcd); +fail3: + kfree(hd); +fail2: + kfree(lcd); +fail1: + kfree(hdc); return ret; } @@ -278,9 +323,10 @@ static int hd44780_remove(struct platform_device *pdev) { struct charlcd *lcd = platform_get_drvdata(pdev); + kfree(lcd->drvdata); charlcd_unregister(lcd); - charlcd_free(lcd); + kfree(lcd); return 0; } diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c new file mode 100644 index 000000000000..3934c2eebf33 --- /dev/null +++ b/drivers/auxdisplay/hd44780_common.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include "charlcd.h" +#include "hd44780_common.h" + +/* LCD commands */ +#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */ + +#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */ +#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */ + +#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */ +#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */ +#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */ +#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */ + +#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */ +#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */ +#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */ + +#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */ +#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */ +#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */ +#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */ + +#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */ + +#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */ + +/* sleeps that many milliseconds with a reschedule */ +static void long_sleep(int ms) +{ + schedule_timeout_interruptible(msecs_to_jiffies(ms)); +} + +int hd44780_common_print(struct charlcd *lcd, int c) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (lcd->addr.x < hdc->bwidth) { + hdc->write_data(hdc, c); + return 0; + } + + return 1; +} +EXPORT_SYMBOL_GPL(hd44780_common_print); + +int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) +{ + struct hd44780_common *hdc = lcd->drvdata; + unsigned int addr; + + /* + * we force the cursor to stay at the end of the + * line if it wants to go farther + */ + addr = x < hdc->bwidth ? x & (hdc->hwidth - 1) : hdc->bwidth - 1; + if (y & 1) + addr += hdc->hwidth; + if (y & 2) + addr += hdc->bwidth; + hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_gotoxy); + +int hd44780_common_home(struct charlcd *lcd) +{ + return hd44780_common_gotoxy(lcd, 0, 0); +} +EXPORT_SYMBOL_GPL(hd44780_common_home); + +/* clears the display and resets X/Y */ +int hd44780_common_clear_display(struct charlcd *lcd) +{ + struct hd44780_common *hdc = lcd->drvdata; + + hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR); + /* datasheet says to wait 1,64 milliseconds */ + long_sleep(2); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_clear_display); + +int hd44780_common_init_display(struct charlcd *lcd) +{ + struct hd44780_common *hdc = lcd->drvdata; + + void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd); + u8 init; + + if (hdc->ifwidth != 4 && hdc->ifwidth != 8) + return -EINVAL; + + hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | + LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B; + + long_sleep(20); /* wait 20 ms after power-up for the paranoid */ + + /* + * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure + * the LCD is in 8-bit mode afterwards + */ + init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS; + if (hdc->ifwidth == 4) { + init >>= 4; + write_cmd_raw = hdc->write_cmd_raw4; + } else { + write_cmd_raw = hdc->write_cmd; + } + write_cmd_raw(hdc, init); + long_sleep(10); + write_cmd_raw(hdc, init); + long_sleep(10); + write_cmd_raw(hdc, init); + long_sleep(10); + + if (hdc->ifwidth == 4) { + /* Switch to 4-bit mode, 1 line, small fonts */ + hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4); + long_sleep(10); + } + + /* set font height and lines number */ + hdc->write_cmd(hdc, + LCD_CMD_FUNCTION_SET | + ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_F) ? + LCD_CMD_FONT_5X10_DOTS : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_N) ? + LCD_CMD_TWO_LINES : 0)); + long_sleep(10); + + /* display off, cursor off, blink off */ + hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL); + long_sleep(10); + + hdc->write_cmd(hdc, + LCD_CMD_DISPLAY_CTRL | /* set display mode */ + ((hdc->hd44780_common_flags & LCD_FLAG_D) ? + LCD_CMD_DISPLAY_ON : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_C) ? + LCD_CMD_CURSOR_ON : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_B) ? + LCD_CMD_BLINK_ON : 0)); + + charlcd_backlight(lcd, + (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0); + + long_sleep(10); + + /* entry mode set : increment, cursor shifting */ + hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); + + hd44780_common_clear_display(lcd); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_init_display); + +int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (dir == CHARLCD_SHIFT_LEFT) { + /* back one char if not at end of line */ + if (lcd->addr.x < hdc->bwidth) + hdc->write_cmd(hdc, LCD_CMD_SHIFT); + } else if (dir == CHARLCD_SHIFT_RIGHT) { + /* allow the cursor to pass the end of the line */ + if (lcd->addr.x < (hdc->bwidth - 1)) + hdc->write_cmd(hdc, + LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor); + +int hd44780_common_shift_display(struct charlcd *lcd, + enum charlcd_shift_dir dir) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (dir == CHARLCD_SHIFT_LEFT) + hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT); + else if (dir == CHARLCD_SHIFT_RIGHT) + hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT | + LCD_CMD_SHIFT_RIGHT); + + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_shift_display); + +static void hd44780_common_set_mode(struct hd44780_common *hdc) +{ + hdc->write_cmd(hdc, + LCD_CMD_DISPLAY_CTRL | + ((hdc->hd44780_common_flags & LCD_FLAG_D) ? + LCD_CMD_DISPLAY_ON : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_C) ? + LCD_CMD_CURSOR_ON : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_B) ? + LCD_CMD_BLINK_ON : 0)); +} + +int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (on == CHARLCD_ON) + hdc->hd44780_common_flags |= LCD_FLAG_D; + else + hdc->hd44780_common_flags &= ~LCD_FLAG_D; + + hd44780_common_set_mode(hdc); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_display); + +int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (on == CHARLCD_ON) + hdc->hd44780_common_flags |= LCD_FLAG_C; + else + hdc->hd44780_common_flags &= ~LCD_FLAG_C; + + hd44780_common_set_mode(hdc); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_cursor); + +int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (on == CHARLCD_ON) + hdc->hd44780_common_flags |= LCD_FLAG_B; + else + hdc->hd44780_common_flags &= ~LCD_FLAG_B; + + hd44780_common_set_mode(hdc); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_blink); + +static void hd44780_common_set_function(struct hd44780_common *hdc) +{ + hdc->write_cmd(hdc, + LCD_CMD_FUNCTION_SET | + ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_F) ? + LCD_CMD_FONT_5X10_DOTS : 0) | + ((hdc->hd44780_common_flags & LCD_FLAG_N) ? + LCD_CMD_TWO_LINES : 0)); +} + +int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (size == CHARLCD_FONTSIZE_LARGE) + hdc->hd44780_common_flags |= LCD_FLAG_F; + else + hdc->hd44780_common_flags &= ~LCD_FLAG_F; + + hd44780_common_set_function(hdc); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_fontsize); + +int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines) +{ + struct hd44780_common *hdc = lcd->drvdata; + + if (lines == CHARLCD_LINES_2) + hdc->hd44780_common_flags |= LCD_FLAG_N; + else + hdc->hd44780_common_flags &= ~LCD_FLAG_N; + + hd44780_common_set_function(hdc); + return 0; +} +EXPORT_SYMBOL_GPL(hd44780_common_lines); + +int hd44780_common_redefine_char(struct charlcd *lcd, char *esc) +{ + /* Generator : LGcxxxxx...xx; must have <c> between '0' + * and '7', representing the numerical ASCII code of the + * redefined character, and <xx...xx> a sequence of 16 + * hex digits representing 8 bytes for each character. + * Most LCDs will only use 5 lower bits of the 7 first + * bytes. + */ + + struct hd44780_common *hdc = lcd->drvdata; + unsigned char cgbytes[8]; + unsigned char cgaddr; + int cgoffset; + int shift; + char value; + int addr; + + if (!strchr(esc, ';')) + return 0; + + esc++; + + cgaddr = *(esc++) - '0'; + if (cgaddr > 7) + return 1; + + cgoffset = 0; + shift = 0; + value = 0; + while (*esc && cgoffset < 8) { + int half; + + shift ^= 4; + half = hex_to_bin(*esc++); + if (half < 0) + continue; + + value |= half << shift; + if (shift == 0) { + cgbytes[cgoffset++] = value; + value = 0; + } + } + + hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8)); + for (addr = 0; addr < cgoffset; addr++) + hdc->write_data(hdc, cgbytes[addr]); + + /* ensures that we stop writing to CGRAM */ + lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); + return 1; +} +EXPORT_SYMBOL_GPL(hd44780_common_redefine_char); + +struct hd44780_common *hd44780_common_alloc(void) +{ + struct hd44780_common *hd; + + hd = kzalloc(sizeof(*hd), GFP_KERNEL); + if (!hd) + return NULL; + + hd->ifwidth = 8; + hd->bwidth = DEFAULT_LCD_BWIDTH; + hd->hwidth = DEFAULT_LCD_HWIDTH; + return hd; +} +EXPORT_SYMBOL_GPL(hd44780_common_alloc); + +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h new file mode 100644 index 000000000000..a16aa8c29c99 --- /dev/null +++ b/drivers/auxdisplay/hd44780_common.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define DEFAULT_LCD_BWIDTH 40 +#define DEFAULT_LCD_HWIDTH 64 + +struct hd44780_common { + int ifwidth; /* 4-bit or 8-bit (default) */ + int bwidth; /* Default set by hd44780_alloc() */ + int hwidth; /* Default set by hd44780_alloc() */ + unsigned long hd44780_common_flags; + void (*write_data)(struct hd44780_common *hdc, int data); + void (*write_cmd)(struct hd44780_common *hdc, int cmd); + /* write_cmd_raw4 is for 4-bit connected displays only */ + void (*write_cmd_raw4)(struct hd44780_common *hdc, int cmd); + void *hd44780; +}; + +int hd44780_common_print(struct charlcd *lcd, int c); +int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y); +int hd44780_common_home(struct charlcd *lcd); +int hd44780_common_clear_display(struct charlcd *lcd); +int hd44780_common_init_display(struct charlcd *lcd); +int hd44780_common_shift_cursor(struct charlcd *lcd, + enum charlcd_shift_dir dir); +int hd44780_common_shift_display(struct charlcd *lcd, + enum charlcd_shift_dir dir); +int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on); +int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on); +int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on); +int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size); +int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines); +int hd44780_common_redefine_char(struct charlcd *lcd, char *esc); +struct hd44780_common *hd44780_common_alloc(void); diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c new file mode 100644 index 000000000000..38ba08628ccb --- /dev/null +++ b/drivers/auxdisplay/lcd2s.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * console driver for LCD2S 4x20 character displays connected through i2c. + * The display also has a spi interface, but the driver does not support + * this yet. + * + * This is a driver allowing you to use a LCD2S 4x20 from modtronix + * engineering as auxdisplay character device. + * + * (C) 2019 by Lemonage Software GmbH + * Author: Lars Pöschel <poeschel@lemonage.de> + * All rights reserved. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> + +#include "charlcd.h" + +#define LCD2S_CMD_CUR_MOVES_FWD 0x09 +#define LCD2S_CMD_CUR_BLINK_OFF 0x10 +#define LCD2S_CMD_CUR_UL_OFF 0x11 +#define LCD2S_CMD_DISPLAY_OFF 0x12 +#define LCD2S_CMD_CUR_BLINK_ON 0x18 +#define LCD2S_CMD_CUR_UL_ON 0x19 +#define LCD2S_CMD_DISPLAY_ON 0x1a +#define LCD2S_CMD_BACKLIGHT_OFF 0x20 +#define LCD2S_CMD_BACKLIGHT_ON 0x28 +#define LCD2S_CMD_WRITE 0x80 +#define LCD2S_CMD_MOV_CUR_RIGHT 0x83 +#define LCD2S_CMD_MOV_CUR_LEFT 0x84 +#define LCD2S_CMD_SHIFT_RIGHT 0x85 +#define LCD2S_CMD_SHIFT_LEFT 0x86 +#define LCD2S_CMD_SHIFT_UP 0x87 +#define LCD2S_CMD_SHIFT_DOWN 0x88 +#define LCD2S_CMD_CUR_ADDR 0x89 +#define LCD2S_CMD_CUR_POS 0x8a +#define LCD2S_CMD_CUR_RESET 0x8b +#define LCD2S_CMD_CLEAR 0x8c +#define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92 +#define LCD2S_CMD_READ_STATUS 0xd0 + +#define LCD2S_CHARACTER_SIZE 8 + +#define LCD2S_STATUS_BUF_MASK 0x7f + +struct lcd2s_data { + struct i2c_client *i2c; + struct charlcd *charlcd; +}; + +static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count) +{ + s32 status; + + status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS); + if (status < 0) + return status; + + while ((status & LCD2S_STATUS_BUF_MASK) < count) { + mdelay(1); + status = i2c_smbus_read_byte_data(client, + LCD2S_CMD_READ_STATUS); + if (status < 0) + return status; + } + return 0; +} + +static int lcd2s_i2c_master_send(const struct i2c_client *client, + const char *buf, int count) +{ + s32 status; + + status = lcd2s_wait_buf_free(client, count); + if (status < 0) + return status; + + return i2c_master_send(client, buf, count); +} + +static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value) +{ + s32 status; + + status = lcd2s_wait_buf_free(client, 1); + if (status < 0) + return status; + + return i2c_smbus_write_byte(client, value); +} + +static int lcd2s_print(struct charlcd *lcd, int c) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + u8 buf[2] = { LCD2S_CMD_WRITE, c }; + + lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + return 0; +} + +static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + u8 buf[] = { LCD2S_CMD_CUR_POS, y + 1, x + 1}; + + lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + + return 0; +} + +static int lcd2s_home(struct charlcd *lcd) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET); + return 0; +} + +static int lcd2s_init_display(struct charlcd *lcd) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + /* turn everything off, but display on */ + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON); + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF); + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD); + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF); + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF); + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR); + + return 0; +} + +static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (dir == CHARLCD_SHIFT_LEFT) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT); + + return 0; +} + +static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (dir == CHARLCD_SHIFT_LEFT) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT); + + return 0; +} + +static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (on) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF); +} + +static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (on) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF); + + return 0; +} + +static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (on) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF); + + return 0; +} + +static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + if (on) + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON); + else + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF); + + return 0; +} + +static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size) +{ + return 0; +} + +static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines) +{ + return 0; +} + +static int lcd2s_redefine_char(struct charlcd *lcd, char *esc) +{ + /* Generator : LGcxxxxx...xx; must have <c> between '0' + * and '7', representing the numerical ASCII code of the + * redefined character, and <xx...xx> a sequence of 16 + * hex digits representing 8 bytes for each character. + * Most LCDs will only use 5 lower bits of the 7 first + * bytes. + */ + + struct lcd2s_data *lcd2s = lcd->drvdata; + u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR }; + u8 value; + int shift, i; + + if (!strchr(esc, ';')) + return 0; + + esc++; + + buf[1] = *(esc++) - '0'; + if (buf[1] > 7) + return 1; + + i = 0; + shift = 0; + value = 0; + while (*esc && i < LCD2S_CHARACTER_SIZE + 2) { + int half; + + shift ^= 4; + half = hex_to_bin(*esc++); + if (half < 0) + continue; + + value |= half << shift; + if (shift == 0) { + buf[i++] = value; + value = 0; + } + } + + lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); + return 1; +} + +static int lcd2s_clear_display(struct charlcd *lcd) +{ + struct lcd2s_data *lcd2s = lcd->drvdata; + + /* This implicitly sets cursor to first row and column */ + lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR); + return 0; +} + +static const struct charlcd_ops lcd2s_ops = { + .print = lcd2s_print, + .backlight = lcd2s_backlight, + .gotoxy = lcd2s_gotoxy, + .home = lcd2s_home, + .clear_display = lcd2s_clear_display, + .init_display = lcd2s_init_display, + .shift_cursor = lcd2s_shift_cursor, + .shift_display = lcd2s_shift_display, + .display = lcd2s_display, + .cursor = lcd2s_cursor, + .blink = lcd2s_blink, + .fontsize = lcd2s_fontsize, + .lines = lcd2s_lines, + .redefine_char = lcd2s_redefine_char, +}; + +static int lcd2s_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct charlcd *lcd; + struct lcd2s_data *lcd2s; + int err; + + if (!i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) + return -EIO; + + /* Test, if the display is responding */ + err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF); + if (err < 0) + return err; + + lcd = charlcd_alloc(); + if (!lcd) + return -ENOMEM; + + lcd2s = kzalloc(sizeof(struct lcd2s_data), GFP_KERNEL); + if (!lcd2s) { + err = -ENOMEM; + goto fail1; + } + + lcd->drvdata = lcd2s; + lcd2s->i2c = i2c; + lcd2s->charlcd = lcd; + + /* Required properties */ + err = device_property_read_u32(&i2c->dev, "display-height-chars", + &lcd->height); + if (err) + goto fail2; + + err = device_property_read_u32(&i2c->dev, "display-width-chars", + &lcd->width); + if (err) + goto fail2; + + lcd->ops = &lcd2s_ops; + + err = charlcd_register(lcd2s->charlcd); + if (err) + goto fail2; + + i2c_set_clientdata(i2c, lcd2s); + return 0; + +fail2: + kfree(lcd2s); +fail1: + kfree(lcd); + return err; +} + +static int lcd2s_i2c_remove(struct i2c_client *i2c) +{ + struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c); + + charlcd_unregister(lcd2s->charlcd); + kfree(lcd2s->charlcd); + return 0; +} + +static const struct i2c_device_id lcd2s_i2c_id[] = { + { "lcd2s", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id lcd2s_of_table[] = { + { .compatible = "modtronix,lcd2s" }, + { } +}; +MODULE_DEVICE_TABLE(of, lcd2s_of_table); +#endif + +static struct i2c_driver lcd2s_i2c_driver = { + .driver = { + .name = "lcd2s", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(lcd2s_of_table), +#endif + }, + .probe = lcd2s_i2c_probe, + .remove = lcd2s_i2c_remove, + .id_table = lcd2s_i2c_id, +}; + +static int __init lcd2s_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&lcd2s_i2c_driver); + if (ret != 0) + pr_err("Failed to register lcd2s driver\n"); + + return ret; +} +module_init(lcd2s_modinit) + +static void __exit lcd2s_exit(void) +{ + i2c_del_driver(&lcd2s_i2c_driver); +} +module_exit(lcd2s_exit) + +MODULE_DESCRIPTION("LCD2S character display driver"); +MODULE_AUTHOR("Lars Poeschel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index 1c82d824ae00..ff5755ee5694 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -56,6 +56,7 @@ #include <linux/uaccess.h> #include "charlcd.h" +#include "hd44780_common.h" #define LCD_MAXBYTES 256 /* max burst write */ @@ -298,8 +299,6 @@ static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES]; #define DEFAULT_LCD_TYPE LCD_TYPE_OLD #define DEFAULT_LCD_HEIGHT 2 #define DEFAULT_LCD_WIDTH 40 -#define DEFAULT_LCD_BWIDTH 40 -#define DEFAULT_LCD_HWIDTH 64 #define DEFAULT_LCD_CHARSET LCD_CHARSET_NORMAL #define DEFAULT_LCD_PROTO LCD_PROTO_PARALLEL @@ -708,7 +707,7 @@ static void lcd_send_serial(int byte) } /* turn the backlight on or off */ -static void lcd_backlight(struct charlcd *charlcd, int on) +static void lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on) { if (lcd.pins.bl == PIN_NONE) return; @@ -724,7 +723,7 @@ static void lcd_backlight(struct charlcd *charlcd, int on) } /* send a command to the LCD panel in serial mode */ -static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd) +static void lcd_write_cmd_s(struct hd44780_common *hdc, int cmd) { spin_lock_irq(&pprt_lock); lcd_send_serial(0x1F); /* R/W=W, RS=0 */ @@ -735,7 +734,7 @@ static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd) } /* send data to the LCD panel in serial mode */ -static void lcd_write_data_s(struct charlcd *charlcd, int data) +static void lcd_write_data_s(struct hd44780_common *hdc, int data) { spin_lock_irq(&pprt_lock); lcd_send_serial(0x5F); /* R/W=W, RS=1 */ @@ -746,7 +745,7 @@ static void lcd_write_data_s(struct charlcd *charlcd, int data) } /* send a command to the LCD panel in 8 bits parallel mode */ -static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd) +static void lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -768,7 +767,7 @@ static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd) } /* send data to the LCD panel in 8 bits parallel mode */ -static void lcd_write_data_p8(struct charlcd *charlcd, int data) +static void lcd_write_data_p8(struct hd44780_common *hdc, int data) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -790,7 +789,7 @@ static void lcd_write_data_p8(struct charlcd *charlcd, int data) } /* send a command to the TI LCD panel */ -static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd) +static void lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd) { spin_lock_irq(&pprt_lock); /* present the data to the control port */ @@ -800,7 +799,7 @@ static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd) } /* send data to the TI LCD panel */ -static void lcd_write_data_tilcd(struct charlcd *charlcd, int data) +static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -809,105 +808,50 @@ static void lcd_write_data_tilcd(struct charlcd *charlcd, int data) spin_unlock_irq(&pprt_lock); } -/* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_s(struct charlcd *charlcd) -{ - int pos; - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { - lcd_send_serial(0x5F); /* R/W=W, RS=1 */ - lcd_send_serial(' ' & 0x0F); - lcd_send_serial((' ' >> 4) & 0x0F); - /* the shortest data takes at least 40 us */ - udelay(40); - } - spin_unlock_irq(&pprt_lock); -} - -/* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_p8(struct charlcd *charlcd) -{ - int pos; - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { - /* present the data to the data port */ - w_dtr(pprt, ' '); - - /* maintain the data during 20 us before the strobe */ - udelay(20); - - set_bit(LCD_BIT_E, bits); - set_bit(LCD_BIT_RS, bits); - clear_bit(LCD_BIT_RW, bits); - set_ctrl_bits(); - - /* maintain the strobe during 40 us */ - udelay(40); - - clear_bit(LCD_BIT_E, bits); - set_ctrl_bits(); - - /* the shortest data takes at least 45 us */ - udelay(45); - } - spin_unlock_irq(&pprt_lock); -} - -/* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_tilcd(struct charlcd *charlcd) -{ - int pos; - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { - /* present the data to the data port */ - w_dtr(pprt, ' '); - udelay(60); - } - - spin_unlock_irq(&pprt_lock); -} - -static const struct charlcd_ops charlcd_serial_ops = { - .write_cmd = lcd_write_cmd_s, - .write_data = lcd_write_data_s, - .clear_fast = lcd_clear_fast_s, - .backlight = lcd_backlight, -}; - -static const struct charlcd_ops charlcd_parallel_ops = { - .write_cmd = lcd_write_cmd_p8, - .write_data = lcd_write_data_p8, - .clear_fast = lcd_clear_fast_p8, - .backlight = lcd_backlight, -}; - -static const struct charlcd_ops charlcd_tilcd_ops = { - .write_cmd = lcd_write_cmd_tilcd, - .write_data = lcd_write_data_tilcd, - .clear_fast = lcd_clear_fast_tilcd, +static const struct charlcd_ops charlcd_ops = { .backlight = lcd_backlight, + .print = hd44780_common_print, + .gotoxy = hd44780_common_gotoxy, + .home = hd44780_common_home, + .clear_display = hd44780_common_clear_display, + .init_display = hd44780_common_init_display, + .shift_cursor = hd44780_common_shift_cursor, + .shift_display = hd44780_common_shift_display, + .display = hd44780_common_display, + .cursor = hd44780_common_cursor, + .blink = hd44780_common_blink, + .fontsize = hd44780_common_fontsize, + .lines = hd44780_common_lines, + .redefine_char = hd44780_common_redefine_char, }; /* initialize the LCD driver */ static void lcd_init(void) { struct charlcd *charlcd; + struct hd44780_common *hdc; - charlcd = charlcd_alloc(0); - if (!charlcd) + hdc = hd44780_common_alloc(); + if (!hdc) return; + charlcd = charlcd_alloc(); + if (!charlcd) { + kfree(hdc); + return; + } + + hdc->hd44780 = &lcd; + charlcd->drvdata = hdc; + /* * Init lcd struct with load-time values to preserve exact * current functionality (at least for now). */ charlcd->height = lcd_height; charlcd->width = lcd_width; - charlcd->bwidth = lcd_bwidth; - charlcd->hwidth = lcd_hwidth; + hdc->bwidth = lcd_bwidth; + hdc->hwidth = lcd_hwidth; switch (selected_lcd_type) { case LCD_TYPE_OLD: @@ -918,8 +862,8 @@ static void lcd_init(void) lcd.pins.rs = PIN_AUTOLF; charlcd->width = 40; - charlcd->bwidth = 40; - charlcd->hwidth = 64; + hdc->bwidth = 40; + hdc->hwidth = 64; charlcd->height = 2; break; case LCD_TYPE_KS0074: @@ -931,8 +875,8 @@ static void lcd_init(void) lcd.pins.da = PIN_D0; charlcd->width = 16; - charlcd->bwidth = 40; - charlcd->hwidth = 16; + hdc->bwidth = 40; + hdc->hwidth = 16; charlcd->height = 2; break; case LCD_TYPE_NEXCOM: @@ -944,8 +888,8 @@ static void lcd_init(void) lcd.pins.rw = PIN_INITP; charlcd->width = 16; - charlcd->bwidth = 40; - charlcd->hwidth = 64; + hdc->bwidth = 40; + hdc->hwidth = 64; charlcd->height = 2; break; case LCD_TYPE_CUSTOM: @@ -963,8 +907,8 @@ static void lcd_init(void) lcd.pins.rs = PIN_SELECP; charlcd->width = 16; - charlcd->bwidth = 40; - charlcd->hwidth = 64; + hdc->bwidth = 40; + hdc->hwidth = 64; charlcd->height = 2; break; } @@ -975,9 +919,9 @@ static void lcd_init(void) if (lcd_width != NOT_SET) charlcd->width = lcd_width; if (lcd_bwidth != NOT_SET) - charlcd->bwidth = lcd_bwidth; + hdc->bwidth = lcd_bwidth; if (lcd_hwidth != NOT_SET) - charlcd->hwidth = lcd_hwidth; + hdc->hwidth = lcd_hwidth; if (lcd_charset != NOT_SET) lcd.charset = lcd_charset; if (lcd_proto != NOT_SET) @@ -998,15 +942,17 @@ static void lcd_init(void) /* this is used to catch wrong and default values */ if (charlcd->width <= 0) charlcd->width = DEFAULT_LCD_WIDTH; - if (charlcd->bwidth <= 0) - charlcd->bwidth = DEFAULT_LCD_BWIDTH; - if (charlcd->hwidth <= 0) - charlcd->hwidth = DEFAULT_LCD_HWIDTH; + if (hdc->bwidth <= 0) + hdc->bwidth = DEFAULT_LCD_BWIDTH; + if (hdc->hwidth <= 0) + hdc->hwidth = DEFAULT_LCD_HWIDTH; if (charlcd->height <= 0) charlcd->height = DEFAULT_LCD_HEIGHT; if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */ - charlcd->ops = &charlcd_serial_ops; + charlcd->ops = &charlcd_ops; + hdc->write_data = lcd_write_data_s; + hdc->write_cmd = lcd_write_cmd_s; if (lcd.pins.cl == PIN_NOT_SET) lcd.pins.cl = DEFAULT_LCD_PIN_SCL; @@ -1014,7 +960,9 @@ static void lcd_init(void) lcd.pins.da = DEFAULT_LCD_PIN_SDA; } else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */ - charlcd->ops = &charlcd_parallel_ops; + charlcd->ops = &charlcd_ops; + hdc->write_data = lcd_write_data_p8; + hdc->write_cmd = lcd_write_cmd_p8; if (lcd.pins.e == PIN_NOT_SET) lcd.pins.e = DEFAULT_LCD_PIN_E; @@ -1023,7 +971,9 @@ static void lcd_init(void) if (lcd.pins.rw == PIN_NOT_SET) lcd.pins.rw = DEFAULT_LCD_PIN_RW; } else { - charlcd->ops = &charlcd_tilcd_ops; + charlcd->ops = &charlcd_ops; + hdc->write_data = lcd_write_data_tilcd; + hdc->write_cmd = lcd_write_cmd_tilcd; } if (lcd.pins.bl == PIN_NOT_SET) @@ -1620,7 +1570,7 @@ err_lcd_unreg: if (lcd.enabled) charlcd_unregister(lcd.charlcd); err_unreg_device: - charlcd_free(lcd.charlcd); + kfree(lcd.charlcd); lcd.charlcd = NULL; parport_unregister_device(pprt); pprt = NULL; @@ -1647,7 +1597,8 @@ static void panel_detach(struct parport *port) if (lcd.enabled) { charlcd_unregister(lcd.charlcd); lcd.initialized = false; - charlcd_free(lcd.charlcd); + kfree(lcd.charlcd->drvdata); + kfree(lcd.charlcd); lcd.charlcd = NULL; } diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index e92c4d9469d8..17c1df8c909a 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -348,19 +348,6 @@ config HW_RANDOM_HISI If unsure, say Y. -config HW_RANDOM_HISI_V2 - tristate "HiSilicon True Random Number Generator V2 support" - depends on HW_RANDOM && ARM64 && ACPI - default HW_RANDOM - help - This driver provides kernel-side support for the True Random Number - Generator V2 hardware found on HiSilicon Hi1620 SoC. - - To compile this driver as a module, choose M here: the - module will be called hisi-trng-v2. - - If unsure, say Y. - config HW_RANDOM_ST tristate "ST Microelectronics HW Random Number Generator support" depends on HW_RANDOM && ARCH_STI @@ -508,6 +495,7 @@ config HW_RANDOM_NPCM config HW_RANDOM_KEYSTONE depends on ARCH_KEYSTONE || COMPILE_TEST + depends on HAS_IOMEM && OF default HW_RANDOM tristate "TI Keystone NETCP SA Hardware random number generator" help diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 5da344509a4d..8933fada74f2 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o -obj-$(CONFIG_HW_RANDOM_HISI_V2) += hisi-trng-v2.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o diff --git a/drivers/char/hw_random/hisi-trng-v2.c b/drivers/char/hw_random/hisi-trng-v2.c deleted file mode 100644 index 6a65b8232ce0..000000000000 --- a/drivers/char/hw_random/hisi-trng-v2.c +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2019 HiSilicon Limited. */ - -#include <linux/acpi.h> -#include <linux/err.h> -#include <linux/hw_random.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/random.h> - -#define HISI_TRNG_REG 0x00F0 -#define HISI_TRNG_BYTES 4 -#define HISI_TRNG_QUALITY 512 -#define SLEEP_US 10 -#define TIMEOUT_US 10000 - -struct hisi_trng { - void __iomem *base; - struct hwrng rng; -}; - -static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) -{ - struct hisi_trng *trng; - int currsize = 0; - u32 val = 0; - u32 ret; - - trng = container_of(rng, struct hisi_trng, rng); - - do { - ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, - val, SLEEP_US, TIMEOUT_US); - if (ret) - return currsize; - - if (max - currsize >= HISI_TRNG_BYTES) { - memcpy(buf + currsize, &val, HISI_TRNG_BYTES); - currsize += HISI_TRNG_BYTES; - if (currsize == max) - return currsize; - continue; - } - - /* copy remaining bytes */ - memcpy(buf + currsize, &val, max - currsize); - currsize = max; - } while (currsize < max); - - return currsize; -} - -static int hisi_trng_probe(struct platform_device *pdev) -{ - struct hisi_trng *trng; - int ret; - - trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); - if (!trng) - return -ENOMEM; - - trng->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(trng->base)) - return PTR_ERR(trng->base); - - trng->rng.name = pdev->name; - trng->rng.read = hisi_trng_read; - trng->rng.quality = HISI_TRNG_QUALITY; - - ret = devm_hwrng_register(&pdev->dev, &trng->rng); - if (ret) - dev_err(&pdev->dev, "failed to register hwrng!\n"); - - return ret; -} - -static const struct acpi_device_id hisi_trng_acpi_match[] = { - { "HISI02B3", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); - -static struct platform_driver hisi_trng_driver = { - .probe = hisi_trng_probe, - .driver = { - .name = "hisi-trng-v2", - .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), - }, -}; - -module_platform_driver(hisi_trng_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); -MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); -MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index 61c844baf26e..b05d676ca814 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -252,10 +252,8 @@ static int imx_rngc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(&pdev->dev, "Couldn't get irq %d\n", irq); + if (irq < 0) return irq; - } ret = clk_prepare_enable(rngc->clk); if (ret) diff --git a/drivers/char/random.c b/drivers/char/random.c index 2a41b21623ae..5f3b8ac9d97b 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -336,7 +336,7 @@ #include <linux/completion.h> #include <linux/uuid.h> #include <crypto/chacha.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/processor.h> #include <linux/uaccess.h> diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 37da0c070a88..bbd51703e738 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -548,6 +548,7 @@ config CRYPTO_DEV_ATMEL_SHA config CRYPTO_DEV_ATMEL_I2C tristate + select BITREVERSE config CRYPTO_DEV_ATMEL_ECC tristate "Support for Microchip / Atmel ECC hw accelerator" @@ -648,7 +649,7 @@ choice default CRYPTO_DEV_QCE_ENABLE_ALL depends on CRYPTO_DEV_QCE help - This option allows to choose whether to build support for all algorihtms + This option allows to choose whether to build support for all algorithms (default), hashes-only, or skciphers-only. The QCE engine does not appear to scale as well as the CPU to handle @@ -900,4 +901,6 @@ config CRYPTO_DEV_SA2UL used for crypto offload. Select this if you want to use hardware acceleration for cryptographic algorithms on these devices. +source "drivers/crypto/keembay/Kconfig" + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 53fc115cf459..fff9a70348e1 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-$(CONFIG_CRYPTO_DEV_ZYNQMP_AES) += xilinx/ obj-y += hisilicon/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ +obj-y += keembay/ diff --git a/drivers/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig index 0cdfe0e8cc66..180c8a9db819 100644 --- a/drivers/crypto/allwinner/Kconfig +++ b/drivers/crypto/allwinner/Kconfig @@ -43,7 +43,7 @@ config CRYPTO_DEV_SUN8I_CE depends on CRYPTO_DEV_ALLWINNER depends on PM help - Select y here to have support for the crypto Engine availlable on + Select y here to have support for the crypto Engine available on Allwinner SoC H2+, H3, H5, H6, R40 and A64. The Crypto Engine handle AES/3DES ciphers in ECB/CBC mode. diff --git a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h index 163962f9e284..5c291e4a6857 100644 --- a/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h +++ b/drivers/crypto/allwinner/sun4i-ss/sun4i-ss.h @@ -25,7 +25,7 @@ #include <linux/pm_runtime.h> #include <crypto/md5.h> #include <crypto/skcipher.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/hash.h> #include <crypto/internal/hash.h> #include <crypto/internal/skcipher.h> diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c index a94bf28f858a..2f09a37306e2 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c @@ -13,7 +13,8 @@ #include <linux/pm_runtime.h> #include <linux/scatterlist.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/md5.h> #include "sun8i-ce.h" @@ -262,13 +263,13 @@ int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq) u32 common; u64 byte_count; __le32 *bf; - void *buf; + void *buf = NULL; int j, i, todo; int nbw = 0; u64 fill, min_fill; __be64 *bebits; __le64 *lebits; - void *result; + void *result = NULL; u64 bs; int digestsize; dma_addr_t addr_res, addr_pad; @@ -285,13 +286,17 @@ int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq) /* the padding could be up to two block. */ buf = kzalloc(bs * 2, GFP_KERNEL | GFP_DMA); - if (!buf) - return -ENOMEM; + if (!buf) { + err = -ENOMEM; + goto theend; + } bf = (__le32 *)buf; result = kzalloc(digestsize, GFP_KERNEL | GFP_DMA); - if (!result) - return -ENOMEM; + if (!result) { + err = -ENOMEM; + goto theend; + } flow = rctx->flow; chan = &ce->chanlist[flow]; @@ -403,11 +408,11 @@ int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq) dma_unmap_sg(ce->dev, areq->src, nr_sgs, DMA_TO_DEVICE); dma_unmap_single(ce->dev, addr_res, digestsize, DMA_FROM_DEVICE); - kfree(buf); memcpy(areq->result, result, algt->alg.hash.halg.digestsize); - kfree(result); theend: + kfree(buf); + kfree(result); crypto_finalize_hash_request(engine, breq, err); return 0; } diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h index 558027516aed..cec781d5063c 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h @@ -16,7 +16,8 @@ #include <crypto/internal/hash.h> #include <crypto/md5.h> #include <crypto/rng.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> /* CE Registers */ #define CE_TDQ 0x00 diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c index b6ab2054f217..11cbcbc83a7b 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c @@ -13,7 +13,8 @@ #include <linux/pm_runtime.h> #include <linux/scatterlist.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/md5.h> #include "sun8i-ss.h" diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h index 1a66457f4a20..28188685b910 100644 --- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h +++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h @@ -15,7 +15,8 @@ #include <linux/crypto.h> #include <crypto/internal/hash.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #define SS_START 1 diff --git a/drivers/crypto/amcc/crypto4xx_alg.c b/drivers/crypto/amcc/crypto4xx_alg.c index 7729a637fb02..a3fa849b139a 100644 --- a/drivers/crypto/amcc/crypto4xx_alg.c +++ b/drivers/crypto/amcc/crypto4xx_alg.c @@ -20,7 +20,7 @@ #include <crypto/aead.h> #include <crypto/aes.h> #include <crypto/gcm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/ctr.h> #include <crypto/skcipher.h> #include "crypto4xx_reg_def.h" diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index 981de43ea5e2..8d1b918a0533 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -30,7 +30,7 @@ #include <crypto/aes.h> #include <crypto/ctr.h> #include <crypto/gcm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/rng.h> #include <crypto/scatterwalk.h> #include <crypto/skcipher.h> @@ -917,7 +917,7 @@ int crypto4xx_build_pd(struct crypto_async_request *req, } pd->pd_ctl.w = PD_CTL_HOST_READY | - ((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AHASH) | + ((crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AHASH) || (crypto_tfm_alg_type(req->tfm) == CRYPTO_ALG_TYPE_AEAD) ? PD_CTL_HASH_FINAL : 0); pd->pd_ctl_len.w = 0x00400000 | (assoclen + datalen); diff --git a/drivers/crypto/atmel-authenc.h b/drivers/crypto/atmel-authenc.h index c6530a1c8c20..45171e89a7d2 100644 --- a/drivers/crypto/atmel-authenc.h +++ b/drivers/crypto/atmel-authenc.h @@ -16,7 +16,8 @@ #include <crypto/authenc.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include "atmel-sha-regs.h" struct atmel_aes_dev; diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 75ccf41a7cb9..352d80cb5ae9 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -33,7 +33,8 @@ #include <linux/crypto.h> #include <crypto/scatterwalk.h> #include <crypto/algapi.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/hash.h> #include <crypto/internal/hash.h> #include "atmel-sha-regs.h" @@ -459,7 +460,6 @@ static int atmel_sha_init(struct ahash_request *req) break; default: return -EINVAL; - break; } ctx->bufcnt = 0; diff --git a/drivers/crypto/axis/artpec6_crypto.c b/drivers/crypto/axis/artpec6_crypto.c index 809c3033ca74..9ad188cffd0d 100644 --- a/drivers/crypto/axis/artpec6_crypto.c +++ b/drivers/crypto/axis/artpec6_crypto.c @@ -28,7 +28,8 @@ #include <crypto/internal/hash.h> #include <crypto/internal/skcipher.h> #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/xts.h> /* Max length of a line in all cache levels for Artpec SoCs. */ diff --git a/drivers/crypto/bcm/cipher.c b/drivers/crypto/bcm/cipher.c index 50d169e61b41..30390a7324b2 100644 --- a/drivers/crypto/bcm/cipher.c +++ b/drivers/crypto/bcm/cipher.c @@ -26,11 +26,12 @@ #include <crypto/aes.h> #include <crypto/internal/des.h> #include <crypto/hmac.h> -#include <crypto/sha.h> #include <crypto/md5.h> #include <crypto/authenc.h> #include <crypto/skcipher.h> #include <crypto/hash.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sha3.h> #include "util.h" diff --git a/drivers/crypto/bcm/cipher.h b/drivers/crypto/bcm/cipher.h index 035c8389cb3d..0ad5892b445d 100644 --- a/drivers/crypto/bcm/cipher.h +++ b/drivers/crypto/bcm/cipher.h @@ -16,7 +16,8 @@ #include <crypto/aead.h> #include <crypto/arc4.h> #include <crypto/gcm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sha3.h> #include "spu.h" diff --git a/drivers/crypto/bcm/spu.h b/drivers/crypto/bcm/spu.h index dd132389bcaa..1c386a2d5506 100644 --- a/drivers/crypto/bcm/spu.h +++ b/drivers/crypto/bcm/spu.h @@ -17,7 +17,8 @@ #include <linux/types.h> #include <linux/scatterlist.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> enum spu_cipher_alg { CIPHER_ALG_NONE = 0x0, diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index cf5bd7666dfc..8697ae53b063 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -3404,8 +3404,8 @@ static int caam_cra_init(struct crypto_skcipher *tfm) fallback = crypto_alloc_skcipher(tfm_name, 0, CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback)) { - dev_err(ctx->jrdev, "Failed to allocate %s fallback: %ld\n", - tfm_name, PTR_ERR(fallback)); + pr_err("Failed to allocate %s fallback: %ld\n", + tfm_name, PTR_ERR(fallback)); return PTR_ERR(fallback); } diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index 66f60d78bdc8..189a7438b29c 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -852,7 +852,7 @@ static struct caam_drv_ctx *get_drv_ctx(struct caam_ctx *ctx, cpu = smp_processor_id(); drv_ctx = caam_drv_ctx_init(ctx->qidev, &cpu, desc); - if (!IS_ERR_OR_NULL(drv_ctx)) + if (!IS_ERR(drv_ctx)) drv_ctx->op_type = type; ctx->drv_ctx[type] = drv_ctx; @@ -955,7 +955,7 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req, struct caam_drv_ctx *drv_ctx; drv_ctx = get_drv_ctx(ctx, encrypt ? ENCRYPT : DECRYPT); - if (IS_ERR_OR_NULL(drv_ctx)) + if (IS_ERR(drv_ctx)) return (struct aead_edesc *)drv_ctx; /* allocate space for base edesc and hw desc commands, link tables */ @@ -1165,7 +1165,7 @@ static inline int aead_crypt(struct aead_request *req, bool encrypt) /* allocate extended descriptor */ edesc = aead_edesc_alloc(req, encrypt); - if (IS_ERR_OR_NULL(edesc)) + if (IS_ERR(edesc)) return PTR_ERR(edesc); /* Create and submit job descriptor */ @@ -1259,7 +1259,7 @@ static struct skcipher_edesc *skcipher_edesc_alloc(struct skcipher_request *req, struct caam_drv_ctx *drv_ctx; drv_ctx = get_drv_ctx(ctx, encrypt ? ENCRYPT : DECRYPT); - if (IS_ERR_OR_NULL(drv_ctx)) + if (IS_ERR(drv_ctx)) return (struct skcipher_edesc *)drv_ctx; src_nents = sg_nents_for_len(req->src, req->cryptlen); @@ -2502,8 +2502,8 @@ static int caam_cra_init(struct crypto_skcipher *tfm) fallback = crypto_alloc_skcipher(tfm_name, 0, CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback)) { - dev_err(ctx->jrdev, "Failed to allocate %s fallback: %ld\n", - tfm_name, PTR_ERR(fallback)); + pr_err("Failed to allocate %s fallback: %ld\n", + tfm_name, PTR_ERR(fallback)); return PTR_ERR(fallback); } diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c index 98c1ff1744bb..a780e627838a 100644 --- a/drivers/crypto/caam/caamalg_qi2.c +++ b/drivers/crypto/caam/caamalg_qi2.c @@ -1611,7 +1611,8 @@ static int caam_cra_init_skcipher(struct crypto_skcipher *tfm) fallback = crypto_alloc_skcipher(tfm_name, 0, CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(fallback)) { - dev_err(ctx->dev, "Failed to allocate %s fallback: %ld\n", + dev_err(caam_alg->caam.dev, + "Failed to allocate %s fallback: %ld\n", tfm_name, PTR_ERR(fallback)); return PTR_ERR(fallback); } diff --git a/drivers/crypto/caam/compat.h b/drivers/crypto/caam/compat.h index c3c22a8de4c0..c4f79764172b 100644 --- a/drivers/crypto/caam/compat.h +++ b/drivers/crypto/caam/compat.h @@ -34,7 +34,8 @@ #include <crypto/ctr.h> #include <crypto/internal/des.h> #include <crypto/gcm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/md5.h> #include <crypto/chacha.h> #include <crypto/poly1305.h> diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index 9112279a4de0..7d45b21bd55a 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -16,6 +16,14 @@ /* Currently comes from Kconfig param as a ^2 (driver-required) */ #define JOBR_DEPTH (1 << CONFIG_CRYPTO_DEV_FSL_CAAM_RINGSIZE) +/* + * Maximum size for crypto-engine software queue based on Job Ring + * size (JOBR_DEPTH) and a THRESHOLD (reserved for the non-crypto-API + * requests that are not passed through crypto-engine) + */ +#define THRESHOLD 15 +#define CRYPTO_ENGINE_MAX_QLEN (JOBR_DEPTH - THRESHOLD) + /* Kconfig params for interrupt coalescing if selected (else zero) */ #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_INTC #define JOBR_INTC JRCFG_ICEN diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 6f669966ba2c..7f2b1101f567 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -550,7 +550,9 @@ static int caam_jr_probe(struct platform_device *pdev) } /* Initialize crypto engine */ - jrpriv->engine = crypto_engine_alloc_init(jrdev, false); + jrpriv->engine = crypto_engine_alloc_init_and_set(jrdev, true, NULL, + false, + CRYPTO_ENGINE_MAX_QLEN); if (!jrpriv->engine) { dev_err(jrdev, "Could not init crypto-engine\n"); return -ENOMEM; diff --git a/drivers/crypto/cavium/cpt/cptpf_main.c b/drivers/crypto/cavium/cpt/cptpf_main.c index 781949027451..711b1acdd4e0 100644 --- a/drivers/crypto/cavium/cpt/cptpf_main.c +++ b/drivers/crypto/cavium/cpt/cptpf_main.c @@ -244,7 +244,7 @@ cpt_init_fail: struct ucode_header { u8 version[CPT_UCODE_VERSION_SZ]; - u32 code_length; + __be32 code_length; u32 data_length; u64 sram_address; }; @@ -288,10 +288,10 @@ static int cpt_ucode_load_fw(struct cpt_device *cpt, const u8 *fw, bool is_ae) /* Byte swap 64-bit */ for (j = 0; j < (mcode->code_size / 8); j++) - ((u64 *)mcode->code)[j] = cpu_to_be64(((u64 *)mcode->code)[j]); + ((__be64 *)mcode->code)[j] = cpu_to_be64(((u64 *)mcode->code)[j]); /* MC needs 16-bit swap */ for (j = 0; j < (mcode->code_size / 2); j++) - ((u16 *)mcode->code)[j] = cpu_to_be16(((u16 *)mcode->code)[j]); + ((__be16 *)mcode->code)[j] = cpu_to_be16(((u16 *)mcode->code)[j]); dev_dbg(dev, "mcode->code_size = %u\n", mcode->code_size); dev_dbg(dev, "mcode->is_ae = %u\n", mcode->is_ae); @@ -569,15 +569,9 @@ static int cpt_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto cpt_err_disable_device; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); if (err) { - dev_err(dev, "Unable to get usable DMA configuration\n"); - goto cpt_err_release_regions; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); - if (err) { - dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); goto cpt_err_release_regions; } diff --git a/drivers/crypto/cavium/cpt/cptvf_main.c b/drivers/crypto/cavium/cpt/cptvf_main.c index a15245992cf9..f016448e43bb 100644 --- a/drivers/crypto/cavium/cpt/cptvf_main.c +++ b/drivers/crypto/cavium/cpt/cptvf_main.c @@ -687,15 +687,9 @@ static int cptvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Mark as VF driver */ cptvf->flags |= CPT_FLAG_VF_DRIVER; - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); if (err) { - dev_err(dev, "Unable to get usable DMA configuration\n"); - goto cptvf_err_release_regions; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); - if (err) { - dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); goto cptvf_err_release_regions; } diff --git a/drivers/crypto/cavium/nitrox/nitrox_aead.c b/drivers/crypto/cavium/nitrox/nitrox_aead.c index 1be2571363fe..c93c4e41d267 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_aead.c +++ b/drivers/crypto/cavium/nitrox/nitrox_aead.c @@ -7,7 +7,6 @@ #include <crypto/aead.h> #include <crypto/authenc.h> #include <crypto/des.h> -#include <crypto/sha.h> #include <crypto/internal/aead.h> #include <crypto/scatterwalk.h> #include <crypto/gcm.h> @@ -45,9 +44,9 @@ static int nitrox_aes_gcm_setkey(struct crypto_aead *aead, const u8 *key, /* fill crypto context */ fctx = nctx->u.fctx; - flags.f = be64_to_cpu(fctx->flags.f); + flags.fu = be64_to_cpu(fctx->flags.f); flags.w0.aes_keylen = aes_keylen; - fctx->flags.f = cpu_to_be64(flags.f); + fctx->flags.f = cpu_to_be64(flags.fu); /* copy enc key to context */ memset(&fctx->crypto, 0, sizeof(fctx->crypto)); @@ -63,9 +62,9 @@ static int nitrox_aead_setauthsize(struct crypto_aead *aead, struct flexi_crypto_context *fctx = nctx->u.fctx; union fc_ctx_flags flags; - flags.f = be64_to_cpu(fctx->flags.f); + flags.fu = be64_to_cpu(fctx->flags.f); flags.w0.mac_len = authsize; - fctx->flags.f = cpu_to_be64(flags.f); + fctx->flags.f = cpu_to_be64(flags.fu); aead->authsize = authsize; @@ -319,7 +318,7 @@ static int nitrox_gcm_common_init(struct crypto_aead *aead) flags->w0.iv_source = IV_FROM_DPTR; /* ask microcode to calculate ipad/opad */ flags->w0.auth_input_type = 1; - flags->f = be64_to_cpu(flags->f); + flags->f = cpu_to_be64(flags->fu); return 0; } diff --git a/drivers/crypto/cavium/nitrox/nitrox_debugfs.c b/drivers/crypto/cavium/nitrox/nitrox_debugfs.c index 16f7d0bd1303..741572a01995 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_debugfs.c +++ b/drivers/crypto/cavium/nitrox/nitrox_debugfs.c @@ -3,6 +3,7 @@ #include <linux/debugfs.h> #include "nitrox_csr.h" +#include "nitrox_debugfs.h" #include "nitrox_dev.h" static int firmware_show(struct seq_file *s, void *v) diff --git a/drivers/crypto/cavium/nitrox/nitrox_hal.c b/drivers/crypto/cavium/nitrox/nitrox_hal.c index 34a2f4f30a7e..13b137410b75 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_hal.c +++ b/drivers/crypto/cavium/nitrox/nitrox_hal.c @@ -3,6 +3,7 @@ #include "nitrox_dev.h" #include "nitrox_csr.h" +#include "nitrox_hal.h" #define PLL_REF_CLK 50 #define MAX_CSR_RETRIES 10 diff --git a/drivers/crypto/cavium/nitrox/nitrox_isr.c b/drivers/crypto/cavium/nitrox/nitrox_isr.c index 3dec570a190a..99b053094f5a 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_isr.c +++ b/drivers/crypto/cavium/nitrox/nitrox_isr.c @@ -7,6 +7,7 @@ #include "nitrox_csr.h" #include "nitrox_common.h" #include "nitrox_hal.h" +#include "nitrox_isr.h" #include "nitrox_mbx.h" /** diff --git a/drivers/crypto/cavium/nitrox/nitrox_isr.h b/drivers/crypto/cavium/nitrox/nitrox_isr.h index 1062c9336c1f..2bb123cd2f1c 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_isr.h +++ b/drivers/crypto/cavium/nitrox/nitrox_isr.h @@ -9,4 +9,13 @@ void nitrox_unregister_interrupts(struct nitrox_device *ndev); int nitrox_sriov_register_interupts(struct nitrox_device *ndev); void nitrox_sriov_unregister_interrupts(struct nitrox_device *ndev); +#ifdef CONFIG_PCI_IOV +int nitrox_sriov_configure(struct pci_dev *pdev, int num_vfs); +#else +static inline int nitrox_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + return 0; +} +#endif + #endif /* __NITROX_ISR_H */ diff --git a/drivers/crypto/cavium/nitrox/nitrox_main.c b/drivers/crypto/cavium/nitrox/nitrox_main.c index 9d14be97e381..facc8e6bc580 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_main.c +++ b/drivers/crypto/cavium/nitrox/nitrox_main.c @@ -49,15 +49,6 @@ static unsigned int qlen = DEFAULT_CMD_QLEN; module_param(qlen, uint, 0644); MODULE_PARM_DESC(qlen, "Command queue length - default 2048"); -#ifdef CONFIG_PCI_IOV -int nitrox_sriov_configure(struct pci_dev *pdev, int num_vfs); -#else -int nitrox_sriov_configure(struct pci_dev *pdev, int num_vfs) -{ - return 0; -} -#endif - /** * struct ucode - Firmware Header * @id: microcode ID @@ -555,10 +546,8 @@ static void nitrox_remove(struct pci_dev *pdev) nitrox_remove_from_devlist(ndev); -#ifdef CONFIG_PCI_IOV /* disable SR-IOV */ nitrox_sriov_configure(pdev, 0); -#endif nitrox_crypto_unregister(); nitrox_debugfs_exit(ndev); nitrox_pf_sw_cleanup(ndev); @@ -584,9 +573,7 @@ static struct pci_driver nitrox_driver = { .probe = nitrox_probe, .remove = nitrox_remove, .shutdown = nitrox_shutdown, -#ifdef CONFIG_PCI_IOV .sriov_configure = nitrox_sriov_configure, -#endif }; module_pci_driver(nitrox_driver); diff --git a/drivers/crypto/cavium/nitrox/nitrox_mbx.c b/drivers/crypto/cavium/nitrox/nitrox_mbx.c index b51b0449b478..c1af9d4fca6e 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_mbx.c +++ b/drivers/crypto/cavium/nitrox/nitrox_mbx.c @@ -4,6 +4,7 @@ #include "nitrox_csr.h" #include "nitrox_hal.h" #include "nitrox_dev.h" +#include "nitrox_mbx.h" #define RING_TO_VFNO(_x, _y) ((_x) / (_y)) @@ -112,7 +113,7 @@ static void pf2vf_resp_handler(struct work_struct *work) case MBX_MSG_TYPE_ACK: case MBX_MSG_TYPE_NACK: break; - }; + } kfree(pf2vf_resp); } diff --git a/drivers/crypto/cavium/nitrox/nitrox_req.h b/drivers/crypto/cavium/nitrox/nitrox_req.h index 12282c1b14f5..ed174883c8e3 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_req.h +++ b/drivers/crypto/cavium/nitrox/nitrox_req.h @@ -149,6 +149,7 @@ struct auth_keys { union fc_ctx_flags { __be64 f; + u64 fu; struct { #if defined(__BIG_ENDIAN_BITFIELD) u64 cipher_type : 4; @@ -280,6 +281,7 @@ struct nitrox_rfc4106_rctx { * - packet payload bytes */ union pkt_instr_hdr { + __be64 bev; u64 value; struct { #if defined(__BIG_ENDIAN_BITFIELD) @@ -324,6 +326,7 @@ union pkt_instr_hdr { * @ctxp: Context pointer. CTXP<63,2:0> must be zero in all cases. */ union pkt_hdr { + __be64 bev[2]; u64 value[2]; struct { #if defined(__BIG_ENDIAN_BITFIELD) @@ -370,6 +373,7 @@ union pkt_hdr { * sglist components at [RPTR] on the remote host. */ union slc_store_info { + __be64 bev[2]; u64 value[2]; struct { #if defined(__BIG_ENDIAN_BITFIELD) diff --git a/drivers/crypto/cavium/nitrox/nitrox_reqmgr.c b/drivers/crypto/cavium/nitrox/nitrox_reqmgr.c index 5826c2c98a50..53ef06792133 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_reqmgr.c +++ b/drivers/crypto/cavium/nitrox/nitrox_reqmgr.c @@ -3,6 +3,7 @@ #include <linux/workqueue.h> #include <crypto/internal/skcipher.h> +#include "nitrox_common.h" #include "nitrox_dev.h" #include "nitrox_req.h" #include "nitrox_csr.h" @@ -448,7 +449,7 @@ int nitrox_process_se_request(struct nitrox_device *ndev, sr->instr.ih.s.ssz = sr->out.sgmap_cnt; sr->instr.ih.s.fsz = FDATA_SIZE + sizeof(struct gphdr); sr->instr.ih.s.tlen = sr->instr.ih.s.fsz + sr->in.total_bytes; - sr->instr.ih.value = cpu_to_be64(sr->instr.ih.value); + sr->instr.ih.bev = cpu_to_be64(sr->instr.ih.value); /* word 2 */ sr->instr.irh.value[0] = 0; @@ -460,7 +461,7 @@ int nitrox_process_se_request(struct nitrox_device *ndev, sr->instr.irh.s.ctxc = req->ctrl.s.ctxc; sr->instr.irh.s.arg = req->ctrl.s.arg; sr->instr.irh.s.opcode = req->opcode; - sr->instr.irh.value[0] = cpu_to_be64(sr->instr.irh.value[0]); + sr->instr.irh.bev[0] = cpu_to_be64(sr->instr.irh.value[0]); /* word 3 */ sr->instr.irh.s.ctxp = cpu_to_be64(ctx_handle); @@ -468,7 +469,7 @@ int nitrox_process_se_request(struct nitrox_device *ndev, /* word 4 */ sr->instr.slc.value[0] = 0; sr->instr.slc.s.ssz = sr->out.sgmap_cnt; - sr->instr.slc.value[0] = cpu_to_be64(sr->instr.slc.value[0]); + sr->instr.slc.bev[0] = cpu_to_be64(sr->instr.slc.value[0]); /* word 5 */ sr->instr.slc.s.rptr = cpu_to_be64(sr->out.sgcomp_dma); diff --git a/drivers/crypto/cavium/zip/zip_main.c b/drivers/crypto/cavium/zip/zip_main.c index d35216e2f6cd..812b4ac9afd6 100644 --- a/drivers/crypto/cavium/zip/zip_main.c +++ b/drivers/crypto/cavium/zip/zip_main.c @@ -263,15 +263,9 @@ static int zip_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_disable_device; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); if (err) { - dev_err(dev, "Unable to get usable DMA configuration\n"); - goto err_release_regions; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); - if (err) { - dev_err(dev, "Unable to get 48-bit DMA for allocations\n"); + dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); goto err_release_regions; } diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c index 8fbfdb9e8cd3..74fa5360e722 100644 --- a/drivers/crypto/ccp/ccp-crypto-sha.c +++ b/drivers/crypto/ccp/ccp-crypto-sha.c @@ -17,7 +17,8 @@ #include <crypto/hash.h> #include <crypto/hmac.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/scatterwalk.h> #include <linux/string.h> diff --git a/drivers/crypto/ccp/ccp-crypto.h b/drivers/crypto/ccp/ccp-crypto.h index aed3d2192d01..e42450d07168 100644 --- a/drivers/crypto/ccp/ccp-crypto.h +++ b/drivers/crypto/ccp/ccp-crypto.h @@ -19,7 +19,8 @@ #include <crypto/aead.h> #include <crypto/ctr.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/akcipher.h> #include <crypto/skcipher.h> #include <crypto/internal/rsa.h> diff --git a/drivers/crypto/ccree/cc_cipher.c b/drivers/crypto/ccree/cc_cipher.c index dafa6577a845..cdfee501fbd9 100644 --- a/drivers/crypto/ccree/cc_cipher.c +++ b/drivers/crypto/ccree/cc_cipher.c @@ -97,6 +97,7 @@ static int validate_keys_sizes(struct cc_cipher_ctx *ctx_p, u32 size) case S_DIN_to_SM4: if (size == SM4_KEY_SIZE) return 0; + break; default: break; } @@ -139,9 +140,11 @@ static int validate_data_size(struct cc_cipher_ctx *ctx_p, case DRV_CIPHER_CBC: if (IS_ALIGNED(size, SM4_BLOCK_SIZE)) return 0; + break; default: break; } + break; default: break; } diff --git a/drivers/crypto/ccree/cc_driver.c b/drivers/crypto/ccree/cc_driver.c index 6f519d3e896c..d0e59e942568 100644 --- a/drivers/crypto/ccree/cc_driver.c +++ b/drivers/crypto/ccree/cc_driver.c @@ -100,6 +100,57 @@ static const struct of_device_id arm_ccree_dev_of_match[] = { }; MODULE_DEVICE_TABLE(of, arm_ccree_dev_of_match); +static void init_cc_cache_params(struct cc_drvdata *drvdata) +{ + struct device *dev = drvdata_to_dev(drvdata); + u32 cache_params, ace_const, val, mask; + + /* compute CC_AXIM_CACHE_PARAMS */ + cache_params = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS)); + dev_dbg(dev, "Cache params previous: 0x%08X\n", cache_params); + + /* non cached or write-back, write allocate */ + val = drvdata->coherent ? 0xb : 0x2; + + mask = CC_GENMASK(CC_AXIM_CACHE_PARAMS_AWCACHE); + cache_params &= ~mask; + cache_params |= FIELD_PREP(mask, val); + + mask = CC_GENMASK(CC_AXIM_CACHE_PARAMS_AWCACHE_LAST); + cache_params &= ~mask; + cache_params |= FIELD_PREP(mask, val); + + mask = CC_GENMASK(CC_AXIM_CACHE_PARAMS_ARCACHE); + cache_params &= ~mask; + cache_params |= FIELD_PREP(mask, val); + + drvdata->cache_params = cache_params; + + dev_dbg(dev, "Cache params current: 0x%08X\n", cache_params); + + if (drvdata->hw_rev <= CC_HW_REV_710) + return; + + /* compute CC_AXIM_ACE_CONST */ + ace_const = cc_ioread(drvdata, CC_REG(AXIM_ACE_CONST)); + dev_dbg(dev, "ACE-const previous: 0x%08X\n", ace_const); + + /* system or outer-sharable */ + val = drvdata->coherent ? 0x2 : 0x3; + + mask = CC_GENMASK(CC_AXIM_ACE_CONST_ARDOMAIN); + ace_const &= ~mask; + ace_const |= FIELD_PREP(mask, val); + + mask = CC_GENMASK(CC_AXIM_ACE_CONST_AWDOMAIN); + ace_const &= ~mask; + ace_const |= FIELD_PREP(mask, val); + + dev_dbg(dev, "ACE-const current: 0x%08X\n", ace_const); + + drvdata->ace_const = ace_const; +} + static u32 cc_read_idr(struct cc_drvdata *drvdata, const u32 *idr_offsets) { int i; @@ -218,9 +269,9 @@ bool cc_wait_for_reset_completion(struct cc_drvdata *drvdata) return false; } -int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe) +int init_cc_regs(struct cc_drvdata *drvdata) { - unsigned int val, cache_params; + unsigned int val; struct device *dev = drvdata_to_dev(drvdata); /* Unmask all AXI interrupt sources AXI_CFG1 register */ @@ -245,19 +296,9 @@ int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe) cc_iowrite(drvdata, CC_REG(HOST_IMR), ~val); - cache_params = (drvdata->coherent ? CC_COHERENT_CACHE_PARAMS : 0x0); - - val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS)); - - if (is_probe) - dev_dbg(dev, "Cache params previous: 0x%08X\n", val); - - cc_iowrite(drvdata, CC_REG(AXIM_CACHE_PARAMS), cache_params); - val = cc_ioread(drvdata, CC_REG(AXIM_CACHE_PARAMS)); - - if (is_probe) - dev_dbg(dev, "Cache params current: 0x%08X (expect: 0x%08X)\n", - val, cache_params); + cc_iowrite(drvdata, CC_REG(AXIM_CACHE_PARAMS), drvdata->cache_params); + if (drvdata->hw_rev >= CC_HW_REV_712) + cc_iowrite(drvdata, CC_REG(AXIM_ACE_CONST), drvdata->ace_const); return 0; } @@ -445,7 +486,9 @@ static int init_cc_resources(struct platform_device *plat_dev) } dev_dbg(dev, "Registered to IRQ: %d\n", irq); - rc = init_cc_regs(new_drvdata, true); + init_cc_cache_params(new_drvdata); + + rc = init_cc_regs(new_drvdata); if (rc) { dev_err(dev, "init_cc_regs failed\n"); goto post_pm_err; diff --git a/drivers/crypto/ccree/cc_driver.h b/drivers/crypto/ccree/cc_driver.h index af77b2020350..5f1d4602eb8f 100644 --- a/drivers/crypto/ccree/cc_driver.h +++ b/drivers/crypto/ccree/cc_driver.h @@ -17,7 +17,8 @@ #include <crypto/algapi.h> #include <crypto/internal/skcipher.h> #include <crypto/aes.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/aead.h> #include <crypto/authenc.h> #include <crypto/hash.h> @@ -49,8 +50,6 @@ enum cc_std_body { CC_STD_ALL = 0x3 }; -#define CC_COHERENT_CACHE_PARAMS 0xEEE - #define CC_PINS_FULL 0x0 #define CC_PINS_SLIM 0x9F @@ -155,6 +154,8 @@ struct cc_drvdata { int std_bodies; bool sec_disabled; u32 comp_mask; + u32 cache_params; + u32 ace_const; }; struct cc_crypto_alg { @@ -205,7 +206,7 @@ static inline void dump_byte_array(const char *name, const u8 *the_array, } bool cc_wait_for_reset_completion(struct cc_drvdata *drvdata); -int init_cc_regs(struct cc_drvdata *drvdata, bool is_probe); +int init_cc_regs(struct cc_drvdata *drvdata); void fini_cc_regs(struct cc_drvdata *drvdata); unsigned int cc_get_default_hash_len(struct cc_drvdata *drvdata); diff --git a/drivers/crypto/ccree/cc_pm.c b/drivers/crypto/ccree/cc_pm.c index 3c65bf070c90..d5421b0c6831 100644 --- a/drivers/crypto/ccree/cc_pm.c +++ b/drivers/crypto/ccree/cc_pm.c @@ -45,7 +45,7 @@ static int cc_pm_resume(struct device *dev) } cc_iowrite(drvdata, CC_REG(HOST_POWER_DOWN_EN), POWER_DOWN_DISABLE); - rc = init_cc_regs(drvdata, false); + rc = init_cc_regs(drvdata); if (rc) { dev_err(dev, "init_cc_regs (%x)\n", rc); return rc; diff --git a/drivers/crypto/chelsio/chcr_algo.c b/drivers/crypto/chelsio/chcr_algo.c index 13b908ea4873..f5a336634daa 100644 --- a/drivers/crypto/chelsio/chcr_algo.c +++ b/drivers/crypto/chelsio/chcr_algo.c @@ -53,7 +53,8 @@ #include <crypto/algapi.h> #include <crypto/hash.h> #include <crypto/gcm.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/authenc.h> #include <crypto/ctr.h> #include <crypto/gf128mul.h> diff --git a/drivers/crypto/hisilicon/Kconfig b/drivers/crypto/hisilicon/Kconfig index 9c3b3ca815e6..843192666dc3 100644 --- a/drivers/crypto/hisilicon/Kconfig +++ b/drivers/crypto/hisilicon/Kconfig @@ -71,3 +71,11 @@ config CRYPTO_DEV_HISI_HPRE help Support for HiSilicon HPRE(High Performance RSA Engine) accelerator, which can accelerate RSA and DH algorithms. + +config CRYPTO_DEV_HISI_TRNG + tristate "Support for HISI TRNG Driver" + depends on ARM64 && ACPI + select HW_RANDOM + select CRYPTO_RNG + help + Support for HiSilicon TRNG Driver. diff --git a/drivers/crypto/hisilicon/Makefile b/drivers/crypto/hisilicon/Makefile index 7f5f74c72baa..1e89269a2e4b 100644 --- a/drivers/crypto/hisilicon/Makefile +++ b/drivers/crypto/hisilicon/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_CRYPTO_DEV_HISI_SEC2) += sec2/ obj-$(CONFIG_CRYPTO_DEV_HISI_QM) += hisi_qm.o hisi_qm-objs = qm.o sgl.o obj-$(CONFIG_CRYPTO_DEV_HISI_ZIP) += zip/ +obj-$(CONFIG_CRYPTO_DEV_HISI_TRNG) += trng/ diff --git a/drivers/crypto/hisilicon/hpre/hpre_main.c b/drivers/crypto/hisilicon/hpre/hpre_main.c index a33394d91bbf..e5c991913f09 100644 --- a/drivers/crypto/hisilicon/hpre/hpre_main.c +++ b/drivers/crypto/hisilicon/hpre/hpre_main.c @@ -705,9 +705,7 @@ static int hpre_debugfs_init(struct hisi_qm *qm) qm->debug.sqe_mask_offset = HPRE_SQE_MASK_OFFSET; qm->debug.sqe_mask_len = HPRE_SQE_MASK_LEN; - ret = hisi_qm_debug_init(qm); - if (ret) - goto failed_to_create; + hisi_qm_debug_init(qm); if (qm->pdev->device == HPRE_PCI_DEVICE_ID) { ret = hpre_ctrl_debug_init(qm); diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 530f23116d7c..f21ccae0e8ea 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -473,7 +473,7 @@ static int qm_wait_mb_ready(struct hisi_qm *qm) return readl_relaxed_poll_timeout(qm->io_base + QM_MB_CMD_SEND_BASE, val, !((val >> QM_MB_BUSY_SHIFT) & - 0x1), 10, 1000); + 0x1), POLL_PERIOD, POLL_TIMEOUT); } /* 128 bit should be written to hardware at one time to trigger a mailbox */ @@ -583,7 +583,8 @@ static int qm_dev_mem_reset(struct hisi_qm *qm) writel(0x1, qm->io_base + QM_MEM_START_INIT); return readl_relaxed_poll_timeout(qm->io_base + QM_MEM_INIT_DONE, val, - val & BIT(0), 10, 1000); + val & BIT(0), POLL_PERIOD, + POLL_TIMEOUT); } static u32 qm_get_irq_num_v1(struct hisi_qm *qm) @@ -804,7 +805,8 @@ static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type, int ret; ret = readl_relaxed_poll_timeout(qm->io_base + QM_VFT_CFG_RDY, val, - val & BIT(0), 10, 1000); + val & BIT(0), POLL_PERIOD, + POLL_TIMEOUT); if (ret) return ret; @@ -818,7 +820,8 @@ static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type, writel(0x1, qm->io_base + QM_VFT_CFG_OP_ENABLE); return readl_relaxed_poll_timeout(qm->io_base + QM_VFT_CFG_RDY, val, - val & BIT(0), 10, 1000); + val & BIT(0), POLL_PERIOD, + POLL_TIMEOUT); } /* The config should be conducted after qm_dev_mem_reset() */ @@ -929,7 +932,8 @@ static ssize_t qm_debug_read(struct file *filp, char __user *buf, return -EINVAL; } mutex_unlock(&file->lock); - ret = sprintf(tbuf, "%u\n", val); + + ret = scnprintf(tbuf, QM_DBG_TMP_BUF_LEN, "%u\n", val); return simple_read_from_buffer(buf, count, pos, tbuf, ret); } @@ -1517,7 +1521,7 @@ static const struct file_operations qm_cmd_fops = { .write = qm_cmd_write, }; -static int qm_create_debugfs_file(struct hisi_qm *qm, enum qm_debug_file index) +static void qm_create_debugfs_file(struct hisi_qm *qm, enum qm_debug_file index) { struct dentry *qm_d = qm->debug.qm_d; struct debugfs_file *file = qm->debug.files + index; @@ -1528,8 +1532,6 @@ static int qm_create_debugfs_file(struct hisi_qm *qm, enum qm_debug_file index) file->index = index; mutex_init(&file->lock); file->debug = &qm->debug; - - return 0; } static void qm_hw_error_init_v1(struct hisi_qm *qm, u32 ce, u32 nfe, u32 fe) @@ -1733,19 +1735,15 @@ void hisi_qm_release_qp(struct hisi_qp *qp) } EXPORT_SYMBOL_GPL(hisi_qm_release_qp); -static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) +static int qm_sq_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) { struct hisi_qm *qm = qp->qm; struct device *dev = &qm->pdev->dev; enum qm_hw_ver ver = qm->ver; struct qm_sqc *sqc; - struct qm_cqc *cqc; dma_addr_t sqc_dma; - dma_addr_t cqc_dma; int ret; - qm_init_qp_status(qp); - sqc = kzalloc(sizeof(struct qm_sqc), GFP_KERNEL); if (!sqc) return -ENOMEM; @@ -1770,8 +1768,18 @@ static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) ret = qm_mb(qm, QM_MB_CMD_SQC, sqc_dma, qp_id, 0); dma_unmap_single(dev, sqc_dma, sizeof(struct qm_sqc), DMA_TO_DEVICE); kfree(sqc); - if (ret) - return ret; + + return ret; +} + +static int qm_cq_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) +{ + struct hisi_qm *qm = qp->qm; + struct device *dev = &qm->pdev->dev; + enum qm_hw_ver ver = qm->ver; + struct qm_cqc *cqc; + dma_addr_t cqc_dma; + int ret; cqc = kzalloc(sizeof(struct qm_cqc), GFP_KERNEL); if (!cqc) @@ -1785,11 +1793,12 @@ static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) INIT_QC_COMMON(cqc, qp->cqe_dma, pasid); if (ver == QM_HW_V1) { - cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V1(0, 0, 0, 4)); + cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V1(0, 0, 0, + QM_QC_CQE_SIZE)); cqc->w8 = cpu_to_le16(QM_Q_DEPTH - 1); } else { - cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V2(4)); - cqc->w8 = 0; + cqc->dw3 = cpu_to_le32(QM_MK_CQC_DW3_V2(QM_QC_CQE_SIZE)); + cqc->w8 = 0; /* rand_qc */ } cqc->dw6 = cpu_to_le32(1 << QM_CQ_PHASE_SHIFT | 1 << QM_CQ_FLAG_SHIFT); @@ -1800,6 +1809,19 @@ static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) return ret; } +static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid) +{ + int ret; + + qm_init_qp_status(qp); + + ret = qm_sq_ctx_cfg(qp, qp_id, pasid); + if (ret) + return ret; + + return qm_cq_ctx_cfg(qp, qp_id, pasid); +} + static int qm_start_qp_nolock(struct hisi_qp *qp, unsigned long arg) { struct hisi_qm *qm = qp->qm; @@ -1843,6 +1865,9 @@ int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg) EXPORT_SYMBOL_GPL(hisi_qm_start_qp); /** + * qm_drain_qp() - Drain a qp. + * @qp: The qp we want to drain. + * * Determine whether the queue is cleared by judging the tail pointers of * sq and cq. */ @@ -2008,7 +2033,8 @@ static void hisi_qm_cache_wb(struct hisi_qm *qm) writel(0x1, qm->io_base + QM_CACHE_WB_START); if (readl_relaxed_poll_timeout(qm->io_base + QM_CACHE_WB_DONE, - val, val & BIT(0), 10, 1000)) + val, val & BIT(0), POLL_PERIOD, + POLL_TIMEOUT)) dev_err(&qm->pdev->dev, "QM writeback sqc cache fail!\n"); } @@ -2112,7 +2138,7 @@ static void hisi_qm_uacce_stop_queue(struct uacce_queue *q) hisi_qm_stop_qp(q->priv); } -static int qm_set_sqctype(struct uacce_queue *q, u16 type) +static void qm_set_sqctype(struct uacce_queue *q, u16 type) { struct hisi_qm *qm = q->uacce->priv; struct hisi_qp *qp = q->priv; @@ -2120,8 +2146,6 @@ static int qm_set_sqctype(struct uacce_queue *q, u16 type) down_write(&qm->qps_lock); qp->alg_type = type; up_write(&qm->qps_lock); - - return 0; } static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd, @@ -2418,6 +2442,16 @@ static void hisi_qm_pre_init(struct hisi_qm *qm) qm->is_frozen = false; } +static void hisi_qm_pci_uninit(struct hisi_qm *qm) +{ + struct pci_dev *pdev = qm->pdev; + + pci_free_irq_vectors(pdev); + iounmap(qm->io_base); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + /** * hisi_qm_uninit() - Uninitialize qm. * @qm: The qm needed uninit. @@ -2436,9 +2470,6 @@ void hisi_qm_uninit(struct hisi_qm *qm) return; } - uacce_remove(qm->uacce); - qm->uacce = NULL; - hisi_qp_memory_uninit(qm, qm->qp_num); idr_destroy(&qm->qp_idr); @@ -2450,10 +2481,9 @@ void hisi_qm_uninit(struct hisi_qm *qm) } qm_irq_unregister(qm); - pci_free_irq_vectors(pdev); - iounmap(qm->io_base); - pci_release_mem_regions(pdev); - pci_disable_device(pdev); + hisi_qm_pci_uninit(qm); + uacce_remove(qm->uacce); + qm->uacce = NULL; up_write(&qm->qps_lock); } @@ -2486,6 +2516,12 @@ int hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number) EXPORT_SYMBOL_GPL(hisi_qm_get_vft); /** + * hisi_qm_set_vft() - Set vft to a qm. + * @qm: The qm we want to set its vft. + * @fun_num: The function number. + * @base: The base number of queue in vft. + * @number: The number of queues in vft. + * * This function is alway called in PF driver, it is used to assign queues * among PF and VFs. * @@ -2519,14 +2555,10 @@ static int qm_eq_ctx_cfg(struct hisi_qm *qm) { struct device *dev = &qm->pdev->dev; struct qm_eqc *eqc; - struct qm_aeqc *aeqc; dma_addr_t eqc_dma; - dma_addr_t aeqc_dma; int ret; - qm_init_eq_aeq_status(qm); - - eqc = kzalloc(sizeof(struct qm_eqc), GFP_KERNEL); + eqc = kzalloc(sizeof(struct qm_eqc), GFP_KERNEL); //todo if (!eqc) return -ENOMEM; eqc_dma = dma_map_single(dev, eqc, sizeof(struct qm_eqc), @@ -2541,11 +2573,20 @@ static int qm_eq_ctx_cfg(struct hisi_qm *qm) if (qm->ver == QM_HW_V1) eqc->dw3 = cpu_to_le32(QM_EQE_AEQE_SIZE); eqc->dw6 = cpu_to_le32((QM_EQ_DEPTH - 1) | (1 << QM_EQC_PHASE_SHIFT)); + ret = qm_mb(qm, QM_MB_CMD_EQC, eqc_dma, 0, 0); dma_unmap_single(dev, eqc_dma, sizeof(struct qm_eqc), DMA_TO_DEVICE); kfree(eqc); - if (ret) - return ret; + + return ret; +} + +static int qm_aeq_ctx_cfg(struct hisi_qm *qm) +{ + struct device *dev = &qm->pdev->dev; + struct qm_aeqc *aeqc; + dma_addr_t aeqc_dma; + int ret; aeqc = kzalloc(sizeof(struct qm_aeqc), GFP_KERNEL); if (!aeqc) @@ -2568,6 +2609,22 @@ static int qm_eq_ctx_cfg(struct hisi_qm *qm) return ret; } +static int qm_eq_aeq_ctx_cfg(struct hisi_qm *qm) +{ + struct device *dev = &qm->pdev->dev; + int ret; + + qm_init_eq_aeq_status(qm); + + ret = qm_eq_ctx_cfg(qm); + if (ret) { + dev_err(dev, "Set eqc failed!\n"); + return ret; + } + + return qm_aeq_ctx_cfg(qm); +} + static int __hisi_qm_start(struct hisi_qm *qm) { int ret; @@ -2584,7 +2641,7 @@ static int __hisi_qm_start(struct hisi_qm *qm) return ret; } - ret = qm_eq_ctx_cfg(qm); + ret = qm_eq_aeq_ctx_cfg(qm); if (ret) return ret; @@ -2690,7 +2747,11 @@ static int qm_stop_started_qp(struct hisi_qm *qm) return 0; } + /** + * qm_clear_queues() - Clear all queues memory in a qm. + * @qm: The qm in which the queues will be cleared. + * * This function clears all queues memory in a qm. Reset of accelerator can * use this to clear queues. */ @@ -2806,12 +2867,12 @@ DEFINE_DEBUGFS_ATTRIBUTE(qm_atomic64_ops, qm_debugfs_atomic64_get, * * Create qm related debugfs files. */ -int hisi_qm_debug_init(struct hisi_qm *qm) +void hisi_qm_debug_init(struct hisi_qm *qm) { struct qm_dfx *dfx = &qm->debug.dfx; struct dentry *qm_d; void *data; - int i, ret; + int i; qm_d = debugfs_create_dir("qm", qm->debug.debug_root); qm->debug.qm_d = qm_d; @@ -2819,10 +2880,7 @@ int hisi_qm_debug_init(struct hisi_qm *qm) /* only show this in PF */ if (qm->fun_type == QM_HW_PF) for (i = CURRENT_Q; i < DEBUG_FILE_NUM; i++) - if (qm_create_debugfs_file(qm, i)) { - ret = -ENOENT; - goto failed_to_create; - } + qm_create_debugfs_file(qm, i); debugfs_create_file("regs", 0444, qm->debug.qm_d, qm, &qm_regs_fops); @@ -2838,12 +2896,6 @@ int hisi_qm_debug_init(struct hisi_qm *qm) data, &qm_atomic64_ops); } - - return 0; - -failed_to_create: - debugfs_remove_recursive(qm_d); - return ret; } EXPORT_SYMBOL_GPL(hisi_qm_debug_init); @@ -3273,7 +3325,7 @@ pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev, } EXPORT_SYMBOL_GPL(hisi_qm_dev_err_detected); -static int qm_get_hw_error_status(struct hisi_qm *qm) +static u32 qm_get_hw_error_status(struct hisi_qm *qm) { return readl(qm->io_base + QM_ABNORMAL_INT_STATUS); } @@ -3572,7 +3624,7 @@ restart_fail: return ret; } -static int qm_get_dev_err_status(struct hisi_qm *qm) +static u32 qm_get_dev_err_status(struct hisi_qm *qm) { return qm->err_ini->get_dev_hw_err_status(qm); } @@ -3992,34 +4044,22 @@ void hisi_qm_alg_unregister(struct hisi_qm *qm, struct hisi_qm_list *qm_list) } EXPORT_SYMBOL_GPL(hisi_qm_alg_unregister); -/** - * hisi_qm_init() - Initialize configures about qm. - * @qm: The qm needing init. - * - * This function init qm, then we can call hisi_qm_start to put qm into work. - */ -int hisi_qm_init(struct hisi_qm *qm) +static int hisi_qm_pci_init(struct hisi_qm *qm) { struct pci_dev *pdev = qm->pdev; struct device *dev = &pdev->dev; unsigned int num_vec; int ret; - hisi_qm_pre_init(qm); - - ret = qm_alloc_uacce(qm); - if (ret < 0) - dev_warn(&pdev->dev, "fail to alloc uacce (%d)\n", ret); - ret = pci_enable_device_mem(pdev); if (ret < 0) { - dev_err(&pdev->dev, "Failed to enable device mem!\n"); - goto err_remove_uacce; + dev_err(dev, "Failed to enable device mem!\n"); + return ret; } ret = pci_request_mem_regions(pdev, qm->dev_name); if (ret < 0) { - dev_err(&pdev->dev, "Failed to request mem regions!\n"); + dev_err(dev, "Failed to request mem regions!\n"); goto err_disable_pcidev; } @@ -4047,9 +4087,42 @@ int hisi_qm_init(struct hisi_qm *qm) goto err_iounmap; } + return 0; + +err_iounmap: + iounmap(qm->io_base); +err_release_mem_regions: + pci_release_mem_regions(pdev); +err_disable_pcidev: + pci_disable_device(pdev); + return ret; +} + +/** + * hisi_qm_init() - Initialize configures about qm. + * @qm: The qm needing init. + * + * This function init qm, then we can call hisi_qm_start to put qm into work. + */ +int hisi_qm_init(struct hisi_qm *qm) +{ + struct pci_dev *pdev = qm->pdev; + struct device *dev = &pdev->dev; + int ret; + + hisi_qm_pre_init(qm); + + ret = qm_alloc_uacce(qm); + if (ret < 0) + dev_warn(dev, "fail to alloc uacce (%d)\n", ret); + + ret = hisi_qm_pci_init(qm); + if (ret) + goto err_remove_uacce; + ret = qm_irq_register(qm); if (ret) - goto err_free_irq_vectors; + goto err_pci_uninit; if (qm->fun_type == QM_HW_VF && qm->ver != QM_HW_V1) { /* v2 starts to support get vft by mailbox */ @@ -4072,14 +4145,8 @@ int hisi_qm_init(struct hisi_qm *qm) err_irq_unregister: qm_irq_unregister(qm); -err_free_irq_vectors: - pci_free_irq_vectors(pdev); -err_iounmap: - iounmap(qm->io_base); -err_release_mem_regions: - pci_release_mem_regions(pdev); -err_disable_pcidev: - pci_disable_device(pdev); +err_pci_uninit: + hisi_qm_pci_uninit(qm); err_remove_uacce: uacce_remove(qm->uacce); qm->uacce = NULL; @@ -4087,7 +4154,6 @@ err_remove_uacce: } EXPORT_SYMBOL_GPL(hisi_qm_init); - MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>"); MODULE_DESCRIPTION("HiSilicon Accelerator queue manager driver"); diff --git a/drivers/crypto/hisilicon/qm.h b/drivers/crypto/hisilicon/qm.h index 0420f4ce7197..8624d1288afe 100644 --- a/drivers/crypto/hisilicon/qm.h +++ b/drivers/crypto/hisilicon/qm.h @@ -350,7 +350,7 @@ void hisi_qm_release_qp(struct hisi_qp *qp); int hisi_qp_send(struct hisi_qp *qp, const void *msg); int hisi_qm_get_free_qp_num(struct hisi_qm *qm); int hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number); -int hisi_qm_debug_init(struct hisi_qm *qm); +void hisi_qm_debug_init(struct hisi_qm *qm); enum qm_hw_ver hisi_qm_get_hw_version(struct pci_dev *pdev); void hisi_qm_debug_regs_clear(struct hisi_qm *qm); int hisi_qm_sriov_enable(struct pci_dev *pdev, int max_vfs); diff --git a/drivers/crypto/hisilicon/sec2/sec.h b/drivers/crypto/hisilicon/sec2/sec.h index 037762b531e2..08491912afd5 100644 --- a/drivers/crypto/hisilicon/sec2/sec.h +++ b/drivers/crypto/hisilicon/sec2/sec.h @@ -109,7 +109,6 @@ struct sec_qp_ctx { struct list_head backlog; struct hisi_acc_sgl_pool *c_in_pool; struct hisi_acc_sgl_pool *c_out_pool; - atomic_t pending_reqs; }; enum sec_alg_type { @@ -180,7 +179,6 @@ struct sec_dev { struct sec_debug debug; u32 ctx_q_num; bool iommu_used; - unsigned long status; }; void sec_destroy_qps(struct hisi_qp **qps, int qp_num); diff --git a/drivers/crypto/hisilicon/sec2/sec_crypto.c b/drivers/crypto/hisilicon/sec2/sec_crypto.c index bb493423668c..2eaa516b3231 100644 --- a/drivers/crypto/hisilicon/sec2/sec_crypto.c +++ b/drivers/crypto/hisilicon/sec2/sec_crypto.c @@ -7,7 +7,8 @@ #include <crypto/des.h> #include <crypto/hash.h> #include <crypto/internal/aead.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/skcipher.h> #include <crypto/xts.h> #include <linux/crypto.h> @@ -101,6 +102,7 @@ static int sec_alloc_req_id(struct sec_req *req, struct sec_qp_ctx *qp_ctx) req->qp_ctx = qp_ctx; qp_ctx->req_list[req_id] = req; + return req_id; } @@ -317,6 +319,7 @@ static int sec_alloc_pbuf_resource(struct device *dev, struct sec_alg_res *res) j * SEC_PBUF_PKG + pbuf_page_offset; } } + return 0; } @@ -345,12 +348,12 @@ static int sec_alg_resource_alloc(struct sec_ctx *ctx, } return 0; + alloc_pbuf_fail: if (ctx->alg_type == SEC_AEAD) sec_free_mac_resource(dev, qp_ctx->res); alloc_fail: sec_free_civ_resource(dev, res); - return ret; } @@ -419,7 +422,6 @@ err_free_c_in_pool: hisi_acc_free_sgl_pool(dev, qp_ctx->c_in_pool); err_destroy_idr: idr_destroy(&qp_ctx->req_idr); - return ret; } @@ -557,9 +559,9 @@ static int sec_skcipher_init(struct crypto_skcipher *tfm) goto err_cipher_init; return 0; + err_cipher_init: sec_ctx_base_uninit(ctx); - return ret; } @@ -740,7 +742,6 @@ static void sec_cipher_pbuf_unmap(struct sec_ctx *ctx, struct sec_req *req, if (unlikely(pbuf_length != copy_size)) dev_err(dev, "copy pbuf data to dst error!\n"); - } static int sec_cipher_map(struct sec_ctx *ctx, struct sec_req *req, @@ -857,7 +858,7 @@ static int sec_aead_auth_set_key(struct sec_auth_ctx *ctx, struct crypto_authenc_keys *keys) { struct crypto_shash *hash_tfm = ctx->hash_tfm; - int blocksize, ret; + int blocksize, digestsize, ret; if (!keys->authkeylen) { pr_err("hisi_sec2: aead auth key error!\n"); @@ -865,6 +866,7 @@ static int sec_aead_auth_set_key(struct sec_auth_ctx *ctx, } blocksize = crypto_shash_blocksize(hash_tfm); + digestsize = crypto_shash_digestsize(hash_tfm); if (keys->authkeylen > blocksize) { ret = crypto_shash_tfm_digest(hash_tfm, keys->authkey, keys->authkeylen, ctx->a_key); @@ -872,7 +874,7 @@ static int sec_aead_auth_set_key(struct sec_auth_ctx *ctx, pr_err("hisi_sec2: aead auth digest error!\n"); return -EINVAL; } - ctx->a_key_len = blocksize; + ctx->a_key_len = digestsize; } else { memcpy(ctx->a_key, keys->authkey, keys->authkeylen); ctx->a_key_len = keys->authkeylen; @@ -913,9 +915,9 @@ static int sec_aead_setkey(struct crypto_aead *tfm, const u8 *key, } return 0; + bad_key: memzero_explicit(&keys, sizeof(struct crypto_authenc_keys)); - return -EINVAL; } @@ -966,7 +968,6 @@ static int sec_request_transfer(struct sec_ctx *ctx, struct sec_req *req) unmap_req_buf: ctx->req_op->buf_unmap(ctx, req); - return ret; } @@ -1107,7 +1108,6 @@ static void sec_skcipher_callback(struct sec_ctx *ctx, struct sec_req *req, atomic64_inc(&ctx->sec->debug.dfx.recv_busy_cnt); } - sk_req->base.complete(&sk_req->base, err); } @@ -1279,7 +1279,6 @@ err_send_req: sec_request_untransfer(ctx, req); err_uninit_req: sec_request_uninit(ctx, req); - return ret; } @@ -1349,7 +1348,6 @@ err_cipher_init: sec_auth_uninit(ctx); err_auth_init: sec_ctx_base_uninit(ctx); - return ret; } @@ -1437,8 +1435,8 @@ static int sec_skcipher_param_check(struct sec_ctx *ctx, struct sec_req *sreq) } return 0; } - dev_err(dev, "skcipher algorithm error!\n"); + return -EINVAL; } @@ -1554,7 +1552,6 @@ static int sec_aead_param_check(struct sec_ctx *ctx, struct sec_req *sreq) if (unlikely(c_alg != SEC_CALG_AES)) { dev_err(SEC_CTX_DEV(ctx), "aead crypto alg error!\n"); return -EINVAL; - } if (sreq->c_req.encrypt) sreq->c_req.c_len = req->cryptlen; diff --git a/drivers/crypto/hisilicon/sec2/sec_main.c b/drivers/crypto/hisilicon/sec2/sec_main.c index 548896394c4b..b35c1c2271a3 100644 --- a/drivers/crypto/hisilicon/sec2/sec_main.c +++ b/drivers/crypto/hisilicon/sec2/sec_main.c @@ -652,20 +652,16 @@ static int sec_debugfs_init(struct hisi_qm *qm) sec_debugfs_root); qm->debug.sqe_mask_offset = SEC_SQE_MASK_OFFSET; qm->debug.sqe_mask_len = SEC_SQE_MASK_LEN; - ret = hisi_qm_debug_init(qm); - if (ret) - goto failed_to_create; + hisi_qm_debug_init(qm); ret = sec_debug_init(qm); if (ret) goto failed_to_create; - return 0; failed_to_create: debugfs_remove_recursive(sec_debugfs_root); - return ret; } @@ -683,13 +679,13 @@ static void sec_log_hw_error(struct hisi_qm *qm, u32 err_sts) while (errs->msg) { if (errs->int_msk & err_sts) { dev_err(dev, "%s [error status=0x%x] found\n", - errs->msg, errs->int_msk); + errs->msg, errs->int_msk); if (SEC_CORE_INT_STATUS_M_ECC & errs->int_msk) { err_val = readl(qm->io_base + SEC_CORE_SRAM_ECC_ERR_INFO); dev_err(dev, "multi ecc sram num=0x%x\n", - SEC_ECC_NUM(err_val)); + SEC_ECC_NUM(err_val)); } } errs++; @@ -724,13 +720,13 @@ static const struct hisi_qm_err_ini sec_err_ini = { .log_dev_hw_err = sec_log_hw_error, .open_axi_master_ooo = sec_open_axi_master_ooo, .err_info = { - .ce = QM_BASE_CE, - .nfe = QM_BASE_NFE | QM_ACC_DO_TASK_TIMEOUT | - QM_ACC_WB_NOT_READY_TIMEOUT, - .fe = 0, - .ecc_2bits_mask = SEC_CORE_INT_STATUS_M_ECC, - .msi_wr_port = BIT(0), - .acpi_rst = "SRST", + .ce = QM_BASE_CE, + .nfe = QM_BASE_NFE | QM_ACC_DO_TASK_TIMEOUT | + QM_ACC_WB_NOT_READY_TIMEOUT, + .fe = 0, + .ecc_2bits_mask = SEC_CORE_INT_STATUS_M_ECC, + .msi_wr_port = BIT(0), + .acpi_rst = "SRST", } }; @@ -899,17 +895,13 @@ static int sec_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_alg_unregister: hisi_qm_alg_unregister(qm, &sec_devices); - err_qm_stop: sec_debugfs_exit(qm); hisi_qm_stop(qm, QM_NORMAL); - err_probe_uninit: sec_probe_uninit(qm); - err_qm_uninit: sec_qm_uninit(qm); - return ret; } @@ -936,9 +928,9 @@ static void sec_remove(struct pci_dev *pdev) static const struct pci_error_handlers sec_err_handler = { .error_detected = hisi_qm_dev_err_detected, - .slot_reset = hisi_qm_dev_slot_reset, - .reset_prepare = hisi_qm_reset_prepare, - .reset_done = hisi_qm_reset_done, + .slot_reset = hisi_qm_dev_slot_reset, + .reset_prepare = hisi_qm_reset_prepare, + .reset_done = hisi_qm_reset_done, }; static struct pci_driver sec_pci_driver = { diff --git a/drivers/crypto/hisilicon/sgl.c b/drivers/crypto/hisilicon/sgl.c index 725a739800b0..3bff6394acaf 100644 --- a/drivers/crypto/hisilicon/sgl.c +++ b/drivers/crypto/hisilicon/sgl.c @@ -246,8 +246,6 @@ EXPORT_SYMBOL_GPL(hisi_acc_sg_buf_map_to_hw_sgl); * @dev: The device which hw sgl belongs to. * @sgl: Related scatterlist. * @hw_sgl: Virtual address of hw sgl. - * @hw_sgl_dma: DMA address of hw sgl. - * @pool: Pool which hw sgl is allocated in. * * This function unmaps allocated hw sgl. */ diff --git a/drivers/crypto/hisilicon/trng/Makefile b/drivers/crypto/hisilicon/trng/Makefile new file mode 100644 index 000000000000..d909079f351c --- /dev/null +++ b/drivers/crypto/hisilicon/trng/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CRYPTO_DEV_HISI_TRNG) += hisi-trng-v2.o +hisi-trng-v2-objs = trng.o diff --git a/drivers/crypto/hisilicon/trng/trng.c b/drivers/crypto/hisilicon/trng/trng.c new file mode 100644 index 000000000000..29712685498a --- /dev/null +++ b/drivers/crypto/hisilicon/trng/trng.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ + +#include <linux/acpi.h> +#include <linux/crypto.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <crypto/internal/rng.h> + +#define HISI_TRNG_REG 0x00F0 +#define HISI_TRNG_BYTES 4 +#define HISI_TRNG_QUALITY 512 +#define SLEEP_US 10 +#define TIMEOUT_US 10000 +#define SW_DRBG_NUM_SHIFT 2 +#define SW_DRBG_KEY_BASE 0x082C +#define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT)) +#define SW_DRBG_SEED_REGS_NUM 12 +#define SW_DRBG_SEED_SIZE 48 +#define SW_DRBG_BLOCKS 0x0830 +#define SW_DRBG_INIT 0x0834 +#define SW_DRBG_GEN 0x083c +#define SW_DRBG_STATUS 0x0840 +#define SW_DRBG_BLOCKS_NUM 4095 +#define SW_DRBG_DATA_BASE 0x0850 +#define SW_DRBG_DATA_NUM 4 +#define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT)) +#define SW_DRBG_BYTES 16 +#define SW_DRBG_ENABLE_SHIFT 12 +#define SEED_SHIFT_24 24 +#define SEED_SHIFT_16 16 +#define SEED_SHIFT_8 8 + +struct hisi_trng_list { + struct mutex lock; + struct list_head list; + bool is_init; +}; + +struct hisi_trng { + void __iomem *base; + struct hisi_trng_list *trng_list; + struct list_head list; + struct hwrng rng; + bool is_used; + struct mutex mutex; +}; + +struct hisi_trng_ctx { + struct hisi_trng *trng; +}; + +static atomic_t trng_active_devs; +static struct hisi_trng_list trng_devices; + +static void hisi_trng_set_seed(struct hisi_trng *trng, const u8 *seed) +{ + u32 val, seed_reg, i; + + for (i = 0; i < SW_DRBG_SEED_SIZE; + i += SW_DRBG_SEED_SIZE / SW_DRBG_SEED_REGS_NUM) { + val = seed[i] << SEED_SHIFT_24; + val |= seed[i + 1UL] << SEED_SHIFT_16; + val |= seed[i + 2UL] << SEED_SHIFT_8; + val |= seed[i + 3UL]; + + seed_reg = (i >> SW_DRBG_NUM_SHIFT) % SW_DRBG_SEED_REGS_NUM; + writel(val, trng->base + SW_DRBG_SEED(seed_reg)); + } +} + +static int hisi_trng_seed(struct crypto_rng *tfm, const u8 *seed, + unsigned int slen) +{ + struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); + struct hisi_trng *trng = ctx->trng; + u32 val = 0; + int ret = 0; + + if (slen < SW_DRBG_SEED_SIZE) { + pr_err("slen(%u) is not matched with trng(%d)\n", slen, + SW_DRBG_SEED_SIZE); + return -EINVAL; + } + + writel(0x0, trng->base + SW_DRBG_BLOCKS); + hisi_trng_set_seed(trng, seed); + + writel(SW_DRBG_BLOCKS_NUM | (0x1 << SW_DRBG_ENABLE_SHIFT), + trng->base + SW_DRBG_BLOCKS); + writel(0x1, trng->base + SW_DRBG_INIT); + + ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, + val, val & BIT(0), SLEEP_US, TIMEOUT_US); + if (ret) + pr_err("fail to init trng(%d)\n", ret); + + return ret; +} + +static int hisi_trng_generate(struct crypto_rng *tfm, const u8 *src, + unsigned int slen, u8 *dstn, unsigned int dlen) +{ + struct hisi_trng_ctx *ctx = crypto_rng_ctx(tfm); + struct hisi_trng *trng = ctx->trng; + u32 data[SW_DRBG_DATA_NUM]; + u32 currsize = 0; + u32 val = 0; + int ret; + u32 i; + + if (dlen > SW_DRBG_BLOCKS_NUM * SW_DRBG_BYTES || dlen == 0) { + pr_err("dlen(%d) exceeds limit(%d)!\n", dlen, + SW_DRBG_BLOCKS_NUM * SW_DRBG_BYTES); + return -EINVAL; + } + + do { + ret = readl_relaxed_poll_timeout(trng->base + SW_DRBG_STATUS, + val, val & BIT(1), SLEEP_US, TIMEOUT_US); + if (ret) { + pr_err("fail to generate random number(%d)!\n", ret); + break; + } + + for (i = 0; i < SW_DRBG_DATA_NUM; i++) + data[i] = readl(trng->base + SW_DRBG_DATA(i)); + + if (dlen - currsize >= SW_DRBG_BYTES) { + memcpy(dstn + currsize, data, SW_DRBG_BYTES); + currsize += SW_DRBG_BYTES; + } else { + memcpy(dstn + currsize, data, dlen - currsize); + currsize = dlen; + } + + writel(0x1, trng->base + SW_DRBG_GEN); + } while (currsize < dlen); + + return ret; +} + +static int hisi_trng_init(struct crypto_tfm *tfm) +{ + struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); + struct hisi_trng *trng; + int ret = -EBUSY; + + mutex_lock(&trng_devices.lock); + list_for_each_entry(trng, &trng_devices.list, list) { + if (!trng->is_used) { + trng->is_used = true; + ctx->trng = trng; + ret = 0; + break; + } + } + mutex_unlock(&trng_devices.lock); + + return ret; +} + +static void hisi_trng_exit(struct crypto_tfm *tfm) +{ + struct hisi_trng_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&trng_devices.lock); + ctx->trng->is_used = false; + mutex_unlock(&trng_devices.lock); +} + +static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct hisi_trng *trng; + int currsize = 0; + u32 val = 0; + u32 ret; + + trng = container_of(rng, struct hisi_trng, rng); + + do { + ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, + val, SLEEP_US, TIMEOUT_US); + if (ret) + return currsize; + + if (max - currsize >= HISI_TRNG_BYTES) { + memcpy(buf + currsize, &val, HISI_TRNG_BYTES); + currsize += HISI_TRNG_BYTES; + if (currsize == max) + return currsize; + continue; + } + + /* copy remaining bytes */ + memcpy(buf + currsize, &val, max - currsize); + currsize = max; + } while (currsize < max); + + return currsize; +} + +static struct rng_alg hisi_trng_alg = { + .generate = hisi_trng_generate, + .seed = hisi_trng_seed, + .seedsize = SW_DRBG_SEED_SIZE, + .base = { + .cra_name = "stdrng", + .cra_driver_name = "hisi_stdrng", + .cra_priority = 300, + .cra_ctxsize = sizeof(struct hisi_trng_ctx), + .cra_module = THIS_MODULE, + .cra_init = hisi_trng_init, + .cra_exit = hisi_trng_exit, + }, +}; + +static void hisi_trng_add_to_list(struct hisi_trng *trng) +{ + mutex_lock(&trng_devices.lock); + list_add_tail(&trng->list, &trng_devices.list); + mutex_unlock(&trng_devices.lock); +} + +static int hisi_trng_del_from_list(struct hisi_trng *trng) +{ + int ret = -EBUSY; + + mutex_lock(&trng_devices.lock); + if (!trng->is_used) { + list_del(&trng->list); + ret = 0; + } + mutex_unlock(&trng_devices.lock); + + return ret; +} + +static int hisi_trng_probe(struct platform_device *pdev) +{ + struct hisi_trng *trng; + int ret; + + trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); + if (!trng) + return -ENOMEM; + + platform_set_drvdata(pdev, trng); + + trng->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(trng->base)) + return PTR_ERR(trng->base); + + trng->is_used = false; + if (!trng_devices.is_init) { + INIT_LIST_HEAD(&trng_devices.list); + mutex_init(&trng_devices.lock); + trng_devices.is_init = true; + } + + hisi_trng_add_to_list(trng); + if (atomic_inc_return(&trng_active_devs) == 1) { + ret = crypto_register_rng(&hisi_trng_alg); + if (ret) { + dev_err(&pdev->dev, + "failed to register crypto(%d)\n", ret); + atomic_dec_return(&trng_active_devs); + goto err_remove_from_list; + } + } + + trng->rng.name = pdev->name; + trng->rng.read = hisi_trng_read; + trng->rng.quality = HISI_TRNG_QUALITY; + ret = devm_hwrng_register(&pdev->dev, &trng->rng); + if (ret) { + dev_err(&pdev->dev, "failed to register hwrng: %d!\n", ret); + goto err_crypto_unregister; + } + + return ret; + +err_crypto_unregister: + if (atomic_dec_return(&trng_active_devs) == 0) + crypto_unregister_rng(&hisi_trng_alg); + +err_remove_from_list: + hisi_trng_del_from_list(trng); + return ret; +} + +static int hisi_trng_remove(struct platform_device *pdev) +{ + struct hisi_trng *trng = platform_get_drvdata(pdev); + + /* Wait until the task is finished */ + while (hisi_trng_del_from_list(trng)) + ; + + if (atomic_dec_return(&trng_active_devs) == 0) + crypto_unregister_rng(&hisi_trng_alg); + + return 0; +} + +static const struct acpi_device_id hisi_trng_acpi_match[] = { + { "HISI02B3", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); + +static struct platform_driver hisi_trng_driver = { + .probe = hisi_trng_probe, + .remove = hisi_trng_remove, + .driver = { + .name = "hisi-trng-v2", + .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), + }, +}; + +module_platform_driver(hisi_trng_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); +MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); +MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/crypto/hisilicon/zip/zip_main.c b/drivers/crypto/hisilicon/zip/zip_main.c index 4bd2c811abba..4fb5a32bf830 100644 --- a/drivers/crypto/hisilicon/zip/zip_main.c +++ b/drivers/crypto/hisilicon/zip/zip_main.c @@ -590,9 +590,7 @@ static int hisi_zip_debugfs_init(struct hisi_qm *qm) qm->debug.sqe_mask_offset = HZIP_SQE_MASK_OFFSET; qm->debug.sqe_mask_len = HZIP_SQE_MASK_LEN; qm->debug.debug_root = dev_d; - ret = hisi_qm_debug_init(qm); - if (ret) - goto failed_to_create; + hisi_qm_debug_init(qm); if (qm->fun_type == QM_HW_PF) { ret = hisi_zip_ctrl_debug_init(qm); @@ -749,6 +747,8 @@ static int hisi_zip_pf_probe_init(struct hisi_zip *hisi_zip) static int hisi_zip_qm_init(struct hisi_qm *qm, struct pci_dev *pdev) { + int ret; + qm->pdev = pdev; qm->ver = pdev->revision; qm->algs = "zlib\ngzip"; @@ -774,7 +774,25 @@ static int hisi_zip_qm_init(struct hisi_qm *qm, struct pci_dev *pdev) qm->qp_num = HZIP_QUEUE_NUM_V1 - HZIP_PF_DEF_Q_NUM; } - return hisi_qm_init(qm); + qm->wq = alloc_workqueue("%s", WQ_HIGHPRI | WQ_MEM_RECLAIM | + WQ_UNBOUND, num_online_cpus(), + pci_name(qm->pdev)); + if (!qm->wq) { + pci_err(qm->pdev, "fail to alloc workqueue\n"); + return -ENOMEM; + } + + ret = hisi_qm_init(qm); + if (ret) + destroy_workqueue(qm->wq); + + return ret; +} + +static void hisi_zip_qm_uninit(struct hisi_qm *qm) +{ + hisi_qm_uninit(qm); + destroy_workqueue(qm->wq); } static int hisi_zip_probe_init(struct hisi_zip *hisi_zip) @@ -856,7 +874,7 @@ err_dev_err_uninit: hisi_qm_dev_err_uninit(qm); err_qm_uninit: - hisi_qm_uninit(qm); + hisi_zip_qm_uninit(qm); return ret; } @@ -874,7 +892,7 @@ static void hisi_zip_remove(struct pci_dev *pdev) hisi_zip_debugfs_exit(qm); hisi_qm_stop(qm, QM_NORMAL); hisi_qm_dev_err_uninit(qm); - hisi_qm_uninit(qm); + hisi_zip_qm_uninit(qm); } static const struct pci_error_handlers hisi_zip_err_handler = { diff --git a/drivers/crypto/img-hash.c b/drivers/crypto/img-hash.c index 91f555ccbb31..e813115d5432 100644 --- a/drivers/crypto/img-hash.c +++ b/drivers/crypto/img-hash.c @@ -19,7 +19,8 @@ #include <crypto/internal/hash.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #define CR_RESET 0 #define CR_RESET_SET 1 diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index eb2418450f12..2e1562108a85 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -1639,7 +1639,7 @@ static int safexcel_probe_generic(void *pdev, priv->ring[i].rdr_req = devm_kcalloc(dev, EIP197_DEFAULT_RING_SIZE, - sizeof(priv->ring[i].rdr_req), + sizeof(*priv->ring[i].rdr_req), GFP_KERNEL); if (!priv->ring[i].rdr_req) return -ENOMEM; diff --git a/drivers/crypto/inside-secure/safexcel.h b/drivers/crypto/inside-secure/safexcel.h index 9045f2d7f4c6..ce1e611a163e 100644 --- a/drivers/crypto/inside-secure/safexcel.h +++ b/drivers/crypto/inside-secure/safexcel.h @@ -11,7 +11,8 @@ #include <crypto/aead.h> #include <crypto/algapi.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sha3.h> #include <crypto/skcipher.h> #include <linux/types.h> diff --git a/drivers/crypto/inside-secure/safexcel_cipher.c b/drivers/crypto/inside-secure/safexcel_cipher.c index 9bcfb79a030f..d68ef16650d4 100644 --- a/drivers/crypto/inside-secure/safexcel_cipher.c +++ b/drivers/crypto/inside-secure/safexcel_cipher.c @@ -18,7 +18,8 @@ #include <crypto/gcm.h> #include <crypto/ghash.h> #include <crypto/poly1305.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sm3.h> #include <crypto/sm4.h> #include <crypto/xts.h> diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c index 56d5ccb5cc00..50fb6d90a2e0 100644 --- a/drivers/crypto/inside-secure/safexcel_hash.c +++ b/drivers/crypto/inside-secure/safexcel_hash.c @@ -8,7 +8,8 @@ #include <crypto/aes.h> #include <crypto/hmac.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/sha3.h> #include <crypto/skcipher.h> #include <crypto/sm3.h> diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index 276012e7c482..8b0f17fc09fb 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -20,7 +20,7 @@ #include <crypto/internal/des.h> #include <crypto/aes.h> #include <crypto/hmac.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <crypto/algapi.h> #include <crypto/internal/aead.h> #include <crypto/internal/skcipher.h> diff --git a/drivers/crypto/keembay/Kconfig b/drivers/crypto/keembay/Kconfig new file mode 100644 index 000000000000..3c16797b25b9 --- /dev/null +++ b/drivers/crypto/keembay/Kconfig @@ -0,0 +1,39 @@ +config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4 + tristate "Support for Intel Keem Bay OCS AES/SM4 HW acceleration" + depends on OF || COMPILE_TEST + select CRYPTO_SKCIPHER + select CRYPTO_AEAD + select CRYPTO_ENGINE + help + Support for Intel Keem Bay Offload and Crypto Subsystem (OCS) AES and + SM4 cihper hardware acceleration for use with Crypto API. + + Provides HW acceleration for the following transformations: + cbc(aes), ctr(aes), ccm(aes), gcm(aes), cbc(sm4), ctr(sm4), ccm(sm4) + and gcm(sm4). + + Optionally, support for the following transformations can also be + enabled: ecb(aes), cts(cbc(aes)), ecb(sm4) and cts(cbc(sm4)). + +config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB + bool "Support for Intel Keem Bay OCS AES/SM4 ECB HW acceleration" + depends on CRYPTO_DEV_KEEMBAY_OCS_AES_SM4 + help + Support for Intel Keem Bay Offload and Crypto Subsystem (OCS) + AES/SM4 ECB mode hardware acceleration for use with Crypto API. + + Provides OCS version of ecb(aes) and ecb(sm4) + + Intel does not recommend use of ECB mode with AES/SM4. + +config CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS + bool "Support for Intel Keem Bay OCS AES/SM4 CTS HW acceleration" + depends on CRYPTO_DEV_KEEMBAY_OCS_AES_SM4 + help + Support for Intel Keem Bay Offload and Crypto Subsystem (OCS) + AES/SM4 CBC with CTS mode hardware acceleration for use with + Crypto API. + + Provides OCS version of cts(cbc(aes)) and cts(cbc(sm4)). + + Intel does not recommend use of CTS mode with AES/SM4. diff --git a/drivers/crypto/keembay/Makefile b/drivers/crypto/keembay/Makefile new file mode 100644 index 000000000000..f21e2c4ab3b3 --- /dev/null +++ b/drivers/crypto/keembay/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Intel Keem Bay OCS Crypto API Linux drivers +# +obj-$(CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4) += keembay-ocs-aes.o +keembay-ocs-aes-objs := keembay-ocs-aes-core.o ocs-aes.o diff --git a/drivers/crypto/keembay/keembay-ocs-aes-core.c b/drivers/crypto/keembay/keembay-ocs-aes-core.c new file mode 100644 index 000000000000..b6b25d994af3 --- /dev/null +++ b/drivers/crypto/keembay/keembay-ocs-aes-core.c @@ -0,0 +1,1713 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Keem Bay OCS AES Crypto Driver. + * + * Copyright (C) 2018-2020 Intel Corporation + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/crypto.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <crypto/aes.h> +#include <crypto/engine.h> +#include <crypto/gcm.h> +#include <crypto/scatterwalk.h> + +#include <crypto/internal/aead.h> +#include <crypto/internal/skcipher.h> + +#include "ocs-aes.h" + +#define KMB_OCS_PRIORITY 350 +#define DRV_NAME "keembay-ocs-aes" + +#define OCS_AES_MIN_KEY_SIZE 16 +#define OCS_AES_MAX_KEY_SIZE 32 +#define OCS_AES_KEYSIZE_128 16 +#define OCS_AES_KEYSIZE_192 24 +#define OCS_AES_KEYSIZE_256 32 +#define OCS_SM4_KEY_SIZE 16 + +/** + * struct ocs_aes_tctx - OCS AES Transform context + * @engine_ctx: Engine context. + * @aes_dev: The OCS AES device. + * @key: AES/SM4 key. + * @key_len: The length (in bytes) of @key. + * @cipher: OCS cipher to use (either AES or SM4). + * @sw_cipher: The cipher to use as fallback. + * @use_fallback: Whether or not fallback cipher should be used. + */ +struct ocs_aes_tctx { + struct crypto_engine_ctx engine_ctx; + struct ocs_aes_dev *aes_dev; + u8 key[OCS_AES_KEYSIZE_256]; + unsigned int key_len; + enum ocs_cipher cipher; + union { + struct crypto_sync_skcipher *sk; + struct crypto_aead *aead; + } sw_cipher; + bool use_fallback; +}; + +/** + * struct ocs_aes_rctx - OCS AES Request context. + * @instruction: Instruction to be executed (encrypt / decrypt). + * @mode: Mode to use (ECB, CBC, CTR, CCm, GCM, CTS) + * @src_nents: Number of source SG entries. + * @dst_nents: Number of destination SG entries. + * @src_dma_count: The number of DMA-mapped entries of the source SG. + * @dst_dma_count: The number of DMA-mapped entries of the destination SG. + * @in_place: Whether or not this is an in place request, i.e., + * src_sg == dst_sg. + * @src_dll: OCS DMA linked list for input data. + * @dst_dll: OCS DMA linked list for output data. + * @last_ct_blk: Buffer to hold last cipher text block (only used in CBC + * mode). + * @cts_swap: Whether or not CTS swap must be performed. + * @aad_src_dll: OCS DMA linked list for input AAD data. + * @aad_dst_dll: OCS DMA linked list for output AAD data. + * @in_tag: Buffer to hold input encrypted tag (only used for + * CCM/GCM decrypt). + * @out_tag: Buffer to hold output encrypted / decrypted tag (only + * used for GCM encrypt / decrypt). + */ +struct ocs_aes_rctx { + /* Fields common across all modes. */ + enum ocs_instruction instruction; + enum ocs_mode mode; + int src_nents; + int dst_nents; + int src_dma_count; + int dst_dma_count; + bool in_place; + struct ocs_dll_desc src_dll; + struct ocs_dll_desc dst_dll; + + /* CBC specific */ + u8 last_ct_blk[AES_BLOCK_SIZE]; + + /* CTS specific */ + int cts_swap; + + /* CCM/GCM specific */ + struct ocs_dll_desc aad_src_dll; + struct ocs_dll_desc aad_dst_dll; + u8 in_tag[AES_BLOCK_SIZE]; + + /* GCM specific */ + u8 out_tag[AES_BLOCK_SIZE]; +}; + +/* Driver data. */ +struct ocs_aes_drv { + struct list_head dev_list; + spinlock_t lock; /* Protects dev_list. */ +}; + +static struct ocs_aes_drv ocs_aes = { + .dev_list = LIST_HEAD_INIT(ocs_aes.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(ocs_aes.lock), +}; + +static struct ocs_aes_dev *kmb_ocs_aes_find_dev(struct ocs_aes_tctx *tctx) +{ + struct ocs_aes_dev *aes_dev; + + spin_lock(&ocs_aes.lock); + + if (tctx->aes_dev) { + aes_dev = tctx->aes_dev; + goto exit; + } + + /* Only a single OCS device available */ + aes_dev = list_first_entry(&ocs_aes.dev_list, struct ocs_aes_dev, list); + tctx->aes_dev = aes_dev; + +exit: + spin_unlock(&ocs_aes.lock); + + return aes_dev; +} + +/* + * Ensure key is 128-bit or 256-bit for AES or 128-bit for SM4 and an actual + * key is being passed in. + * + * Return: 0 if key is valid, -EINVAL otherwise. + */ +static int check_key(const u8 *in_key, size_t key_len, enum ocs_cipher cipher) +{ + if (!in_key) + return -EINVAL; + + /* For AES, only 128-byte or 256-byte keys are supported. */ + if (cipher == OCS_AES && (key_len == OCS_AES_KEYSIZE_128 || + key_len == OCS_AES_KEYSIZE_256)) + return 0; + + /* For SM4, only 128-byte keys are supported. */ + if (cipher == OCS_SM4 && key_len == OCS_AES_KEYSIZE_128) + return 0; + + /* Everything else is unsupported. */ + return -EINVAL; +} + +/* Save key into transformation context. */ +static int save_key(struct ocs_aes_tctx *tctx, const u8 *in_key, size_t key_len, + enum ocs_cipher cipher) +{ + int ret; + + ret = check_key(in_key, key_len, cipher); + if (ret) + return ret; + + memcpy(tctx->key, in_key, key_len); + tctx->key_len = key_len; + tctx->cipher = cipher; + + return 0; +} + +/* Set key for symmetric cypher. */ +static int kmb_ocs_sk_set_key(struct crypto_skcipher *tfm, const u8 *in_key, + size_t key_len, enum ocs_cipher cipher) +{ + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + + /* Fallback is used for AES with 192-bit key. */ + tctx->use_fallback = (cipher == OCS_AES && + key_len == OCS_AES_KEYSIZE_192); + + if (!tctx->use_fallback) + return save_key(tctx, in_key, key_len, cipher); + + crypto_sync_skcipher_clear_flags(tctx->sw_cipher.sk, + CRYPTO_TFM_REQ_MASK); + crypto_sync_skcipher_set_flags(tctx->sw_cipher.sk, + tfm->base.crt_flags & + CRYPTO_TFM_REQ_MASK); + + return crypto_sync_skcipher_setkey(tctx->sw_cipher.sk, in_key, key_len); +} + +/* Set key for AEAD cipher. */ +static int kmb_ocs_aead_set_key(struct crypto_aead *tfm, const u8 *in_key, + size_t key_len, enum ocs_cipher cipher) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(tfm); + + /* Fallback is used for AES with 192-bit key. */ + tctx->use_fallback = (cipher == OCS_AES && + key_len == OCS_AES_KEYSIZE_192); + + if (!tctx->use_fallback) + return save_key(tctx, in_key, key_len, cipher); + + crypto_aead_clear_flags(tctx->sw_cipher.aead, CRYPTO_TFM_REQ_MASK); + crypto_aead_set_flags(tctx->sw_cipher.aead, + crypto_aead_get_flags(tfm) & CRYPTO_TFM_REQ_MASK); + + return crypto_aead_setkey(tctx->sw_cipher.aead, in_key, key_len); +} + +/* Swap two AES blocks in SG lists. */ +static void sg_swap_blocks(struct scatterlist *sgl, unsigned int nents, + off_t blk1_offset, off_t blk2_offset) +{ + u8 tmp_buf1[AES_BLOCK_SIZE], tmp_buf2[AES_BLOCK_SIZE]; + + /* + * No easy way to copy within sg list, so copy both blocks to temporary + * buffers first. + */ + sg_pcopy_to_buffer(sgl, nents, tmp_buf1, AES_BLOCK_SIZE, blk1_offset); + sg_pcopy_to_buffer(sgl, nents, tmp_buf2, AES_BLOCK_SIZE, blk2_offset); + sg_pcopy_from_buffer(sgl, nents, tmp_buf1, AES_BLOCK_SIZE, blk2_offset); + sg_pcopy_from_buffer(sgl, nents, tmp_buf2, AES_BLOCK_SIZE, blk1_offset); +} + +/* Initialize request context to default values. */ +static void ocs_aes_init_rctx(struct ocs_aes_rctx *rctx) +{ + /* Zero everything. */ + memset(rctx, 0, sizeof(*rctx)); + + /* Set initial value for DMA addresses. */ + rctx->src_dll.dma_addr = DMA_MAPPING_ERROR; + rctx->dst_dll.dma_addr = DMA_MAPPING_ERROR; + rctx->aad_src_dll.dma_addr = DMA_MAPPING_ERROR; + rctx->aad_dst_dll.dma_addr = DMA_MAPPING_ERROR; +} + +static int kmb_ocs_sk_validate_input(struct skcipher_request *req, + enum ocs_mode mode) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + int iv_size = crypto_skcipher_ivsize(tfm); + + switch (mode) { + case OCS_MODE_ECB: + /* Ensure input length is multiple of block size */ + if (req->cryptlen % AES_BLOCK_SIZE != 0) + return -EINVAL; + + return 0; + + case OCS_MODE_CBC: + /* Ensure input length is multiple of block size */ + if (req->cryptlen % AES_BLOCK_SIZE != 0) + return -EINVAL; + + /* Ensure IV is present and block size in length */ + if (!req->iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + /* + * NOTE: Since req->cryptlen == 0 case was already handled in + * kmb_ocs_sk_common(), the above two conditions also guarantee + * that: cryptlen >= iv_size + */ + return 0; + + case OCS_MODE_CTR: + /* Ensure IV is present and block size in length */ + if (!req->iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + return 0; + + case OCS_MODE_CTS: + /* Ensure input length >= block size */ + if (req->cryptlen < AES_BLOCK_SIZE) + return -EINVAL; + + /* Ensure IV is present and block size in length */ + if (!req->iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + return 0; + default: + return -EINVAL; + } +} + +/* + * Called by encrypt() / decrypt() skcipher functions. + * + * Use fallback if needed, otherwise initialize context and enqueue request + * into engine. + */ +static int kmb_ocs_sk_common(struct skcipher_request *req, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + enum ocs_mode mode) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_rctx *rctx = skcipher_request_ctx(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + struct ocs_aes_dev *aes_dev; + int rc; + + if (tctx->use_fallback) { + SYNC_SKCIPHER_REQUEST_ON_STACK(subreq, tctx->sw_cipher.sk); + + skcipher_request_set_sync_tfm(subreq, tctx->sw_cipher.sk); + skcipher_request_set_callback(subreq, req->base.flags, NULL, + NULL); + skcipher_request_set_crypt(subreq, req->src, req->dst, + req->cryptlen, req->iv); + + if (instruction == OCS_ENCRYPT) + rc = crypto_skcipher_encrypt(subreq); + else + rc = crypto_skcipher_decrypt(subreq); + + skcipher_request_zero(subreq); + + return rc; + } + + /* + * If cryptlen == 0, no processing needed for ECB, CBC and CTR. + * + * For CTS continue: kmb_ocs_sk_validate_input() will return -EINVAL. + */ + if (!req->cryptlen && mode != OCS_MODE_CTS) + return 0; + + rc = kmb_ocs_sk_validate_input(req, mode); + if (rc) + return rc; + + aes_dev = kmb_ocs_aes_find_dev(tctx); + if (!aes_dev) + return -ENODEV; + + if (cipher != tctx->cipher) + return -EINVAL; + + ocs_aes_init_rctx(rctx); + rctx->instruction = instruction; + rctx->mode = mode; + + return crypto_transfer_skcipher_request_to_engine(aes_dev->engine, req); +} + +static void cleanup_ocs_dma_linked_list(struct device *dev, + struct ocs_dll_desc *dll) +{ + if (dll->vaddr) + dma_free_coherent(dev, dll->size, dll->vaddr, dll->dma_addr); + dll->vaddr = NULL; + dll->size = 0; + dll->dma_addr = DMA_MAPPING_ERROR; +} + +static void kmb_ocs_sk_dma_cleanup(struct skcipher_request *req) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_rctx *rctx = skcipher_request_ctx(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + struct device *dev = tctx->aes_dev->dev; + + if (rctx->src_dma_count) { + dma_unmap_sg(dev, req->src, rctx->src_nents, DMA_TO_DEVICE); + rctx->src_dma_count = 0; + } + + if (rctx->dst_dma_count) { + dma_unmap_sg(dev, req->dst, rctx->dst_nents, rctx->in_place ? + DMA_BIDIRECTIONAL : + DMA_FROM_DEVICE); + rctx->dst_dma_count = 0; + } + + /* Clean up OCS DMA linked lists */ + cleanup_ocs_dma_linked_list(dev, &rctx->src_dll); + cleanup_ocs_dma_linked_list(dev, &rctx->dst_dll); +} + +static int kmb_ocs_sk_prepare_inplace(struct skcipher_request *req) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_rctx *rctx = skcipher_request_ctx(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + int iv_size = crypto_skcipher_ivsize(tfm); + int rc; + + /* + * For CBC decrypt, save last block (iv) to last_ct_blk buffer. + * + * Note: if we are here, we already checked that cryptlen >= iv_size + * and iv_size == AES_BLOCK_SIZE (i.e., the size of last_ct_blk); see + * kmb_ocs_sk_validate_input(). + */ + if (rctx->mode == OCS_MODE_CBC && rctx->instruction == OCS_DECRYPT) + scatterwalk_map_and_copy(rctx->last_ct_blk, req->src, + req->cryptlen - iv_size, iv_size, 0); + + /* For CTS decrypt, swap last two blocks, if needed. */ + if (rctx->cts_swap && rctx->instruction == OCS_DECRYPT) + sg_swap_blocks(req->dst, rctx->dst_nents, + req->cryptlen - AES_BLOCK_SIZE, + req->cryptlen - (2 * AES_BLOCK_SIZE)); + + /* src and dst buffers are the same, use bidirectional DMA mapping. */ + rctx->dst_dma_count = dma_map_sg(tctx->aes_dev->dev, req->dst, + rctx->dst_nents, DMA_BIDIRECTIONAL); + if (rctx->dst_dma_count == 0) { + dev_err(tctx->aes_dev->dev, "Failed to map destination sg\n"); + return -ENOMEM; + } + + /* Create DST linked list */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, &rctx->dst_dll, + req->cryptlen, 0); + if (rc) + return rc; + /* + * If descriptor creation was successful, set the src_dll.dma_addr to + * the value of dst_dll.dma_addr, as we do in-place AES operation on + * the src. + */ + rctx->src_dll.dma_addr = rctx->dst_dll.dma_addr; + + return 0; +} + +static int kmb_ocs_sk_prepare_notinplace(struct skcipher_request *req) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_rctx *rctx = skcipher_request_ctx(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + int rc; + + rctx->src_nents = sg_nents_for_len(req->src, req->cryptlen); + if (rctx->src_nents < 0) + return -EBADMSG; + + /* Map SRC SG. */ + rctx->src_dma_count = dma_map_sg(tctx->aes_dev->dev, req->src, + rctx->src_nents, DMA_TO_DEVICE); + if (rctx->src_dma_count == 0) { + dev_err(tctx->aes_dev->dev, "Failed to map source sg\n"); + return -ENOMEM; + } + + /* Create SRC linked list */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->src, + rctx->src_dma_count, &rctx->src_dll, + req->cryptlen, 0); + if (rc) + return rc; + + /* Map DST SG. */ + rctx->dst_dma_count = dma_map_sg(tctx->aes_dev->dev, req->dst, + rctx->dst_nents, DMA_FROM_DEVICE); + if (rctx->dst_dma_count == 0) { + dev_err(tctx->aes_dev->dev, "Failed to map destination sg\n"); + return -ENOMEM; + } + + /* Create DST linked list */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, &rctx->dst_dll, + req->cryptlen, 0); + if (rc) + return rc; + + /* If this is not a CTS decrypt operation with swapping, we are done. */ + if (!(rctx->cts_swap && rctx->instruction == OCS_DECRYPT)) + return 0; + + /* + * Otherwise, we have to copy src to dst (as we cannot modify src). + * Use OCS AES bypass mode to copy src to dst via DMA. + * + * NOTE: for anything other than small data sizes this is rather + * inefficient. + */ + rc = ocs_aes_bypass_op(tctx->aes_dev, rctx->dst_dll.dma_addr, + rctx->src_dll.dma_addr, req->cryptlen); + if (rc) + return rc; + + /* + * Now dst == src, so clean up what we did so far and use in_place + * logic. + */ + kmb_ocs_sk_dma_cleanup(req); + rctx->in_place = true; + + return kmb_ocs_sk_prepare_inplace(req); +} + +static int kmb_ocs_sk_run(struct skcipher_request *req) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_rctx *rctx = skcipher_request_ctx(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + struct ocs_aes_dev *aes_dev = tctx->aes_dev; + int iv_size = crypto_skcipher_ivsize(tfm); + int rc; + + rctx->dst_nents = sg_nents_for_len(req->dst, req->cryptlen); + if (rctx->dst_nents < 0) + return -EBADMSG; + + /* + * If 2 blocks or greater, and multiple of block size swap last two + * blocks to be compatible with other crypto API CTS implementations: + * OCS mode uses CBC-CS2, whereas other crypto API implementations use + * CBC-CS3. + * CBC-CS2 and CBC-CS3 defined by: + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a-add.pdf + */ + rctx->cts_swap = (rctx->mode == OCS_MODE_CTS && + req->cryptlen > AES_BLOCK_SIZE && + req->cryptlen % AES_BLOCK_SIZE == 0); + + rctx->in_place = (req->src == req->dst); + + if (rctx->in_place) + rc = kmb_ocs_sk_prepare_inplace(req); + else + rc = kmb_ocs_sk_prepare_notinplace(req); + + if (rc) + goto error; + + rc = ocs_aes_op(aes_dev, rctx->mode, tctx->cipher, rctx->instruction, + rctx->dst_dll.dma_addr, rctx->src_dll.dma_addr, + req->cryptlen, req->iv, iv_size); + if (rc) + goto error; + + /* Clean-up DMA before further processing output. */ + kmb_ocs_sk_dma_cleanup(req); + + /* For CTS Encrypt, swap last 2 blocks, if needed. */ + if (rctx->cts_swap && rctx->instruction == OCS_ENCRYPT) { + sg_swap_blocks(req->dst, rctx->dst_nents, + req->cryptlen - AES_BLOCK_SIZE, + req->cryptlen - (2 * AES_BLOCK_SIZE)); + return 0; + } + + /* For CBC copy IV to req->IV. */ + if (rctx->mode == OCS_MODE_CBC) { + /* CBC encrypt case. */ + if (rctx->instruction == OCS_ENCRYPT) { + scatterwalk_map_and_copy(req->iv, req->dst, + req->cryptlen - iv_size, + iv_size, 0); + return 0; + } + /* CBC decrypt case. */ + if (rctx->in_place) + memcpy(req->iv, rctx->last_ct_blk, iv_size); + else + scatterwalk_map_and_copy(req->iv, req->src, + req->cryptlen - iv_size, + iv_size, 0); + return 0; + } + /* For all other modes there's nothing to do. */ + + return 0; + +error: + kmb_ocs_sk_dma_cleanup(req); + + return rc; +} + +static int kmb_ocs_aead_validate_input(struct aead_request *req, + enum ocs_instruction instruction, + enum ocs_mode mode) +{ + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + int tag_size = crypto_aead_authsize(tfm); + int iv_size = crypto_aead_ivsize(tfm); + + /* For decrypt crytplen == len(PT) + len(tag). */ + if (instruction == OCS_DECRYPT && req->cryptlen < tag_size) + return -EINVAL; + + /* IV is mandatory. */ + if (!req->iv) + return -EINVAL; + + switch (mode) { + case OCS_MODE_GCM: + if (iv_size != GCM_AES_IV_SIZE) + return -EINVAL; + + return 0; + + case OCS_MODE_CCM: + /* Ensure IV is present and block size in length */ + if (iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + return 0; + + default: + return -EINVAL; + } +} + +/* + * Called by encrypt() / decrypt() aead functions. + * + * Use fallback if needed, otherwise initialize context and enqueue request + * into engine. + */ +static int kmb_ocs_aead_common(struct aead_request *req, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + enum ocs_mode mode) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct ocs_aes_rctx *rctx = aead_request_ctx(req); + struct ocs_aes_dev *dd; + int rc; + + if (tctx->use_fallback) { + struct aead_request *subreq = aead_request_ctx(req); + + aead_request_set_tfm(subreq, tctx->sw_cipher.aead); + aead_request_set_callback(subreq, req->base.flags, + req->base.complete, req->base.data); + aead_request_set_crypt(subreq, req->src, req->dst, + req->cryptlen, req->iv); + aead_request_set_ad(subreq, req->assoclen); + rc = crypto_aead_setauthsize(tctx->sw_cipher.aead, + crypto_aead_authsize(crypto_aead_reqtfm(req))); + if (rc) + return rc; + + return (instruction == OCS_ENCRYPT) ? + crypto_aead_encrypt(subreq) : + crypto_aead_decrypt(subreq); + } + + rc = kmb_ocs_aead_validate_input(req, instruction, mode); + if (rc) + return rc; + + dd = kmb_ocs_aes_find_dev(tctx); + if (!dd) + return -ENODEV; + + if (cipher != tctx->cipher) + return -EINVAL; + + ocs_aes_init_rctx(rctx); + rctx->instruction = instruction; + rctx->mode = mode; + + return crypto_transfer_aead_request_to_engine(dd->engine, req); +} + +static void kmb_ocs_aead_dma_cleanup(struct aead_request *req) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct ocs_aes_rctx *rctx = aead_request_ctx(req); + struct device *dev = tctx->aes_dev->dev; + + if (rctx->src_dma_count) { + dma_unmap_sg(dev, req->src, rctx->src_nents, DMA_TO_DEVICE); + rctx->src_dma_count = 0; + } + + if (rctx->dst_dma_count) { + dma_unmap_sg(dev, req->dst, rctx->dst_nents, rctx->in_place ? + DMA_BIDIRECTIONAL : + DMA_FROM_DEVICE); + rctx->dst_dma_count = 0; + } + /* Clean up OCS DMA linked lists */ + cleanup_ocs_dma_linked_list(dev, &rctx->src_dll); + cleanup_ocs_dma_linked_list(dev, &rctx->dst_dll); + cleanup_ocs_dma_linked_list(dev, &rctx->aad_src_dll); + cleanup_ocs_dma_linked_list(dev, &rctx->aad_dst_dll); +} + +/** + * kmb_ocs_aead_dma_prepare() - Do DMA mapping for AEAD processing. + * @req: The AEAD request being processed. + * @src_dll_size: Where to store the length of the data mapped into the + * src_dll OCS DMA list. + * + * Do the following: + * - DMA map req->src and req->dst + * - Initialize the following OCS DMA linked lists: rctx->src_dll, + * rctx->dst_dll, rctx->aad_src_dll and rxtc->aad_dst_dll. + * + * Return: 0 on success, negative error code otherwise. + */ +static int kmb_ocs_aead_dma_prepare(struct aead_request *req, u32 *src_dll_size) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + const int tag_size = crypto_aead_authsize(crypto_aead_reqtfm(req)); + struct ocs_aes_rctx *rctx = aead_request_ctx(req); + u32 in_size; /* The length of the data to be mapped by src_dll. */ + u32 out_size; /* The length of the data to be mapped by dst_dll. */ + u32 dst_size; /* The length of the data in dst_sg. */ + int rc; + + /* Get number of entries in input data SG list. */ + rctx->src_nents = sg_nents_for_len(req->src, + req->assoclen + req->cryptlen); + if (rctx->src_nents < 0) + return -EBADMSG; + + if (rctx->instruction == OCS_DECRYPT) { + /* + * For decrypt: + * - src sg list is: AAD|CT|tag + * - dst sg list expects: AAD|PT + * + * in_size == len(CT); out_size == len(PT) + */ + + /* req->cryptlen includes both CT and tag. */ + in_size = req->cryptlen - tag_size; + + /* out_size = PT size == CT size */ + out_size = in_size; + + /* len(dst_sg) == len(AAD) + len(PT) */ + dst_size = req->assoclen + out_size; + + /* + * Copy tag from source SG list to 'in_tag' buffer. + * + * Note: this needs to be done here, before DMA mapping src_sg. + */ + sg_pcopy_to_buffer(req->src, rctx->src_nents, rctx->in_tag, + tag_size, req->assoclen + in_size); + + } else { /* OCS_ENCRYPT */ + /* + * For encrypt: + * src sg list is: AAD|PT + * dst sg list expects: AAD|CT|tag + */ + /* in_size == len(PT) */ + in_size = req->cryptlen; + + /* + * In CCM mode the OCS engine appends the tag to the ciphertext, + * but in GCM mode the tag must be read from the tag registers + * and appended manually below + */ + out_size = (rctx->mode == OCS_MODE_CCM) ? in_size + tag_size : + in_size; + /* len(dst_sg) == len(AAD) + len(CT) + len(tag) */ + dst_size = req->assoclen + in_size + tag_size; + } + *src_dll_size = in_size; + + /* Get number of entries in output data SG list. */ + rctx->dst_nents = sg_nents_for_len(req->dst, dst_size); + if (rctx->dst_nents < 0) + return -EBADMSG; + + rctx->in_place = (req->src == req->dst) ? 1 : 0; + + /* Map destination; use bidirectional mapping for in-place case. */ + rctx->dst_dma_count = dma_map_sg(tctx->aes_dev->dev, req->dst, + rctx->dst_nents, + rctx->in_place ? DMA_BIDIRECTIONAL : + DMA_FROM_DEVICE); + if (rctx->dst_dma_count == 0 && rctx->dst_nents != 0) { + dev_err(tctx->aes_dev->dev, "Failed to map destination sg\n"); + return -ENOMEM; + } + + /* Create AAD DST list: maps dst[0:AAD_SIZE-1]. */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, + &rctx->aad_dst_dll, req->assoclen, + 0); + if (rc) + return rc; + + /* Create DST list: maps dst[AAD_SIZE:out_size] */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, &rctx->dst_dll, + out_size, req->assoclen); + if (rc) + return rc; + + if (rctx->in_place) { + /* If this is not CCM encrypt, we are done. */ + if (!(rctx->mode == OCS_MODE_CCM && + rctx->instruction == OCS_ENCRYPT)) { + /* + * SRC and DST are the same, so re-use the same DMA + * addresses (to avoid allocating new DMA lists + * identical to the dst ones). + */ + rctx->src_dll.dma_addr = rctx->dst_dll.dma_addr; + rctx->aad_src_dll.dma_addr = rctx->aad_dst_dll.dma_addr; + + return 0; + } + /* + * For CCM encrypt the input and output linked lists contain + * different amounts of data, so, we need to create different + * SRC and AAD SRC lists, even for the in-place case. + */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, + &rctx->aad_src_dll, + req->assoclen, 0); + if (rc) + return rc; + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->dst, + rctx->dst_dma_count, + &rctx->src_dll, in_size, + req->assoclen); + if (rc) + return rc; + + return 0; + } + /* Not in-place case. */ + + /* Map source SG. */ + rctx->src_dma_count = dma_map_sg(tctx->aes_dev->dev, req->src, + rctx->src_nents, DMA_TO_DEVICE); + if (rctx->src_dma_count == 0 && rctx->src_nents != 0) { + dev_err(tctx->aes_dev->dev, "Failed to map source sg\n"); + return -ENOMEM; + } + + /* Create AAD SRC list. */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->src, + rctx->src_dma_count, + &rctx->aad_src_dll, + req->assoclen, 0); + if (rc) + return rc; + + /* Create SRC list. */ + rc = ocs_create_linked_list_from_sg(tctx->aes_dev, req->src, + rctx->src_dma_count, + &rctx->src_dll, in_size, + req->assoclen); + if (rc) + return rc; + + if (req->assoclen == 0) + return 0; + + /* Copy AAD from src sg to dst sg using OCS DMA. */ + rc = ocs_aes_bypass_op(tctx->aes_dev, rctx->aad_dst_dll.dma_addr, + rctx->aad_src_dll.dma_addr, req->cryptlen); + if (rc) + dev_err(tctx->aes_dev->dev, + "Failed to copy source AAD to destination AAD\n"); + + return rc; +} + +static int kmb_ocs_aead_run(struct aead_request *req) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + const int tag_size = crypto_aead_authsize(crypto_aead_reqtfm(req)); + struct ocs_aes_rctx *rctx = aead_request_ctx(req); + u32 in_size; /* The length of the data mapped by src_dll. */ + int rc; + + rc = kmb_ocs_aead_dma_prepare(req, &in_size); + if (rc) + goto exit; + + /* For CCM, we just call the OCS processing and we are done. */ + if (rctx->mode == OCS_MODE_CCM) { + rc = ocs_aes_ccm_op(tctx->aes_dev, tctx->cipher, + rctx->instruction, rctx->dst_dll.dma_addr, + rctx->src_dll.dma_addr, in_size, + req->iv, + rctx->aad_src_dll.dma_addr, req->assoclen, + rctx->in_tag, tag_size); + goto exit; + } + /* GCM case; invoke OCS processing. */ + rc = ocs_aes_gcm_op(tctx->aes_dev, tctx->cipher, + rctx->instruction, + rctx->dst_dll.dma_addr, + rctx->src_dll.dma_addr, in_size, + req->iv, + rctx->aad_src_dll.dma_addr, req->assoclen, + rctx->out_tag, tag_size); + if (rc) + goto exit; + + /* For GCM decrypt, we have to compare in_tag with out_tag. */ + if (rctx->instruction == OCS_DECRYPT) { + rc = memcmp(rctx->in_tag, rctx->out_tag, tag_size) ? + -EBADMSG : 0; + goto exit; + } + + /* For GCM encrypt, we must manually copy out_tag to DST sg. */ + + /* Clean-up must be called before the sg_pcopy_from_buffer() below. */ + kmb_ocs_aead_dma_cleanup(req); + + /* Copy tag to destination sg after AAD and CT. */ + sg_pcopy_from_buffer(req->dst, rctx->dst_nents, rctx->out_tag, + tag_size, req->assoclen + req->cryptlen); + + /* Return directly as DMA cleanup already done. */ + return 0; + +exit: + kmb_ocs_aead_dma_cleanup(req); + + return rc; +} + +static int kmb_ocs_aes_sk_do_one_request(struct crypto_engine *engine, + void *areq) +{ + struct skcipher_request *req = + container_of(areq, struct skcipher_request, base); + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + int err; + + if (!tctx->aes_dev) { + err = -ENODEV; + goto exit; + } + + err = ocs_aes_set_key(tctx->aes_dev, tctx->key_len, tctx->key, + tctx->cipher); + if (err) + goto exit; + + err = kmb_ocs_sk_run(req); + +exit: + crypto_finalize_skcipher_request(engine, req, err); + + return 0; +} + +static int kmb_ocs_aes_aead_do_one_request(struct crypto_engine *engine, + void *areq) +{ + struct aead_request *req = container_of(areq, + struct aead_request, base); + struct ocs_aes_tctx *tctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); + int err; + + if (!tctx->aes_dev) + return -ENODEV; + + err = ocs_aes_set_key(tctx->aes_dev, tctx->key_len, tctx->key, + tctx->cipher); + if (err) + goto exit; + + err = kmb_ocs_aead_run(req); + +exit: + crypto_finalize_aead_request(tctx->aes_dev->engine, req, err); + + return 0; +} + +static int kmb_ocs_aes_set_key(struct crypto_skcipher *tfm, const u8 *in_key, + unsigned int key_len) +{ + return kmb_ocs_sk_set_key(tfm, in_key, key_len, OCS_AES); +} + +static int kmb_ocs_aes_aead_set_key(struct crypto_aead *tfm, const u8 *in_key, + unsigned int key_len) +{ + return kmb_ocs_aead_set_key(tfm, in_key, key_len, OCS_AES); +} + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB +static int kmb_ocs_aes_ecb_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_ECB); +} + +static int kmb_ocs_aes_ecb_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_ECB); +} +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB */ + +static int kmb_ocs_aes_cbc_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_CBC); +} + +static int kmb_ocs_aes_cbc_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_CBC); +} + +static int kmb_ocs_aes_ctr_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_CTR); +} + +static int kmb_ocs_aes_ctr_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_CTR); +} + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS +static int kmb_ocs_aes_cts_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_CTS); +} + +static int kmb_ocs_aes_cts_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_CTS); +} +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS */ + +static int kmb_ocs_aes_gcm_encrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_GCM); +} + +static int kmb_ocs_aes_gcm_decrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_GCM); +} + +static int kmb_ocs_aes_ccm_encrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_AES, OCS_ENCRYPT, OCS_MODE_CCM); +} + +static int kmb_ocs_aes_ccm_decrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_AES, OCS_DECRYPT, OCS_MODE_CCM); +} + +static int kmb_ocs_sm4_set_key(struct crypto_skcipher *tfm, const u8 *in_key, + unsigned int key_len) +{ + return kmb_ocs_sk_set_key(tfm, in_key, key_len, OCS_SM4); +} + +static int kmb_ocs_sm4_aead_set_key(struct crypto_aead *tfm, const u8 *in_key, + unsigned int key_len) +{ + return kmb_ocs_aead_set_key(tfm, in_key, key_len, OCS_SM4); +} + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB +static int kmb_ocs_sm4_ecb_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_ECB); +} + +static int kmb_ocs_sm4_ecb_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_ECB); +} +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB */ + +static int kmb_ocs_sm4_cbc_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_CBC); +} + +static int kmb_ocs_sm4_cbc_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_CBC); +} + +static int kmb_ocs_sm4_ctr_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_CTR); +} + +static int kmb_ocs_sm4_ctr_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_CTR); +} + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS +static int kmb_ocs_sm4_cts_encrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_CTS); +} + +static int kmb_ocs_sm4_cts_decrypt(struct skcipher_request *req) +{ + return kmb_ocs_sk_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_CTS); +} +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS */ + +static int kmb_ocs_sm4_gcm_encrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_GCM); +} + +static int kmb_ocs_sm4_gcm_decrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_GCM); +} + +static int kmb_ocs_sm4_ccm_encrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_SM4, OCS_ENCRYPT, OCS_MODE_CCM); +} + +static int kmb_ocs_sm4_ccm_decrypt(struct aead_request *req) +{ + return kmb_ocs_aead_common(req, OCS_SM4, OCS_DECRYPT, OCS_MODE_CCM); +} + +static inline int ocs_common_init(struct ocs_aes_tctx *tctx) +{ + tctx->engine_ctx.op.prepare_request = NULL; + tctx->engine_ctx.op.do_one_request = kmb_ocs_aes_sk_do_one_request; + tctx->engine_ctx.op.unprepare_request = NULL; + + return 0; +} + +static int ocs_aes_init_tfm(struct crypto_skcipher *tfm) +{ + const char *alg_name = crypto_tfm_alg_name(&tfm->base); + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + struct crypto_sync_skcipher *blk; + + /* set fallback cipher in case it will be needed */ + blk = crypto_alloc_sync_skcipher(alg_name, 0, CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(blk)) + return PTR_ERR(blk); + + tctx->sw_cipher.sk = blk; + + crypto_skcipher_set_reqsize(tfm, sizeof(struct ocs_aes_rctx)); + + return ocs_common_init(tctx); +} + +static int ocs_sm4_init_tfm(struct crypto_skcipher *tfm) +{ + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + + crypto_skcipher_set_reqsize(tfm, sizeof(struct ocs_aes_rctx)); + + return ocs_common_init(tctx); +} + +static inline void clear_key(struct ocs_aes_tctx *tctx) +{ + memzero_explicit(tctx->key, OCS_AES_KEYSIZE_256); + + /* Zero key registers if set */ + if (tctx->aes_dev) + ocs_aes_set_key(tctx->aes_dev, OCS_AES_KEYSIZE_256, + tctx->key, OCS_AES); +} + +static void ocs_exit_tfm(struct crypto_skcipher *tfm) +{ + struct ocs_aes_tctx *tctx = crypto_skcipher_ctx(tfm); + + clear_key(tctx); + + if (tctx->sw_cipher.sk) { + crypto_free_sync_skcipher(tctx->sw_cipher.sk); + tctx->sw_cipher.sk = NULL; + } +} + +static inline int ocs_common_aead_init(struct ocs_aes_tctx *tctx) +{ + tctx->engine_ctx.op.prepare_request = NULL; + tctx->engine_ctx.op.do_one_request = kmb_ocs_aes_aead_do_one_request; + tctx->engine_ctx.op.unprepare_request = NULL; + + return 0; +} + +static int ocs_aes_aead_cra_init(struct crypto_aead *tfm) +{ + const char *alg_name = crypto_tfm_alg_name(&tfm->base); + struct ocs_aes_tctx *tctx = crypto_aead_ctx(tfm); + struct crypto_aead *blk; + + /* Set fallback cipher in case it will be needed */ + blk = crypto_alloc_aead(alg_name, 0, CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(blk)) + return PTR_ERR(blk); + + tctx->sw_cipher.aead = blk; + + crypto_aead_set_reqsize(tfm, + max(sizeof(struct ocs_aes_rctx), + (sizeof(struct aead_request) + + crypto_aead_reqsize(tctx->sw_cipher.aead)))); + + return ocs_common_aead_init(tctx); +} + +static int kmb_ocs_aead_ccm_setauthsize(struct crypto_aead *tfm, + unsigned int authsize) +{ + switch (authsize) { + case 4: + case 6: + case 8: + case 10: + case 12: + case 14: + case 16: + return 0; + default: + return -EINVAL; + } +} + +static int kmb_ocs_aead_gcm_setauthsize(struct crypto_aead *tfm, + unsigned int authsize) +{ + return crypto_gcm_check_authsize(authsize); +} + +static int ocs_sm4_aead_cra_init(struct crypto_aead *tfm) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(tfm); + + crypto_aead_set_reqsize(tfm, sizeof(struct ocs_aes_rctx)); + + return ocs_common_aead_init(tctx); +} + +static void ocs_aead_cra_exit(struct crypto_aead *tfm) +{ + struct ocs_aes_tctx *tctx = crypto_aead_ctx(tfm); + + clear_key(tctx); + + if (tctx->sw_cipher.aead) { + crypto_free_aead(tctx->sw_cipher.aead); + tctx->sw_cipher.aead = NULL; + } +} + +static struct skcipher_alg algs[] = { +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB + { + .base.cra_name = "ecb(aes)", + .base.cra_driver_name = "ecb-aes-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_AES_MIN_KEY_SIZE, + .max_keysize = OCS_AES_MAX_KEY_SIZE, + .setkey = kmb_ocs_aes_set_key, + .encrypt = kmb_ocs_aes_ecb_encrypt, + .decrypt = kmb_ocs_aes_ecb_decrypt, + .init = ocs_aes_init_tfm, + .exit = ocs_exit_tfm, + }, +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB */ + { + .base.cra_name = "cbc(aes)", + .base.cra_driver_name = "cbc-aes-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_AES_MIN_KEY_SIZE, + .max_keysize = OCS_AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_aes_set_key, + .encrypt = kmb_ocs_aes_cbc_encrypt, + .decrypt = kmb_ocs_aes_cbc_decrypt, + .init = ocs_aes_init_tfm, + .exit = ocs_exit_tfm, + }, + { + .base.cra_name = "ctr(aes)", + .base.cra_driver_name = "ctr-aes-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_AES_MIN_KEY_SIZE, + .max_keysize = OCS_AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_aes_set_key, + .encrypt = kmb_ocs_aes_ctr_encrypt, + .decrypt = kmb_ocs_aes_ctr_decrypt, + .init = ocs_aes_init_tfm, + .exit = ocs_exit_tfm, + }, +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS + { + .base.cra_name = "cts(cbc(aes))", + .base.cra_driver_name = "cts-aes-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_AES_MIN_KEY_SIZE, + .max_keysize = OCS_AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_aes_set_key, + .encrypt = kmb_ocs_aes_cts_encrypt, + .decrypt = kmb_ocs_aes_cts_decrypt, + .init = ocs_aes_init_tfm, + .exit = ocs_exit_tfm, + }, +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS */ +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB + { + .base.cra_name = "ecb(sm4)", + .base.cra_driver_name = "ecb-sm4-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_SM4_KEY_SIZE, + .max_keysize = OCS_SM4_KEY_SIZE, + .setkey = kmb_ocs_sm4_set_key, + .encrypt = kmb_ocs_sm4_ecb_encrypt, + .decrypt = kmb_ocs_sm4_ecb_decrypt, + .init = ocs_sm4_init_tfm, + .exit = ocs_exit_tfm, + }, +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB */ + { + .base.cra_name = "cbc(sm4)", + .base.cra_driver_name = "cbc-sm4-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_SM4_KEY_SIZE, + .max_keysize = OCS_SM4_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_sm4_set_key, + .encrypt = kmb_ocs_sm4_cbc_encrypt, + .decrypt = kmb_ocs_sm4_cbc_decrypt, + .init = ocs_sm4_init_tfm, + .exit = ocs_exit_tfm, + }, + { + .base.cra_name = "ctr(sm4)", + .base.cra_driver_name = "ctr-sm4-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_SM4_KEY_SIZE, + .max_keysize = OCS_SM4_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_sm4_set_key, + .encrypt = kmb_ocs_sm4_ctr_encrypt, + .decrypt = kmb_ocs_sm4_ctr_decrypt, + .init = ocs_sm4_init_tfm, + .exit = ocs_exit_tfm, + }, +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS + { + .base.cra_name = "cts(cbc(sm4))", + .base.cra_driver_name = "cts-sm4-keembay-ocs", + .base.cra_priority = KMB_OCS_PRIORITY, + .base.cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct ocs_aes_tctx), + .base.cra_module = THIS_MODULE, + .base.cra_alignmask = 0, + + .min_keysize = OCS_SM4_KEY_SIZE, + .max_keysize = OCS_SM4_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = kmb_ocs_sm4_set_key, + .encrypt = kmb_ocs_sm4_cts_encrypt, + .decrypt = kmb_ocs_sm4_cts_decrypt, + .init = ocs_sm4_init_tfm, + .exit = ocs_exit_tfm, + } +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS */ +}; + +static struct aead_alg algs_aead[] = { + { + .base = { + .cra_name = "gcm(aes)", + .cra_driver_name = "gcm-aes-keembay-ocs", + .cra_priority = KMB_OCS_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct ocs_aes_tctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + }, + .init = ocs_aes_aead_cra_init, + .exit = ocs_aead_cra_exit, + .ivsize = GCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .setauthsize = kmb_ocs_aead_gcm_setauthsize, + .setkey = kmb_ocs_aes_aead_set_key, + .encrypt = kmb_ocs_aes_gcm_encrypt, + .decrypt = kmb_ocs_aes_gcm_decrypt, + }, + { + .base = { + .cra_name = "ccm(aes)", + .cra_driver_name = "ccm-aes-keembay-ocs", + .cra_priority = KMB_OCS_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct ocs_aes_tctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + }, + .init = ocs_aes_aead_cra_init, + .exit = ocs_aead_cra_exit, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .setauthsize = kmb_ocs_aead_ccm_setauthsize, + .setkey = kmb_ocs_aes_aead_set_key, + .encrypt = kmb_ocs_aes_ccm_encrypt, + .decrypt = kmb_ocs_aes_ccm_decrypt, + }, + { + .base = { + .cra_name = "gcm(sm4)", + .cra_driver_name = "gcm-sm4-keembay-ocs", + .cra_priority = KMB_OCS_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct ocs_aes_tctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + }, + .init = ocs_sm4_aead_cra_init, + .exit = ocs_aead_cra_exit, + .ivsize = GCM_AES_IV_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .setauthsize = kmb_ocs_aead_gcm_setauthsize, + .setkey = kmb_ocs_sm4_aead_set_key, + .encrypt = kmb_ocs_sm4_gcm_encrypt, + .decrypt = kmb_ocs_sm4_gcm_decrypt, + }, + { + .base = { + .cra_name = "ccm(sm4)", + .cra_driver_name = "ccm-sm4-keembay-ocs", + .cra_priority = KMB_OCS_PRIORITY, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_KERN_DRIVER_ONLY, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct ocs_aes_tctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + }, + .init = ocs_sm4_aead_cra_init, + .exit = ocs_aead_cra_exit, + .ivsize = AES_BLOCK_SIZE, + .maxauthsize = AES_BLOCK_SIZE, + .setauthsize = kmb_ocs_aead_ccm_setauthsize, + .setkey = kmb_ocs_sm4_aead_set_key, + .encrypt = kmb_ocs_sm4_ccm_encrypt, + .decrypt = kmb_ocs_sm4_ccm_decrypt, + } +}; + +static void unregister_aes_algs(struct ocs_aes_dev *aes_dev) +{ + crypto_unregister_aeads(algs_aead, ARRAY_SIZE(algs_aead)); + crypto_unregister_skciphers(algs, ARRAY_SIZE(algs)); +} + +static int register_aes_algs(struct ocs_aes_dev *aes_dev) +{ + int ret; + + /* + * If any algorithm fails to register, all preceding algorithms that + * were successfully registered will be automatically unregistered. + */ + ret = crypto_register_aeads(algs_aead, ARRAY_SIZE(algs_aead)); + if (ret) + return ret; + + ret = crypto_register_skciphers(algs, ARRAY_SIZE(algs)); + if (ret) + crypto_unregister_aeads(algs_aead, ARRAY_SIZE(algs)); + + return ret; +} + +/* Device tree driver match. */ +static const struct of_device_id kmb_ocs_aes_of_match[] = { + { + .compatible = "intel,keembay-ocs-aes", + }, + {} +}; + +static int kmb_ocs_aes_remove(struct platform_device *pdev) +{ + struct ocs_aes_dev *aes_dev; + + aes_dev = platform_get_drvdata(pdev); + if (!aes_dev) + return -ENODEV; + + unregister_aes_algs(aes_dev); + + spin_lock(&ocs_aes.lock); + list_del(&aes_dev->list); + spin_unlock(&ocs_aes.lock); + + crypto_engine_exit(aes_dev->engine); + + return 0; +} + +static int kmb_ocs_aes_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ocs_aes_dev *aes_dev; + struct resource *aes_mem; + int rc; + + aes_dev = devm_kzalloc(dev, sizeof(*aes_dev), GFP_KERNEL); + if (!aes_dev) + return -ENOMEM; + + aes_dev->dev = dev; + + platform_set_drvdata(pdev, aes_dev); + + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (rc) { + dev_err(dev, "Failed to set 32 bit dma mask %d\n", rc); + return rc; + } + + /* Get base register address. */ + aes_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!aes_mem) { + dev_err(dev, "Could not retrieve io mem resource\n"); + return -ENODEV; + } + + aes_dev->base_reg = devm_ioremap_resource(&pdev->dev, aes_mem); + if (IS_ERR(aes_dev->base_reg)) { + dev_err(dev, "Failed to get base address\n"); + return PTR_ERR(aes_dev->base_reg); + } + + /* Get and request IRQ */ + aes_dev->irq = platform_get_irq(pdev, 0); + if (aes_dev->irq < 0) + return aes_dev->irq; + + rc = devm_request_threaded_irq(dev, aes_dev->irq, ocs_aes_irq_handler, + NULL, 0, "keembay-ocs-aes", aes_dev); + if (rc < 0) { + dev_err(dev, "Could not request IRQ\n"); + return rc; + } + + INIT_LIST_HEAD(&aes_dev->list); + spin_lock(&ocs_aes.lock); + list_add_tail(&aes_dev->list, &ocs_aes.dev_list); + spin_unlock(&ocs_aes.lock); + + init_completion(&aes_dev->irq_completion); + + /* Initialize crypto engine */ + aes_dev->engine = crypto_engine_alloc_init(dev, true); + if (!aes_dev->engine) + goto list_del; + + rc = crypto_engine_start(aes_dev->engine); + if (rc) { + dev_err(dev, "Could not start crypto engine\n"); + goto cleanup; + } + + rc = register_aes_algs(aes_dev); + if (rc) { + dev_err(dev, + "Could not register OCS algorithms with Crypto API\n"); + goto cleanup; + } + + return 0; + +cleanup: + crypto_engine_exit(aes_dev->engine); +list_del: + spin_lock(&ocs_aes.lock); + list_del(&aes_dev->list); + spin_unlock(&ocs_aes.lock); + + return rc; +} + +/* The OCS driver is a platform device. */ +static struct platform_driver kmb_ocs_aes_driver = { + .probe = kmb_ocs_aes_probe, + .remove = kmb_ocs_aes_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = kmb_ocs_aes_of_match, + }, +}; + +module_platform_driver(kmb_ocs_aes_driver); + +MODULE_DESCRIPTION("Intel Keem Bay Offload and Crypto Subsystem (OCS) AES/SM4 Driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS_CRYPTO("cbc-aes-keembay-ocs"); +MODULE_ALIAS_CRYPTO("ctr-aes-keembay-ocs"); +MODULE_ALIAS_CRYPTO("gcm-aes-keembay-ocs"); +MODULE_ALIAS_CRYPTO("ccm-aes-keembay-ocs"); + +MODULE_ALIAS_CRYPTO("cbc-sm4-keembay-ocs"); +MODULE_ALIAS_CRYPTO("ctr-sm4-keembay-ocs"); +MODULE_ALIAS_CRYPTO("gcm-sm4-keembay-ocs"); +MODULE_ALIAS_CRYPTO("ccm-sm4-keembay-ocs"); + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB +MODULE_ALIAS_CRYPTO("ecb-aes-keembay-ocs"); +MODULE_ALIAS_CRYPTO("ecb-sm4-keembay-ocs"); +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_ECB */ + +#ifdef CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS +MODULE_ALIAS_CRYPTO("cts-aes-keembay-ocs"); +MODULE_ALIAS_CRYPTO("cts-sm4-keembay-ocs"); +#endif /* CONFIG_CRYPTO_DEV_KEEMBAY_OCS_AES_SM4_CTS */ diff --git a/drivers/crypto/keembay/ocs-aes.c b/drivers/crypto/keembay/ocs-aes.c new file mode 100644 index 000000000000..cc286adb1c4a --- /dev/null +++ b/drivers/crypto/keembay/ocs-aes.c @@ -0,0 +1,1489 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Keem Bay OCS AES Crypto Driver. + * + * Copyright (C) 2018-2020 Intel Corporation + */ + +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/swab.h> + +#include <asm/byteorder.h> +#include <asm/errno.h> + +#include <crypto/aes.h> +#include <crypto/gcm.h> + +#include "ocs-aes.h" + +#define AES_COMMAND_OFFSET 0x0000 +#define AES_KEY_0_OFFSET 0x0004 +#define AES_KEY_1_OFFSET 0x0008 +#define AES_KEY_2_OFFSET 0x000C +#define AES_KEY_3_OFFSET 0x0010 +#define AES_KEY_4_OFFSET 0x0014 +#define AES_KEY_5_OFFSET 0x0018 +#define AES_KEY_6_OFFSET 0x001C +#define AES_KEY_7_OFFSET 0x0020 +#define AES_IV_0_OFFSET 0x0024 +#define AES_IV_1_OFFSET 0x0028 +#define AES_IV_2_OFFSET 0x002C +#define AES_IV_3_OFFSET 0x0030 +#define AES_ACTIVE_OFFSET 0x0034 +#define AES_STATUS_OFFSET 0x0038 +#define AES_KEY_SIZE_OFFSET 0x0044 +#define AES_IER_OFFSET 0x0048 +#define AES_ISR_OFFSET 0x005C +#define AES_MULTIPURPOSE1_0_OFFSET 0x0200 +#define AES_MULTIPURPOSE1_1_OFFSET 0x0204 +#define AES_MULTIPURPOSE1_2_OFFSET 0x0208 +#define AES_MULTIPURPOSE1_3_OFFSET 0x020C +#define AES_MULTIPURPOSE2_0_OFFSET 0x0220 +#define AES_MULTIPURPOSE2_1_OFFSET 0x0224 +#define AES_MULTIPURPOSE2_2_OFFSET 0x0228 +#define AES_MULTIPURPOSE2_3_OFFSET 0x022C +#define AES_BYTE_ORDER_CFG_OFFSET 0x02C0 +#define AES_TLEN_OFFSET 0x0300 +#define AES_T_MAC_0_OFFSET 0x0304 +#define AES_T_MAC_1_OFFSET 0x0308 +#define AES_T_MAC_2_OFFSET 0x030C +#define AES_T_MAC_3_OFFSET 0x0310 +#define AES_PLEN_OFFSET 0x0314 +#define AES_A_DMA_SRC_ADDR_OFFSET 0x0400 +#define AES_A_DMA_DST_ADDR_OFFSET 0x0404 +#define AES_A_DMA_SRC_SIZE_OFFSET 0x0408 +#define AES_A_DMA_DST_SIZE_OFFSET 0x040C +#define AES_A_DMA_DMA_MODE_OFFSET 0x0410 +#define AES_A_DMA_NEXT_SRC_DESCR_OFFSET 0x0418 +#define AES_A_DMA_NEXT_DST_DESCR_OFFSET 0x041C +#define AES_A_DMA_WHILE_ACTIVE_MODE_OFFSET 0x0420 +#define AES_A_DMA_LOG_OFFSET 0x0424 +#define AES_A_DMA_STATUS_OFFSET 0x0428 +#define AES_A_DMA_PERF_CNTR_OFFSET 0x042C +#define AES_A_DMA_MSI_ISR_OFFSET 0x0480 +#define AES_A_DMA_MSI_IER_OFFSET 0x0484 +#define AES_A_DMA_MSI_MASK_OFFSET 0x0488 +#define AES_A_DMA_INBUFFER_WRITE_FIFO_OFFSET 0x0600 +#define AES_A_DMA_OUTBUFFER_READ_FIFO_OFFSET 0x0700 + +/* + * AES_A_DMA_DMA_MODE register. + * Default: 0x00000000. + * bit[31] ACTIVE + * This bit activates the DMA. When the DMA finishes, it resets + * this bit to zero. + * bit[30:26] Unused by this driver. + * bit[25] SRC_LINK_LIST_EN + * Source link list enable bit. When the linked list is terminated + * this bit is reset by the DMA. + * bit[24] DST_LINK_LIST_EN + * Destination link list enable bit. When the linked list is + * terminated this bit is reset by the DMA. + * bit[23:0] Unused by this driver. + */ +#define AES_A_DMA_DMA_MODE_ACTIVE BIT(31) +#define AES_A_DMA_DMA_MODE_SRC_LINK_LIST_EN BIT(25) +#define AES_A_DMA_DMA_MODE_DST_LINK_LIST_EN BIT(24) + +/* + * AES_ACTIVE register + * default 0x00000000 + * bit[31:10] Reserved + * bit[9] LAST_ADATA + * bit[8] LAST_GCX + * bit[7:2] Reserved + * bit[1] TERMINATION + * bit[0] TRIGGER + */ +#define AES_ACTIVE_LAST_ADATA BIT(9) +#define AES_ACTIVE_LAST_CCM_GCM BIT(8) +#define AES_ACTIVE_TERMINATION BIT(1) +#define AES_ACTIVE_TRIGGER BIT(0) + +#define AES_DISABLE_INT 0x00000000 +#define AES_DMA_CPD_ERR_INT BIT(8) +#define AES_DMA_OUTBUF_RD_ERR_INT BIT(7) +#define AES_DMA_OUTBUF_WR_ERR_INT BIT(6) +#define AES_DMA_INBUF_RD_ERR_INT BIT(5) +#define AES_DMA_INBUF_WR_ERR_INT BIT(4) +#define AES_DMA_BAD_COMP_INT BIT(3) +#define AES_DMA_SAI_INT BIT(2) +#define AES_DMA_SRC_DONE_INT BIT(0) +#define AES_COMPLETE_INT BIT(1) + +#define AES_DMA_MSI_MASK_CLEAR BIT(0) + +#define AES_128_BIT_KEY 0x00000000 +#define AES_256_BIT_KEY BIT(0) + +#define AES_DEACTIVATE_PERF_CNTR 0x00000000 +#define AES_ACTIVATE_PERF_CNTR BIT(0) + +#define AES_MAX_TAG_SIZE_U32 4 + +#define OCS_LL_DMA_FLAG_TERMINATE BIT(31) + +/* + * There is an inconsistency in the documentation. This is documented as a + * 11-bit value, but it is actually 10-bits. + */ +#define AES_DMA_STATUS_INPUT_BUFFER_OCCUPANCY_MASK 0x3FF + +/* + * During CCM decrypt, the OCS block needs to finish processing the ciphertext + * before the tag is written. For 128-bit mode this required delay is 28 OCS + * clock cycles. For 256-bit mode it is 36 OCS clock cycles. + */ +#define CCM_DECRYPT_DELAY_TAG_CLK_COUNT 36UL + +/* + * During CCM decrypt there must be a delay of at least 42 OCS clock cycles + * between setting the TRIGGER bit in AES_ACTIVE and setting the LAST_CCM_GCM + * bit in the same register (as stated in the OCS databook) + */ +#define CCM_DECRYPT_DELAY_LAST_GCX_CLK_COUNT 42UL + +/* See RFC3610 section 2.2 */ +#define L_PRIME_MIN (1) +#define L_PRIME_MAX (7) +/* + * CCM IV format from RFC 3610 section 2.3 + * + * Octet Number Contents + * ------------ --------- + * 0 Flags + * 1 ... 15-L Nonce N + * 16-L ... 15 Counter i + * + * Flags = L' = L - 1 + */ +#define L_PRIME_IDX 0 +#define COUNTER_START(lprime) (16 - ((lprime) + 1)) +#define COUNTER_LEN(lprime) ((lprime) + 1) + +enum aes_counter_mode { + AES_CTR_M_NO_INC = 0, + AES_CTR_M_32_INC = 1, + AES_CTR_M_64_INC = 2, + AES_CTR_M_128_INC = 3, +}; + +/** + * struct ocs_dma_linked_list - OCS DMA linked list entry. + * @src_addr: Source address of the data. + * @src_len: Length of data to be fetched. + * @next: Next dma_list to fetch. + * @ll_flags: Flags (Freeze @ terminate) for the DMA engine. + */ +struct ocs_dma_linked_list { + u32 src_addr; + u32 src_len; + u32 next; + u32 ll_flags; +} __packed; + +/* + * Set endianness of inputs and outputs + * AES_BYTE_ORDER_CFG + * default 0x00000000 + * bit [10] - KEY_HI_LO_SWAP + * bit [9] - KEY_HI_SWAP_DWORDS_IN_OCTWORD + * bit [8] - KEY_HI_SWAP_BYTES_IN_DWORD + * bit [7] - KEY_LO_SWAP_DWORDS_IN_OCTWORD + * bit [6] - KEY_LO_SWAP_BYTES_IN_DWORD + * bit [5] - IV_SWAP_DWORDS_IN_OCTWORD + * bit [4] - IV_SWAP_BYTES_IN_DWORD + * bit [3] - DOUT_SWAP_DWORDS_IN_OCTWORD + * bit [2] - DOUT_SWAP_BYTES_IN_DWORD + * bit [1] - DOUT_SWAP_DWORDS_IN_OCTWORD + * bit [0] - DOUT_SWAP_BYTES_IN_DWORD + */ +static inline void aes_a_set_endianness(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(0x7FF, aes_dev->base_reg + AES_BYTE_ORDER_CFG_OFFSET); +} + +/* Trigger AES process start. */ +static inline void aes_a_op_trigger(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_ACTIVE_TRIGGER, aes_dev->base_reg + AES_ACTIVE_OFFSET); +} + +/* Indicate last bulk of data. */ +static inline void aes_a_op_termination(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_ACTIVE_TERMINATION, + aes_dev->base_reg + AES_ACTIVE_OFFSET); +} + +/* + * Set LAST_CCM_GCM in AES_ACTIVE register and clear all other bits. + * + * Called when DMA is programmed to fetch the last batch of data. + * - For AES-CCM it is called for the last batch of Payload data and Ciphertext + * data. + * - For AES-GCM, it is called for the last batch of Plaintext data and + * Ciphertext data. + */ +static inline void aes_a_set_last_gcx(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_ACTIVE_LAST_CCM_GCM, + aes_dev->base_reg + AES_ACTIVE_OFFSET); +} + +/* Wait for LAST_CCM_GCM bit to be unset. */ +static inline void aes_a_wait_last_gcx(const struct ocs_aes_dev *aes_dev) +{ + u32 aes_active_reg; + + do { + aes_active_reg = ioread32(aes_dev->base_reg + + AES_ACTIVE_OFFSET); + } while (aes_active_reg & AES_ACTIVE_LAST_CCM_GCM); +} + +/* Wait for 10 bits of input occupancy. */ +static void aes_a_dma_wait_input_buffer_occupancy(const struct ocs_aes_dev *aes_dev) +{ + u32 reg; + + do { + reg = ioread32(aes_dev->base_reg + AES_A_DMA_STATUS_OFFSET); + } while (reg & AES_DMA_STATUS_INPUT_BUFFER_OCCUPANCY_MASK); +} + + /* + * Set LAST_CCM_GCM and LAST_ADATA bits in AES_ACTIVE register (and clear all + * other bits). + * + * Called when DMA is programmed to fetch the last batch of Associated Data + * (CCM case) or Additional Authenticated Data (GCM case). + */ +static inline void aes_a_set_last_gcx_and_adata(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_ACTIVE_LAST_ADATA | AES_ACTIVE_LAST_CCM_GCM, + aes_dev->base_reg + AES_ACTIVE_OFFSET); +} + +/* Set DMA src and dst transfer size to 0 */ +static inline void aes_a_dma_set_xfer_size_zero(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(0, aes_dev->base_reg + AES_A_DMA_SRC_SIZE_OFFSET); + iowrite32(0, aes_dev->base_reg + AES_A_DMA_DST_SIZE_OFFSET); +} + +/* Activate DMA for zero-byte transfer case. */ +static inline void aes_a_dma_active(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_A_DMA_DMA_MODE_ACTIVE, + aes_dev->base_reg + AES_A_DMA_DMA_MODE_OFFSET); +} + +/* Activate DMA and enable src linked list */ +static inline void aes_a_dma_active_src_ll_en(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_A_DMA_DMA_MODE_ACTIVE | + AES_A_DMA_DMA_MODE_SRC_LINK_LIST_EN, + aes_dev->base_reg + AES_A_DMA_DMA_MODE_OFFSET); +} + +/* Activate DMA and enable dst linked list */ +static inline void aes_a_dma_active_dst_ll_en(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_A_DMA_DMA_MODE_ACTIVE | + AES_A_DMA_DMA_MODE_DST_LINK_LIST_EN, + aes_dev->base_reg + AES_A_DMA_DMA_MODE_OFFSET); +} + +/* Activate DMA and enable src and dst linked lists */ +static inline void aes_a_dma_active_src_dst_ll_en(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(AES_A_DMA_DMA_MODE_ACTIVE | + AES_A_DMA_DMA_MODE_SRC_LINK_LIST_EN | + AES_A_DMA_DMA_MODE_DST_LINK_LIST_EN, + aes_dev->base_reg + AES_A_DMA_DMA_MODE_OFFSET); +} + +/* Reset PERF_CNTR to 0 and activate it */ +static inline void aes_a_dma_reset_and_activate_perf_cntr(const struct ocs_aes_dev *aes_dev) +{ + iowrite32(0x00000000, aes_dev->base_reg + AES_A_DMA_PERF_CNTR_OFFSET); + iowrite32(AES_ACTIVATE_PERF_CNTR, + aes_dev->base_reg + AES_A_DMA_WHILE_ACTIVE_MODE_OFFSET); +} + +/* Wait until PERF_CNTR is > delay, then deactivate it */ +static inline void aes_a_dma_wait_and_deactivate_perf_cntr(const struct ocs_aes_dev *aes_dev, + int delay) +{ + while (ioread32(aes_dev->base_reg + AES_A_DMA_PERF_CNTR_OFFSET) < delay) + ; + iowrite32(AES_DEACTIVATE_PERF_CNTR, + aes_dev->base_reg + AES_A_DMA_WHILE_ACTIVE_MODE_OFFSET); +} + +/* Disable AES and DMA IRQ. */ +static void aes_irq_disable(struct ocs_aes_dev *aes_dev) +{ + u32 isr_val = 0; + + /* Disable interrupts */ + iowrite32(AES_DISABLE_INT, + aes_dev->base_reg + AES_A_DMA_MSI_IER_OFFSET); + iowrite32(AES_DISABLE_INT, aes_dev->base_reg + AES_IER_OFFSET); + + /* Clear any pending interrupt */ + isr_val = ioread32(aes_dev->base_reg + AES_A_DMA_MSI_ISR_OFFSET); + if (isr_val) + iowrite32(isr_val, + aes_dev->base_reg + AES_A_DMA_MSI_ISR_OFFSET); + + isr_val = ioread32(aes_dev->base_reg + AES_A_DMA_MSI_MASK_OFFSET); + if (isr_val) + iowrite32(isr_val, + aes_dev->base_reg + AES_A_DMA_MSI_MASK_OFFSET); + + isr_val = ioread32(aes_dev->base_reg + AES_ISR_OFFSET); + if (isr_val) + iowrite32(isr_val, aes_dev->base_reg + AES_ISR_OFFSET); +} + +/* Enable AES or DMA IRQ. IRQ is disabled once fired. */ +static void aes_irq_enable(struct ocs_aes_dev *aes_dev, u8 irq) +{ + if (irq == AES_COMPLETE_INT) { + /* Ensure DMA error interrupts are enabled */ + iowrite32(AES_DMA_CPD_ERR_INT | + AES_DMA_OUTBUF_RD_ERR_INT | + AES_DMA_OUTBUF_WR_ERR_INT | + AES_DMA_INBUF_RD_ERR_INT | + AES_DMA_INBUF_WR_ERR_INT | + AES_DMA_BAD_COMP_INT | + AES_DMA_SAI_INT, + aes_dev->base_reg + AES_A_DMA_MSI_IER_OFFSET); + /* + * AES_IER + * default 0x00000000 + * bits [31:3] - reserved + * bit [2] - EN_SKS_ERR + * bit [1] - EN_AES_COMPLETE + * bit [0] - reserved + */ + iowrite32(AES_COMPLETE_INT, aes_dev->base_reg + AES_IER_OFFSET); + return; + } + if (irq == AES_DMA_SRC_DONE_INT) { + /* Ensure AES interrupts are disabled */ + iowrite32(AES_DISABLE_INT, aes_dev->base_reg + AES_IER_OFFSET); + /* + * DMA_MSI_IER + * default 0x00000000 + * bits [31:9] - reserved + * bit [8] - CPD_ERR_INT_EN + * bit [7] - OUTBUF_RD_ERR_INT_EN + * bit [6] - OUTBUF_WR_ERR_INT_EN + * bit [5] - INBUF_RD_ERR_INT_EN + * bit [4] - INBUF_WR_ERR_INT_EN + * bit [3] - BAD_COMP_INT_EN + * bit [2] - SAI_INT_EN + * bit [1] - DST_DONE_INT_EN + * bit [0] - SRC_DONE_INT_EN + */ + iowrite32(AES_DMA_CPD_ERR_INT | + AES_DMA_OUTBUF_RD_ERR_INT | + AES_DMA_OUTBUF_WR_ERR_INT | + AES_DMA_INBUF_RD_ERR_INT | + AES_DMA_INBUF_WR_ERR_INT | + AES_DMA_BAD_COMP_INT | + AES_DMA_SAI_INT | + AES_DMA_SRC_DONE_INT, + aes_dev->base_reg + AES_A_DMA_MSI_IER_OFFSET); + } +} + +/* Enable and wait for IRQ (either from OCS AES engine or DMA) */ +static int ocs_aes_irq_enable_and_wait(struct ocs_aes_dev *aes_dev, u8 irq) +{ + int rc; + + reinit_completion(&aes_dev->irq_completion); + aes_irq_enable(aes_dev, irq); + rc = wait_for_completion_interruptible(&aes_dev->irq_completion); + if (rc) + return rc; + + return aes_dev->dma_err_mask ? -EIO : 0; +} + +/* Configure DMA to OCS, linked list mode */ +static inline void dma_to_ocs_aes_ll(struct ocs_aes_dev *aes_dev, + dma_addr_t dma_list) +{ + iowrite32(0, aes_dev->base_reg + AES_A_DMA_SRC_SIZE_OFFSET); + iowrite32(dma_list, + aes_dev->base_reg + AES_A_DMA_NEXT_SRC_DESCR_OFFSET); +} + +/* Configure DMA from OCS, linked list mode */ +static inline void dma_from_ocs_aes_ll(struct ocs_aes_dev *aes_dev, + dma_addr_t dma_list) +{ + iowrite32(0, aes_dev->base_reg + AES_A_DMA_DST_SIZE_OFFSET); + iowrite32(dma_list, + aes_dev->base_reg + AES_A_DMA_NEXT_DST_DESCR_OFFSET); +} + +irqreturn_t ocs_aes_irq_handler(int irq, void *dev_id) +{ + struct ocs_aes_dev *aes_dev = dev_id; + u32 aes_dma_isr; + + /* Read DMA ISR status. */ + aes_dma_isr = ioread32(aes_dev->base_reg + AES_A_DMA_MSI_ISR_OFFSET); + + /* Disable and clear interrupts. */ + aes_irq_disable(aes_dev); + + /* Save DMA error status. */ + aes_dev->dma_err_mask = aes_dma_isr & + (AES_DMA_CPD_ERR_INT | + AES_DMA_OUTBUF_RD_ERR_INT | + AES_DMA_OUTBUF_WR_ERR_INT | + AES_DMA_INBUF_RD_ERR_INT | + AES_DMA_INBUF_WR_ERR_INT | + AES_DMA_BAD_COMP_INT | + AES_DMA_SAI_INT); + + /* Signal IRQ completion. */ + complete(&aes_dev->irq_completion); + + return IRQ_HANDLED; +} + +/** + * ocs_aes_set_key() - Write key into OCS AES hardware. + * @aes_dev: The OCS AES device to write the key to. + * @key_size: The size of the key (in bytes). + * @key: The key to write. + * @cipher: The cipher the key is for. + * + * For AES @key_size must be either 16 or 32. For SM4 @key_size must be 16. + * + * Return: 0 on success, negative error code otherwise. + */ +int ocs_aes_set_key(struct ocs_aes_dev *aes_dev, u32 key_size, const u8 *key, + enum ocs_cipher cipher) +{ + const u32 *key_u32; + u32 val; + int i; + + /* OCS AES supports 128-bit and 256-bit keys only. */ + if (cipher == OCS_AES && !(key_size == 32 || key_size == 16)) { + dev_err(aes_dev->dev, + "%d-bit keys not supported by AES cipher\n", + key_size * 8); + return -EINVAL; + } + /* OCS SM4 supports 128-bit keys only. */ + if (cipher == OCS_SM4 && key_size != 16) { + dev_err(aes_dev->dev, + "%d-bit keys not supported for SM4 cipher\n", + key_size * 8); + return -EINVAL; + } + + if (!key) + return -EINVAL; + + key_u32 = (const u32 *)key; + + /* Write key to AES_KEY[0-7] registers */ + for (i = 0; i < (key_size / sizeof(u32)); i++) { + iowrite32(key_u32[i], + aes_dev->base_reg + AES_KEY_0_OFFSET + + (i * sizeof(u32))); + } + /* + * Write key size + * bits [31:1] - reserved + * bit [0] - AES_KEY_SIZE + * 0 - 128 bit key + * 1 - 256 bit key + */ + val = (key_size == 16) ? AES_128_BIT_KEY : AES_256_BIT_KEY; + iowrite32(val, aes_dev->base_reg + AES_KEY_SIZE_OFFSET); + + return 0; +} + +/* Write AES_COMMAND */ +static inline void set_ocs_aes_command(struct ocs_aes_dev *aes_dev, + enum ocs_cipher cipher, + enum ocs_mode mode, + enum ocs_instruction instruction) +{ + u32 val; + + /* AES_COMMAND + * default 0x000000CC + * bit [14] - CIPHER_SELECT + * 0 - AES + * 1 - SM4 + * bits [11:8] - OCS_AES_MODE + * 0000 - ECB + * 0001 - CBC + * 0010 - CTR + * 0110 - CCM + * 0111 - GCM + * 1001 - CTS + * bits [7:6] - AES_INSTRUCTION + * 00 - ENCRYPT + * 01 - DECRYPT + * 10 - EXPAND + * 11 - BYPASS + * bits [3:2] - CTR_M_BITS + * 00 - No increment + * 01 - Least significant 32 bits are incremented + * 10 - Least significant 64 bits are incremented + * 11 - Full 128 bits are incremented + */ + val = (cipher << 14) | (mode << 8) | (instruction << 6) | + (AES_CTR_M_128_INC << 2); + iowrite32(val, aes_dev->base_reg + AES_COMMAND_OFFSET); +} + +static void ocs_aes_init(struct ocs_aes_dev *aes_dev, + enum ocs_mode mode, + enum ocs_cipher cipher, + enum ocs_instruction instruction) +{ + /* Ensure interrupts are disabled and pending interrupts cleared. */ + aes_irq_disable(aes_dev); + + /* Set endianness recommended by data-sheet. */ + aes_a_set_endianness(aes_dev); + + /* Set AES_COMMAND register. */ + set_ocs_aes_command(aes_dev, cipher, mode, instruction); +} + +/* + * Write the byte length of the last AES/SM4 block of Payload data (without + * zero padding and without the length of the MAC) in register AES_PLEN. + */ +static inline void ocs_aes_write_last_data_blk_len(struct ocs_aes_dev *aes_dev, + u32 size) +{ + u32 val; + + if (size == 0) { + val = 0; + goto exit; + } + + val = size % AES_BLOCK_SIZE; + if (val == 0) + val = AES_BLOCK_SIZE; + +exit: + iowrite32(val, aes_dev->base_reg + AES_PLEN_OFFSET); +} + +/* + * Validate inputs according to mode. + * If OK return 0; else return -EINVAL. + */ +static int ocs_aes_validate_inputs(dma_addr_t src_dma_list, u32 src_size, + const u8 *iv, u32 iv_size, + dma_addr_t aad_dma_list, u32 aad_size, + const u8 *tag, u32 tag_size, + enum ocs_cipher cipher, enum ocs_mode mode, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list) +{ + /* Ensure cipher, mode and instruction are valid. */ + if (!(cipher == OCS_AES || cipher == OCS_SM4)) + return -EINVAL; + + if (mode != OCS_MODE_ECB && mode != OCS_MODE_CBC && + mode != OCS_MODE_CTR && mode != OCS_MODE_CCM && + mode != OCS_MODE_GCM && mode != OCS_MODE_CTS) + return -EINVAL; + + if (instruction != OCS_ENCRYPT && instruction != OCS_DECRYPT && + instruction != OCS_EXPAND && instruction != OCS_BYPASS) + return -EINVAL; + + /* + * When instruction is OCS_BYPASS, OCS simply copies data from source + * to destination using DMA. + * + * AES mode is irrelevant, but both source and destination DMA + * linked-list must be defined. + */ + if (instruction == OCS_BYPASS) { + if (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + return 0; + } + + /* + * For performance reasons switch based on mode to limit unnecessary + * conditionals for each mode + */ + switch (mode) { + case OCS_MODE_ECB: + /* Ensure input length is multiple of block size */ + if (src_size % AES_BLOCK_SIZE != 0) + return -EINVAL; + + /* Ensure source and destination linked lists are created */ + if (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + return 0; + + case OCS_MODE_CBC: + /* Ensure input length is multiple of block size */ + if (src_size % AES_BLOCK_SIZE != 0) + return -EINVAL; + + /* Ensure source and destination linked lists are created */ + if (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* Ensure IV is present and block size in length */ + if (!iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + return 0; + + case OCS_MODE_CTR: + /* Ensure input length of 1 byte or greater */ + if (src_size == 0) + return -EINVAL; + + /* Ensure source and destination linked lists are created */ + if (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* Ensure IV is present and block size in length */ + if (!iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + return 0; + + case OCS_MODE_CTS: + /* Ensure input length >= block size */ + if (src_size < AES_BLOCK_SIZE) + return -EINVAL; + + /* Ensure source and destination linked lists are created */ + if (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* Ensure IV is present and block size in length */ + if (!iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + return 0; + + case OCS_MODE_GCM: + /* Ensure IV is present and GCM_AES_IV_SIZE in length */ + if (!iv || iv_size != GCM_AES_IV_SIZE) + return -EINVAL; + + /* + * If input data present ensure source and destination linked + * lists are created + */ + if (src_size && (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR)) + return -EINVAL; + + /* If aad present ensure aad linked list is created */ + if (aad_size && aad_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* Ensure tag destination is set */ + if (!tag) + return -EINVAL; + + /* Just ensure that tag_size doesn't cause overflows. */ + if (tag_size > (AES_MAX_TAG_SIZE_U32 * sizeof(u32))) + return -EINVAL; + + return 0; + + case OCS_MODE_CCM: + /* Ensure IV is present and block size in length */ + if (!iv || iv_size != AES_BLOCK_SIZE) + return -EINVAL; + + /* 2 <= L <= 8, so 1 <= L' <= 7 */ + if (iv[L_PRIME_IDX] < L_PRIME_MIN || + iv[L_PRIME_IDX] > L_PRIME_MAX) + return -EINVAL; + + /* If aad present ensure aad linked list is created */ + if (aad_size && aad_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* Just ensure that tag_size doesn't cause overflows. */ + if (tag_size > (AES_MAX_TAG_SIZE_U32 * sizeof(u32))) + return -EINVAL; + + if (instruction == OCS_DECRYPT) { + /* + * If input data present ensure source and destination + * linked lists are created + */ + if (src_size && (src_dma_list == DMA_MAPPING_ERROR || + dst_dma_list == DMA_MAPPING_ERROR)) + return -EINVAL; + + /* Ensure input tag is present */ + if (!tag) + return -EINVAL; + + return 0; + } + + /* Instruction == OCS_ENCRYPT */ + + /* + * Destination linked list always required (for tag even if no + * input data) + */ + if (dst_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + /* If input data present ensure src linked list is created */ + if (src_size && src_dma_list == DMA_MAPPING_ERROR) + return -EINVAL; + + return 0; + + default: + return -EINVAL; + } +} + +/** + * ocs_aes_op() - Perform AES/SM4 operation. + * @aes_dev: The OCS AES device to use. + * @mode: The mode to use (ECB, CBC, CTR, or CTS). + * @cipher: The cipher to use (AES or SM4). + * @instruction: The instruction to perform (encrypt or decrypt). + * @dst_dma_list: The OCS DMA list mapping output memory. + * @src_dma_list: The OCS DMA list mapping input payload data. + * @src_size: The amount of data mapped by @src_dma_list. + * @iv: The IV vector. + * @iv_size: The size (in bytes) of @iv. + * + * Return: 0 on success, negative error code otherwise. + */ +int ocs_aes_op(struct ocs_aes_dev *aes_dev, + enum ocs_mode mode, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + u8 *iv, + u32 iv_size) +{ + u32 *iv32; + int rc; + + rc = ocs_aes_validate_inputs(src_dma_list, src_size, iv, iv_size, 0, 0, + NULL, 0, cipher, mode, instruction, + dst_dma_list); + if (rc) + return rc; + /* + * ocs_aes_validate_inputs() is a generic check, now ensure mode is not + * GCM or CCM. + */ + if (mode == OCS_MODE_GCM || mode == OCS_MODE_CCM) + return -EINVAL; + + /* Cast IV to u32 array. */ + iv32 = (u32 *)iv; + + ocs_aes_init(aes_dev, mode, cipher, instruction); + + if (mode == OCS_MODE_CTS) { + /* Write the byte length of the last data block to engine. */ + ocs_aes_write_last_data_blk_len(aes_dev, src_size); + } + + /* ECB is the only mode that doesn't use IV. */ + if (mode != OCS_MODE_ECB) { + iowrite32(iv32[0], aes_dev->base_reg + AES_IV_0_OFFSET); + iowrite32(iv32[1], aes_dev->base_reg + AES_IV_1_OFFSET); + iowrite32(iv32[2], aes_dev->base_reg + AES_IV_2_OFFSET); + iowrite32(iv32[3], aes_dev->base_reg + AES_IV_3_OFFSET); + } + + /* Set AES_ACTIVE.TRIGGER to start the operation. */ + aes_a_op_trigger(aes_dev); + + /* Configure and activate input / output DMA. */ + dma_to_ocs_aes_ll(aes_dev, src_dma_list); + dma_from_ocs_aes_ll(aes_dev, dst_dma_list); + aes_a_dma_active_src_dst_ll_en(aes_dev); + + if (mode == OCS_MODE_CTS) { + /* + * For CTS mode, instruct engine to activate ciphertext + * stealing if last block of data is incomplete. + */ + aes_a_set_last_gcx(aes_dev); + } else { + /* For all other modes, just write the 'termination' bit. */ + aes_a_op_termination(aes_dev); + } + + /* Wait for engine to complete processing. */ + rc = ocs_aes_irq_enable_and_wait(aes_dev, AES_COMPLETE_INT); + if (rc) + return rc; + + if (mode == OCS_MODE_CTR) { + /* Read back IV for streaming mode */ + iv32[0] = ioread32(aes_dev->base_reg + AES_IV_0_OFFSET); + iv32[1] = ioread32(aes_dev->base_reg + AES_IV_1_OFFSET); + iv32[2] = ioread32(aes_dev->base_reg + AES_IV_2_OFFSET); + iv32[3] = ioread32(aes_dev->base_reg + AES_IV_3_OFFSET); + } + + return 0; +} + +/* Compute and write J0 to engine registers. */ +static void ocs_aes_gcm_write_j0(const struct ocs_aes_dev *aes_dev, + const u8 *iv) +{ + const u32 *j0 = (u32 *)iv; + + /* + * IV must be 12 bytes; Other sizes not supported as Linux crypto API + * does only expects/allows 12 byte IV for GCM + */ + iowrite32(0x00000001, aes_dev->base_reg + AES_IV_0_OFFSET); + iowrite32(__swab32(j0[2]), aes_dev->base_reg + AES_IV_1_OFFSET); + iowrite32(__swab32(j0[1]), aes_dev->base_reg + AES_IV_2_OFFSET); + iowrite32(__swab32(j0[0]), aes_dev->base_reg + AES_IV_3_OFFSET); +} + +/* Read GCM tag from engine registers. */ +static inline void ocs_aes_gcm_read_tag(struct ocs_aes_dev *aes_dev, + u8 *tag, u32 tag_size) +{ + u32 tag_u32[AES_MAX_TAG_SIZE_U32]; + + /* + * The Authentication Tag T is stored in Little Endian order in the + * registers with the most significant bytes stored from AES_T_MAC[3] + * downward. + */ + tag_u32[0] = __swab32(ioread32(aes_dev->base_reg + AES_T_MAC_3_OFFSET)); + tag_u32[1] = __swab32(ioread32(aes_dev->base_reg + AES_T_MAC_2_OFFSET)); + tag_u32[2] = __swab32(ioread32(aes_dev->base_reg + AES_T_MAC_1_OFFSET)); + tag_u32[3] = __swab32(ioread32(aes_dev->base_reg + AES_T_MAC_0_OFFSET)); + + memcpy(tag, tag_u32, tag_size); +} + +/** + * ocs_aes_gcm_op() - Perform GCM operation. + * @aes_dev: The OCS AES device to use. + * @cipher: The Cipher to use (AES or SM4). + * @instruction: The instruction to perform (encrypt or decrypt). + * @dst_dma_list: The OCS DMA list mapping output memory. + * @src_dma_list: The OCS DMA list mapping input payload data. + * @src_size: The amount of data mapped by @src_dma_list. + * @iv: The input IV vector. + * @aad_dma_list: The OCS DMA list mapping input AAD data. + * @aad_size: The amount of data mapped by @aad_dma_list. + * @out_tag: Where to store computed tag. + * @tag_size: The size (in bytes) of @out_tag. + * + * Return: 0 on success, negative error code otherwise. + */ +int ocs_aes_gcm_op(struct ocs_aes_dev *aes_dev, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + const u8 *iv, + dma_addr_t aad_dma_list, + u32 aad_size, + u8 *out_tag, + u32 tag_size) +{ + u64 bit_len; + u32 val; + int rc; + + rc = ocs_aes_validate_inputs(src_dma_list, src_size, iv, + GCM_AES_IV_SIZE, aad_dma_list, + aad_size, out_tag, tag_size, cipher, + OCS_MODE_GCM, instruction, + dst_dma_list); + if (rc) + return rc; + + ocs_aes_init(aes_dev, OCS_MODE_GCM, cipher, instruction); + + /* Compute and write J0 to OCS HW. */ + ocs_aes_gcm_write_j0(aes_dev, iv); + + /* Write out_tag byte length */ + iowrite32(tag_size, aes_dev->base_reg + AES_TLEN_OFFSET); + + /* Write the byte length of the last plaintext / ciphertext block. */ + ocs_aes_write_last_data_blk_len(aes_dev, src_size); + + /* Write ciphertext bit length */ + bit_len = src_size * 8; + val = bit_len & 0xFFFFFFFF; + iowrite32(val, aes_dev->base_reg + AES_MULTIPURPOSE2_0_OFFSET); + val = bit_len >> 32; + iowrite32(val, aes_dev->base_reg + AES_MULTIPURPOSE2_1_OFFSET); + + /* Write aad bit length */ + bit_len = aad_size * 8; + val = bit_len & 0xFFFFFFFF; + iowrite32(val, aes_dev->base_reg + AES_MULTIPURPOSE2_2_OFFSET); + val = bit_len >> 32; + iowrite32(val, aes_dev->base_reg + AES_MULTIPURPOSE2_3_OFFSET); + + /* Set AES_ACTIVE.TRIGGER to start the operation. */ + aes_a_op_trigger(aes_dev); + + /* Process AAD. */ + if (aad_size) { + /* If aad present, configure DMA to feed it to the engine. */ + dma_to_ocs_aes_ll(aes_dev, aad_dma_list); + aes_a_dma_active_src_ll_en(aes_dev); + + /* Instructs engine to pad last block of aad, if needed. */ + aes_a_set_last_gcx_and_adata(aes_dev); + + /* Wait for DMA transfer to complete. */ + rc = ocs_aes_irq_enable_and_wait(aes_dev, AES_DMA_SRC_DONE_INT); + if (rc) + return rc; + } else { + aes_a_set_last_gcx_and_adata(aes_dev); + } + + /* Wait until adata (if present) has been processed. */ + aes_a_wait_last_gcx(aes_dev); + aes_a_dma_wait_input_buffer_occupancy(aes_dev); + + /* Now process payload. */ + if (src_size) { + /* Configure and activate DMA for both input and output data. */ + dma_to_ocs_aes_ll(aes_dev, src_dma_list); + dma_from_ocs_aes_ll(aes_dev, dst_dma_list); + aes_a_dma_active_src_dst_ll_en(aes_dev); + } else { + aes_a_dma_set_xfer_size_zero(aes_dev); + aes_a_dma_active(aes_dev); + } + + /* Instruct AES/SMA4 engine payload processing is over. */ + aes_a_set_last_gcx(aes_dev); + + /* Wait for OCS AES engine to complete processing. */ + rc = ocs_aes_irq_enable_and_wait(aes_dev, AES_COMPLETE_INT); + if (rc) + return rc; + + ocs_aes_gcm_read_tag(aes_dev, out_tag, tag_size); + + return 0; +} + +/* Write encrypted tag to AES/SM4 engine. */ +static void ocs_aes_ccm_write_encrypted_tag(struct ocs_aes_dev *aes_dev, + const u8 *in_tag, u32 tag_size) +{ + int i; + + /* Ensure DMA input buffer is empty */ + aes_a_dma_wait_input_buffer_occupancy(aes_dev); + + /* + * During CCM decrypt, the OCS block needs to finish processing the + * ciphertext before the tag is written. So delay needed after DMA has + * completed writing the ciphertext + */ + aes_a_dma_reset_and_activate_perf_cntr(aes_dev); + aes_a_dma_wait_and_deactivate_perf_cntr(aes_dev, + CCM_DECRYPT_DELAY_TAG_CLK_COUNT); + + /* Write encrypted tag to AES/SM4 engine. */ + for (i = 0; i < tag_size; i++) { + iowrite8(in_tag[i], aes_dev->base_reg + + AES_A_DMA_INBUFFER_WRITE_FIFO_OFFSET); + } +} + +/* + * Write B0 CCM block to OCS AES HW. + * + * Note: B0 format is documented in NIST Special Publication 800-38C + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38c.pdf + * (see Section A.2.1) + */ +static int ocs_aes_ccm_write_b0(const struct ocs_aes_dev *aes_dev, + const u8 *iv, u32 adata_size, u32 tag_size, + u32 cryptlen) +{ + u8 b0[16]; /* CCM B0 block is 16 bytes long. */ + int i, q; + + /* Initialize B0 to 0. */ + memset(b0, 0, sizeof(b0)); + + /* + * B0[0] is the 'Flags Octet' and has the following structure: + * bit 7: Reserved + * bit 6: Adata flag + * bit 5-3: t value encoded as (t-2)/2 + * bit 2-0: q value encoded as q - 1 + */ + /* If there is AAD data, set the Adata flag. */ + if (adata_size) + b0[0] |= BIT(6); + /* + * t denotes the octet length of T. + * t can only be an element of { 4, 6, 8, 10, 12, 14, 16} and is + * encoded as (t - 2) / 2 + */ + b0[0] |= (((tag_size - 2) / 2) & 0x7) << 3; + /* + * q is the octet length of Q. + * q can only be an element of {2, 3, 4, 5, 6, 7, 8} and is encoded as + * q - 1 == iv[0] + */ + b0[0] |= iv[0] & 0x7; + /* + * Copy the Nonce N from IV to B0; N is located in iv[1]..iv[15 - q] + * and must be copied to b0[1]..b0[15-q]. + * q == iv[0] + 1 + */ + q = iv[0] + 1; + for (i = 1; i <= 15 - q; i++) + b0[i] = iv[i]; + /* + * The rest of B0 must contain Q, i.e., the message length. + * Q is encoded in q octets, in big-endian order, so to write it, we + * start from the end of B0 and we move backward. + */ + i = sizeof(b0) - 1; + while (q) { + b0[i] = cryptlen & 0xff; + cryptlen >>= 8; + i--; + q--; + } + /* + * If cryptlen is not zero at this point, it means that its original + * value was too big. + */ + if (cryptlen) + return -EOVERFLOW; + /* Now write B0 to OCS AES input buffer. */ + for (i = 0; i < sizeof(b0); i++) + iowrite8(b0[i], aes_dev->base_reg + + AES_A_DMA_INBUFFER_WRITE_FIFO_OFFSET); + return 0; +} + +/* + * Write adata length to OCS AES HW. + * + * Note: adata len encoding is documented in NIST Special Publication 800-38C + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38c.pdf + * (see Section A.2.2) + */ +static void ocs_aes_ccm_write_adata_len(const struct ocs_aes_dev *aes_dev, + u64 adata_len) +{ + u8 enc_a[10]; /* Maximum encoded size: 10 octets. */ + int i, len; + + /* + * adata_len ('a') is encoded as follows: + * If 0 < a < 2^16 - 2^8 ==> 'a' encoded as [a]16, i.e., two octets + * (big endian). + * If 2^16 - 2^8 ≤ a < 2^32 ==> 'a' encoded as 0xff || 0xfe || [a]32, + * i.e., six octets (big endian). + * If 2^32 ≤ a < 2^64 ==> 'a' encoded as 0xff || 0xff || [a]64, + * i.e., ten octets (big endian). + */ + if (adata_len < 65280) { + len = 2; + *(__be16 *)enc_a = cpu_to_be16(adata_len); + } else if (adata_len <= 0xFFFFFFFF) { + len = 6; + *(__be16 *)enc_a = cpu_to_be16(0xfffe); + *(__be32 *)&enc_a[2] = cpu_to_be32(adata_len); + } else { /* adata_len >= 2^32 */ + len = 10; + *(__be16 *)enc_a = cpu_to_be16(0xffff); + *(__be64 *)&enc_a[2] = cpu_to_be64(adata_len); + } + for (i = 0; i < len; i++) + iowrite8(enc_a[i], + aes_dev->base_reg + + AES_A_DMA_INBUFFER_WRITE_FIFO_OFFSET); +} + +static int ocs_aes_ccm_do_adata(struct ocs_aes_dev *aes_dev, + dma_addr_t adata_dma_list, u32 adata_size) +{ + int rc; + + if (!adata_size) { + /* Since no aad the LAST_GCX bit can be set now */ + aes_a_set_last_gcx_and_adata(aes_dev); + goto exit; + } + + /* Adata case. */ + + /* + * Form the encoding of the Associated data length and write it + * to the AES/SM4 input buffer. + */ + ocs_aes_ccm_write_adata_len(aes_dev, adata_size); + + /* Configure the AES/SM4 DMA to fetch the Associated Data */ + dma_to_ocs_aes_ll(aes_dev, adata_dma_list); + + /* Activate DMA to fetch Associated data. */ + aes_a_dma_active_src_ll_en(aes_dev); + + /* Set LAST_GCX and LAST_ADATA in AES ACTIVE register. */ + aes_a_set_last_gcx_and_adata(aes_dev); + + /* Wait for DMA transfer to complete. */ + rc = ocs_aes_irq_enable_and_wait(aes_dev, AES_DMA_SRC_DONE_INT); + if (rc) + return rc; + +exit: + /* Wait until adata (if present) has been processed. */ + aes_a_wait_last_gcx(aes_dev); + aes_a_dma_wait_input_buffer_occupancy(aes_dev); + + return 0; +} + +static int ocs_aes_ccm_encrypt_do_payload(struct ocs_aes_dev *aes_dev, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size) +{ + if (src_size) { + /* + * Configure and activate DMA for both input and output + * data. + */ + dma_to_ocs_aes_ll(aes_dev, src_dma_list); + dma_from_ocs_aes_ll(aes_dev, dst_dma_list); + aes_a_dma_active_src_dst_ll_en(aes_dev); + } else { + /* Configure and activate DMA for output data only. */ + dma_from_ocs_aes_ll(aes_dev, dst_dma_list); + aes_a_dma_active_dst_ll_en(aes_dev); + } + + /* + * Set the LAST GCX bit in AES_ACTIVE Register to instruct + * AES/SM4 engine to pad the last block of data. + */ + aes_a_set_last_gcx(aes_dev); + + /* We are done, wait for IRQ and return. */ + return ocs_aes_irq_enable_and_wait(aes_dev, AES_COMPLETE_INT); +} + +static int ocs_aes_ccm_decrypt_do_payload(struct ocs_aes_dev *aes_dev, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size) +{ + if (!src_size) { + /* Let engine process 0-length input. */ + aes_a_dma_set_xfer_size_zero(aes_dev); + aes_a_dma_active(aes_dev); + aes_a_set_last_gcx(aes_dev); + + return 0; + } + + /* + * Configure and activate DMA for both input and output + * data. + */ + dma_to_ocs_aes_ll(aes_dev, src_dma_list); + dma_from_ocs_aes_ll(aes_dev, dst_dma_list); + aes_a_dma_active_src_dst_ll_en(aes_dev); + /* + * Set the LAST GCX bit in AES_ACTIVE Register; this allows the + * AES/SM4 engine to differentiate between encrypted data and + * encrypted MAC. + */ + aes_a_set_last_gcx(aes_dev); + /* + * Enable DMA DONE interrupt; once DMA transfer is over, + * interrupt handler will process the MAC/tag. + */ + return ocs_aes_irq_enable_and_wait(aes_dev, AES_DMA_SRC_DONE_INT); +} + +/* + * Compare Tag to Yr. + * + * Only used at the end of CCM decrypt. If tag == yr, message authentication + * has succeeded. + */ +static inline int ccm_compare_tag_to_yr(struct ocs_aes_dev *aes_dev, + u8 tag_size_bytes) +{ + u32 tag[AES_MAX_TAG_SIZE_U32]; + u32 yr[AES_MAX_TAG_SIZE_U32]; + u8 i; + + /* Read Tag and Yr from AES registers. */ + for (i = 0; i < AES_MAX_TAG_SIZE_U32; i++) { + tag[i] = ioread32(aes_dev->base_reg + + AES_T_MAC_0_OFFSET + (i * sizeof(u32))); + yr[i] = ioread32(aes_dev->base_reg + + AES_MULTIPURPOSE2_0_OFFSET + + (i * sizeof(u32))); + } + + return memcmp(tag, yr, tag_size_bytes) ? -EBADMSG : 0; +} + +/** + * ocs_aes_ccm_op() - Perform CCM operation. + * @aes_dev: The OCS AES device to use. + * @cipher: The Cipher to use (AES or SM4). + * @instruction: The instruction to perform (encrypt or decrypt). + * @dst_dma_list: The OCS DMA list mapping output memory. + * @src_dma_list: The OCS DMA list mapping input payload data. + * @src_size: The amount of data mapped by @src_dma_list. + * @iv: The input IV vector. + * @adata_dma_list: The OCS DMA list mapping input A-data. + * @adata_size: The amount of data mapped by @adata_dma_list. + * @in_tag: Input tag. + * @tag_size: The size (in bytes) of @in_tag. + * + * Note: for encrypt the tag is appended to the ciphertext (in the memory + * mapped by @dst_dma_list). + * + * Return: 0 on success, negative error code otherwise. + */ +int ocs_aes_ccm_op(struct ocs_aes_dev *aes_dev, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + u8 *iv, + dma_addr_t adata_dma_list, + u32 adata_size, + u8 *in_tag, + u32 tag_size) +{ + u32 *iv_32; + u8 lprime; + int rc; + + rc = ocs_aes_validate_inputs(src_dma_list, src_size, iv, + AES_BLOCK_SIZE, adata_dma_list, adata_size, + in_tag, tag_size, cipher, OCS_MODE_CCM, + instruction, dst_dma_list); + if (rc) + return rc; + + ocs_aes_init(aes_dev, OCS_MODE_CCM, cipher, instruction); + + /* + * Note: rfc 3610 and NIST 800-38C require counter of zero to encrypt + * auth tag so ensure this is the case + */ + lprime = iv[L_PRIME_IDX]; + memset(&iv[COUNTER_START(lprime)], 0, COUNTER_LEN(lprime)); + + /* + * Nonce is already converted to ctr0 before being passed into this + * function as iv. + */ + iv_32 = (u32 *)iv; + iowrite32(__swab32(iv_32[0]), + aes_dev->base_reg + AES_MULTIPURPOSE1_3_OFFSET); + iowrite32(__swab32(iv_32[1]), + aes_dev->base_reg + AES_MULTIPURPOSE1_2_OFFSET); + iowrite32(__swab32(iv_32[2]), + aes_dev->base_reg + AES_MULTIPURPOSE1_1_OFFSET); + iowrite32(__swab32(iv_32[3]), + aes_dev->base_reg + AES_MULTIPURPOSE1_0_OFFSET); + + /* Write MAC/tag length in register AES_TLEN */ + iowrite32(tag_size, aes_dev->base_reg + AES_TLEN_OFFSET); + /* + * Write the byte length of the last AES/SM4 block of Payload data + * (without zero padding and without the length of the MAC) in register + * AES_PLEN. + */ + ocs_aes_write_last_data_blk_len(aes_dev, src_size); + + /* Set AES_ACTIVE.TRIGGER to start the operation. */ + aes_a_op_trigger(aes_dev); + + aes_a_dma_reset_and_activate_perf_cntr(aes_dev); + + /* Form block B0 and write it to the AES/SM4 input buffer. */ + rc = ocs_aes_ccm_write_b0(aes_dev, iv, adata_size, tag_size, src_size); + if (rc) + return rc; + /* + * Ensure there has been at least CCM_DECRYPT_DELAY_LAST_GCX_CLK_COUNT + * clock cycles since TRIGGER bit was set + */ + aes_a_dma_wait_and_deactivate_perf_cntr(aes_dev, + CCM_DECRYPT_DELAY_LAST_GCX_CLK_COUNT); + + /* Process Adata. */ + ocs_aes_ccm_do_adata(aes_dev, adata_dma_list, adata_size); + + /* For Encrypt case we just process the payload and return. */ + if (instruction == OCS_ENCRYPT) { + return ocs_aes_ccm_encrypt_do_payload(aes_dev, dst_dma_list, + src_dma_list, src_size); + } + /* For Decypt we need to process the payload and then the tag. */ + rc = ocs_aes_ccm_decrypt_do_payload(aes_dev, dst_dma_list, + src_dma_list, src_size); + if (rc) + return rc; + + /* Process MAC/tag directly: feed tag to engine and wait for IRQ. */ + ocs_aes_ccm_write_encrypted_tag(aes_dev, in_tag, tag_size); + rc = ocs_aes_irq_enable_and_wait(aes_dev, AES_COMPLETE_INT); + if (rc) + return rc; + + return ccm_compare_tag_to_yr(aes_dev, tag_size); +} + +/** + * ocs_create_linked_list_from_sg() - Create OCS DMA linked list from SG list. + * @aes_dev: The OCS AES device the list will be created for. + * @sg: The SG list OCS DMA linked list will be created from. When + * passed to this function, @sg must have been already mapped + * with dma_map_sg(). + * @sg_dma_count: The number of DMA-mapped entries in @sg. This must be the + * value returned by dma_map_sg() when @sg was mapped. + * @dll_desc: The OCS DMA dma_list to use to store information about the + * created linked list. + * @data_size: The size of the data (from the SG list) to be mapped into the + * OCS DMA linked list. + * @data_offset: The offset (within the SG list) of the data to be mapped. + * + * Return: 0 on success, negative error code otherwise. + */ +int ocs_create_linked_list_from_sg(const struct ocs_aes_dev *aes_dev, + struct scatterlist *sg, + int sg_dma_count, + struct ocs_dll_desc *dll_desc, + size_t data_size, size_t data_offset) +{ + struct ocs_dma_linked_list *ll = NULL; + struct scatterlist *sg_tmp; + unsigned int tmp; + int dma_nents; + int i; + + if (!dll_desc || !sg || !aes_dev) + return -EINVAL; + + /* Default values for when no ddl_desc is created. */ + dll_desc->vaddr = NULL; + dll_desc->dma_addr = DMA_MAPPING_ERROR; + dll_desc->size = 0; + + if (data_size == 0) + return 0; + + /* Loop over sg_list until we reach entry at specified offset. */ + while (data_offset >= sg_dma_len(sg)) { + data_offset -= sg_dma_len(sg); + sg_dma_count--; + sg = sg_next(sg); + /* If we reach the end of the list, offset was invalid. */ + if (!sg || sg_dma_count == 0) + return -EINVAL; + } + + /* Compute number of DMA-mapped SG entries to add into OCS DMA list. */ + dma_nents = 0; + tmp = 0; + sg_tmp = sg; + while (tmp < data_offset + data_size) { + /* If we reach the end of the list, data_size was invalid. */ + if (!sg_tmp) + return -EINVAL; + tmp += sg_dma_len(sg_tmp); + dma_nents++; + sg_tmp = sg_next(sg_tmp); + } + if (dma_nents > sg_dma_count) + return -EINVAL; + + /* Allocate the DMA list, one entry for each SG entry. */ + dll_desc->size = sizeof(struct ocs_dma_linked_list) * dma_nents; + dll_desc->vaddr = dma_alloc_coherent(aes_dev->dev, dll_desc->size, + &dll_desc->dma_addr, GFP_KERNEL); + if (!dll_desc->vaddr) + return -ENOMEM; + + /* Populate DMA linked list entries. */ + ll = dll_desc->vaddr; + for (i = 0; i < dma_nents; i++, sg = sg_next(sg)) { + ll[i].src_addr = sg_dma_address(sg) + data_offset; + ll[i].src_len = (sg_dma_len(sg) - data_offset) < data_size ? + (sg_dma_len(sg) - data_offset) : data_size; + data_offset = 0; + data_size -= ll[i].src_len; + /* Current element points to the DMA address of the next one. */ + ll[i].next = dll_desc->dma_addr + (sizeof(*ll) * (i + 1)); + ll[i].ll_flags = 0; + } + /* Terminate last element. */ + ll[i - 1].next = 0; + ll[i - 1].ll_flags = OCS_LL_DMA_FLAG_TERMINATE; + + return 0; +} diff --git a/drivers/crypto/keembay/ocs-aes.h b/drivers/crypto/keembay/ocs-aes.h new file mode 100644 index 000000000000..c035fc48b7ed --- /dev/null +++ b/drivers/crypto/keembay/ocs-aes.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Intel Keem Bay OCS AES Crypto Driver. + * + * Copyright (C) 2018-2020 Intel Corporation + */ + +#ifndef _CRYPTO_OCS_AES_H +#define _CRYPTO_OCS_AES_H + +#include <linux/dma-mapping.h> + +enum ocs_cipher { + OCS_AES = 0, + OCS_SM4 = 1, +}; + +enum ocs_mode { + OCS_MODE_ECB = 0, + OCS_MODE_CBC = 1, + OCS_MODE_CTR = 2, + OCS_MODE_CCM = 6, + OCS_MODE_GCM = 7, + OCS_MODE_CTS = 9, +}; + +enum ocs_instruction { + OCS_ENCRYPT = 0, + OCS_DECRYPT = 1, + OCS_EXPAND = 2, + OCS_BYPASS = 3, +}; + +/** + * struct ocs_aes_dev - AES device context. + * @list: List head for insertion into device list hold + * by driver. + * @dev: OCS AES device. + * @irq: IRQ number. + * @base_reg: IO base address of OCS AES. + * @irq_copy_completion: Completion to indicate IRQ has been triggered. + * @dma_err_mask: Error reported by OCS DMA interrupts. + * @engine: Crypto engine for the device. + */ +struct ocs_aes_dev { + struct list_head list; + struct device *dev; + int irq; + void __iomem *base_reg; + struct completion irq_completion; + u32 dma_err_mask; + struct crypto_engine *engine; +}; + +/** + * struct ocs_dll_desc - Descriptor of an OCS DMA Linked List. + * @vaddr: Virtual address of the linked list head. + * @dma_addr: DMA address of the linked list head. + * @size: Size (in bytes) of the linked list. + */ +struct ocs_dll_desc { + void *vaddr; + dma_addr_t dma_addr; + size_t size; +}; + +int ocs_aes_set_key(struct ocs_aes_dev *aes_dev, const u32 key_size, + const u8 *key, const enum ocs_cipher cipher); + +int ocs_aes_op(struct ocs_aes_dev *aes_dev, + enum ocs_mode mode, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + u8 *iv, + u32 iv_size); + +/** + * ocs_aes_bypass_op() - Use OCS DMA to copy data. + * @aes_dev: The OCS AES device to use. + * @dst_dma_list: The OCS DMA list mapping the memory where input data + * will be copied to. + * @src_dma_list: The OCS DMA list mapping input data. + * @src_size: The amount of data to copy. + */ +static inline int ocs_aes_bypass_op(struct ocs_aes_dev *aes_dev, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, u32 src_size) +{ + return ocs_aes_op(aes_dev, OCS_MODE_ECB, OCS_AES, OCS_BYPASS, + dst_dma_list, src_dma_list, src_size, NULL, 0); +} + +int ocs_aes_gcm_op(struct ocs_aes_dev *aes_dev, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + const u8 *iv, + dma_addr_t aad_dma_list, + u32 aad_size, + u8 *out_tag, + u32 tag_size); + +int ocs_aes_ccm_op(struct ocs_aes_dev *aes_dev, + enum ocs_cipher cipher, + enum ocs_instruction instruction, + dma_addr_t dst_dma_list, + dma_addr_t src_dma_list, + u32 src_size, + u8 *iv, + dma_addr_t adata_dma_list, + u32 adata_size, + u8 *in_tag, + u32 tag_size); + +int ocs_create_linked_list_from_sg(const struct ocs_aes_dev *aes_dev, + struct scatterlist *sg, + int sg_dma_count, + struct ocs_dll_desc *dll_desc, + size_t data_size, + size_t data_offset); + +irqreturn_t ocs_aes_irq_handler(int irq, void *dev_id); + +#endif diff --git a/drivers/crypto/marvell/cesa/hash.c b/drivers/crypto/marvell/cesa/hash.c index add7ea011c98..8cf9fd518d86 100644 --- a/drivers/crypto/marvell/cesa/hash.c +++ b/drivers/crypto/marvell/cesa/hash.c @@ -11,7 +11,8 @@ #include <crypto/hmac.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <linux/device.h> #include <linux/dma-mapping.h> diff --git a/drivers/crypto/marvell/octeontx/otx_cptpf_main.c b/drivers/crypto/marvell/octeontx/otx_cptpf_main.c index 34bb3063eb70..14a42559f81d 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptpf_main.c +++ b/drivers/crypto/marvell/octeontx/otx_cptpf_main.c @@ -212,15 +212,9 @@ static int otx_cpt_probe(struct pci_dev *pdev, goto err_disable_device; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); if (err) { - dev_err(dev, "Unable to get usable DMA configuration\n"); - goto err_release_regions; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); - if (err) { - dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); goto err_release_regions; } diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c b/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c index 90bb31329d4b..ccbef01888d4 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_algs.c @@ -13,7 +13,8 @@ #include <crypto/cryptd.h> #include <crypto/des.h> #include <crypto/internal/aead.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/xts.h> #include <crypto/scatterwalk.h> #include <linux/rtnetlink.h> diff --git a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c index 228fe8e47e0e..c076d0b3ad5f 100644 --- a/drivers/crypto/marvell/octeontx/otx_cptvf_main.c +++ b/drivers/crypto/marvell/octeontx/otx_cptvf_main.c @@ -804,15 +804,9 @@ static int otx_cptvf_probe(struct pci_dev *pdev, dev_err(dev, "PCI request regions failed 0x%x\n", err); goto disable_device; } - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(48)); if (err) { - dev_err(dev, "Unable to get usable DMA configuration\n"); - goto release_regions; - } - - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); - if (err) { - dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); + dev_err(dev, "Unable to get usable 48-bit DMA configuration\n"); goto release_regions; } diff --git a/drivers/crypto/mediatek/mtk-sha.c b/drivers/crypto/mediatek/mtk-sha.c index 3d5d7d68b03b..f55aacdafbef 100644 --- a/drivers/crypto/mediatek/mtk-sha.c +++ b/drivers/crypto/mediatek/mtk-sha.c @@ -10,7 +10,8 @@ */ #include <crypto/hmac.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include "mtk-platform.h" #define SHA_ALIGN_MSK (sizeof(u32) - 1) diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c index 909a7eb748e3..d6a7784d2988 100644 --- a/drivers/crypto/mxs-dcp.c +++ b/drivers/crypto/mxs-dcp.c @@ -17,7 +17,8 @@ #include <linux/clk.h> #include <crypto/aes.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/internal/hash.h> #include <crypto/internal/skcipher.h> #include <crypto/scatterwalk.h> diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c index 3642bf83d809..3b0bf6fea491 100644 --- a/drivers/crypto/n2_core.c +++ b/drivers/crypto/n2_core.c @@ -15,7 +15,8 @@ #include <linux/interrupt.h> #include <linux/crypto.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/aes.h> #include <crypto/internal/des.h> #include <linux/mutex.h> diff --git a/drivers/crypto/nx/nx-sha256.c b/drivers/crypto/nx/nx-sha256.c index 02fb53453195..90d9a37a57f6 100644 --- a/drivers/crypto/nx/nx-sha256.c +++ b/drivers/crypto/nx/nx-sha256.c @@ -8,7 +8,7 @@ */ #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/module.h> #include <asm/vio.h> #include <asm/byteorder.h> diff --git a/drivers/crypto/nx/nx-sha512.c b/drivers/crypto/nx/nx-sha512.c index 4c7a3e3eeebf..eb8627a0f317 100644 --- a/drivers/crypto/nx/nx-sha512.c +++ b/drivers/crypto/nx/nx-sha512.c @@ -8,7 +8,7 @@ */ #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/module.h> #include <asm/vio.h> diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c index 40882d6d52c1..0d2dc5be7f19 100644 --- a/drivers/crypto/nx/nx.c +++ b/drivers/crypto/nx/nx.c @@ -10,7 +10,7 @@ #include <crypto/internal/aead.h> #include <crypto/internal/hash.h> #include <crypto/aes.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/algapi.h> #include <crypto/scatterwalk.h> #include <linux/module.h> diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 4fd14d90cc40..a45bdcf3026d 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -105,6 +105,7 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd) err = pm_runtime_get_sync(dd->dev); if (err < 0) { + pm_runtime_put_noidle(dd->dev); dev_err(dd->dev, "failed to get sync: %d\n", err); return err; } @@ -1137,7 +1138,7 @@ static int omap_aes_probe(struct platform_device *pdev) if (err < 0) { dev_err(dev, "%s: failed to get_sync(%d)\n", __func__, err); - goto err_res; + goto err_pm_disable; } omap_aes_dma_stop(dd); @@ -1246,6 +1247,7 @@ err_engine: omap_aes_dma_cleanup(dd); err_irq: tasklet_kill(&dd->done_task); +err_pm_disable: pm_runtime_disable(dev); err_res: dd = NULL; diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index a3b38d2c92e7..ae0d320d3c60 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -35,7 +35,8 @@ #include <linux/crypto.h> #include <crypto/scatterwalk.h> #include <crypto/algapi.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/hash.h> #include <crypto/hmac.h> #include <crypto/internal/hash.h> diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index a697a4a3f2d0..6865c7f1fc1a 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -9,7 +9,8 @@ #include <crypto/internal/hash.h> #include <crypto/padlock.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c index fb34bf92861d..84f9c16d984c 100644 --- a/drivers/crypto/picoxcell_crypto.c +++ b/drivers/crypto/picoxcell_crypto.c @@ -8,7 +8,8 @@ #include <crypto/authenc.h> #include <crypto/internal/des.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/internal/skcipher.h> #include <linux/clk.h> #include <linux/crypto.h> diff --git a/drivers/crypto/qat/Kconfig b/drivers/crypto/qat/Kconfig index 2006322345de..beb379b23dc3 100644 --- a/drivers/crypto/qat/Kconfig +++ b/drivers/crypto/qat/Kconfig @@ -46,6 +46,17 @@ config CRYPTO_DEV_QAT_C62X To compile this as a module, choose M here: the module will be called qat_c62x. +config CRYPTO_DEV_QAT_4XXX + tristate "Support for Intel(R) QAT_4XXX" + depends on X86 && PCI + select CRYPTO_DEV_QAT + help + Support for Intel(R) QuickAssist Technology QAT_4xxx + for accelerating crypto and compression workloads. + + To compile this as a module, choose M here: the module + will be called qat_4xxx. + config CRYPTO_DEV_QAT_DH895xCCVF tristate "Support for Intel(R) DH895xCC Virtual Function" depends on X86 && PCI diff --git a/drivers/crypto/qat/Makefile b/drivers/crypto/qat/Makefile index 7dd15e751d02..258c8a626ce0 100644 --- a/drivers/crypto/qat/Makefile +++ b/drivers/crypto/qat/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_CRYPTO_DEV_QAT) += qat_common/ obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCC) += qat_dh895xcc/ obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXX) += qat_c3xxx/ obj-$(CONFIG_CRYPTO_DEV_QAT_C62X) += qat_c62x/ +obj-$(CONFIG_CRYPTO_DEV_QAT_4XXX) += qat_4xxx/ obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCCVF) += qat_dh895xccvf/ obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXXVF) += qat_c3xxxvf/ obj-$(CONFIG_CRYPTO_DEV_QAT_C62XVF) += qat_c62xvf/ diff --git a/drivers/crypto/qat/qat_4xxx/Makefile b/drivers/crypto/qat/qat_4xxx/Makefile new file mode 100644 index 000000000000..ff9c8b5897ea --- /dev/null +++ b/drivers/crypto/qat/qat_4xxx/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +ccflags-y := -I $(srctree)/$(src)/../qat_common +obj-$(CONFIG_CRYPTO_DEV_QAT_4XXX) += qat_4xxx.o +qat_4xxx-objs := adf_drv.o adf_4xxx_hw_data.o diff --git a/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.c b/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.c new file mode 100644 index 000000000000..344bfae45bff --- /dev/null +++ b/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +/* Copyright(c) 2020 Intel Corporation */ +#include <adf_accel_devices.h> +#include <adf_common_drv.h> +#include <adf_pf2vf_msg.h> +#include <adf_gen4_hw_data.h> +#include "adf_4xxx_hw_data.h" +#include "icp_qat_hw.h" + +struct adf_fw_config { + u32 ae_mask; + char *obj_name; +}; + +static struct adf_fw_config adf_4xxx_fw_config[] = { + {0xF0, ADF_4XXX_SYM_OBJ}, + {0xF, ADF_4XXX_ASYM_OBJ}, + {0x100, ADF_4XXX_ADMIN_OBJ}, +}; + +/* Worker thread to service arbiter mappings */ +static u32 thrd_to_arb_map[] = { + 0x5555555, 0x5555555, 0x5555555, 0x5555555, + 0xAAAAAAA, 0xAAAAAAA, 0xAAAAAAA, 0xAAAAAAA, + 0x0 +}; + +static struct adf_hw_device_class adf_4xxx_class = { + .name = ADF_4XXX_DEVICE_NAME, + .type = DEV_4XXX, + .instances = 0, +}; + +static u32 get_accel_mask(struct adf_hw_device_data *self) +{ + return ADF_4XXX_ACCELERATORS_MASK; +} + +static u32 get_ae_mask(struct adf_hw_device_data *self) +{ + u32 me_disable = self->fuses; + + return ~me_disable & ADF_4XXX_ACCELENGINES_MASK; +} + +static u32 get_num_accels(struct adf_hw_device_data *self) +{ + return ADF_4XXX_MAX_ACCELERATORS; +} + +static u32 get_num_aes(struct adf_hw_device_data *self) +{ + if (!self || !self->ae_mask) + return 0; + + return hweight32(self->ae_mask); +} + +static u32 get_misc_bar_id(struct adf_hw_device_data *self) +{ + return ADF_4XXX_PMISC_BAR; +} + +static u32 get_etr_bar_id(struct adf_hw_device_data *self) +{ + return ADF_4XXX_ETR_BAR; +} + +static u32 get_sram_bar_id(struct adf_hw_device_data *self) +{ + return ADF_4XXX_SRAM_BAR; +} + +/* + * The vector routing table is used to select the MSI-X entry to use for each + * interrupt source. + * The first ADF_4XXX_ETR_MAX_BANKS entries correspond to ring interrupts. + * The final entry corresponds to VF2PF or error interrupts. + * This vector table could be used to configure one MSI-X entry to be shared + * between multiple interrupt sources. + * + * The default routing is set to have a one to one correspondence between the + * interrupt source and the MSI-X entry used. + */ +static void set_msix_default_rttable(struct adf_accel_dev *accel_dev) +{ + void __iomem *csr; + int i; + + csr = (&GET_BARS(accel_dev)[ADF_4XXX_PMISC_BAR])->virt_addr; + for (i = 0; i <= ADF_4XXX_ETR_MAX_BANKS; i++) + ADF_CSR_WR(csr, ADF_4XXX_MSIX_RTTABLE_OFFSET(i), i); +} + +static u32 get_accel_cap(struct adf_accel_dev *accel_dev) +{ + struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev; + u32 fusectl1; + u32 capabilities = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC | + ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | + ICP_ACCEL_CAPABILITIES_AUTHENTICATION | + ICP_ACCEL_CAPABILITIES_AES_V2; + + /* Read accelerator capabilities mask */ + pci_read_config_dword(pdev, ADF_4XXX_FUSECTL1_OFFSET, &fusectl1); + + if (fusectl1 & ICP_ACCEL_4XXX_MASK_CIPHER_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC; + if (fusectl1 & ICP_ACCEL_4XXX_MASK_AUTH_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION; + if (fusectl1 & ICP_ACCEL_4XXX_MASK_PKE_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; + + return capabilities; +} + +static enum dev_sku_info get_sku(struct adf_hw_device_data *self) +{ + return DEV_SKU_1; +} + +static void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev, + u32 const **arb_map_config) +{ + struct adf_hw_device_data *hw_device = accel_dev->hw_device; + unsigned long ae_mask = hw_device->ae_mask; + int i; + + for_each_clear_bit(i, &ae_mask, ADF_4XXX_MAX_ACCELENGINES) + thrd_to_arb_map[i] = 0; + + *arb_map_config = thrd_to_arb_map; +} + +static void get_arb_info(struct arb_info *arb_info) +{ + arb_info->arb_cfg = ADF_4XXX_ARB_CONFIG; + arb_info->arb_offset = ADF_4XXX_ARB_OFFSET; + arb_info->wt2sam_offset = ADF_4XXX_ARB_WRK_2_SER_MAP_OFFSET; +} + +static void get_admin_info(struct admin_info *admin_csrs_info) +{ + admin_csrs_info->mailbox_offset = ADF_4XXX_MAILBOX_BASE_OFFSET; + admin_csrs_info->admin_msg_ur = ADF_4XXX_ADMINMSGUR_OFFSET; + admin_csrs_info->admin_msg_lr = ADF_4XXX_ADMINMSGLR_OFFSET; +} + +static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) +{ + struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_4XXX_PMISC_BAR]; + void __iomem *csr = misc_bar->virt_addr; + + /* Enable all in errsou3 except VFLR notification on host */ + ADF_CSR_WR(csr, ADF_4XXX_ERRMSK3, ADF_4XXX_VFLNOTIFY); +} + +static void adf_enable_ints(struct adf_accel_dev *accel_dev) +{ + void __iomem *addr; + + addr = (&GET_BARS(accel_dev)[ADF_4XXX_PMISC_BAR])->virt_addr; + + /* Enable bundle interrupts */ + ADF_CSR_WR(addr, ADF_4XXX_SMIAPF_RP_X0_MASK_OFFSET, 0); + ADF_CSR_WR(addr, ADF_4XXX_SMIAPF_RP_X1_MASK_OFFSET, 0); + + /* Enable misc interrupts */ + ADF_CSR_WR(addr, ADF_4XXX_SMIAPF_MASK_OFFSET, 0); +} + +static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev) +{ + return 0; +} + +static u32 uof_get_num_objs(void) +{ + return ARRAY_SIZE(adf_4xxx_fw_config); +} + +static char *uof_get_name(u32 obj_num) +{ + return adf_4xxx_fw_config[obj_num].obj_name; +} + +static u32 uof_get_ae_mask(u32 obj_num) +{ + return adf_4xxx_fw_config[obj_num].ae_mask; +} + +void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data) +{ + hw_data->dev_class = &adf_4xxx_class; + hw_data->instance_id = adf_4xxx_class.instances++; + hw_data->num_banks = ADF_4XXX_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_4XXX_NUM_RINGS_PER_BANK; + hw_data->num_accel = ADF_4XXX_MAX_ACCELERATORS; + hw_data->num_engines = ADF_4XXX_MAX_ACCELENGINES; + hw_data->num_logical_accel = 1; + hw_data->tx_rx_gap = ADF_4XXX_RX_RINGS_OFFSET; + hw_data->tx_rings_mask = ADF_4XXX_TX_RINGS_MASK; + hw_data->alloc_irq = adf_isr_resource_alloc; + hw_data->free_irq = adf_isr_resource_free; + hw_data->enable_error_correction = adf_enable_error_correction; + hw_data->get_accel_mask = get_accel_mask; + hw_data->get_ae_mask = get_ae_mask; + hw_data->get_num_accels = get_num_accels; + hw_data->get_num_aes = get_num_aes; + hw_data->get_sram_bar_id = get_sram_bar_id; + hw_data->get_etr_bar_id = get_etr_bar_id; + hw_data->get_misc_bar_id = get_misc_bar_id; + hw_data->get_arb_info = get_arb_info; + hw_data->get_admin_info = get_admin_info; + hw_data->get_accel_cap = get_accel_cap; + hw_data->get_sku = get_sku; + hw_data->fw_name = ADF_4XXX_FW; + hw_data->fw_mmp_name = ADF_4XXX_MMP; + hw_data->init_admin_comms = adf_init_admin_comms; + hw_data->exit_admin_comms = adf_exit_admin_comms; + hw_data->disable_iov = adf_disable_sriov; + hw_data->send_admin_init = adf_send_admin_init; + hw_data->init_arb = adf_init_arb; + hw_data->exit_arb = adf_exit_arb; + hw_data->get_arb_mapping = adf_get_arbiter_mapping; + hw_data->enable_ints = adf_enable_ints; + hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms; + hw_data->reset_device = adf_reset_flr; + hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; + hw_data->admin_ae_mask = ADF_4XXX_ADMIN_AE_MASK; + hw_data->uof_get_num_objs = uof_get_num_objs; + hw_data->uof_get_name = uof_get_name; + hw_data->uof_get_ae_mask = uof_get_ae_mask; + hw_data->set_msix_rttable = set_msix_default_rttable; + + adf_gen4_init_hw_csr_ops(&hw_data->csr_ops); +} + +void adf_clean_hw_data_4xxx(struct adf_hw_device_data *hw_data) +{ + hw_data->dev_class->instances--; +} diff --git a/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.h b/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.h new file mode 100644 index 000000000000..4fe2a776293c --- /dev/null +++ b/drivers/crypto/qat/qat_4xxx/adf_4xxx_hw_data.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ +/* Copyright(c) 2014 - 2020 Intel Corporation */ +#ifndef ADF_4XXX_HW_DATA_H_ +#define ADF_4XXX_HW_DATA_H_ + +#include <adf_accel_devices.h> + +/* PCIe configuration space */ +#define ADF_4XXX_SRAM_BAR 0 +#define ADF_4XXX_PMISC_BAR 1 +#define ADF_4XXX_ETR_BAR 2 +#define ADF_4XXX_RX_RINGS_OFFSET 1 +#define ADF_4XXX_TX_RINGS_MASK 0x1 +#define ADF_4XXX_MAX_ACCELERATORS 1 +#define ADF_4XXX_MAX_ACCELENGINES 9 +#define ADF_4XXX_BAR_MASK (BIT(0) | BIT(2) | BIT(4)) + +/* Physical function fuses */ +#define ADF_4XXX_FUSECTL0_OFFSET (0x2C8) +#define ADF_4XXX_FUSECTL1_OFFSET (0x2CC) +#define ADF_4XXX_FUSECTL2_OFFSET (0x2D0) +#define ADF_4XXX_FUSECTL3_OFFSET (0x2D4) +#define ADF_4XXX_FUSECTL4_OFFSET (0x2D8) +#define ADF_4XXX_FUSECTL5_OFFSET (0x2DC) + +#define ADF_4XXX_ACCELERATORS_MASK (0x1) +#define ADF_4XXX_ACCELENGINES_MASK (0x1FF) +#define ADF_4XXX_ADMIN_AE_MASK (0x100) + +#define ADF_4XXX_ETR_MAX_BANKS 64 + +/* MSIX interrupt */ +#define ADF_4XXX_SMIAPF_RP_X0_MASK_OFFSET (0x41A040) +#define ADF_4XXX_SMIAPF_RP_X1_MASK_OFFSET (0x41A044) +#define ADF_4XXX_SMIAPF_MASK_OFFSET (0x41A084) +#define ADF_4XXX_MSIX_RTTABLE_OFFSET(i) (0x409000 + ((i) * 0x04)) + +/* Bank and ring configuration */ +#define ADF_4XXX_NUM_RINGS_PER_BANK 2 + +/* Error source registers */ +#define ADF_4XXX_ERRSOU0 (0x41A200) +#define ADF_4XXX_ERRSOU1 (0x41A204) +#define ADF_4XXX_ERRSOU2 (0x41A208) +#define ADF_4XXX_ERRSOU3 (0x41A20C) + +/* Error source mask registers */ +#define ADF_4XXX_ERRMSK0 (0x41A210) +#define ADF_4XXX_ERRMSK1 (0x41A214) +#define ADF_4XXX_ERRMSK2 (0x41A218) +#define ADF_4XXX_ERRMSK3 (0x41A21C) + +#define ADF_4XXX_VFLNOTIFY BIT(7) + +/* Arbiter configuration */ +#define ADF_4XXX_ARB_CONFIG (BIT(31) | BIT(6) | BIT(0)) +#define ADF_4XXX_ARB_OFFSET (0x0) +#define ADF_4XXX_ARB_WRK_2_SER_MAP_OFFSET (0x400) + +/* Admin Interface Reg Offset */ +#define ADF_4XXX_ADMINMSGUR_OFFSET (0x500574) +#define ADF_4XXX_ADMINMSGLR_OFFSET (0x500578) +#define ADF_4XXX_MAILBOX_BASE_OFFSET (0x600970) + +/* Firmware Binaries */ +#define ADF_4XXX_FW "qat_4xxx.bin" +#define ADF_4XXX_MMP "qat_4xxx_mmp.bin" +#define ADF_4XXX_SYM_OBJ "qat_4xxx_sym.bin" +#define ADF_4XXX_ASYM_OBJ "qat_4xxx_asym.bin" +#define ADF_4XXX_ADMIN_OBJ "qat_4xxx_admin.bin" + +/* qat_4xxx fuse bits are different from old GENs, redefine them */ +enum icp_qat_4xxx_slice_mask { + ICP_ACCEL_4XXX_MASK_CIPHER_SLICE = BIT(0), + ICP_ACCEL_4XXX_MASK_AUTH_SLICE = BIT(1), + ICP_ACCEL_4XXX_MASK_PKE_SLICE = BIT(2), + ICP_ACCEL_4XXX_MASK_COMPRESS_SLICE = BIT(3), + ICP_ACCEL_4XXX_MASK_UCS_SLICE = BIT(4), + ICP_ACCEL_4XXX_MASK_EIA3_SLICE = BIT(5), + ICP_ACCEL_4XXX_MASK_SMX_SLICE = BIT(6), +}; + +void adf_init_hw_data_4xxx(struct adf_hw_device_data *hw_data); +void adf_clean_hw_data_4xxx(struct adf_hw_device_data *hw_data); + +#endif diff --git a/drivers/crypto/qat/qat_4xxx/adf_drv.c b/drivers/crypto/qat/qat_4xxx/adf_drv.c new file mode 100644 index 000000000000..a8805c815d16 --- /dev/null +++ b/drivers/crypto/qat/qat_4xxx/adf_drv.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +/* Copyright(c) 2020 Intel Corporation */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include <adf_accel_devices.h> +#include <adf_cfg.h> +#include <adf_common_drv.h> + +#include "adf_4xxx_hw_data.h" +#include "qat_crypto.h" +#include "adf_transport_access_macros.h" + +static const struct pci_device_id adf_pci_tbl[] = { + { PCI_VDEVICE(INTEL, ADF_4XXX_PCI_DEVICE_ID), }, + { } +}; +MODULE_DEVICE_TABLE(pci, adf_pci_tbl); + +static void adf_cleanup_accel(struct adf_accel_dev *accel_dev) +{ + if (accel_dev->hw_device) { + adf_clean_hw_data_4xxx(accel_dev->hw_device); + accel_dev->hw_device = NULL; + } + adf_cfg_dev_remove(accel_dev); + debugfs_remove(accel_dev->debugfs_dir); + adf_devmgr_rm_dev(accel_dev, NULL); +} + +static int adf_crypto_dev_config(struct adf_accel_dev *accel_dev) +{ + char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES]; + int banks = GET_MAX_BANKS(accel_dev); + int cpus = num_online_cpus(); + unsigned long bank, val; + int instances; + int ret; + int i; + + if (adf_hw_dev_has_crypto(accel_dev)) + instances = min(cpus, banks / 2); + else + instances = 0; + + ret = adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC); + if (ret) + goto err; + + ret = adf_cfg_section_add(accel_dev, "Accelerator0"); + if (ret) + goto err; + + for (i = 0; i < instances; i++) { + val = i; + bank = i * 2; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_BANK_NUM, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &bank, ADF_DEC); + if (ret) + goto err; + + bank += 1; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_BANK_NUM, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &bank, ADF_DEC); + if (ret) + goto err; + + snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY, + i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i); + val = 128; + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = 512; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = 0; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = 0; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = 1; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = 1; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + val = ADF_COALESCING_DEF_TIME; + snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i); + ret = adf_cfg_add_key_value_param(accel_dev, "Accelerator0", + key, &val, ADF_DEC); + if (ret) + goto err; + } + + val = i; + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY, + &val, ADF_DEC); + if (ret) + goto err; + + set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); + return 0; +err: + dev_err(&GET_DEV(accel_dev), "Failed to start QAT accel dev\n"); + return ret; +} + +static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct adf_accel_dev *accel_dev; + struct adf_accel_pci *accel_pci_dev; + struct adf_hw_device_data *hw_data; + char name[ADF_DEVICE_NAME_LENGTH]; + unsigned int i, bar_nr; + unsigned long bar_mask; + struct adf_bar *bar; + int ret; + + if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) { + /* + * If the accelerator is connected to a node with no memory + * there is no point in using the accelerator since the remote + * memory transaction will be very slow. + */ + dev_err(&pdev->dev, "Invalid NUMA configuration.\n"); + return -EINVAL; + } + + accel_dev = devm_kzalloc(&pdev->dev, sizeof(*accel_dev), GFP_KERNEL); + if (!accel_dev) + return -ENOMEM; + + INIT_LIST_HEAD(&accel_dev->crypto_list); + accel_pci_dev = &accel_dev->accel_pci_dev; + accel_pci_dev->pci_dev = pdev; + + /* + * Add accel device to accel table + * This should be called before adf_cleanup_accel is called + */ + if (adf_devmgr_add_dev(accel_dev, NULL)) { + dev_err(&pdev->dev, "Failed to add new accelerator device.\n"); + return -EFAULT; + } + + accel_dev->owner = THIS_MODULE; + /* Allocate and initialise device hardware meta-data structure */ + hw_data = devm_kzalloc(&pdev->dev, sizeof(*hw_data), GFP_KERNEL); + if (!hw_data) { + ret = -ENOMEM; + goto out_err; + } + + accel_dev->hw_device = hw_data; + adf_init_hw_data_4xxx(accel_dev->hw_device); + + pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid); + pci_read_config_dword(pdev, ADF_4XXX_FUSECTL4_OFFSET, &hw_data->fuses); + + /* Get Accelerators and Accelerators Engines masks */ + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); + accel_pci_dev->sku = hw_data->get_sku(hw_data); + /* If the device has no acceleration engines then ignore it */ + if (!hw_data->accel_mask || !hw_data->ae_mask || + (~hw_data->ae_mask & 0x01)) { + dev_err(&pdev->dev, "No acceleration units found.\n"); + ret = -EFAULT; + goto out_err; + } + + /* Create dev top level debugfs entry */ + snprintf(name, sizeof(name), "%s%s_%s", ADF_DEVICE_NAME_PREFIX, + hw_data->dev_class->name, pci_name(pdev)); + + accel_dev->debugfs_dir = debugfs_create_dir(name, NULL); + + /* Create device configuration table */ + ret = adf_cfg_dev_add(accel_dev); + if (ret) + goto out_err; + + /* Enable PCI device */ + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Can't enable PCI device.\n"); + goto out_err; + } + + /* Set DMA identifier */ + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) { + dev_err(&pdev->dev, "No usable DMA configuration.\n"); + ret = -EFAULT; + goto out_err; + } else { + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + } + } else { + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + } + + /* Get accelerator capabilities mask */ + hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev); + + /* Find and map all the device's BARS */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM) & ADF_4XXX_BAR_MASK; + + ret = pcim_iomap_regions_request_all(pdev, bar_mask, pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "Failed to map pci regions.\n"); + goto out_err; + } + + i = 0; + for_each_set_bit(bar_nr, &bar_mask, PCI_STD_NUM_BARS) { + bar = &accel_pci_dev->pci_bars[i++]; + bar->virt_addr = pcim_iomap_table(pdev)[bar_nr]; + } + + pci_set_master(pdev); + + if (adf_enable_aer(accel_dev)) { + dev_err(&pdev->dev, "Failed to enable aer.\n"); + ret = -EFAULT; + goto out_err; + } + + if (pci_save_state(pdev)) { + dev_err(&pdev->dev, "Failed to save pci state.\n"); + ret = -ENOMEM; + goto out_err_disable_aer; + } + + ret = adf_crypto_dev_config(accel_dev); + if (ret) + goto out_err_disable_aer; + + ret = adf_dev_init(accel_dev); + if (ret) + goto out_err_dev_shutdown; + + ret = adf_dev_start(accel_dev); + if (ret) + goto out_err_dev_stop; + + return ret; + +out_err_dev_stop: + adf_dev_stop(accel_dev); +out_err_dev_shutdown: + adf_dev_shutdown(accel_dev); +out_err_disable_aer: + adf_disable_aer(accel_dev); +out_err: + adf_cleanup_accel(accel_dev); + return ret; +} + +static void adf_remove(struct pci_dev *pdev) +{ + struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); + + if (!accel_dev) { + pr_err("QAT: Driver removal failed\n"); + return; + } + adf_dev_stop(accel_dev); + adf_dev_shutdown(accel_dev); + adf_disable_aer(accel_dev); + adf_cleanup_accel(accel_dev); +} + +static struct pci_driver adf_driver = { + .id_table = adf_pci_tbl, + .name = ADF_4XXX_DEVICE_NAME, + .probe = adf_probe, + .remove = adf_remove, + .sriov_configure = adf_sriov_configure, +}; + +module_pci_driver(adf_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel"); +MODULE_FIRMWARE(ADF_4XXX_FW); +MODULE_FIRMWARE(ADF_4XXX_MMP); +MODULE_DESCRIPTION("Intel(R) QuickAssist Technology"); +MODULE_VERSION(ADF_DRV_VERSION); +MODULE_SOFTDEP("pre: crypto-intel_qat"); diff --git a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c index aee494d3da52..eb45f1b1ae3e 100644 --- a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c +++ b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c @@ -3,7 +3,9 @@ #include <adf_accel_devices.h> #include <adf_common_drv.h> #include <adf_pf2vf_msg.h> +#include <adf_gen2_hw_data.h> #include "adf_c3xxx_hw_data.h" +#include "icp_qat_hw.h" /* Worker thread to service arbiter mappings based on dev SKUs */ static const u32 thrd_to_arb_map_6_me_sku[] = { @@ -17,15 +19,33 @@ static struct adf_hw_device_class c3xxx_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { - return (~fuse) >> ADF_C3XXX_ACCELERATORS_REG_OFFSET & - ADF_C3XXX_ACCELERATORS_MASK; + u32 straps = self->straps; + u32 fuses = self->fuses; + u32 accel; + + accel = ~(fuses | straps) >> ADF_C3XXX_ACCELERATORS_REG_OFFSET; + accel &= ADF_C3XXX_ACCELERATORS_MASK; + + return accel; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { - return (~fuse) & ADF_C3XXX_ACCELENGINES_MASK; + u32 straps = self->straps; + u32 fuses = self->fuses; + unsigned long disabled; + u32 ae_disable; + int accel; + + /* If an accel is disabled, then disable the corresponding two AEs */ + disabled = ~get_accel_mask(self) & ADF_C3XXX_ACCELERATORS_MASK; + ae_disable = BIT(1) | BIT(0); + for_each_set_bit(accel, &disabled, ADF_C3XXX_MAX_ACCELERATORS) + straps |= ae_disable << (accel << 1); + + return ~(fuses | straps) & ADF_C3XXX_ACCELENGINES_MASK; } static u32 get_num_accels(struct adf_hw_device_data *self) @@ -109,11 +129,13 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_device = accel_dev->hw_device; struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_C3XXX_PMISC_BAR]; + unsigned long accel_mask = hw_device->accel_mask; + unsigned long ae_mask = hw_device->ae_mask; void __iomem *csr = misc_bar->virt_addr; unsigned int val, i; /* Enable Accel Engine error detection & correction */ - for (i = 0; i < hw_device->get_num_aes(hw_device); i++) { + for_each_set_bit(i, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) { val = ADF_CSR_RD(csr, ADF_C3XXX_AE_CTX_ENABLES(i)); val |= ADF_C3XXX_ENABLE_AE_ECC_ERR; ADF_CSR_WR(csr, ADF_C3XXX_AE_CTX_ENABLES(i), val); @@ -123,7 +145,7 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) } /* Enable shared memory error detection & correction */ - for (i = 0; i < hw_device->get_num_accels(hw_device); i++) { + for_each_set_bit(i, &accel_mask, ADF_C3XXX_MAX_ACCELERATORS) { val = ADF_CSR_RD(csr, ADF_C3XXX_UERRSSMSH(i)); val |= ADF_C3XXX_ERRSSMSH_EN; ADF_CSR_WR(csr, ADF_C3XXX_UERRSSMSH(i), val); @@ -151,11 +173,19 @@ static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev) return 0; } +static void configure_iov_threads(struct adf_accel_dev *accel_dev, bool enable) +{ + adf_gen2_cfg_iov_thds(accel_dev, enable, + ADF_C3XXX_AE2FUNC_MAP_GRP_A_NUM_REGS, + ADF_C3XXX_AE2FUNC_MAP_GRP_B_NUM_REGS); +} + void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &c3xxx_class; hw_data->instance_id = c3xxx_class.instances++; hw_data->num_banks = ADF_C3XXX_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_C3XXX_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_C3XXX_MAX_ACCELENGINES; @@ -166,6 +196,7 @@ void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data) hw_data->enable_error_correction = adf_enable_error_correction; hw_data->get_accel_mask = get_accel_mask; hw_data->get_ae_mask = get_ae_mask; + hw_data->get_accel_cap = adf_gen2_get_accel_cap; hw_data->get_num_accels = get_num_accels; hw_data->get_num_aes = get_num_aes; hw_data->get_sram_bar_id = get_sram_bar_id; @@ -173,11 +204,14 @@ void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data) hw_data->get_misc_bar_id = get_misc_bar_id; hw_data->get_pf2vf_offset = get_pf2vf_offset; hw_data->get_vintmsk_offset = get_vintmsk_offset; + hw_data->get_admin_info = adf_gen2_get_admin_info; + hw_data->get_arb_info = adf_gen2_get_arb_info; hw_data->get_sku = get_sku; hw_data->fw_name = ADF_C3XXX_FW; hw_data->fw_mmp_name = ADF_C3XXX_MMP; hw_data->init_admin_comms = adf_init_admin_comms; hw_data->exit_admin_comms = adf_exit_admin_comms; + hw_data->configure_iov_threads = configure_iov_threads; hw_data->disable_iov = adf_disable_sriov; hw_data->send_admin_init = adf_send_admin_init; hw_data->init_arb = adf_init_arb; @@ -187,6 +221,7 @@ void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data) hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms; hw_data->reset_device = adf_reset_flr; hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_c3xxx(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h index 8b5dd2c94ebf..fece8e38025a 100644 --- a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h +++ b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h @@ -18,6 +18,7 @@ #define ADF_C3XXX_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30) #define ADF_C3XXX_SMIA0_MASK 0xFFFF #define ADF_C3XXX_SMIA1_MASK 0x1 +#define ADF_C3XXX_SOFTSTRAP_CSR_OFFSET 0x2EC /* Error detection and correction */ #define ADF_C3XXX_AE_CTX_ENABLES(i) (i * 0x1000 + 0x20818) #define ADF_C3XXX_AE_MISC_CONTROL(i) (i * 0x1000 + 0x20960) @@ -30,6 +31,10 @@ #define ADF_C3XXX_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04)) #define ADF_C3XXX_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04)) +/* AE to function mapping */ +#define ADF_C3XXX_AE2FUNC_MAP_GRP_A_NUM_REGS 48 +#define ADF_C3XXX_AE2FUNC_MAP_GRP_B_NUM_REGS 6 + /* Firmware Binary */ #define ADF_C3XXX_FW "qat_c3xxx.bin" #define ADF_C3XXX_MMP "qat_c3xxx_mmp.bin" diff --git a/drivers/crypto/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/qat/qat_c3xxx/adf_drv.c index ed0e8e33fe4b..7fb3343ae8b0 100644 --- a/drivers/crypto/qat/qat_c3xxx/adf_drv.c +++ b/drivers/crypto/qat/qat_c3xxx/adf_drv.c @@ -126,10 +126,12 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid); pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET, &hw_data->fuses); + pci_read_config_dword(pdev, ADF_C3XXX_SOFTSTRAP_CSR_OFFSET, + &hw_data->straps); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* If the device has no acceleration engines then ignore it. */ if (!hw_data->accel_mask || !hw_data->ae_mask || @@ -175,9 +177,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_disable; } - /* Read accelerator capabilities mask */ - pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, - &hw_data->accel_capabilities_mask); + /* Get accelerator capabilities mask */ + hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev); /* Find and map all the device's BARS */ i = 0; diff --git a/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c index d2fedbd7113c..15f6b9bdfb22 100644 --- a/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c +++ b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c @@ -3,6 +3,7 @@ #include <adf_accel_devices.h> #include <adf_pf2vf_msg.h> #include <adf_common_drv.h> +#include <adf_gen2_hw_data.h> #include "adf_c3xxxvf_hw_data.h" static struct adf_hw_device_class c3xxxiov_class = { @@ -11,12 +12,12 @@ static struct adf_hw_device_class c3xxxiov_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { return ADF_C3XXXIOV_ACCELERATORS_MASK; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { return ADF_C3XXXIOV_ACCELENGINES_MASK; } @@ -69,6 +70,7 @@ void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &c3xxxiov_class; hw_data->num_banks = ADF_C3XXXIOV_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_C3XXXIOV_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_C3XXXIOV_MAX_ACCELENGINES; @@ -97,6 +99,7 @@ void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data) hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; hw_data->dev_class->instances++; adf_devmgr_update_class_index(hw_data); + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c index 456979b136a2..1d1532e8fb6d 100644 --- a/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c +++ b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c @@ -119,8 +119,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adf_init_hw_data_c3xxxiov(accel_dev->hw_device); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ diff --git a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c index 844ad5ed33fc..babdffbcb846 100644 --- a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c +++ b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c @@ -3,7 +3,9 @@ #include <adf_accel_devices.h> #include <adf_common_drv.h> #include <adf_pf2vf_msg.h> +#include <adf_gen2_hw_data.h> #include "adf_c62x_hw_data.h" +#include "icp_qat_hw.h" /* Worker thread to service arbiter mappings based on dev SKUs */ static const u32 thrd_to_arb_map_8_me_sku[] = { @@ -22,15 +24,33 @@ static struct adf_hw_device_class c62x_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { - return (~fuse) >> ADF_C62X_ACCELERATORS_REG_OFFSET & - ADF_C62X_ACCELERATORS_MASK; + u32 straps = self->straps; + u32 fuses = self->fuses; + u32 accel; + + accel = ~(fuses | straps) >> ADF_C62X_ACCELERATORS_REG_OFFSET; + accel &= ADF_C62X_ACCELERATORS_MASK; + + return accel; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { - return (~fuse) & ADF_C62X_ACCELENGINES_MASK; + u32 straps = self->straps; + u32 fuses = self->fuses; + unsigned long disabled; + u32 ae_disable; + int accel; + + /* If an accel is disabled, then disable the corresponding two AEs */ + disabled = ~get_accel_mask(self) & ADF_C62X_ACCELERATORS_MASK; + ae_disable = BIT(1) | BIT(0); + for_each_set_bit(accel, &disabled, ADF_C62X_MAX_ACCELERATORS) + straps |= ae_disable << (accel << 1); + + return ~(fuses | straps) & ADF_C62X_ACCELENGINES_MASK; } static u32 get_num_accels(struct adf_hw_device_data *self) @@ -119,11 +139,13 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_device = accel_dev->hw_device; struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_C62X_PMISC_BAR]; + unsigned long accel_mask = hw_device->accel_mask; + unsigned long ae_mask = hw_device->ae_mask; void __iomem *csr = misc_bar->virt_addr; unsigned int val, i; /* Enable Accel Engine error detection & correction */ - for (i = 0; i < hw_device->get_num_aes(hw_device); i++) { + for_each_set_bit(i, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) { val = ADF_CSR_RD(csr, ADF_C62X_AE_CTX_ENABLES(i)); val |= ADF_C62X_ENABLE_AE_ECC_ERR; ADF_CSR_WR(csr, ADF_C62X_AE_CTX_ENABLES(i), val); @@ -133,7 +155,7 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) } /* Enable shared memory error detection & correction */ - for (i = 0; i < hw_device->get_num_accels(hw_device); i++) { + for_each_set_bit(i, &accel_mask, ADF_C62X_MAX_ACCELERATORS) { val = ADF_CSR_RD(csr, ADF_C62X_UERRSSMSH(i)); val |= ADF_C62X_ERRSSMSH_EN; ADF_CSR_WR(csr, ADF_C62X_UERRSSMSH(i), val); @@ -161,11 +183,19 @@ static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev) return 0; } +static void configure_iov_threads(struct adf_accel_dev *accel_dev, bool enable) +{ + adf_gen2_cfg_iov_thds(accel_dev, enable, + ADF_C62X_AE2FUNC_MAP_GRP_A_NUM_REGS, + ADF_C62X_AE2FUNC_MAP_GRP_B_NUM_REGS); +} + void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &c62x_class; hw_data->instance_id = c62x_class.instances++; hw_data->num_banks = ADF_C62X_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_C62X_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_C62X_MAX_ACCELENGINES; @@ -176,6 +206,7 @@ void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data) hw_data->enable_error_correction = adf_enable_error_correction; hw_data->get_accel_mask = get_accel_mask; hw_data->get_ae_mask = get_ae_mask; + hw_data->get_accel_cap = adf_gen2_get_accel_cap; hw_data->get_num_accels = get_num_accels; hw_data->get_num_aes = get_num_aes; hw_data->get_sram_bar_id = get_sram_bar_id; @@ -183,11 +214,14 @@ void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data) hw_data->get_misc_bar_id = get_misc_bar_id; hw_data->get_pf2vf_offset = get_pf2vf_offset; hw_data->get_vintmsk_offset = get_vintmsk_offset; + hw_data->get_admin_info = adf_gen2_get_admin_info; + hw_data->get_arb_info = adf_gen2_get_arb_info; hw_data->get_sku = get_sku; hw_data->fw_name = ADF_C62X_FW; hw_data->fw_mmp_name = ADF_C62X_MMP; hw_data->init_admin_comms = adf_init_admin_comms; hw_data->exit_admin_comms = adf_exit_admin_comms; + hw_data->configure_iov_threads = configure_iov_threads; hw_data->disable_iov = adf_disable_sriov; hw_data->send_admin_init = adf_send_admin_init; hw_data->init_arb = adf_init_arb; @@ -197,6 +231,7 @@ void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data) hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms; hw_data->reset_device = adf_reset_flr; hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_c62x(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h index 88504d2bf30d..53d3cb577f5b 100644 --- a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h +++ b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h @@ -19,6 +19,7 @@ #define ADF_C62X_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30) #define ADF_C62X_SMIA0_MASK 0xFFFF #define ADF_C62X_SMIA1_MASK 0x1 +#define ADF_C62X_SOFTSTRAP_CSR_OFFSET 0x2EC /* Error detection and correction */ #define ADF_C62X_AE_CTX_ENABLES(i) (i * 0x1000 + 0x20818) #define ADF_C62X_AE_MISC_CONTROL(i) (i * 0x1000 + 0x20960) @@ -31,6 +32,10 @@ #define ADF_C62X_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04)) #define ADF_C62X_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04)) +/* AE to function mapping */ +#define ADF_C62X_AE2FUNC_MAP_GRP_A_NUM_REGS 80 +#define ADF_C62X_AE2FUNC_MAP_GRP_B_NUM_REGS 10 + /* Firmware Binary */ #define ADF_C62X_FW "qat_c62x.bin" #define ADF_C62X_MMP "qat_c62x_mmp.bin" diff --git a/drivers/crypto/qat/qat_c62x/adf_drv.c b/drivers/crypto/qat/qat_c62x/adf_drv.c index d8e7c9c25590..1f5de442e1e6 100644 --- a/drivers/crypto/qat/qat_c62x/adf_drv.c +++ b/drivers/crypto/qat/qat_c62x/adf_drv.c @@ -126,10 +126,12 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid); pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET, &hw_data->fuses); + pci_read_config_dword(pdev, ADF_C62X_SOFTSTRAP_CSR_OFFSET, + &hw_data->straps); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* If the device has no acceleration engines then ignore it. */ if (!hw_data->accel_mask || !hw_data->ae_mask || @@ -175,9 +177,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_disable; } - /* Read accelerator capabilities mask */ - pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, - &hw_data->accel_capabilities_mask); + /* Get accelerator capabilities mask */ + hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev); /* Find and map all the device's BARS */ i = (hw_data->fuses & ADF_DEVICE_FUSECTL_MASK) ? 1 : 0; diff --git a/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c index 29fd3f1091ab..d231583428c9 100644 --- a/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c +++ b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c @@ -3,6 +3,7 @@ #include <adf_accel_devices.h> #include <adf_pf2vf_msg.h> #include <adf_common_drv.h> +#include <adf_gen2_hw_data.h> #include "adf_c62xvf_hw_data.h" static struct adf_hw_device_class c62xiov_class = { @@ -11,12 +12,12 @@ static struct adf_hw_device_class c62xiov_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { return ADF_C62XIOV_ACCELERATORS_MASK; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { return ADF_C62XIOV_ACCELENGINES_MASK; } @@ -69,6 +70,7 @@ void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &c62xiov_class; hw_data->num_banks = ADF_C62XIOV_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_C62XIOV_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_C62XIOV_MAX_ACCELENGINES; @@ -97,6 +99,7 @@ void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data) hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; hw_data->dev_class->instances++; adf_devmgr_update_class_index(hw_data); + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_c62xiov(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/qat/qat_c62xvf/adf_drv.c index b9810f79eb84..04742a6d91ca 100644 --- a/drivers/crypto/qat/qat_c62xvf/adf_drv.c +++ b/drivers/crypto/qat/qat_c62xvf/adf_drv.c @@ -119,8 +119,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adf_init_hw_data_c62xiov(accel_dev->hw_device); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ diff --git a/drivers/crypto/qat/qat_common/Makefile b/drivers/crypto/qat/qat_common/Makefile index 47a8e3d8b81a..9c57abdf56b7 100644 --- a/drivers/crypto/qat/qat_common/Makefile +++ b/drivers/crypto/qat/qat_common/Makefile @@ -10,6 +10,8 @@ intel_qat-objs := adf_cfg.o \ adf_transport.o \ adf_admin.o \ adf_hw_arbiter.o \ + adf_gen2_hw_data.o \ + adf_gen4_hw_data.o \ qat_crypto.o \ qat_algs.o \ qat_asym_algs.o \ diff --git a/drivers/crypto/qat/qat_common/adf_accel_devices.h b/drivers/crypto/qat/qat_common/adf_accel_devices.h index 06952ece53d9..c46a5805b294 100644 --- a/drivers/crypto/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h @@ -15,6 +15,9 @@ #define ADF_C62XVF_DEVICE_NAME "c6xxvf" #define ADF_C3XXX_DEVICE_NAME "c3xxx" #define ADF_C3XXXVF_DEVICE_NAME "c3xxxvf" +#define ADF_4XXX_DEVICE_NAME "4xxx" +#define ADF_4XXX_PCI_DEVICE_ID 0x4940 +#define ADF_4XXXIOV_PCI_DEVICE_ID 0x4941 #define ADF_ERRSOU3 (0x3A000 + 0x0C) #define ADF_ERRSOU5 (0x3A000 + 0xD8) #define ADF_DEVICE_FUSECTL_OFFSET 0x40 @@ -97,6 +100,46 @@ struct adf_hw_device_class { u32 instances; } __packed; +struct arb_info { + u32 arb_cfg; + u32 arb_offset; + u32 wt2sam_offset; +}; + +struct admin_info { + u32 admin_msg_ur; + u32 admin_msg_lr; + u32 mailbox_offset; +}; + +struct adf_hw_csr_ops { + u64 (*build_csr_ring_base_addr)(dma_addr_t addr, u32 size); + u32 (*read_csr_ring_head)(void __iomem *csr_base_addr, u32 bank, + u32 ring); + void (*write_csr_ring_head)(void __iomem *csr_base_addr, u32 bank, + u32 ring, u32 value); + u32 (*read_csr_ring_tail)(void __iomem *csr_base_addr, u32 bank, + u32 ring); + void (*write_csr_ring_tail)(void __iomem *csr_base_addr, u32 bank, + u32 ring, u32 value); + u32 (*read_csr_e_stat)(void __iomem *csr_base_addr, u32 bank); + void (*write_csr_ring_config)(void __iomem *csr_base_addr, u32 bank, + u32 ring, u32 value); + void (*write_csr_ring_base)(void __iomem *csr_base_addr, u32 bank, + u32 ring, dma_addr_t addr); + void (*write_csr_int_flag)(void __iomem *csr_base_addr, u32 bank, + u32 value); + void (*write_csr_int_srcsel)(void __iomem *csr_base_addr, u32 bank); + void (*write_csr_int_col_en)(void __iomem *csr_base_addr, u32 bank, + u32 value); + void (*write_csr_int_col_ctl)(void __iomem *csr_base_addr, u32 bank, + u32 value); + void (*write_csr_int_flag_and_col)(void __iomem *csr_base_addr, + u32 bank, u32 value); + void (*write_csr_ring_srv_arb_en)(void __iomem *csr_base_addr, u32 bank, + u32 value); +}; + struct adf_cfg_device_data; struct adf_accel_dev; struct adf_etr_data; @@ -104,8 +147,9 @@ struct adf_etr_ring_data; struct adf_hw_device_data { struct adf_hw_device_class *dev_class; - u32 (*get_accel_mask)(u32 fuse); - u32 (*get_ae_mask)(u32 fuse); + u32 (*get_accel_mask)(struct adf_hw_device_data *self); + u32 (*get_ae_mask)(struct adf_hw_device_data *self); + u32 (*get_accel_cap)(struct adf_accel_dev *accel_dev); u32 (*get_sram_bar_id)(struct adf_hw_device_data *self); u32 (*get_misc_bar_id)(struct adf_hw_device_data *self); u32 (*get_etr_bar_id)(struct adf_hw_device_data *self); @@ -113,6 +157,8 @@ struct adf_hw_device_data { u32 (*get_num_accels)(struct adf_hw_device_data *self); u32 (*get_pf2vf_offset)(u32 i); u32 (*get_vintmsk_offset)(u32 i); + void (*get_arb_info)(struct arb_info *arb_csrs_info); + void (*get_admin_info)(struct admin_info *admin_csrs_info); enum dev_sku_info (*get_sku)(struct adf_hw_device_data *self); int (*alloc_irq)(struct adf_accel_dev *accel_dev); void (*free_irq)(struct adf_accel_dev *accel_dev); @@ -125,19 +171,29 @@ struct adf_hw_device_data { void (*get_arb_mapping)(struct adf_accel_dev *accel_dev, const u32 **cfg); void (*disable_iov)(struct adf_accel_dev *accel_dev); + void (*configure_iov_threads)(struct adf_accel_dev *accel_dev, + bool enable); void (*enable_ints)(struct adf_accel_dev *accel_dev); int (*enable_vf2pf_comms)(struct adf_accel_dev *accel_dev); void (*reset_device)(struct adf_accel_dev *accel_dev); + void (*set_msix_rttable)(struct adf_accel_dev *accel_dev); + char *(*uof_get_name)(u32 obj_num); + u32 (*uof_get_num_objs)(void); + u32 (*uof_get_ae_mask)(u32 obj_num); + struct adf_hw_csr_ops csr_ops; const char *fw_name; const char *fw_mmp_name; u32 fuses; + u32 straps; u32 accel_capabilities_mask; u32 instance_id; u16 accel_mask; - u16 ae_mask; + u32 ae_mask; + u32 admin_ae_mask; u16 tx_rings_mask; u8 tx_rx_gap; u8 num_banks; + u8 num_rings_per_bank; u8 num_accel; u8 num_logical_accel; u8 num_engines; @@ -155,7 +211,10 @@ struct adf_hw_device_data { #define GET_BARS(accel_dev) ((accel_dev)->accel_pci_dev.pci_bars) #define GET_HW_DATA(accel_dev) (accel_dev->hw_device) #define GET_MAX_BANKS(accel_dev) (GET_HW_DATA(accel_dev)->num_banks) +#define GET_NUM_RINGS_PER_BANK(accel_dev) \ + GET_HW_DATA(accel_dev)->num_rings_per_bank #define GET_MAX_ACCELENGINES(accel_dev) (GET_HW_DATA(accel_dev)->num_engines) +#define GET_CSR_OPS(accel_dev) (&(accel_dev)->hw_device->csr_ops) #define accel_to_pci_dev(accel_ptr) accel_ptr->accel_pci_dev.pci_dev struct adf_admin_comms; diff --git a/drivers/crypto/qat/qat_common/adf_accel_engine.c b/drivers/crypto/qat/qat_common/adf_accel_engine.c index c8ad85b882be..ca4eae8cdd0b 100644 --- a/drivers/crypto/qat/qat_common/adf_accel_engine.c +++ b/drivers/crypto/qat/qat_common/adf_accel_engine.c @@ -7,12 +7,55 @@ #include "adf_common_drv.h" #include "icp_qat_uclo.h" +static int adf_ae_fw_load_images(struct adf_accel_dev *accel_dev, void *fw_addr, + u32 fw_size) +{ + struct adf_fw_loader_data *loader_data = accel_dev->fw_loader; + struct adf_hw_device_data *hw_device = accel_dev->hw_device; + struct icp_qat_fw_loader_handle *loader; + char *obj_name; + u32 num_objs; + u32 ae_mask; + int i; + + loader = loader_data->fw_loader; + num_objs = hw_device->uof_get_num_objs(); + + for (i = 0; i < num_objs; i++) { + obj_name = hw_device->uof_get_name(i); + ae_mask = hw_device->uof_get_ae_mask(i); + + if (qat_uclo_set_cfg_ae_mask(loader, ae_mask)) { + dev_err(&GET_DEV(accel_dev), + "Invalid mask for UOF image\n"); + goto out_err; + } + if (qat_uclo_map_obj(loader, fw_addr, fw_size, obj_name)) { + dev_err(&GET_DEV(accel_dev), + "Failed to map UOF firmware\n"); + goto out_err; + } + if (qat_uclo_wr_all_uimage(loader)) { + dev_err(&GET_DEV(accel_dev), + "Failed to load UOF firmware\n"); + goto out_err; + } + qat_uclo_del_obj(loader); + } + + return 0; + +out_err: + adf_ae_fw_release(accel_dev); + return -EFAULT; +} + int adf_ae_fw_load(struct adf_accel_dev *accel_dev) { struct adf_fw_loader_data *loader_data = accel_dev->fw_loader; struct adf_hw_device_data *hw_device = accel_dev->hw_device; - void *uof_addr, *mmp_addr; - u32 uof_size, mmp_size; + void *fw_addr, *mmp_addr; + u32 fw_size, mmp_size; if (!hw_device->fw_name) return 0; @@ -30,15 +73,20 @@ int adf_ae_fw_load(struct adf_accel_dev *accel_dev) goto out_err; } - uof_size = loader_data->uof_fw->size; - uof_addr = (void *)loader_data->uof_fw->data; + fw_size = loader_data->uof_fw->size; + fw_addr = (void *)loader_data->uof_fw->data; mmp_size = loader_data->mmp_fw->size; mmp_addr = (void *)loader_data->mmp_fw->data; + if (qat_uclo_wr_mimage(loader_data->fw_loader, mmp_addr, mmp_size)) { dev_err(&GET_DEV(accel_dev), "Failed to load MMP\n"); goto out_err; } - if (qat_uclo_map_obj(loader_data->fw_loader, uof_addr, uof_size)) { + + if (hw_device->uof_get_num_objs) + return adf_ae_fw_load_images(accel_dev, fw_addr, fw_size); + + if (qat_uclo_map_obj(loader_data->fw_loader, fw_addr, fw_size, NULL)) { dev_err(&GET_DEV(accel_dev), "Failed to map FW\n"); goto out_err; } @@ -61,7 +109,7 @@ void adf_ae_fw_release(struct adf_accel_dev *accel_dev) if (!hw_device->fw_name) return; - qat_uclo_del_uof_obj(loader_data->fw_loader); + qat_uclo_del_obj(loader_data->fw_loader); qat_hal_deinit(loader_data->fw_loader); release_firmware(loader_data->uof_fw); release_firmware(loader_data->mmp_fw); @@ -74,17 +122,12 @@ int adf_ae_start(struct adf_accel_dev *accel_dev) { struct adf_fw_loader_data *loader_data = accel_dev->fw_loader; struct adf_hw_device_data *hw_data = accel_dev->hw_device; - u32 ae_ctr, ae, max_aes = GET_MAX_ACCELENGINES(accel_dev); + u32 ae_ctr; if (!hw_data->fw_name) return 0; - for (ae = 0, ae_ctr = 0; ae < max_aes; ae++) { - if (hw_data->ae_mask & (1 << ae)) { - qat_hal_start(loader_data->fw_loader, ae, 0xFF); - ae_ctr++; - } - } + ae_ctr = qat_hal_start(loader_data->fw_loader); dev_info(&GET_DEV(accel_dev), "qat_dev%d started %d acceleration engines\n", accel_dev->accel_id, ae_ctr); diff --git a/drivers/crypto/qat/qat_common/adf_admin.c b/drivers/crypto/qat/qat_common/adf_admin.c index ec9b390276d6..43680e178242 100644 --- a/drivers/crypto/qat/qat_common/adf_admin.c +++ b/drivers/crypto/qat/qat_common/adf_admin.c @@ -10,11 +10,7 @@ #include "adf_common_drv.h" #include "icp_qat_fw_init_admin.h" -/* Admin Messages Registers */ -#define ADF_DH895XCC_ADMINMSGUR_OFFSET (0x3A000 + 0x574) -#define ADF_DH895XCC_ADMINMSGLR_OFFSET (0x3A000 + 0x578) -#define ADF_DH895XCC_MAILBOX_BASE_OFFSET 0x20970 -#define ADF_DH895XCC_MAILBOX_STRIDE 0x1000 +#define ADF_ADMIN_MAILBOX_STRIDE 0x1000 #define ADF_ADMINMSG_LEN 32 #define ADF_CONST_TABLE_SIZE 1024 #define ADF_ADMIN_POLL_DELAY_US 20 @@ -70,28 +66,28 @@ static const u8 const_tab[1024] __aligned(1024) = { 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, 0x51, 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b, 0x5b, 0xe0, 0xcd, 0x19, 0x13, -0x7e, 0x21, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7e, 0x21, 0x79, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x02, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x2B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -118,7 +114,7 @@ static int adf_put_admin_msg_sync(struct adf_accel_dev *accel_dev, u32 ae, struct adf_admin_comms *admin = accel_dev->admin; int offset = ae * ADF_ADMINMSG_LEN * 2; void __iomem *mailbox = admin->mailbox_addr; - int mb_offset = ae * ADF_DH895XCC_MAILBOX_STRIDE; + int mb_offset = ae * ADF_ADMIN_MAILBOX_STRIDE; struct icp_qat_fw_init_admin_req *request = in; mutex_lock(&admin->lock); @@ -167,7 +163,7 @@ static int adf_send_admin(struct adf_accel_dev *accel_dev, return 0; } -static int adf_init_me(struct adf_accel_dev *accel_dev) +static int adf_init_ae(struct adf_accel_dev *accel_dev) { struct icp_qat_fw_init_admin_req req; struct icp_qat_fw_init_admin_resp resp; @@ -176,7 +172,7 @@ static int adf_init_me(struct adf_accel_dev *accel_dev) memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); - req.cmd_id = ICP_QAT_FW_INIT_ME; + req.cmd_id = ICP_QAT_FW_INIT_AE; return adf_send_admin(accel_dev, &req, &resp, ae_mask); } @@ -186,7 +182,7 @@ static int adf_set_fw_constants(struct adf_accel_dev *accel_dev) struct icp_qat_fw_init_admin_req req; struct icp_qat_fw_init_admin_resp resp; struct adf_hw_device_data *hw_device = accel_dev->hw_device; - u32 ae_mask = hw_device->ae_mask; + u32 ae_mask = hw_device->admin_ae_mask ?: hw_device->ae_mask; memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); @@ -210,11 +206,11 @@ int adf_send_admin_init(struct adf_accel_dev *accel_dev) { int ret; - ret = adf_init_me(accel_dev); + ret = adf_set_fw_constants(accel_dev); if (ret) return ret; - return adf_set_fw_constants(accel_dev); + return adf_init_ae(accel_dev); } EXPORT_SYMBOL_GPL(adf_send_admin_init); @@ -225,8 +221,9 @@ int adf_init_admin_comms(struct adf_accel_dev *accel_dev) struct adf_bar *pmisc = &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; void __iomem *csr = pmisc->virt_addr; - void __iomem *mailbox = (void __iomem *)((uintptr_t)csr + - ADF_DH895XCC_MAILBOX_BASE_OFFSET); + struct admin_info admin_csrs_info; + u32 mailbox_offset, adminmsg_u, adminmsg_l; + void __iomem *mailbox; u64 reg_val; admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL, @@ -254,9 +251,17 @@ int adf_init_admin_comms(struct adf_accel_dev *accel_dev) } memcpy(admin->virt_tbl_addr, const_tab, sizeof(const_tab)); + hw_data->get_admin_info(&admin_csrs_info); + + mailbox_offset = admin_csrs_info.mailbox_offset; + mailbox = csr + mailbox_offset; + adminmsg_u = admin_csrs_info.admin_msg_ur; + adminmsg_l = admin_csrs_info.admin_msg_lr; + reg_val = (u64)admin->phy_addr; - ADF_CSR_WR(csr, ADF_DH895XCC_ADMINMSGUR_OFFSET, reg_val >> 32); - ADF_CSR_WR(csr, ADF_DH895XCC_ADMINMSGLR_OFFSET, reg_val); + ADF_CSR_WR(csr, adminmsg_u, upper_32_bits(reg_val)); + ADF_CSR_WR(csr, adminmsg_l, lower_32_bits(reg_val)); + mutex_init(&admin->lock); admin->mailbox_addr = mailbox; accel_dev->admin = admin; diff --git a/drivers/crypto/qat/qat_common/adf_cfg.c b/drivers/crypto/qat/qat_common/adf_cfg.c index 22ae32838113..575b6f002303 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg.c +++ b/drivers/crypto/qat/qat_common/adf_cfg.c @@ -196,7 +196,7 @@ static int adf_cfg_key_val_get(struct adf_accel_dev *accel_dev, memcpy(val, keyval->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES); return 0; } - return -1; + return -ENODATA; } /** @@ -243,7 +243,7 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev, } else { dev_err(&GET_DEV(accel_dev), "Unknown type given.\n"); kfree(key_val); - return -1; + return -EINVAL; } key_val->type = type; down_write(&cfg->lock); diff --git a/drivers/crypto/qat/qat_common/adf_cfg_common.h b/drivers/crypto/qat/qat_common/adf_cfg_common.h index 1ef46ccfba47..4fabb70b1f18 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg_common.h +++ b/drivers/crypto/qat/qat_common/adf_cfg_common.h @@ -32,7 +32,8 @@ enum adf_device_type { DEV_C62X, DEV_C62XVF, DEV_C3XXX, - DEV_C3XXXVF + DEV_C3XXXVF, + DEV_4XXX, }; struct adf_dev_status_info { diff --git a/drivers/crypto/qat/qat_common/adf_cfg_strings.h b/drivers/crypto/qat/qat_common/adf_cfg_strings.h index 314790f5b0af..09651e1f937a 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg_strings.h +++ b/drivers/crypto/qat/qat_common/adf_cfg_strings.h @@ -18,7 +18,8 @@ #define ADF_RING_DC_TX "RingTx" #define ADF_RING_DC_RX "RingRx" #define ADF_ETRMGR_BANK "Bank" -#define ADF_RING_BANK_NUM "BankNumber" +#define ADF_RING_SYM_BANK_NUM "BankSymNumber" +#define ADF_RING_ASYM_BANK_NUM "BankAsymNumber" #define ADF_CY "Cy" #define ADF_DC "Dc" #define ADF_ETRMGR_COALESCING_ENABLED "InterruptCoalescingEnabled" diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h index f22342f612c1..c61476553728 100644 --- a/drivers/crypto/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/qat/qat_common/adf_common_drv.h @@ -133,8 +133,7 @@ void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev); int qat_hal_init(struct adf_accel_dev *accel_dev); void qat_hal_deinit(struct icp_qat_fw_loader_handle *handle); -void qat_hal_start(struct icp_qat_fw_loader_handle *handle, unsigned char ae, - unsigned int ctx_mask); +int qat_hal_start(struct icp_qat_fw_loader_handle *handle); void qat_hal_stop(struct icp_qat_fw_loader_handle *handle, unsigned char ae, unsigned int ctx_mask); void qat_hal_reset(struct icp_qat_fw_loader_handle *handle); @@ -163,28 +162,32 @@ int qat_hal_batch_wr_lm(struct icp_qat_fw_loader_handle *handle, unsigned char ae, struct icp_qat_uof_batch_init *lm_init_header); int qat_hal_init_gpr(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata); int qat_hal_init_wr_xfer(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata); int qat_hal_init_rd_xfer(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata); int qat_hal_init_nn(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, unsigned short reg_num, unsigned int regdata); int qat_hal_wr_lm(struct icp_qat_fw_loader_handle *handle, unsigned char ae, unsigned short lm_addr, unsigned int value); +void qat_hal_set_ae_tindex_mode(struct icp_qat_fw_loader_handle *handle, + unsigned char ae, unsigned char mode); int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle); -void qat_uclo_del_uof_obj(struct icp_qat_fw_loader_handle *handle); +void qat_uclo_del_obj(struct icp_qat_fw_loader_handle *handle); int qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle, void *addr_ptr, int mem_size); int qat_uclo_map_obj(struct icp_qat_fw_loader_handle *handle, - void *addr_ptr, int mem_size); + void *addr_ptr, u32 mem_size, char *obj_name); +int qat_uclo_set_cfg_ae_mask(struct icp_qat_fw_loader_handle *handle, + unsigned int cfg_ae_mask); #if defined(CONFIG_PCI_IOV) int adf_sriov_configure(struct pci_dev *pdev, int numvfs); void adf_disable_sriov(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/qat/qat_common/adf_dev_mgr.c index 92ec035576df..4c752eed10fe 100644 --- a/drivers/crypto/qat/qat_common/adf_dev_mgr.c +++ b/drivers/crypto/qat/qat_common/adf_dev_mgr.c @@ -151,8 +151,8 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev, mutex_lock(&table_lock); atomic_set(&accel_dev->ref_count, 0); - /* PF on host or VF on guest */ - if (!accel_dev->is_vf || (accel_dev->is_vf && !pf)) { + /* PF on host or VF on guest - optimized to remove redundant is_vf */ + if (!accel_dev->is_vf || !pf) { struct vf_id_map *map; list_for_each(itr, &accel_table) { @@ -248,7 +248,8 @@ void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev, struct adf_accel_dev *pf) { mutex_lock(&table_lock); - if (!accel_dev->is_vf || (accel_dev->is_vf && !pf)) { + /* PF on host or VF on guest - optimized to remove redundant is_vf */ + if (!accel_dev->is_vf || !pf) { id_map[accel_dev->accel_id] = 0; num_devices--; } else if (accel_dev->is_vf && pf) { @@ -285,9 +286,9 @@ struct adf_accel_dev *adf_devmgr_get_first(void) /** * adf_devmgr_pci_to_accel_dev() - Get accel_dev associated with the pci_dev. - * @pci_dev: Pointer to pci device. + * @pci_dev: Pointer to PCI device. * - * Function returns acceleration device associated with the given pci device. + * Function returns acceleration device associated with the given PCI device. * To be used by QAT device specific drivers. * * Return: pointer to accel_dev or NULL if not found. diff --git a/drivers/crypto/qat/qat_common/adf_gen2_hw_data.c b/drivers/crypto/qat/qat_common/adf_gen2_hw_data.c new file mode 100644 index 000000000000..1aa17303838d --- /dev/null +++ b/drivers/crypto/qat/qat_common/adf_gen2_hw_data.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +/* Copyright(c) 2020 Intel Corporation */ +#include "adf_gen2_hw_data.h" +#include "icp_qat_hw.h" +#include <linux/pci.h> + +void adf_gen2_cfg_iov_thds(struct adf_accel_dev *accel_dev, bool enable, + int num_a_regs, int num_b_regs) +{ + struct adf_hw_device_data *hw_data = accel_dev->hw_device; + void __iomem *pmisc_addr; + struct adf_bar *pmisc; + int pmisc_id, i; + u32 reg; + + pmisc_id = hw_data->get_misc_bar_id(hw_data); + pmisc = &GET_BARS(accel_dev)[pmisc_id]; + pmisc_addr = pmisc->virt_addr; + + /* Set/Unset Valid bit in AE Thread to PCIe Function Mapping Group A */ + for (i = 0; i < num_a_regs; i++) { + reg = READ_CSR_AE2FUNCTION_MAP_A(pmisc_addr, i); + if (enable) + reg |= AE2FUNCTION_MAP_VALID; + else + reg &= ~AE2FUNCTION_MAP_VALID; + WRITE_CSR_AE2FUNCTION_MAP_A(pmisc_addr, i, reg); + } + + /* Set/Unset Valid bit in AE Thread to PCIe Function Mapping Group B */ + for (i = 0; i < num_b_regs; i++) { + reg = READ_CSR_AE2FUNCTION_MAP_B(pmisc_addr, i); + if (enable) + reg |= AE2FUNCTION_MAP_VALID; + else + reg &= ~AE2FUNCTION_MAP_VALID; + WRITE_CSR_AE2FUNCTION_MAP_B(pmisc_addr, i, reg); + } +} +EXPORT_SYMBOL_GPL(adf_gen2_cfg_iov_thds); + +void adf_gen2_get_admin_info(struct admin_info *admin_csrs_info) +{ + admin_csrs_info->mailbox_offset = ADF_MAILBOX_BASE_OFFSET; + admin_csrs_info->admin_msg_ur = ADF_ADMINMSGUR_OFFSET; + admin_csrs_info->admin_msg_lr = ADF_ADMINMSGLR_OFFSET; +} +EXPORT_SYMBOL_GPL(adf_gen2_get_admin_info); + +void adf_gen2_get_arb_info(struct arb_info *arb_info) +{ + arb_info->arb_cfg = ADF_ARB_CONFIG; + arb_info->arb_offset = ADF_ARB_OFFSET; + arb_info->wt2sam_offset = ADF_ARB_WRK_2_SER_MAP_OFFSET; +} +EXPORT_SYMBOL_GPL(adf_gen2_get_arb_info); + +static u64 build_csr_ring_base_addr(dma_addr_t addr, u32 size) +{ + return BUILD_RING_BASE_ADDR(addr, size); +} + +static u32 read_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring) +{ + return READ_CSR_RING_HEAD(csr_base_addr, bank, ring); +} + +static void write_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring, + u32 value) +{ + WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value); +} + +static u32 read_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring) +{ + return READ_CSR_RING_TAIL(csr_base_addr, bank, ring); +} + +static void write_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring, + u32 value) +{ + WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value); +} + +static u32 read_csr_e_stat(void __iomem *csr_base_addr, u32 bank) +{ + return READ_CSR_E_STAT(csr_base_addr, bank); +} + +static void write_csr_ring_config(void __iomem *csr_base_addr, u32 bank, + u32 ring, u32 value) +{ + WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value); +} + +static void write_csr_ring_base(void __iomem *csr_base_addr, u32 bank, u32 ring, + dma_addr_t addr) +{ + WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, addr); +} + +static void write_csr_int_flag(void __iomem *csr_base_addr, u32 bank, u32 value) +{ + WRITE_CSR_INT_FLAG(csr_base_addr, bank, value); +} + +static void write_csr_int_srcsel(void __iomem *csr_base_addr, u32 bank) +{ + WRITE_CSR_INT_SRCSEL(csr_base_addr, bank); +} + +static void write_csr_int_col_en(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value); +} + +static void write_csr_int_col_ctl(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value); +} + +static void write_csr_int_flag_and_col(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value); +} + +static void write_csr_ring_srv_arb_en(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value); +} + +void adf_gen2_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops) +{ + csr_ops->build_csr_ring_base_addr = build_csr_ring_base_addr; + csr_ops->read_csr_ring_head = read_csr_ring_head; + csr_ops->write_csr_ring_head = write_csr_ring_head; + csr_ops->read_csr_ring_tail = read_csr_ring_tail; + csr_ops->write_csr_ring_tail = write_csr_ring_tail; + csr_ops->read_csr_e_stat = read_csr_e_stat; + csr_ops->write_csr_ring_config = write_csr_ring_config; + csr_ops->write_csr_ring_base = write_csr_ring_base; + csr_ops->write_csr_int_flag = write_csr_int_flag; + csr_ops->write_csr_int_srcsel = write_csr_int_srcsel; + csr_ops->write_csr_int_col_en = write_csr_int_col_en; + csr_ops->write_csr_int_col_ctl = write_csr_int_col_ctl; + csr_ops->write_csr_int_flag_and_col = write_csr_int_flag_and_col; + csr_ops->write_csr_ring_srv_arb_en = write_csr_ring_srv_arb_en; +} +EXPORT_SYMBOL_GPL(adf_gen2_init_hw_csr_ops); + +u32 adf_gen2_get_accel_cap(struct adf_accel_dev *accel_dev) +{ + struct adf_hw_device_data *hw_data = accel_dev->hw_device; + struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev; + u32 straps = hw_data->straps; + u32 fuses = hw_data->fuses; + u32 legfuses; + u32 capabilities = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC | + ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | + ICP_ACCEL_CAPABILITIES_AUTHENTICATION; + + /* Read accelerator capabilities mask */ + pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, &legfuses); + + if (legfuses & ICP_ACCEL_MASK_CIPHER_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC; + if (legfuses & ICP_ACCEL_MASK_PKE_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; + if (legfuses & ICP_ACCEL_MASK_AUTH_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION; + + if ((straps | fuses) & ADF_POWERGATE_PKE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; + + return capabilities; +} +EXPORT_SYMBOL_GPL(adf_gen2_get_accel_cap); diff --git a/drivers/crypto/qat/qat_common/adf_gen2_hw_data.h b/drivers/crypto/qat/qat_common/adf_gen2_hw_data.h new file mode 100644 index 000000000000..3816e6500352 --- /dev/null +++ b/drivers/crypto/qat/qat_common/adf_gen2_hw_data.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ +/* Copyright(c) 2020 Intel Corporation */ +#ifndef ADF_GEN2_HW_DATA_H_ +#define ADF_GEN2_HW_DATA_H_ + +#include "adf_accel_devices.h" + +/* Transport access */ +#define ADF_BANK_INT_SRC_SEL_MASK_0 0x4444444CUL +#define ADF_BANK_INT_SRC_SEL_MASK_X 0x44444444UL +#define ADF_RING_CSR_RING_CONFIG 0x000 +#define ADF_RING_CSR_RING_LBASE 0x040 +#define ADF_RING_CSR_RING_UBASE 0x080 +#define ADF_RING_CSR_RING_HEAD 0x0C0 +#define ADF_RING_CSR_RING_TAIL 0x100 +#define ADF_RING_CSR_E_STAT 0x14C +#define ADF_RING_CSR_INT_FLAG 0x170 +#define ADF_RING_CSR_INT_SRCSEL 0x174 +#define ADF_RING_CSR_INT_SRCSEL_2 0x178 +#define ADF_RING_CSR_INT_COL_EN 0x17C +#define ADF_RING_CSR_INT_COL_CTL 0x180 +#define ADF_RING_CSR_INT_FLAG_AND_COL 0x184 +#define ADF_RING_CSR_INT_COL_CTL_ENABLE 0x80000000 +#define ADF_RING_BUNDLE_SIZE 0x1000 + +#define BUILD_RING_BASE_ADDR(addr, size) \ + (((addr) >> 6) & (GENMASK_ULL(63, 0) << (size))) +#define READ_CSR_RING_HEAD(csr_base_addr, bank, ring) \ + ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_HEAD + ((ring) << 2)) +#define READ_CSR_RING_TAIL(csr_base_addr, bank, ring) \ + ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_TAIL + ((ring) << 2)) +#define READ_CSR_E_STAT(csr_base_addr, bank) \ + ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_E_STAT) +#define WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_CONFIG + ((ring) << 2), value) +#define WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, value) \ +do { \ + u32 l_base = 0, u_base = 0; \ + l_base = (u32)((value) & 0xFFFFFFFF); \ + u_base = (u32)(((value) & 0xFFFFFFFF00000000ULL) >> 32); \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_LBASE + ((ring) << 2), l_base); \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_UBASE + ((ring) << 2), u_base); \ +} while (0) + +#define WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_HEAD + ((ring) << 2), value) +#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_RING_TAIL + ((ring) << 2), value) +#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_FLAG, value) +#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \ +do { \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_SRCSEL, ADF_BANK_INT_SRC_SEL_MASK_0); \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_SRCSEL_2, ADF_BANK_INT_SRC_SEL_MASK_X); \ +} while (0) +#define WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_COL_EN, value) +#define WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_COL_CTL, \ + ADF_RING_CSR_INT_COL_CTL_ENABLE | (value)) +#define WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value) \ + ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ + ADF_RING_CSR_INT_FLAG_AND_COL, value) + +/* AE to function map */ +#define AE2FUNCTION_MAP_A_OFFSET (0x3A400 + 0x190) +#define AE2FUNCTION_MAP_B_OFFSET (0x3A400 + 0x310) +#define AE2FUNCTION_MAP_REG_SIZE 4 +#define AE2FUNCTION_MAP_VALID BIT(7) + +#define READ_CSR_AE2FUNCTION_MAP_A(pmisc_bar_addr, index) \ + ADF_CSR_RD(pmisc_bar_addr, AE2FUNCTION_MAP_A_OFFSET + \ + AE2FUNCTION_MAP_REG_SIZE * (index)) +#define WRITE_CSR_AE2FUNCTION_MAP_A(pmisc_bar_addr, index, value) \ + ADF_CSR_WR(pmisc_bar_addr, AE2FUNCTION_MAP_A_OFFSET + \ + AE2FUNCTION_MAP_REG_SIZE * (index), value) +#define READ_CSR_AE2FUNCTION_MAP_B(pmisc_bar_addr, index) \ + ADF_CSR_RD(pmisc_bar_addr, AE2FUNCTION_MAP_B_OFFSET + \ + AE2FUNCTION_MAP_REG_SIZE * (index)) +#define WRITE_CSR_AE2FUNCTION_MAP_B(pmisc_bar_addr, index, value) \ + ADF_CSR_WR(pmisc_bar_addr, AE2FUNCTION_MAP_B_OFFSET + \ + AE2FUNCTION_MAP_REG_SIZE * (index), value) + +/* Admin Interface Offsets */ +#define ADF_ADMINMSGUR_OFFSET (0x3A000 + 0x574) +#define ADF_ADMINMSGLR_OFFSET (0x3A000 + 0x578) +#define ADF_MAILBOX_BASE_OFFSET 0x20970 + +/* Arbiter configuration */ +#define ADF_ARB_OFFSET 0x30000 +#define ADF_ARB_WRK_2_SER_MAP_OFFSET 0x180 +#define ADF_ARB_CONFIG (BIT(31) | BIT(6) | BIT(0)) +#define ADF_ARB_REG_SLOT 0x1000 +#define ADF_ARB_RINGSRVARBEN_OFFSET 0x19C + +#define WRITE_CSR_RING_SRV_ARB_EN(csr_addr, index, value) \ + ADF_CSR_WR(csr_addr, ADF_ARB_RINGSRVARBEN_OFFSET + \ + (ADF_ARB_REG_SLOT * (index)), value) + +/* Power gating */ +#define ADF_POWERGATE_PKE BIT(24) + +void adf_gen2_cfg_iov_thds(struct adf_accel_dev *accel_dev, bool enable, + int num_a_regs, int num_b_regs); +void adf_gen2_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops); +void adf_gen2_get_admin_info(struct admin_info *admin_csrs_info); +void adf_gen2_get_arb_info(struct arb_info *arb_info); +u32 adf_gen2_get_accel_cap(struct adf_accel_dev *accel_dev); + +#endif diff --git a/drivers/crypto/qat/qat_common/adf_gen4_hw_data.c b/drivers/crypto/qat/qat_common/adf_gen4_hw_data.c new file mode 100644 index 000000000000..b72ff58e0bc7 --- /dev/null +++ b/drivers/crypto/qat/qat_common/adf_gen4_hw_data.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) +/* Copyright(c) 2020 Intel Corporation */ +#include "adf_accel_devices.h" +#include "adf_gen4_hw_data.h" + +static u64 build_csr_ring_base_addr(dma_addr_t addr, u32 size) +{ + return BUILD_RING_BASE_ADDR(addr, size); +} + +static u32 read_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring) +{ + return READ_CSR_RING_HEAD(csr_base_addr, bank, ring); +} + +static void write_csr_ring_head(void __iomem *csr_base_addr, u32 bank, u32 ring, + u32 value) +{ + WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value); +} + +static u32 read_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring) +{ + return READ_CSR_RING_TAIL(csr_base_addr, bank, ring); +} + +static void write_csr_ring_tail(void __iomem *csr_base_addr, u32 bank, u32 ring, + u32 value) +{ + WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value); +} + +static u32 read_csr_e_stat(void __iomem *csr_base_addr, u32 bank) +{ + return READ_CSR_E_STAT(csr_base_addr, bank); +} + +static void write_csr_ring_config(void __iomem *csr_base_addr, u32 bank, u32 ring, + u32 value) +{ + WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value); +} + +static void write_csr_ring_base(void __iomem *csr_base_addr, u32 bank, u32 ring, + dma_addr_t addr) +{ + WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, addr); +} + +static void write_csr_int_flag(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_FLAG(csr_base_addr, bank, value); +} + +static void write_csr_int_srcsel(void __iomem *csr_base_addr, u32 bank) +{ + WRITE_CSR_INT_SRCSEL(csr_base_addr, bank); +} + +static void write_csr_int_col_en(void __iomem *csr_base_addr, u32 bank, u32 value) +{ + WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value); +} + +static void write_csr_int_col_ctl(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value); +} + +static void write_csr_int_flag_and_col(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value); +} + +static void write_csr_ring_srv_arb_en(void __iomem *csr_base_addr, u32 bank, + u32 value) +{ + WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value); +} + +void adf_gen4_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops) +{ + csr_ops->build_csr_ring_base_addr = build_csr_ring_base_addr; + csr_ops->read_csr_ring_head = read_csr_ring_head; + csr_ops->write_csr_ring_head = write_csr_ring_head; + csr_ops->read_csr_ring_tail = read_csr_ring_tail; + csr_ops->write_csr_ring_tail = write_csr_ring_tail; + csr_ops->read_csr_e_stat = read_csr_e_stat; + csr_ops->write_csr_ring_config = write_csr_ring_config; + csr_ops->write_csr_ring_base = write_csr_ring_base; + csr_ops->write_csr_int_flag = write_csr_int_flag; + csr_ops->write_csr_int_srcsel = write_csr_int_srcsel; + csr_ops->write_csr_int_col_en = write_csr_int_col_en; + csr_ops->write_csr_int_col_ctl = write_csr_int_col_ctl; + csr_ops->write_csr_int_flag_and_col = write_csr_int_flag_and_col; + csr_ops->write_csr_ring_srv_arb_en = write_csr_ring_srv_arb_en; +} +EXPORT_SYMBOL_GPL(adf_gen4_init_hw_csr_ops); diff --git a/drivers/crypto/qat/qat_common/adf_gen4_hw_data.h b/drivers/crypto/qat/qat_common/adf_gen4_hw_data.h new file mode 100644 index 000000000000..8ab62b2ac311 --- /dev/null +++ b/drivers/crypto/qat/qat_common/adf_gen4_hw_data.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only) */ +/* Copyright(c) 2020 Intel Corporation */ +#ifndef ADF_GEN4_HW_CSR_DATA_H_ +#define ADF_GEN4_HW_CSR_DATA_H_ + +#include "adf_accel_devices.h" + +/* Transport access */ +#define ADF_BANK_INT_SRC_SEL_MASK 0x44UL +#define ADF_RING_CSR_RING_CONFIG 0x1000 +#define ADF_RING_CSR_RING_LBASE 0x1040 +#define ADF_RING_CSR_RING_UBASE 0x1080 +#define ADF_RING_CSR_RING_HEAD 0x0C0 +#define ADF_RING_CSR_RING_TAIL 0x100 +#define ADF_RING_CSR_E_STAT 0x14C +#define ADF_RING_CSR_INT_FLAG 0x170 +#define ADF_RING_CSR_INT_SRCSEL 0x174 +#define ADF_RING_CSR_INT_COL_CTL 0x180 +#define ADF_RING_CSR_INT_FLAG_AND_COL 0x184 +#define ADF_RING_CSR_INT_COL_CTL_ENABLE 0x80000000 +#define ADF_RING_CSR_INT_COL_EN 0x17C +#define ADF_RING_CSR_ADDR_OFFSET 0x100000 +#define ADF_RING_BUNDLE_SIZE 0x2000 + +#define BUILD_RING_BASE_ADDR(addr, size) \ + ((((addr) >> 6) & (GENMASK_ULL(63, 0) << (size))) << 6) +#define READ_CSR_RING_HEAD(csr_base_addr, bank, ring) \ + ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_HEAD + ((ring) << 2)) +#define READ_CSR_RING_TAIL(csr_base_addr, bank, ring) \ + ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_TAIL + ((ring) << 2)) +#define READ_CSR_E_STAT(csr_base_addr, bank) \ + ADF_CSR_RD((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + ADF_RING_CSR_E_STAT) +#define WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_CONFIG + ((ring) << 2), value) +#define WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, value) \ +do { \ + void __iomem *_csr_base_addr = csr_base_addr; \ + u32 _bank = bank; \ + u32 _ring = ring; \ + dma_addr_t _value = value; \ + u32 l_base = 0, u_base = 0; \ + l_base = lower_32_bits(_value); \ + u_base = upper_32_bits(_value); \ + ADF_CSR_WR((_csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (_bank) + \ + ADF_RING_CSR_RING_LBASE + ((_ring) << 2), l_base); \ + ADF_CSR_WR((_csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (_bank) + \ + ADF_RING_CSR_RING_UBASE + ((_ring) << 2), u_base); \ +} while (0) + +#define WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_HEAD + ((ring) << 2), value) +#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_TAIL + ((ring) << 2), value) +#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_INT_FLAG, (value)) +#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_INT_SRCSEL, ADF_BANK_INT_SRC_SEL_MASK) +#define WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_INT_COL_EN, (value)) +#define WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_INT_COL_CTL, \ + ADF_RING_CSR_INT_COL_CTL_ENABLE | (value)) +#define WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_INT_FLAG_AND_COL, (value)) + +/* Arbiter configuration */ +#define ADF_RING_CSR_RING_SRV_ARB_EN 0x19C + +#define WRITE_CSR_RING_SRV_ARB_EN(csr_base_addr, bank, value) \ + ADF_CSR_WR((csr_base_addr) + ADF_RING_CSR_ADDR_OFFSET, \ + ADF_RING_BUNDLE_SIZE * (bank) + \ + ADF_RING_CSR_RING_SRV_ARB_EN, (value)) + +void adf_gen4_init_hw_csr_ops(struct adf_hw_csr_ops *csr_ops); + +#endif diff --git a/drivers/crypto/qat/qat_common/adf_hw_arbiter.c b/drivers/crypto/qat/qat_common/adf_hw_arbiter.c index d4162783f970..9f5240d9488b 100644 --- a/drivers/crypto/qat/qat_common/adf_hw_arbiter.c +++ b/drivers/crypto/qat/qat_common/adf_hw_arbiter.c @@ -6,48 +6,33 @@ #define ADF_ARB_NUM 4 #define ADF_ARB_REG_SIZE 0x4 -#define ADF_ARB_WTR_SIZE 0x20 -#define ADF_ARB_OFFSET 0x30000 -#define ADF_ARB_REG_SLOT 0x1000 -#define ADF_ARB_WTR_OFFSET 0x010 -#define ADF_ARB_RO_EN_OFFSET 0x090 -#define ADF_ARB_WQCFG_OFFSET 0x100 -#define ADF_ARB_WRK_2_SER_MAP_OFFSET 0x180 -#define ADF_ARB_RINGSRVARBEN_OFFSET 0x19C - -#define WRITE_CSR_ARB_RINGSRVARBEN(csr_addr, index, value) \ - ADF_CSR_WR(csr_addr, ADF_ARB_RINGSRVARBEN_OFFSET + \ - (ADF_ARB_REG_SLOT * index), value) - -#define WRITE_CSR_ARB_SARCONFIG(csr_addr, index, value) \ - ADF_CSR_WR(csr_addr, ADF_ARB_OFFSET + \ - (ADF_ARB_REG_SIZE * index), value) - -#define WRITE_CSR_ARB_WRK_2_SER_MAP(csr_addr, index, value) \ - ADF_CSR_WR(csr_addr, (ADF_ARB_OFFSET + \ - ADF_ARB_WRK_2_SER_MAP_OFFSET) + \ - (ADF_ARB_REG_SIZE * index), value) - -#define WRITE_CSR_ARB_WQCFG(csr_addr, index, value) \ - ADF_CSR_WR(csr_addr, (ADF_ARB_OFFSET + \ - ADF_ARB_WQCFG_OFFSET) + (ADF_ARB_REG_SIZE * index), value) + +#define WRITE_CSR_ARB_SARCONFIG(csr_addr, arb_offset, index, value) \ + ADF_CSR_WR(csr_addr, (arb_offset) + \ + (ADF_ARB_REG_SIZE * (index)), value) + +#define WRITE_CSR_ARB_WT2SAM(csr_addr, arb_offset, wt_offset, index, value) \ + ADF_CSR_WR(csr_addr, ((arb_offset) + (wt_offset)) + \ + (ADF_ARB_REG_SIZE * (index)), value) int adf_init_arb(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_data = accel_dev->hw_device; void __iomem *csr = accel_dev->transport->banks[0].csr_addr; - u32 arb_cfg = 0x1 << 31 | 0x4 << 4 | 0x1; - u32 arb, i; + u32 arb_off, wt_off, arb_cfg; const u32 *thd_2_arb_cfg; + struct arb_info info; + int arb, i; + + hw_data->get_arb_info(&info); + arb_cfg = info.arb_cfg; + arb_off = info.arb_offset; + wt_off = info.wt2sam_offset; /* Service arb configured for 32 bytes responses and * ring flow control check enabled. */ for (arb = 0; arb < ADF_ARB_NUM; arb++) - WRITE_CSR_ARB_SARCONFIG(csr, arb, arb_cfg); - - /* Setup worker queue registers */ - for (i = 0; i < hw_data->num_engines; i++) - WRITE_CSR_ARB_WQCFG(csr, i, i); + WRITE_CSR_ARB_SARCONFIG(csr, arb_off, arb, arb_cfg); /* Map worker threads to service arbiters */ hw_data->get_arb_mapping(accel_dev, &thd_2_arb_cfg); @@ -56,7 +41,7 @@ int adf_init_arb(struct adf_accel_dev *accel_dev) return -EFAULT; for (i = 0; i < hw_data->num_engines; i++) - WRITE_CSR_ARB_WRK_2_SER_MAP(csr, i, *(thd_2_arb_cfg + i)); + WRITE_CSR_ARB_WT2SAM(csr, arb_off, wt_off, i, thd_2_arb_cfg[i]); return 0; } @@ -64,36 +49,59 @@ EXPORT_SYMBOL_GPL(adf_init_arb); void adf_update_ring_arb(struct adf_etr_ring_data *ring) { - WRITE_CSR_ARB_RINGSRVARBEN(ring->bank->csr_addr, - ring->bank->bank_number, - ring->bank->ring_mask & 0xFF); + struct adf_accel_dev *accel_dev = ring->bank->accel_dev; + struct adf_hw_device_data *hw_data = accel_dev->hw_device; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); + u32 tx_ring_mask = hw_data->tx_rings_mask; + u32 shift = hw_data->tx_rx_gap; + u32 arben, arben_tx, arben_rx; + u32 rx_ring_mask; + + /* + * Enable arbitration on a ring only if the TX half of the ring mask + * matches the RX part. This results in writes to CSR on both TX and + * RX update - only one is necessary, but both are done for + * simplicity. + */ + rx_ring_mask = tx_ring_mask << shift; + arben_tx = (ring->bank->ring_mask & tx_ring_mask) >> 0; + arben_rx = (ring->bank->ring_mask & rx_ring_mask) >> shift; + arben = arben_tx & arben_rx; + + csr_ops->write_csr_ring_srv_arb_en(ring->bank->csr_addr, + ring->bank->bank_number, arben); } void adf_exit_arb(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_data = accel_dev->hw_device; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); + u32 arb_off, wt_off; + struct arb_info info; void __iomem *csr; unsigned int i; + hw_data->get_arb_info(&info); + arb_off = info.arb_offset; + wt_off = info.wt2sam_offset; + if (!accel_dev->transport) return; csr = accel_dev->transport->banks[0].csr_addr; + hw_data->get_arb_info(&info); + /* Reset arbiter configuration */ for (i = 0; i < ADF_ARB_NUM; i++) - WRITE_CSR_ARB_SARCONFIG(csr, i, 0); - - /* Shutdown work queue */ - for (i = 0; i < hw_data->num_engines; i++) - WRITE_CSR_ARB_WQCFG(csr, i, 0); + WRITE_CSR_ARB_SARCONFIG(csr, arb_off, i, 0); /* Unmap worker threads to service arbiters */ for (i = 0; i < hw_data->num_engines; i++) - WRITE_CSR_ARB_WRK_2_SER_MAP(csr, i, 0); + WRITE_CSR_ARB_WT2SAM(csr, arb_off, wt_off, i, 0); /* Disable arbitration on all rings */ for (i = 0; i < GET_MAX_BANKS(accel_dev); i++) - WRITE_CSR_ARB_RINGSRVARBEN(csr, i, 0); + csr_ops->write_csr_ring_srv_arb_en(csr, i, 0); } EXPORT_SYMBOL_GPL(adf_exit_arb); diff --git a/drivers/crypto/qat/qat_common/adf_isr.c b/drivers/crypto/qat/qat_common/adf_isr.c index 36136f7db509..c45853463530 100644 --- a/drivers/crypto/qat/qat_common/adf_isr.c +++ b/drivers/crypto/qat/qat_common/adf_isr.c @@ -21,6 +21,9 @@ static int adf_enable_msix(struct adf_accel_dev *accel_dev) struct adf_hw_device_data *hw_data = accel_dev->hw_device; u32 msix_num_entries = 1; + if (hw_data->set_msix_rttable) + hw_data->set_msix_rttable(accel_dev); + /* If SR-IOV is disabled, add entries for each bank */ if (!accel_dev->pf.vf_info) { int i; @@ -50,8 +53,10 @@ static void adf_disable_msix(struct adf_accel_pci *pci_dev_info) static irqreturn_t adf_msix_isr_bundle(int irq, void *bank_ptr) { struct adf_etr_bank_data *bank = bank_ptr; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); - WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number, 0); + csr_ops->write_csr_int_flag_and_col(bank->csr_addr, bank->bank_number, + 0); tasklet_hi_schedule(&bank->resp_handler); return IRQ_HANDLED; } diff --git a/drivers/crypto/qat/qat_common/adf_sriov.c b/drivers/crypto/qat/qat_common/adf_sriov.c index 963b2bea78f2..8c822c2861c2 100644 --- a/drivers/crypto/qat/qat_common/adf_sriov.c +++ b/drivers/crypto/qat/qat_common/adf_sriov.c @@ -10,31 +10,6 @@ static struct workqueue_struct *pf2vf_resp_wq; -#define ME2FUNCTION_MAP_A_OFFSET (0x3A400 + 0x190) -#define ME2FUNCTION_MAP_A_NUM_REGS 96 - -#define ME2FUNCTION_MAP_B_OFFSET (0x3A400 + 0x310) -#define ME2FUNCTION_MAP_B_NUM_REGS 12 - -#define ME2FUNCTION_MAP_REG_SIZE 4 -#define ME2FUNCTION_MAP_VALID BIT(7) - -#define READ_CSR_ME2FUNCTION_MAP_A(pmisc_bar_addr, index) \ - ADF_CSR_RD(pmisc_bar_addr, ME2FUNCTION_MAP_A_OFFSET + \ - ME2FUNCTION_MAP_REG_SIZE * index) - -#define WRITE_CSR_ME2FUNCTION_MAP_A(pmisc_bar_addr, index, value) \ - ADF_CSR_WR(pmisc_bar_addr, ME2FUNCTION_MAP_A_OFFSET + \ - ME2FUNCTION_MAP_REG_SIZE * index, value) - -#define READ_CSR_ME2FUNCTION_MAP_B(pmisc_bar_addr, index) \ - ADF_CSR_RD(pmisc_bar_addr, ME2FUNCTION_MAP_B_OFFSET + \ - ME2FUNCTION_MAP_REG_SIZE * index) - -#define WRITE_CSR_ME2FUNCTION_MAP_B(pmisc_bar_addr, index, value) \ - ADF_CSR_WR(pmisc_bar_addr, ME2FUNCTION_MAP_B_OFFSET + \ - ME2FUNCTION_MAP_REG_SIZE * index, value) - struct adf_pf2vf_resp { struct work_struct pf2vf_resp_work; struct adf_accel_vf_info *vf_info; @@ -68,12 +43,8 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) struct pci_dev *pdev = accel_to_pci_dev(accel_dev); int totalvfs = pci_sriov_get_totalvfs(pdev); struct adf_hw_device_data *hw_data = accel_dev->hw_device; - struct adf_bar *pmisc = - &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; - void __iomem *pmisc_addr = pmisc->virt_addr; struct adf_accel_vf_info *vf_info; int i; - u32 reg; for (i = 0, vf_info = accel_dev->pf.vf_info; i < totalvfs; i++, vf_info++) { @@ -90,22 +61,13 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) DEFAULT_RATELIMIT_BURST); } - /* Set Valid bits in ME Thread to PCIe Function Mapping Group A */ - for (i = 0; i < ME2FUNCTION_MAP_A_NUM_REGS; i++) { - reg = READ_CSR_ME2FUNCTION_MAP_A(pmisc_addr, i); - reg |= ME2FUNCTION_MAP_VALID; - WRITE_CSR_ME2FUNCTION_MAP_A(pmisc_addr, i, reg); - } - - /* Set Valid bits in ME Thread to PCIe Function Mapping Group B */ - for (i = 0; i < ME2FUNCTION_MAP_B_NUM_REGS; i++) { - reg = READ_CSR_ME2FUNCTION_MAP_B(pmisc_addr, i); - reg |= ME2FUNCTION_MAP_VALID; - WRITE_CSR_ME2FUNCTION_MAP_B(pmisc_addr, i, reg); - } + /* Set Valid bits in AE Thread to PCIe Function Mapping */ + if (hw_data->configure_iov_threads) + hw_data->configure_iov_threads(accel_dev, true); /* Enable VF to PF interrupts for all VFs */ - adf_enable_vf2pf_interrupts(accel_dev, GENMASK_ULL(totalvfs - 1, 0)); + if (hw_data->get_pf2vf_offset) + adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); /* * Due to the hardware design, when SR-IOV and the ring arbiter @@ -127,37 +89,25 @@ static int adf_enable_sriov(struct adf_accel_dev *accel_dev) void adf_disable_sriov(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_data = accel_dev->hw_device; - struct adf_bar *pmisc = - &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; - void __iomem *pmisc_addr = pmisc->virt_addr; int totalvfs = pci_sriov_get_totalvfs(accel_to_pci_dev(accel_dev)); struct adf_accel_vf_info *vf; - u32 reg; int i; if (!accel_dev->pf.vf_info) return; - adf_pf2vf_notify_restarting(accel_dev); + if (hw_data->get_pf2vf_offset) + adf_pf2vf_notify_restarting(accel_dev); pci_disable_sriov(accel_to_pci_dev(accel_dev)); /* Disable VF to PF interrupts */ - adf_disable_vf2pf_interrupts(accel_dev, 0xFFFFFFFF); + if (hw_data->get_pf2vf_offset) + adf_disable_vf2pf_interrupts(accel_dev, GENMASK(31, 0)); - /* Clear Valid bits in ME Thread to PCIe Function Mapping Group A */ - for (i = 0; i < ME2FUNCTION_MAP_A_NUM_REGS; i++) { - reg = READ_CSR_ME2FUNCTION_MAP_A(pmisc_addr, i); - reg &= ~ME2FUNCTION_MAP_VALID; - WRITE_CSR_ME2FUNCTION_MAP_A(pmisc_addr, i, reg); - } - - /* Clear Valid bits in ME Thread to PCIe Function Mapping Group B */ - for (i = 0; i < ME2FUNCTION_MAP_B_NUM_REGS; i++) { - reg = READ_CSR_ME2FUNCTION_MAP_B(pmisc_addr, i); - reg &= ~ME2FUNCTION_MAP_VALID; - WRITE_CSR_ME2FUNCTION_MAP_B(pmisc_addr, i, reg); - } + /* Clear Valid bits in AE Thread to PCIe Function Mapping */ + if (hw_data->configure_iov_threads) + hw_data->configure_iov_threads(accel_dev, false); for (i = 0, vf = accel_dev->pf.vf_info; i < totalvfs; i++, vf++) { tasklet_disable(&vf->vf2pf_bh_tasklet); @@ -172,13 +122,13 @@ EXPORT_SYMBOL_GPL(adf_disable_sriov); /** * adf_sriov_configure() - Enable SRIOV for the device - * @pdev: Pointer to pci device. + * @pdev: Pointer to PCI device. * @numvfs: Number of virtual functions (VFs) to enable. * * Note that the @numvfs parameter is ignored and all VFs supported by the * device are enabled due to the design of the hardware. * - * Function enables SRIOV for the pci device. + * Function enables SRIOV for the PCI device. * * Return: number of VFs enabled on success, error code otherwise. */ diff --git a/drivers/crypto/qat/qat_common/adf_transport.c b/drivers/crypto/qat/qat_common/adf_transport.c index 2ad774017200..5a7030acdc33 100644 --- a/drivers/crypto/qat/qat_common/adf_transport.c +++ b/drivers/crypto/qat/qat_common/adf_transport.c @@ -54,24 +54,32 @@ static void adf_unreserve_ring(struct adf_etr_bank_data *bank, u32 ring) static void adf_enable_ring_irq(struct adf_etr_bank_data *bank, u32 ring) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); + spin_lock_bh(&bank->lock); bank->irq_mask |= (1 << ring); spin_unlock_bh(&bank->lock); - WRITE_CSR_INT_COL_EN(bank->csr_addr, bank->bank_number, bank->irq_mask); - WRITE_CSR_INT_COL_CTL(bank->csr_addr, bank->bank_number, - bank->irq_coalesc_timer); + csr_ops->write_csr_int_col_en(bank->csr_addr, bank->bank_number, + bank->irq_mask); + csr_ops->write_csr_int_col_ctl(bank->csr_addr, bank->bank_number, + bank->irq_coalesc_timer); } static void adf_disable_ring_irq(struct adf_etr_bank_data *bank, u32 ring) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); + spin_lock_bh(&bank->lock); bank->irq_mask &= ~(1 << ring); spin_unlock_bh(&bank->lock); - WRITE_CSR_INT_COL_EN(bank->csr_addr, bank->bank_number, bank->irq_mask); + csr_ops->write_csr_int_col_en(bank->csr_addr, bank->bank_number, + bank->irq_mask); } int adf_send_message(struct adf_etr_ring_data *ring, u32 *msg) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(ring->bank->accel_dev); + if (atomic_add_return(1, ring->inflights) > ADF_MAX_INFLIGHTS(ring->ring_size, ring->msg_size)) { atomic_dec(ring->inflights); @@ -84,14 +92,17 @@ int adf_send_message(struct adf_etr_ring_data *ring, u32 *msg) ring->tail = adf_modulo(ring->tail + ADF_MSG_SIZE_TO_BYTES(ring->msg_size), ADF_RING_SIZE_MODULO(ring->ring_size)); - WRITE_CSR_RING_TAIL(ring->bank->csr_addr, ring->bank->bank_number, - ring->ring_number, ring->tail); + csr_ops->write_csr_ring_tail(ring->bank->csr_addr, + ring->bank->bank_number, ring->ring_number, + ring->tail); spin_unlock_bh(&ring->lock); + return 0; } static int adf_handle_response(struct adf_etr_ring_data *ring) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(ring->bank->accel_dev); u32 msg_counter = 0; u32 *msg = (u32 *)((uintptr_t)ring->base_addr + ring->head); @@ -105,30 +116,36 @@ static int adf_handle_response(struct adf_etr_ring_data *ring) msg_counter++; msg = (u32 *)((uintptr_t)ring->base_addr + ring->head); } - if (msg_counter > 0) - WRITE_CSR_RING_HEAD(ring->bank->csr_addr, - ring->bank->bank_number, - ring->ring_number, ring->head); + if (msg_counter > 0) { + csr_ops->write_csr_ring_head(ring->bank->csr_addr, + ring->bank->bank_number, + ring->ring_number, ring->head); + } return 0; } static void adf_configure_tx_ring(struct adf_etr_ring_data *ring) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(ring->bank->accel_dev); u32 ring_config = BUILD_RING_CONFIG(ring->ring_size); - WRITE_CSR_RING_CONFIG(ring->bank->csr_addr, ring->bank->bank_number, - ring->ring_number, ring_config); + csr_ops->write_csr_ring_config(ring->bank->csr_addr, + ring->bank->bank_number, + ring->ring_number, ring_config); + } static void adf_configure_rx_ring(struct adf_etr_ring_data *ring) { + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(ring->bank->accel_dev); u32 ring_config = BUILD_RESP_RING_CONFIG(ring->ring_size, ADF_RING_NEAR_WATERMARK_512, ADF_RING_NEAR_WATERMARK_0); - WRITE_CSR_RING_CONFIG(ring->bank->csr_addr, ring->bank->bank_number, - ring->ring_number, ring_config); + csr_ops->write_csr_ring_config(ring->bank->csr_addr, + ring->bank->bank_number, + ring->ring_number, ring_config); } static int adf_init_ring(struct adf_etr_ring_data *ring) @@ -136,6 +153,7 @@ static int adf_init_ring(struct adf_etr_ring_data *ring) struct adf_etr_bank_data *bank = ring->bank; struct adf_accel_dev *accel_dev = bank->accel_dev; struct adf_hw_device_data *hw_data = accel_dev->hw_device; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); u64 ring_base; u32 ring_size_bytes = ADF_SIZE_TO_RING_SIZE_IN_BYTES(ring->ring_size); @@ -162,9 +180,12 @@ static int adf_init_ring(struct adf_etr_ring_data *ring) else adf_configure_rx_ring(ring); - ring_base = BUILD_RING_BASE_ADDR(ring->dma_addr, ring->ring_size); - WRITE_CSR_RING_BASE(ring->bank->csr_addr, ring->bank->bank_number, - ring->ring_number, ring_base); + ring_base = csr_ops->build_csr_ring_base_addr(ring->dma_addr, + ring->ring_size); + + csr_ops->write_csr_ring_base(ring->bank->csr_addr, + ring->bank->bank_number, ring->ring_number, + ring_base); spin_lock_init(&ring->lock); return 0; } @@ -190,6 +211,7 @@ int adf_create_ring(struct adf_accel_dev *accel_dev, const char *section, struct adf_etr_ring_data **ring_ptr) { struct adf_etr_data *transport_data = accel_dev->transport; + u8 num_rings_per_bank = GET_NUM_RINGS_PER_BANK(accel_dev); struct adf_etr_bank_data *bank; struct adf_etr_ring_data *ring; char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES]; @@ -219,7 +241,7 @@ int adf_create_ring(struct adf_accel_dev *accel_dev, const char *section, dev_err(&GET_DEV(accel_dev), "Can't get ring number\n"); return -EFAULT; } - if (ring_num >= ADF_ETR_MAX_RINGS_PER_BANK) { + if (ring_num >= num_rings_per_bank) { dev_err(&GET_DEV(accel_dev), "Invalid ring number\n"); return -EFAULT; } @@ -268,15 +290,17 @@ err: void adf_remove_ring(struct adf_etr_ring_data *ring) { struct adf_etr_bank_data *bank = ring->bank; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); /* Disable interrupts for the given ring */ adf_disable_ring_irq(bank, ring->ring_number); /* Clear PCI config space */ - WRITE_CSR_RING_CONFIG(bank->csr_addr, bank->bank_number, - ring->ring_number, 0); - WRITE_CSR_RING_BASE(bank->csr_addr, bank->bank_number, - ring->ring_number, 0); + + csr_ops->write_csr_ring_config(bank->csr_addr, bank->bank_number, + ring->ring_number, 0); + csr_ops->write_csr_ring_base(bank->csr_addr, bank->bank_number, + ring->ring_number, 0); adf_ring_debugfs_rm(ring); adf_unreserve_ring(bank, ring->ring_number); /* Disable HW arbitration for the given ring */ @@ -286,25 +310,30 @@ void adf_remove_ring(struct adf_etr_ring_data *ring) static void adf_ring_response_handler(struct adf_etr_bank_data *bank) { - u32 empty_rings, i; + struct adf_accel_dev *accel_dev = bank->accel_dev; + u8 num_rings_per_bank = GET_NUM_RINGS_PER_BANK(accel_dev); + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(accel_dev); + unsigned long empty_rings; + int i; - empty_rings = READ_CSR_E_STAT(bank->csr_addr, bank->bank_number); + empty_rings = csr_ops->read_csr_e_stat(bank->csr_addr, + bank->bank_number); empty_rings = ~empty_rings & bank->irq_mask; - for (i = 0; i < ADF_ETR_MAX_RINGS_PER_BANK; ++i) { - if (empty_rings & (1 << i)) - adf_handle_response(&bank->rings[i]); - } + for_each_set_bit(i, &empty_rings, num_rings_per_bank) + adf_handle_response(&bank->rings[i]); } void adf_response_handler(uintptr_t bank_addr) { struct adf_etr_bank_data *bank = (void *)bank_addr; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); /* Handle all the responses and reenable IRQs */ adf_ring_response_handler(bank); - WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number, - bank->irq_mask); + + csr_ops->write_csr_int_flag_and_col(bank->csr_addr, bank->bank_number, + bank->irq_mask); } static inline int adf_get_cfg_int(struct adf_accel_dev *accel_dev, @@ -343,9 +372,14 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, u32 bank_num, void __iomem *csr_addr) { struct adf_hw_device_data *hw_data = accel_dev->hw_device; + u8 num_rings_per_bank = hw_data->num_rings_per_bank; + struct adf_hw_csr_ops *csr_ops = &hw_data->csr_ops; + u32 irq_mask = BIT(num_rings_per_bank) - 1; struct adf_etr_ring_data *ring; struct adf_etr_ring_data *tx_ring; u32 i, coalesc_enabled = 0; + unsigned long ring_mask; + int size; memset(bank, 0, sizeof(*bank)); bank->bank_number = bank_num; @@ -353,6 +387,13 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, bank->accel_dev = accel_dev; spin_lock_init(&bank->lock); + /* Allocate the rings in the bank */ + size = num_rings_per_bank * sizeof(struct adf_etr_ring_data); + bank->rings = kzalloc_node(size, GFP_KERNEL, + dev_to_node(&GET_DEV(accel_dev))); + if (!bank->rings) + return -ENOMEM; + /* Enable IRQ coalescing always. This will allow to use * the optimised flag and coalesc register. * If it is disabled in the config file just use min time value */ @@ -363,9 +404,10 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, else bank->irq_coalesc_timer = ADF_COALESCING_MIN_TIME; - for (i = 0; i < ADF_ETR_MAX_RINGS_PER_BANK; i++) { - WRITE_CSR_RING_CONFIG(csr_addr, bank_num, i, 0); - WRITE_CSR_RING_BASE(csr_addr, bank_num, i, 0); + for (i = 0; i < num_rings_per_bank; i++) { + csr_ops->write_csr_ring_config(csr_addr, bank_num, i, 0); + csr_ops->write_csr_ring_base(csr_addr, bank_num, i, 0); + ring = &bank->rings[i]; if (hw_data->tx_rings_mask & (1 << i)) { ring->inflights = @@ -390,15 +432,18 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, goto err; } - WRITE_CSR_INT_FLAG(csr_addr, bank_num, ADF_BANK_INT_FLAG_CLEAR_MASK); - WRITE_CSR_INT_SRCSEL(csr_addr, bank_num); + csr_ops->write_csr_int_flag(csr_addr, bank_num, irq_mask); + csr_ops->write_csr_int_srcsel(csr_addr, bank_num); + return 0; err: - for (i = 0; i < ADF_ETR_MAX_RINGS_PER_BANK; i++) { + ring_mask = hw_data->tx_rings_mask; + for_each_set_bit(i, &ring_mask, num_rings_per_bank) { ring = &bank->rings[i]; - if (hw_data->tx_rings_mask & (1 << i)) - kfree(ring->inflights); + kfree(ring->inflights); + ring->inflights = NULL; } + kfree(bank->rings); return -ENOMEM; } @@ -464,11 +509,12 @@ EXPORT_SYMBOL_GPL(adf_init_etr_data); static void cleanup_bank(struct adf_etr_bank_data *bank) { + struct adf_accel_dev *accel_dev = bank->accel_dev; + struct adf_hw_device_data *hw_data = accel_dev->hw_device; + u8 num_rings_per_bank = hw_data->num_rings_per_bank; u32 i; - for (i = 0; i < ADF_ETR_MAX_RINGS_PER_BANK; i++) { - struct adf_accel_dev *accel_dev = bank->accel_dev; - struct adf_hw_device_data *hw_data = accel_dev->hw_device; + for (i = 0; i < num_rings_per_bank; i++) { struct adf_etr_ring_data *ring = &bank->rings[i]; if (bank->ring_mask & (1 << i)) @@ -477,6 +523,7 @@ static void cleanup_bank(struct adf_etr_bank_data *bank) if (hw_data->tx_rings_mask & (1 << i)) kfree(ring->inflights); } + kfree(bank->rings); adf_bank_debugfs_rm(bank); memset(bank, 0, sizeof(*bank)); } @@ -507,6 +554,7 @@ void adf_cleanup_etr_data(struct adf_accel_dev *accel_dev) if (etr_data) { adf_cleanup_etr_handles(accel_dev); debugfs_remove(etr_data->debug); + kfree(etr_data->banks->rings); kfree(etr_data->banks); kfree(etr_data); accel_dev->transport = NULL; diff --git a/drivers/crypto/qat/qat_common/adf_transport_access_macros.h b/drivers/crypto/qat/qat_common/adf_transport_access_macros.h index 950d1988556c..3b6b0267bbec 100644 --- a/drivers/crypto/qat/qat_common/adf_transport_access_macros.h +++ b/drivers/crypto/qat/qat_common/adf_transport_access_macros.h @@ -4,23 +4,6 @@ #define ADF_TRANSPORT_ACCESS_MACROS_H #include "adf_accel_devices.h" -#define ADF_BANK_INT_SRC_SEL_MASK_0 0x4444444CUL -#define ADF_BANK_INT_SRC_SEL_MASK_X 0x44444444UL -#define ADF_BANK_INT_FLAG_CLEAR_MASK 0xFFFF -#define ADF_RING_CSR_RING_CONFIG 0x000 -#define ADF_RING_CSR_RING_LBASE 0x040 -#define ADF_RING_CSR_RING_UBASE 0x080 -#define ADF_RING_CSR_RING_HEAD 0x0C0 -#define ADF_RING_CSR_RING_TAIL 0x100 -#define ADF_RING_CSR_E_STAT 0x14C -#define ADF_RING_CSR_INT_FLAG 0x170 -#define ADF_RING_CSR_INT_SRCSEL 0x174 -#define ADF_RING_CSR_INT_SRCSEL_2 0x178 -#define ADF_RING_CSR_INT_COL_EN 0x17C -#define ADF_RING_CSR_INT_COL_CTL 0x180 -#define ADF_RING_CSR_INT_FLAG_AND_COL 0x184 -#define ADF_RING_CSR_INT_COL_CTL_ENABLE 0x80000000 -#define ADF_RING_BUNDLE_SIZE 0x1000 #define ADF_RING_CONFIG_NEAR_FULL_WM 0x0A #define ADF_RING_CONFIG_NEAR_EMPTY_WM 0x05 #define ADF_COALESCING_MIN_TIME 0x1FF @@ -72,54 +55,4 @@ ((watermark_nf << ADF_RING_CONFIG_NEAR_FULL_WM) \ | (watermark_ne << ADF_RING_CONFIG_NEAR_EMPTY_WM) \ | size) -#define BUILD_RING_BASE_ADDR(addr, size) \ - ((addr >> 6) & (0xFFFFFFFFFFFFFFFFULL << size)) -#define READ_CSR_RING_HEAD(csr_base_addr, bank, ring) \ - ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_HEAD + (ring << 2)) -#define READ_CSR_RING_TAIL(csr_base_addr, bank, ring) \ - ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_TAIL + (ring << 2)) -#define READ_CSR_E_STAT(csr_base_addr, bank) \ - ADF_CSR_RD(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_E_STAT) -#define WRITE_CSR_RING_CONFIG(csr_base_addr, bank, ring, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_CONFIG + (ring << 2), value) -#define WRITE_CSR_RING_BASE(csr_base_addr, bank, ring, value) \ -do { \ - u32 l_base = 0, u_base = 0; \ - l_base = (u32)(value & 0xFFFFFFFF); \ - u_base = (u32)((value & 0xFFFFFFFF00000000ULL) >> 32); \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_LBASE + (ring << 2), l_base); \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_UBASE + (ring << 2), u_base); \ -} while (0) -#define WRITE_CSR_RING_HEAD(csr_base_addr, bank, ring, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_HEAD + (ring << 2), value) -#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_RING_TAIL + (ring << 2), value) -#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \ - ADF_RING_CSR_INT_FLAG, value) -#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \ -do { \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_INT_SRCSEL, ADF_BANK_INT_SRC_SEL_MASK_0); \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_INT_SRCSEL_2, ADF_BANK_INT_SRC_SEL_MASK_X); \ -} while (0) -#define WRITE_CSR_INT_COL_EN(csr_base_addr, bank, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_INT_COL_EN, value) -#define WRITE_CSR_INT_COL_CTL(csr_base_addr, bank, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_INT_COL_CTL, \ - ADF_RING_CSR_INT_COL_CTL_ENABLE | value) -#define WRITE_CSR_INT_FLAG_AND_COL(csr_base_addr, bank, value) \ - ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \ - ADF_RING_CSR_INT_FLAG_AND_COL, value) #endif diff --git a/drivers/crypto/qat/qat_common/adf_transport_debug.c b/drivers/crypto/qat/qat_common/adf_transport_debug.c index dac25ba47260..1205186ad51e 100644 --- a/drivers/crypto/qat/qat_common/adf_transport_debug.c +++ b/drivers/crypto/qat/qat_common/adf_transport_debug.c @@ -42,16 +42,17 @@ static int adf_ring_show(struct seq_file *sfile, void *v) { struct adf_etr_ring_data *ring = sfile->private; struct adf_etr_bank_data *bank = ring->bank; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); void __iomem *csr = ring->bank->csr_addr; if (v == SEQ_START_TOKEN) { int head, tail, empty; - head = READ_CSR_RING_HEAD(csr, bank->bank_number, - ring->ring_number); - tail = READ_CSR_RING_TAIL(csr, bank->bank_number, - ring->ring_number); - empty = READ_CSR_E_STAT(csr, bank->bank_number); + head = csr_ops->read_csr_ring_head(csr, bank->bank_number, + ring->ring_number); + tail = csr_ops->read_csr_ring_tail(csr, bank->bank_number, + ring->ring_number); + empty = csr_ops->read_csr_e_stat(csr, bank->bank_number); seq_puts(sfile, "------- Ring configuration -------\n"); seq_printf(sfile, "ring name: %s\n", @@ -117,11 +118,14 @@ void adf_ring_debugfs_rm(struct adf_etr_ring_data *ring) static void *adf_bank_start(struct seq_file *sfile, loff_t *pos) { + struct adf_etr_bank_data *bank = sfile->private; + u8 num_rings_per_bank = GET_NUM_RINGS_PER_BANK(bank->accel_dev); + mutex_lock(&bank_read_lock); if (*pos == 0) return SEQ_START_TOKEN; - if (*pos >= ADF_ETR_MAX_RINGS_PER_BANK) + if (*pos >= num_rings_per_bank) return NULL; return pos; @@ -129,7 +133,10 @@ static void *adf_bank_start(struct seq_file *sfile, loff_t *pos) static void *adf_bank_next(struct seq_file *sfile, void *v, loff_t *pos) { - if (++(*pos) >= ADF_ETR_MAX_RINGS_PER_BANK) + struct adf_etr_bank_data *bank = sfile->private; + u8 num_rings_per_bank = GET_NUM_RINGS_PER_BANK(bank->accel_dev); + + if (++(*pos) >= num_rings_per_bank) return NULL; return pos; @@ -138,6 +145,7 @@ static void *adf_bank_next(struct seq_file *sfile, void *v, loff_t *pos) static int adf_bank_show(struct seq_file *sfile, void *v) { struct adf_etr_bank_data *bank = sfile->private; + struct adf_hw_csr_ops *csr_ops = GET_CSR_OPS(bank->accel_dev); if (v == SEQ_START_TOKEN) { seq_printf(sfile, "------- Bank %d configuration -------\n", @@ -151,11 +159,11 @@ static int adf_bank_show(struct seq_file *sfile, void *v) if (!(bank->ring_mask & 1 << ring_id)) return 0; - head = READ_CSR_RING_HEAD(csr, bank->bank_number, - ring->ring_number); - tail = READ_CSR_RING_TAIL(csr, bank->bank_number, - ring->ring_number); - empty = READ_CSR_E_STAT(csr, bank->bank_number); + head = csr_ops->read_csr_ring_head(csr, bank->bank_number, + ring->ring_number); + tail = csr_ops->read_csr_ring_tail(csr, bank->bank_number, + ring->ring_number); + empty = csr_ops->read_csr_e_stat(csr, bank->bank_number); seq_printf(sfile, "ring num %02d, head %04x, tail %04x, empty: %d\n", diff --git a/drivers/crypto/qat/qat_common/adf_transport_internal.h b/drivers/crypto/qat/qat_common/adf_transport_internal.h index c7faf4e2d302..501bcf0f1809 100644 --- a/drivers/crypto/qat/qat_common/adf_transport_internal.h +++ b/drivers/crypto/qat/qat_common/adf_transport_internal.h @@ -28,7 +28,7 @@ struct adf_etr_ring_data { }; struct adf_etr_bank_data { - struct adf_etr_ring_data rings[ADF_ETR_MAX_RINGS_PER_BANK]; + struct adf_etr_ring_data *rings; struct tasklet_struct resp_handler; void __iomem *csr_addr; u32 irq_coalesc_timer; diff --git a/drivers/crypto/qat/qat_common/adf_vf_isr.c b/drivers/crypto/qat/qat_common/adf_vf_isr.c index c4a44dc6af3e..38d316a42ba6 100644 --- a/drivers/crypto/qat/qat_common/adf_vf_isr.c +++ b/drivers/crypto/qat/qat_common/adf_vf_isr.c @@ -156,6 +156,7 @@ static irqreturn_t adf_isr(int irq, void *privdata) { struct adf_accel_dev *accel_dev = privdata; struct adf_hw_device_data *hw_data = accel_dev->hw_device; + struct adf_hw_csr_ops *csr_ops = &hw_data->csr_ops; struct adf_bar *pmisc = &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; void __iomem *pmisc_bar_addr = pmisc->virt_addr; @@ -180,8 +181,8 @@ static irqreturn_t adf_isr(int irq, void *privdata) struct adf_etr_bank_data *bank = &etr_data->banks[0]; /* Disable Flag and Coalesce Ring Interrupts */ - WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number, - 0); + csr_ops->write_csr_int_flag_and_col(bank->csr_addr, + bank->bank_number, 0); tasklet_hi_schedule(&bank->resp_handler); return IRQ_HANDLED; } diff --git a/drivers/crypto/qat/qat_common/icp_qat_fw_init_admin.h b/drivers/crypto/qat/qat_common/icp_qat_fw_init_admin.h index d4d188cd7ed0..f05ad17fbdd6 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_fw_init_admin.h +++ b/drivers/crypto/qat/qat_common/icp_qat_fw_init_admin.h @@ -6,7 +6,7 @@ #include "icp_qat_fw.h" enum icp_qat_fw_init_admin_cmd_id { - ICP_QAT_FW_INIT_ME = 0, + ICP_QAT_FW_INIT_AE = 0, ICP_QAT_FW_TRNG_ENABLE = 1, ICP_QAT_FW_TRNG_DISABLE = 2, ICP_QAT_FW_CONSTANTS_CFG = 3, @@ -39,7 +39,7 @@ struct icp_qat_fw_init_admin_req { }; __u32 resrvd4; -}; +} __packed; struct icp_qat_fw_init_admin_resp { __u8 flags; @@ -92,7 +92,7 @@ struct icp_qat_fw_init_admin_resp { __u64 resrvd8; }; }; -}; +} __packed; #define ICP_QAT_FW_COMN_HEARTBEAT_OK 0 #define ICP_QAT_FW_COMN_HEARTBEAT_BLOCKED 1 diff --git a/drivers/crypto/qat/qat_common/icp_qat_fw_la.h b/drivers/crypto/qat/qat_common/icp_qat_fw_la.h index 6757ec09d81f..28fa17f14be4 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_fw_la.h +++ b/drivers/crypto/qat/qat_common/icp_qat_fw_la.h @@ -33,6 +33,9 @@ struct icp_qat_fw_la_bulk_req { struct icp_qat_fw_comn_req_cd_ctrl cd_ctrl; }; +#define ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE 1 +#define QAT_LA_SLICE_TYPE_BITPOS 14 +#define QAT_LA_SLICE_TYPE_MASK 0x3 #define ICP_QAT_FW_LA_GCM_IV_LEN_12_OCTETS 1 #define ICP_QAT_FW_LA_GCM_IV_LEN_NOT_12_OCTETS 0 #define QAT_FW_LA_ZUC_3G_PROTO_FLAG_BITPOS 12 @@ -179,6 +182,10 @@ struct icp_qat_fw_la_bulk_req { QAT_FIELD_SET(flags, val, QAT_LA_PARTIAL_BITPOS, \ QAT_LA_PARTIAL_MASK) +#define ICP_QAT_FW_LA_SLICE_TYPE_SET(flags, val) \ + QAT_FIELD_SET(flags, val, QAT_LA_SLICE_TYPE_BITPOS, \ + QAT_LA_SLICE_TYPE_MASK) + struct icp_qat_fw_cipher_req_hdr_cd_pars { union { struct { diff --git a/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h b/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h index 3e8e291cd122..b8f3463be6ef 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h +++ b/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h @@ -15,6 +15,7 @@ struct icp_qat_fw_loader_ae_data { struct icp_qat_fw_loader_hal_handle { struct icp_qat_fw_loader_ae_data aes[ICP_QAT_UCLO_MAX_AE]; unsigned int ae_mask; + unsigned int admin_ae_mask; unsigned int slice_mask; unsigned int revision_id; unsigned int ae_max_num; @@ -22,12 +23,35 @@ struct icp_qat_fw_loader_hal_handle { unsigned int max_ustore; }; +struct icp_qat_fw_loader_chip_info { + bool sram_visible; + bool nn; + bool lm2lm3; + u32 lm_size; + u32 icp_rst_csr; + u32 icp_rst_mask; + u32 glb_clk_enable_csr; + u32 misc_ctl_csr; + u32 wakeup_event_val; + bool fw_auth; + bool css_3k; + bool tgroup_share_ustore; + u32 fcu_ctl_csr; + u32 fcu_sts_csr; + u32 fcu_dram_addr_hi; + u32 fcu_dram_addr_lo; + u32 fcu_loaded_ae_csr; + u8 fcu_loaded_ae_pos; +}; + struct icp_qat_fw_loader_handle { struct icp_qat_fw_loader_hal_handle *hal_handle; + struct icp_qat_fw_loader_chip_info *chip_info; struct pci_dev *pci_dev; void *obj_handle; void *sobj_handle; - bool fw_auth; + void *mobj_handle; + unsigned int cfg_ae_mask; void __iomem *hal_sram_addr_v; void __iomem *hal_cap_g_ctl_csr_addr_v; void __iomem *hal_cap_ae_xfer_csr_addr_v; diff --git a/drivers/crypto/qat/qat_common/icp_qat_hal.h b/drivers/crypto/qat/qat_common/icp_qat_hal.h index c0e9fc0c93dd..20b2ee1fc65a 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_hal.h +++ b/drivers/crypto/qat/qat_common/icp_qat_hal.h @@ -5,9 +5,17 @@ #include "icp_qat_fw_loader_handle.h" enum hal_global_csr { - MISC_CONTROL = 0x04, - ICP_RESET = 0x0c, - ICP_GLOBAL_CLK_ENABLE = 0x50 + MISC_CONTROL = 0xA04, + ICP_RESET = 0xA0c, + ICP_GLOBAL_CLK_ENABLE = 0xA50 +}; + +enum { + MISC_CONTROL_C4XXX = 0xAA0, + ICP_RESET_CPP0 = 0x938, + ICP_RESET_CPP1 = 0x93c, + ICP_GLOBAL_CLK_ENABLE_CPP0 = 0x964, + ICP_GLOBAL_CLK_ENABLE_CPP1 = 0x968 }; enum hal_ae_csr { @@ -26,8 +34,14 @@ enum hal_ae_csr { CTX_WAKEUP_EVENTS_INDIRECT = 0x050, LM_ADDR_0_INDIRECT = 0x060, LM_ADDR_1_INDIRECT = 0x068, + LM_ADDR_2_INDIRECT = 0x0cc, + LM_ADDR_3_INDIRECT = 0x0d4, INDIRECT_LM_ADDR_0_BYTE_INDEX = 0x0e0, INDIRECT_LM_ADDR_1_BYTE_INDEX = 0x0e8, + INDIRECT_LM_ADDR_2_BYTE_INDEX = 0x10c, + INDIRECT_LM_ADDR_3_BYTE_INDEX = 0x114, + INDIRECT_T_INDEX = 0x0f8, + INDIRECT_T_INDEX_BYTE_INDEX = 0x0fc, FUTURE_COUNT_SIGNAL_INDIRECT = 0x078, TIMESTAMP_LOW = 0x0c0, TIMESTAMP_HIGH = 0x0c4, @@ -47,6 +61,15 @@ enum fcu_csr { FCU_RAMBASE_ADDR_LO = 0x8d8 }; +enum fcu_csr_4xxx { + FCU_CONTROL_4XXX = 0x1000, + FCU_STATUS_4XXX = 0x1004, + FCU_ME_BROADCAST_MASK_TYPE = 0x1008, + FCU_AE_LOADED_4XXX = 0x1010, + FCU_DRAM_ADDR_LO_4XXX = 0x1014, + FCU_DRAM_ADDR_HI_4XXX = 0x1018, +}; + enum fcu_cmd { FCU_CTRL_CMD_NOOP = 0, FCU_CTRL_CMD_AUTH = 1, @@ -62,12 +85,17 @@ enum fcu_sts { FCU_STS_LOAD_FAIL = 4, FCU_STS_BUSY = 5 }; + +#define ALL_AE_MASK 0xFFFFFFFF #define UA_ECS (0x1 << 31) #define ACS_ABO_BITPOS 31 #define ACS_ACNO 0x7 #define CE_ENABLE_BITPOS 0x8 #define CE_LMADDR_0_GLOBAL_BITPOS 16 #define CE_LMADDR_1_GLOBAL_BITPOS 17 +#define CE_LMADDR_2_GLOBAL_BITPOS 22 +#define CE_LMADDR_3_GLOBAL_BITPOS 23 +#define CE_T_INDEX_GLOBAL_BITPOS 21 #define CE_NN_MODE_BITPOS 20 #define CE_REG_PAR_ERR_BITPOS 25 #define CE_BREAKPOINT_BITPOS 27 @@ -78,7 +106,8 @@ enum fcu_sts { #define XCWE_VOLUNTARY (0x1) #define LCS_STATUS (0x1) #define MMC_SHARE_CS_BITPOS 2 -#define GLOBAL_CSR 0xA00 +#define WAKEUP_EVENT 0x10000 +#define FCU_CTRL_BROADCAST_POS 0x4 #define FCU_CTRL_AE_POS 0x8 #define FCU_AUTH_STS_MASK 0x7 #define FCU_STS_DONE_POS 0x9 @@ -86,27 +115,29 @@ enum fcu_sts { #define FCU_LOADED_AE_POS 0x16 #define FW_AUTH_WAIT_PERIOD 10 #define FW_AUTH_MAX_RETRY 300 - +#define ICP_QAT_AE_OFFSET 0x20000 +#define ICP_QAT_CAP_OFFSET (ICP_QAT_AE_OFFSET + 0x10000) +#define LOCAL_TO_XFER_REG_OFFSET 0x800 +#define ICP_QAT_EP_OFFSET 0x3a000 +#define ICP_QAT_EP_OFFSET_4XXX 0x200000 /* HI MMIO CSRs */ +#define ICP_QAT_AE_OFFSET_4XXX 0x600000 +#define ICP_QAT_CAP_OFFSET_4XXX 0x640000 #define SET_CAP_CSR(handle, csr, val) \ - ADF_CSR_WR(handle->hal_cap_g_ctl_csr_addr_v, csr, val) + ADF_CSR_WR((handle)->hal_cap_g_ctl_csr_addr_v, csr, val) #define GET_CAP_CSR(handle, csr) \ - ADF_CSR_RD(handle->hal_cap_g_ctl_csr_addr_v, csr) -#define SET_GLB_CSR(handle, csr, val) SET_CAP_CSR(handle, csr + GLOBAL_CSR, val) -#define GET_GLB_CSR(handle, csr) GET_CAP_CSR(handle, GLOBAL_CSR + csr) + ADF_CSR_RD((handle)->hal_cap_g_ctl_csr_addr_v, csr) #define AE_CSR(handle, ae) \ - ((char __iomem *)handle->hal_cap_ae_local_csr_addr_v + \ - ((ae & handle->hal_handle->ae_mask) << 12)) -#define AE_CSR_ADDR(handle, ae, csr) (AE_CSR(handle, ae) + (0x3ff & csr)) + ((char __iomem *)(handle)->hal_cap_ae_local_csr_addr_v + ((ae) << 12)) +#define AE_CSR_ADDR(handle, ae, csr) (AE_CSR(handle, ae) + (0x3ff & (csr))) #define SET_AE_CSR(handle, ae, csr, val) \ ADF_CSR_WR(AE_CSR_ADDR(handle, ae, csr), 0, val) #define GET_AE_CSR(handle, ae, csr) ADF_CSR_RD(AE_CSR_ADDR(handle, ae, csr), 0) #define AE_XFER(handle, ae) \ - ((char __iomem *)handle->hal_cap_ae_xfer_csr_addr_v + \ - ((ae & handle->hal_handle->ae_mask) << 12)) + ((char __iomem *)(handle)->hal_cap_ae_xfer_csr_addr_v + ((ae) << 12)) #define AE_XFER_ADDR(handle, ae, reg) (AE_XFER(handle, ae) + \ - ((reg & 0xff) << 2)) + (((reg) & 0xff) << 2)) #define SET_AE_XFER(handle, ae, reg, val) \ ADF_CSR_WR(AE_XFER_ADDR(handle, ae, reg), 0, val) #define SRAM_WRITE(handle, addr, val) \ - ADF_CSR_WR(handle->hal_sram_addr_v, addr, val) + ADF_CSR_WR((handle)->hal_sram_addr_v, addr, val) #endif diff --git a/drivers/crypto/qat/qat_common/icp_qat_hw.h b/drivers/crypto/qat/qat_common/icp_qat_hw.h index c4b6ef1506ab..e39e8a2d51a7 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_hw.h +++ b/drivers/crypto/qat/qat_common/icp_qat_hw.h @@ -65,6 +65,36 @@ struct icp_qat_hw_auth_config { __u32 reserved; }; +struct icp_qat_hw_ucs_cipher_config { + __u32 val; + __u32 reserved[3]; +}; + +enum icp_qat_slice_mask { + ICP_ACCEL_MASK_CIPHER_SLICE = BIT(0), + ICP_ACCEL_MASK_AUTH_SLICE = BIT(1), + ICP_ACCEL_MASK_PKE_SLICE = BIT(2), + ICP_ACCEL_MASK_COMPRESS_SLICE = BIT(3), + ICP_ACCEL_MASK_LZS_SLICE = BIT(4), + ICP_ACCEL_MASK_EIA3_SLICE = BIT(5), + ICP_ACCEL_MASK_SHA3_SLICE = BIT(6), +}; + +enum icp_qat_capabilities_mask { + ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC = BIT(0), + ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC = BIT(1), + ICP_ACCEL_CAPABILITIES_CIPHER = BIT(2), + ICP_ACCEL_CAPABILITIES_AUTHENTICATION = BIT(3), + ICP_ACCEL_CAPABILITIES_RESERVED_1 = BIT(4), + ICP_ACCEL_CAPABILITIES_COMPRESSION = BIT(5), + ICP_ACCEL_CAPABILITIES_LZS_COMPRESSION = BIT(6), + ICP_ACCEL_CAPABILITIES_RAND = BIT(7), + ICP_ACCEL_CAPABILITIES_ZUC = BIT(8), + ICP_ACCEL_CAPABILITIES_SHA3 = BIT(9), + /* Bits 10-25 are currently reserved */ + ICP_ACCEL_CAPABILITIES_AES_V2 = BIT(26) +}; + #define QAT_AUTH_MODE_BITPOS 4 #define QAT_AUTH_MODE_MASK 0xF #define QAT_AUTH_ALGO_BITPOS 0 @@ -255,7 +285,15 @@ struct icp_qat_hw_cipher_aes256_f8 { __u8 key[ICP_QAT_HW_AES_256_F8_KEY_SZ]; }; +struct icp_qat_hw_ucs_cipher_aes256_f8 { + struct icp_qat_hw_ucs_cipher_config cipher_config; + __u8 key[ICP_QAT_HW_AES_256_F8_KEY_SZ]; +}; + struct icp_qat_hw_cipher_algo_blk { - struct icp_qat_hw_cipher_aes256_f8 aes; + union { + struct icp_qat_hw_cipher_aes256_f8 aes; + struct icp_qat_hw_ucs_cipher_aes256_f8 ucs_aes; + }; } __aligned(64); #endif diff --git a/drivers/crypto/qat/qat_common/icp_qat_uclo.h b/drivers/crypto/qat/qat_common/icp_qat_uclo.h index 8fe1ec344fa2..4b36869bf460 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_uclo.h +++ b/drivers/crypto/qat/qat_common/icp_qat_uclo.h @@ -6,6 +6,7 @@ #define ICP_QAT_AC_895XCC_DEV_TYPE 0x00400000 #define ICP_QAT_AC_C62X_DEV_TYPE 0x01000000 #define ICP_QAT_AC_C3XXX_DEV_TYPE 0x02000000 +#define ICP_QAT_AC_4XXX_A_DEV_TYPE 0x08000000 #define ICP_QAT_UCLO_MAX_AE 12 #define ICP_QAT_UCLO_MAX_CTX 8 #define ICP_QAT_UCLO_MAX_UIMAGE (ICP_QAT_UCLO_MAX_AE * ICP_QAT_UCLO_MAX_CTX) @@ -13,6 +14,7 @@ #define ICP_QAT_UCLO_MAX_XFER_REG 128 #define ICP_QAT_UCLO_MAX_GPR_REG 128 #define ICP_QAT_UCLO_MAX_LMEM_REG 1024 +#define ICP_QAT_UCLO_MAX_LMEM_REG_2X 1280 #define ICP_QAT_UCLO_AE_ALL_CTX 0xff #define ICP_QAT_UOF_OBJID_LEN 8 #define ICP_QAT_UOF_FID 0xc6c2 @@ -31,26 +33,59 @@ #define ICP_QAT_SUOF_FID 0x53554f46 #define ICP_QAT_SUOF_MAJVER 0x0 #define ICP_QAT_SUOF_MINVER 0x1 +#define ICP_QAT_SUOF_OBJ_NAME_LEN 128 +#define ICP_QAT_MOF_OBJ_ID_LEN 8 +#define ICP_QAT_MOF_OBJ_CHUNKID_LEN 8 +#define ICP_QAT_MOF_FID 0x00666f6d +#define ICP_QAT_MOF_MAJVER 0x0 +#define ICP_QAT_MOF_MINVER 0x1 +#define ICP_QAT_MOF_SYM_OBJS "SYM_OBJS" +#define ICP_QAT_SUOF_OBJS "SUF_OBJS" +#define ICP_QAT_SUOF_IMAG "SUF_IMAG" #define ICP_QAT_SIMG_AE_INIT_SEQ_LEN (50 * sizeof(unsigned long long)) #define ICP_QAT_SIMG_AE_INSTS_LEN (0x4000 * sizeof(unsigned long long)) -#define ICP_QAT_CSS_FWSK_MODULUS_LEN 256 -#define ICP_QAT_CSS_FWSK_EXPONENT_LEN 4 -#define ICP_QAT_CSS_FWSK_PAD_LEN 252 -#define ICP_QAT_CSS_FWSK_PUB_LEN (ICP_QAT_CSS_FWSK_MODULUS_LEN + \ - ICP_QAT_CSS_FWSK_EXPONENT_LEN + \ - ICP_QAT_CSS_FWSK_PAD_LEN) -#define ICP_QAT_CSS_SIGNATURE_LEN 256 + +#define DSS_FWSK_MODULUS_LEN 384 /* RSA3K */ +#define DSS_FWSK_EXPONENT_LEN 4 +#define DSS_FWSK_PADDING_LEN 380 +#define DSS_SIGNATURE_LEN 384 /* RSA3K */ + +#define CSS_FWSK_MODULUS_LEN 256 /* RSA2K */ +#define CSS_FWSK_EXPONENT_LEN 4 +#define CSS_FWSK_PADDING_LEN 252 +#define CSS_SIGNATURE_LEN 256 /* RSA2K */ + +#define ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) ((handle)->chip_info->css_3k ? \ + DSS_FWSK_MODULUS_LEN : \ + CSS_FWSK_MODULUS_LEN) + +#define ICP_QAT_CSS_FWSK_EXPONENT_LEN(handle) ((handle)->chip_info->css_3k ? \ + DSS_FWSK_EXPONENT_LEN : \ + CSS_FWSK_EXPONENT_LEN) + +#define ICP_QAT_CSS_FWSK_PAD_LEN(handle) ((handle)->chip_info->css_3k ? \ + DSS_FWSK_PADDING_LEN : \ + CSS_FWSK_PADDING_LEN) + +#define ICP_QAT_CSS_FWSK_PUB_LEN(handle) (ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) + \ + ICP_QAT_CSS_FWSK_EXPONENT_LEN(handle) + \ + ICP_QAT_CSS_FWSK_PAD_LEN(handle)) + +#define ICP_QAT_CSS_SIGNATURE_LEN(handle) ((handle)->chip_info->css_3k ? \ + DSS_SIGNATURE_LEN : \ + CSS_SIGNATURE_LEN) + #define ICP_QAT_CSS_AE_IMG_LEN (sizeof(struct icp_qat_simg_ae_mode) + \ ICP_QAT_SIMG_AE_INIT_SEQ_LEN + \ ICP_QAT_SIMG_AE_INSTS_LEN) -#define ICP_QAT_CSS_AE_SIMG_LEN (sizeof(struct icp_qat_css_hdr) + \ - ICP_QAT_CSS_FWSK_PUB_LEN + \ - ICP_QAT_CSS_SIGNATURE_LEN + \ - ICP_QAT_CSS_AE_IMG_LEN) -#define ICP_QAT_AE_IMG_OFFSET (sizeof(struct icp_qat_css_hdr) + \ - ICP_QAT_CSS_FWSK_MODULUS_LEN + \ - ICP_QAT_CSS_FWSK_EXPONENT_LEN + \ - ICP_QAT_CSS_SIGNATURE_LEN) +#define ICP_QAT_CSS_AE_SIMG_LEN(handle) (sizeof(struct icp_qat_css_hdr) + \ + ICP_QAT_CSS_FWSK_PUB_LEN(handle) + \ + ICP_QAT_CSS_SIGNATURE_LEN(handle) + \ + ICP_QAT_CSS_AE_IMG_LEN) +#define ICP_QAT_AE_IMG_OFFSET(handle) (sizeof(struct icp_qat_css_hdr) + \ + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) + \ + ICP_QAT_CSS_FWSK_EXPONENT_LEN(handle) + \ + ICP_QAT_CSS_SIGNATURE_LEN(handle)) #define ICP_QAT_CSS_MAX_IMAGE_LEN 0x40000 #define ICP_QAT_CTX_MODE(ae_mode) ((ae_mode) & 0xf) @@ -60,6 +95,9 @@ #define ICP_QAT_LOC_MEM0_MODE(ae_mode) (((ae_mode) >> 0x8) & 0x1) #define ICP_QAT_LOC_MEM1_MODE(ae_mode) (((ae_mode) >> 0x9) & 0x1) +#define ICP_QAT_LOC_MEM2_MODE(ae_mode) (((ae_mode) >> 0x6) & 0x1) +#define ICP_QAT_LOC_MEM3_MODE(ae_mode) (((ae_mode) >> 0x7) & 0x1) +#define ICP_QAT_LOC_TINDEX_MODE(ae_mode) (((ae_mode) >> 0xe) & 0x1) enum icp_qat_uof_mem_region { ICP_QAT_UOF_SRAM_REGION = 0x0, @@ -89,6 +127,8 @@ enum icp_qat_uof_regtype { ICP_LMEM0 = 27, ICP_LMEM1 = 28, ICP_NEIGH_REL = 31, + ICP_LMEM2 = 61, + ICP_LMEM3 = 62, }; enum icp_qat_css_fwtype { @@ -394,7 +434,7 @@ struct icp_qat_suof_handle { struct icp_qat_fw_auth_desc { unsigned int img_len; - unsigned int reserved; + unsigned int ae_mask; unsigned int css_hdr_high; unsigned int css_hdr_low; unsigned int img_high; @@ -481,4 +521,64 @@ struct icp_qat_suof_objhdr { unsigned int img_length; unsigned int reserved; }; + +struct icp_qat_mof_file_hdr { + unsigned int file_id; + unsigned int checksum; + char min_ver; + char maj_ver; + unsigned short reserved; + unsigned short max_chunks; + unsigned short num_chunks; +}; + +struct icp_qat_mof_chunkhdr { + char chunk_id[ICP_QAT_MOF_OBJ_ID_LEN]; + u64 offset; + u64 size; +}; + +struct icp_qat_mof_str_table { + unsigned int tab_len; + unsigned int strings; +}; + +struct icp_qat_mof_obj_hdr { + unsigned short max_chunks; + unsigned short num_chunks; + unsigned int reserved; +}; + +struct icp_qat_mof_obj_chunkhdr { + char chunk_id[ICP_QAT_MOF_OBJ_CHUNKID_LEN]; + u64 offset; + u64 size; + unsigned int name; + unsigned int reserved; +}; + +struct icp_qat_mof_objhdr { + char *obj_name; + char *obj_buf; + unsigned int obj_size; +}; + +struct icp_qat_mof_table { + unsigned int num_objs; + struct icp_qat_mof_objhdr *obj_hdr; +}; + +struct icp_qat_mof_handle { + unsigned int file_id; + unsigned int checksum; + char min_ver; + char maj_ver; + char *mof_buf; + u32 mof_size; + char *sym_str; + unsigned int sym_size; + char *uobjs_hdr; + char *sobjs_hdr; + struct icp_qat_mof_table obj_table; +}; #endif diff --git a/drivers/crypto/qat/qat_common/qat_algs.c b/drivers/crypto/qat/qat_common/qat_algs.c index d552dbcfe0a0..31c7a206a629 100644 --- a/drivers/crypto/qat/qat_common/qat_algs.c +++ b/drivers/crypto/qat/qat_common/qat_algs.c @@ -6,11 +6,13 @@ #include <crypto/internal/aead.h> #include <crypto/internal/skcipher.h> #include <crypto/aes.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/hash.h> #include <crypto/hmac.h> #include <crypto/algapi.h> #include <crypto/authenc.h> +#include <crypto/scatterwalk.h> #include <crypto/xts.h> #include <linux/dma-mapping.h> #include "adf_accel_devices.h" @@ -31,6 +33,15 @@ ICP_QAT_HW_CIPHER_KEY_CONVERT, \ ICP_QAT_HW_CIPHER_DECRYPT) +#define QAT_AES_HW_CONFIG_DEC_NO_CONV(alg, mode) \ + ICP_QAT_HW_CIPHER_CONFIG_BUILD(mode, alg, \ + ICP_QAT_HW_CIPHER_NO_CONVERT, \ + ICP_QAT_HW_CIPHER_DECRYPT) + +#define HW_CAP_AES_V2(accel_dev) \ + (GET_HW_DATA(accel_dev)->accel_capabilities_mask & \ + ICP_ACCEL_CAPABILITIES_AES_V2) + static DEFINE_MUTEX(algs_lock); static unsigned int active_devs; @@ -89,7 +100,9 @@ struct qat_alg_skcipher_ctx { struct icp_qat_fw_la_bulk_req dec_fw_req; struct qat_crypto_instance *inst; struct crypto_skcipher *ftfm; + struct crypto_cipher *tweak; bool fallback; + int mode; }; static int qat_get_inter_state_size(enum icp_qat_hw_auth_algo qat_hash_alg) @@ -103,7 +116,7 @@ static int qat_get_inter_state_size(enum icp_qat_hw_auth_algo qat_hash_alg) return ICP_QAT_HW_SHA512_STATE1_SZ; default: return -EFAULT; - }; + } return -EFAULT; } @@ -214,24 +227,7 @@ static int qat_alg_do_precomputes(struct icp_qat_hw_auth_algo_blk *hash, return 0; } -static void qat_alg_init_hdr_iv_updt(struct icp_qat_fw_comn_req_hdr *header) -{ - ICP_QAT_FW_LA_CIPH_IV_FLD_FLAG_SET(header->serv_specif_flags, - ICP_QAT_FW_CIPH_IV_64BIT_PTR); - ICP_QAT_FW_LA_UPDATE_STATE_SET(header->serv_specif_flags, - ICP_QAT_FW_LA_UPDATE_STATE); -} - -static void qat_alg_init_hdr_no_iv_updt(struct icp_qat_fw_comn_req_hdr *header) -{ - ICP_QAT_FW_LA_CIPH_IV_FLD_FLAG_SET(header->serv_specif_flags, - ICP_QAT_FW_CIPH_IV_16BYTE_DATA); - ICP_QAT_FW_LA_UPDATE_STATE_SET(header->serv_specif_flags, - ICP_QAT_FW_LA_NO_UPDATE_STATE); -} - -static void qat_alg_init_common_hdr(struct icp_qat_fw_comn_req_hdr *header, - int aead) +static void qat_alg_init_common_hdr(struct icp_qat_fw_comn_req_hdr *header) { header->hdr_flags = ICP_QAT_FW_COMN_HDR_FLAGS_BUILD(ICP_QAT_FW_COMN_REQ_FLAG_SET); @@ -241,12 +237,12 @@ static void qat_alg_init_common_hdr(struct icp_qat_fw_comn_req_hdr *header, QAT_COMN_PTR_TYPE_SGL); ICP_QAT_FW_LA_PARTIAL_SET(header->serv_specif_flags, ICP_QAT_FW_LA_PARTIAL_NONE); - if (aead) - qat_alg_init_hdr_no_iv_updt(header); - else - qat_alg_init_hdr_iv_updt(header); + ICP_QAT_FW_LA_CIPH_IV_FLD_FLAG_SET(header->serv_specif_flags, + ICP_QAT_FW_CIPH_IV_16BYTE_DATA); ICP_QAT_FW_LA_PROTO_SET(header->serv_specif_flags, ICP_QAT_FW_LA_NO_PROTO); + ICP_QAT_FW_LA_UPDATE_STATE_SET(header->serv_specif_flags, + ICP_QAT_FW_LA_NO_UPDATE_STATE); } static int qat_alg_aead_init_enc_session(struct crypto_aead *aead_tfm, @@ -281,7 +277,7 @@ static int qat_alg_aead_init_enc_session(struct crypto_aead *aead_tfm, return -EFAULT; /* Request setup */ - qat_alg_init_common_hdr(header, 1); + qat_alg_init_common_hdr(header); header->service_cmd_id = ICP_QAT_FW_LA_CMD_CIPHER_HASH; ICP_QAT_FW_LA_DIGEST_IN_BUFFER_SET(header->serv_specif_flags, ICP_QAT_FW_LA_DIGEST_IN_BUFFER); @@ -368,7 +364,7 @@ static int qat_alg_aead_init_dec_session(struct crypto_aead *aead_tfm, return -EFAULT; /* Request setup */ - qat_alg_init_common_hdr(header, 1); + qat_alg_init_common_hdr(header); header->service_cmd_id = ICP_QAT_FW_LA_CMD_HASH_CIPHER; ICP_QAT_FW_LA_DIGEST_IN_BUFFER_SET(header->serv_specif_flags, ICP_QAT_FW_LA_DIGEST_IN_BUFFER); @@ -430,12 +426,32 @@ static void qat_alg_skcipher_init_com(struct qat_alg_skcipher_ctx *ctx, struct icp_qat_fw_comn_req_hdr_cd_pars *cd_pars = &req->cd_pars; struct icp_qat_fw_comn_req_hdr *header = &req->comn_hdr; struct icp_qat_fw_cipher_cd_ctrl_hdr *cd_ctrl = (void *)&req->cd_ctrl; + bool aes_v2_capable = HW_CAP_AES_V2(ctx->inst->accel_dev); + int mode = ctx->mode; - memcpy(cd->aes.key, key, keylen); - qat_alg_init_common_hdr(header, 0); + qat_alg_init_common_hdr(header); header->service_cmd_id = ICP_QAT_FW_LA_CMD_CIPHER; cd_pars->u.s.content_desc_params_sz = sizeof(struct icp_qat_hw_cipher_algo_blk) >> 3; + + if (aes_v2_capable && mode == ICP_QAT_HW_CIPHER_XTS_MODE) { + ICP_QAT_FW_LA_SLICE_TYPE_SET(header->serv_specif_flags, + ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE); + + /* Store both XTS keys in CD, only the first key is sent + * to the HW, the second key is used for tweak calculation + */ + memcpy(cd->ucs_aes.key, key, keylen); + keylen = keylen / 2; + } else if (aes_v2_capable && mode == ICP_QAT_HW_CIPHER_CTR_MODE) { + ICP_QAT_FW_LA_SLICE_TYPE_SET(header->serv_specif_flags, + ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE); + keylen = round_up(keylen, 16); + memcpy(cd->ucs_aes.key, key, keylen); + } else { + memcpy(cd->aes.key, key, keylen); + } + /* Cipher CD config setup */ cd_ctrl->cipher_key_sz = keylen >> 3; cd_ctrl->cipher_state_sz = AES_BLOCK_SIZE >> 3; @@ -457,6 +473,28 @@ static void qat_alg_skcipher_init_enc(struct qat_alg_skcipher_ctx *ctx, enc_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_ENC(alg, mode); } +static void qat_alg_xts_reverse_key(const u8 *key_forward, unsigned int keylen, + u8 *key_reverse) +{ + struct crypto_aes_ctx aes_expanded; + int nrounds; + u8 *key; + + aes_expandkey(&aes_expanded, key_forward, keylen); + if (keylen == AES_KEYSIZE_128) { + nrounds = 10; + key = (u8 *)aes_expanded.key_enc + (AES_BLOCK_SIZE * nrounds); + memcpy(key_reverse, key, AES_BLOCK_SIZE); + } else { + /* AES_KEYSIZE_256 */ + nrounds = 14; + key = (u8 *)aes_expanded.key_enc + (AES_BLOCK_SIZE * nrounds); + memcpy(key_reverse, key, AES_BLOCK_SIZE); + memcpy(key_reverse + AES_BLOCK_SIZE, key - AES_BLOCK_SIZE, + AES_BLOCK_SIZE); + } +} + static void qat_alg_skcipher_init_dec(struct qat_alg_skcipher_ctx *ctx, int alg, const u8 *key, unsigned int keylen, int mode) @@ -464,16 +502,26 @@ static void qat_alg_skcipher_init_dec(struct qat_alg_skcipher_ctx *ctx, struct icp_qat_hw_cipher_algo_blk *dec_cd = ctx->dec_cd; struct icp_qat_fw_la_bulk_req *req = &ctx->dec_fw_req; struct icp_qat_fw_comn_req_hdr_cd_pars *cd_pars = &req->cd_pars; + bool aes_v2_capable = HW_CAP_AES_V2(ctx->inst->accel_dev); qat_alg_skcipher_init_com(ctx, req, dec_cd, key, keylen); cd_pars->u.s.content_desc_addr = ctx->dec_cd_paddr; - if (mode != ICP_QAT_HW_CIPHER_CTR_MODE) + if (aes_v2_capable && mode == ICP_QAT_HW_CIPHER_XTS_MODE) { + /* Key reversing not supported, set no convert */ + dec_cd->aes.cipher_config.val = + QAT_AES_HW_CONFIG_DEC_NO_CONV(alg, mode); + + /* In-place key reversal */ + qat_alg_xts_reverse_key(dec_cd->ucs_aes.key, keylen / 2, + dec_cd->ucs_aes.key); + } else if (mode != ICP_QAT_HW_CIPHER_CTR_MODE) { dec_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_DEC(alg, mode); - else + } else { dec_cd->aes.cipher_config.val = QAT_AES_HW_CONFIG_ENC(alg, mode); + } } static int qat_alg_validate_key(int key_len, int *alg, int mode) @@ -787,6 +835,61 @@ static void qat_aead_alg_callback(struct icp_qat_fw_la_resp *qat_resp, areq->base.complete(&areq->base, res); } +static void qat_alg_update_iv_ctr_mode(struct qat_crypto_request *qat_req) +{ + struct skcipher_request *sreq = qat_req->skcipher_req; + u64 iv_lo_prev; + u64 iv_lo; + u64 iv_hi; + + memcpy(qat_req->iv, sreq->iv, AES_BLOCK_SIZE); + + iv_lo = be64_to_cpu(qat_req->iv_lo); + iv_hi = be64_to_cpu(qat_req->iv_hi); + + iv_lo_prev = iv_lo; + iv_lo += DIV_ROUND_UP(sreq->cryptlen, AES_BLOCK_SIZE); + if (iv_lo < iv_lo_prev) + iv_hi++; + + qat_req->iv_lo = cpu_to_be64(iv_lo); + qat_req->iv_hi = cpu_to_be64(iv_hi); +} + +static void qat_alg_update_iv_cbc_mode(struct qat_crypto_request *qat_req) +{ + struct skcipher_request *sreq = qat_req->skcipher_req; + int offset = sreq->cryptlen - AES_BLOCK_SIZE; + struct scatterlist *sgl; + + if (qat_req->encryption) + sgl = sreq->dst; + else + sgl = sreq->src; + + scatterwalk_map_and_copy(qat_req->iv, sgl, offset, AES_BLOCK_SIZE, 0); +} + +static void qat_alg_update_iv(struct qat_crypto_request *qat_req) +{ + struct qat_alg_skcipher_ctx *ctx = qat_req->skcipher_ctx; + struct device *dev = &GET_DEV(ctx->inst->accel_dev); + + switch (ctx->mode) { + case ICP_QAT_HW_CIPHER_CTR_MODE: + qat_alg_update_iv_ctr_mode(qat_req); + break; + case ICP_QAT_HW_CIPHER_CBC_MODE: + qat_alg_update_iv_cbc_mode(qat_req); + break; + case ICP_QAT_HW_CIPHER_XTS_MODE: + break; + default: + dev_warn(dev, "Unsupported IV update for cipher mode %d\n", + ctx->mode); + } +} + static void qat_skcipher_alg_callback(struct icp_qat_fw_la_resp *qat_resp, struct qat_crypto_request *qat_req) { @@ -794,16 +897,16 @@ static void qat_skcipher_alg_callback(struct icp_qat_fw_la_resp *qat_resp, struct qat_crypto_instance *inst = ctx->inst; struct skcipher_request *sreq = qat_req->skcipher_req; u8 stat_filed = qat_resp->comn_resp.comn_status; - struct device *dev = &GET_DEV(ctx->inst->accel_dev); int res = 0, qat_res = ICP_QAT_FW_COMN_RESP_CRYPTO_STAT_GET(stat_filed); qat_alg_free_bufl(inst, qat_req); if (unlikely(qat_res != ICP_QAT_FW_COMN_STATUS_FLAG_OK)) res = -EINVAL; + if (qat_req->encryption) + qat_alg_update_iv(qat_req); + memcpy(sreq->iv, qat_req->iv, AES_BLOCK_SIZE); - dma_free_coherent(dev, AES_BLOCK_SIZE, qat_req->iv, - qat_req->iv_paddr); sreq->base.complete(&sreq->base, res); } @@ -981,6 +1084,8 @@ static int qat_alg_skcipher_setkey(struct crypto_skcipher *tfm, { struct qat_alg_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); + ctx->mode = mode; + if (ctx->enc_cd) return qat_alg_skcipher_rekey(ctx, key, keylen, mode); else @@ -1023,8 +1128,33 @@ static int qat_alg_skcipher_xts_setkey(struct crypto_skcipher *tfm, ctx->fallback = false; - return qat_alg_skcipher_setkey(tfm, key, keylen, - ICP_QAT_HW_CIPHER_XTS_MODE); + ret = qat_alg_skcipher_setkey(tfm, key, keylen, + ICP_QAT_HW_CIPHER_XTS_MODE); + if (ret) + return ret; + + if (HW_CAP_AES_V2(ctx->inst->accel_dev)) + ret = crypto_cipher_setkey(ctx->tweak, key + (keylen / 2), + keylen / 2); + + return ret; +} + +static void qat_alg_set_req_iv(struct qat_crypto_request *qat_req) +{ + struct icp_qat_fw_la_cipher_req_params *cipher_param; + struct qat_alg_skcipher_ctx *ctx = qat_req->skcipher_ctx; + bool aes_v2_capable = HW_CAP_AES_V2(ctx->inst->accel_dev); + u8 *iv = qat_req->skcipher_req->iv; + + cipher_param = (void *)&qat_req->req.serv_specif_rqpars; + + if (aes_v2_capable && ctx->mode == ICP_QAT_HW_CIPHER_XTS_MODE) + crypto_cipher_encrypt_one(ctx->tweak, + (u8 *)cipher_param->u.cipher_IV_array, + iv); + else + memcpy(cipher_param->u.cipher_IV_array, iv, AES_BLOCK_SIZE); } static int qat_alg_skcipher_encrypt(struct skcipher_request *req) @@ -1035,23 +1165,14 @@ static int qat_alg_skcipher_encrypt(struct skcipher_request *req) struct qat_crypto_request *qat_req = skcipher_request_ctx(req); struct icp_qat_fw_la_cipher_req_params *cipher_param; struct icp_qat_fw_la_bulk_req *msg; - struct device *dev = &GET_DEV(ctx->inst->accel_dev); int ret, ctr = 0; if (req->cryptlen == 0) return 0; - qat_req->iv = dma_alloc_coherent(dev, AES_BLOCK_SIZE, - &qat_req->iv_paddr, GFP_ATOMIC); - if (!qat_req->iv) - return -ENOMEM; - ret = qat_alg_sgl_to_bufl(ctx->inst, req->src, req->dst, qat_req); - if (unlikely(ret)) { - dma_free_coherent(dev, AES_BLOCK_SIZE, qat_req->iv, - qat_req->iv_paddr); + if (unlikely(ret)) return ret; - } msg = &qat_req->req; *msg = ctx->enc_fw_req; @@ -1061,19 +1182,19 @@ static int qat_alg_skcipher_encrypt(struct skcipher_request *req) qat_req->req.comn_mid.opaque_data = (u64)(__force long)qat_req; qat_req->req.comn_mid.src_data_addr = qat_req->buf.blp; qat_req->req.comn_mid.dest_data_addr = qat_req->buf.bloutp; + qat_req->encryption = true; cipher_param = (void *)&qat_req->req.serv_specif_rqpars; cipher_param->cipher_length = req->cryptlen; cipher_param->cipher_offset = 0; - cipher_param->u.s.cipher_IV_ptr = qat_req->iv_paddr; - memcpy(qat_req->iv, req->iv, AES_BLOCK_SIZE); + + qat_alg_set_req_iv(qat_req); + do { ret = adf_send_message(ctx->inst->sym_tx, (u32 *)msg); } while (ret == -EAGAIN && ctr++ < 10); if (ret == -EAGAIN) { qat_alg_free_bufl(ctx->inst, qat_req); - dma_free_coherent(dev, AES_BLOCK_SIZE, qat_req->iv, - qat_req->iv_paddr); return -EBUSY; } return -EINPROGRESS; @@ -1113,23 +1234,14 @@ static int qat_alg_skcipher_decrypt(struct skcipher_request *req) struct qat_crypto_request *qat_req = skcipher_request_ctx(req); struct icp_qat_fw_la_cipher_req_params *cipher_param; struct icp_qat_fw_la_bulk_req *msg; - struct device *dev = &GET_DEV(ctx->inst->accel_dev); int ret, ctr = 0; if (req->cryptlen == 0) return 0; - qat_req->iv = dma_alloc_coherent(dev, AES_BLOCK_SIZE, - &qat_req->iv_paddr, GFP_ATOMIC); - if (!qat_req->iv) - return -ENOMEM; - ret = qat_alg_sgl_to_bufl(ctx->inst, req->src, req->dst, qat_req); - if (unlikely(ret)) { - dma_free_coherent(dev, AES_BLOCK_SIZE, qat_req->iv, - qat_req->iv_paddr); + if (unlikely(ret)) return ret; - } msg = &qat_req->req; *msg = ctx->dec_fw_req; @@ -1139,19 +1251,20 @@ static int qat_alg_skcipher_decrypt(struct skcipher_request *req) qat_req->req.comn_mid.opaque_data = (u64)(__force long)qat_req; qat_req->req.comn_mid.src_data_addr = qat_req->buf.blp; qat_req->req.comn_mid.dest_data_addr = qat_req->buf.bloutp; + qat_req->encryption = false; cipher_param = (void *)&qat_req->req.serv_specif_rqpars; cipher_param->cipher_length = req->cryptlen; cipher_param->cipher_offset = 0; - cipher_param->u.s.cipher_IV_ptr = qat_req->iv_paddr; - memcpy(qat_req->iv, req->iv, AES_BLOCK_SIZE); + + qat_alg_set_req_iv(qat_req); + qat_alg_update_iv(qat_req); + do { ret = adf_send_message(ctx->inst->sym_tx, (u32 *)msg); } while (ret == -EAGAIN && ctr++ < 10); if (ret == -EAGAIN) { qat_alg_free_bufl(ctx->inst, qat_req); - dma_free_coherent(dev, AES_BLOCK_SIZE, qat_req->iv, - qat_req->iv_paddr); return -EBUSY; } return -EINPROGRESS; @@ -1253,6 +1366,12 @@ static int qat_alg_skcipher_init_xts_tfm(struct crypto_skcipher *tfm) if (IS_ERR(ctx->ftfm)) return PTR_ERR(ctx->ftfm); + ctx->tweak = crypto_alloc_cipher("aes", 0, 0); + if (IS_ERR(ctx->tweak)) { + crypto_free_skcipher(ctx->ftfm); + return PTR_ERR(ctx->tweak); + } + reqsize = max(sizeof(struct qat_crypto_request), sizeof(struct skcipher_request) + crypto_skcipher_reqsize(ctx->ftfm)); @@ -1295,6 +1414,9 @@ static void qat_alg_skcipher_exit_xts_tfm(struct crypto_skcipher *tfm) if (ctx->ftfm) crypto_free_skcipher(ctx->ftfm); + if (ctx->tweak) + crypto_free_cipher(ctx->tweak); + qat_alg_skcipher_exit_tfm(tfm); } diff --git a/drivers/crypto/qat/qat_common/qat_asym_algs.c b/drivers/crypto/qat/qat_common/qat_asym_algs.c index 846569ec9066..2c863d25327a 100644 --- a/drivers/crypto/qat/qat_common/qat_asym_algs.c +++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c @@ -201,12 +201,7 @@ static unsigned long qat_dh_fn_id(unsigned int len, bool g2) return g2 ? PKE_DH_G2_4096 : PKE_DH_4096; default: return 0; - }; -} - -static inline struct qat_dh_ctx *qat_dh_get_params(struct crypto_kpp *tfm) -{ - return kpp_tfm_ctx(tfm); + } } static int qat_dh_compute_value(struct kpp_request *req) @@ -577,7 +572,7 @@ static unsigned long qat_rsa_enc_fn_id(unsigned int len) return PKE_RSA_EP_4096; default: return 0; - }; + } } #define PKE_RSA_DP1_512 0x1c161b3c @@ -606,7 +601,7 @@ static unsigned long qat_rsa_dec_fn_id(unsigned int len) return PKE_RSA_DP1_4096; default: return 0; - }; + } } #define PKE_RSA_DP2_512 0x1c131b57 @@ -635,7 +630,7 @@ static unsigned long qat_rsa_dec_fn_id_crt(unsigned int len) return PKE_RSA_DP2_4096; default: return 0; - }; + } } static int qat_rsa_enc(struct akcipher_request *req) diff --git a/drivers/crypto/qat/qat_common/qat_crypto.c b/drivers/crypto/qat/qat_common/qat_crypto.c index ab621b7dbd20..ece6776fbd53 100644 --- a/drivers/crypto/qat/qat_common/qat_crypto.c +++ b/drivers/crypto/qat/qat_common/qat_crypto.c @@ -115,165 +115,217 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node) */ int qat_crypto_dev_config(struct adf_accel_dev *accel_dev) { - int cpus = num_online_cpus(); - int banks = GET_MAX_BANKS(accel_dev); - int instances = min(cpus, banks); char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES]; - int i; + int banks = GET_MAX_BANKS(accel_dev); + int cpus = num_online_cpus(); unsigned long val; + int instances; + int ret; + int i; + + if (adf_hw_dev_has_crypto(accel_dev)) + instances = min(cpus, banks); + else + instances = 0; - if (adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC)) + ret = adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC); + if (ret) goto err; - if (adf_cfg_section_add(accel_dev, "Accelerator0")) + + ret = adf_cfg_section_add(accel_dev, "Accelerator0"); + if (ret) goto err; + for (i = 0; i < instances; i++) { val = i; - snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_BANK_NUM, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_BANK_NUM, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) + goto err; + + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_BANK_NUM, i); + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i); val = 128; - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = 512; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = 0; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = 2; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = 8; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = 10; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i); - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, + key, &val, ADF_DEC); + if (ret) goto err; val = ADF_COALESCING_DEF_TIME; snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i); - if (adf_cfg_add_key_value_param(accel_dev, "Accelerator0", - key, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, "Accelerator0", + key, &val, ADF_DEC); + if (ret) goto err; } val = i; - if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, - ADF_NUM_CY, (void *)&val, ADF_DEC)) + ret = adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, ADF_NUM_CY, + &val, ADF_DEC); + if (ret) goto err; set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status); return 0; err: dev_err(&GET_DEV(accel_dev), "Failed to start QAT accel dev\n"); - return -EINVAL; + return ret; } EXPORT_SYMBOL_GPL(qat_crypto_dev_config); static int qat_crypto_create_instances(struct adf_accel_dev *accel_dev) { - int i; - unsigned long bank; unsigned long num_inst, num_msg_sym, num_msg_asym; - int msg_size; - struct qat_crypto_instance *inst; char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES]; char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES]; + unsigned long sym_bank, asym_bank; + struct qat_crypto_instance *inst; + int msg_size; + int ret; + int i; INIT_LIST_HEAD(&accel_dev->crypto_list); - if (adf_cfg_get_param_value(accel_dev, SEC, ADF_NUM_CY, val)) - return -EFAULT; + ret = adf_cfg_get_param_value(accel_dev, SEC, ADF_NUM_CY, val); + if (ret) + return ret; - if (kstrtoul(val, 0, &num_inst)) - return -EFAULT; + ret = kstrtoul(val, 0, &num_inst); + if (ret) + return ret; for (i = 0; i < num_inst; i++) { inst = kzalloc_node(sizeof(*inst), GFP_KERNEL, dev_to_node(&GET_DEV(accel_dev))); - if (!inst) + if (!inst) { + ret = -ENOMEM; goto err; + } list_add_tail(&inst->list, &accel_dev->crypto_list); inst->id = i; atomic_set(&inst->refctr, 0); inst->accel_dev = accel_dev; - snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_BANK_NUM, i); - if (adf_cfg_get_param_value(accel_dev, SEC, key, val)) + + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_BANK_NUM, i); + ret = adf_cfg_get_param_value(accel_dev, SEC, key, val); + if (ret) + goto err; + + ret = kstrtoul(val, 10, &sym_bank); + if (ret) goto err; - if (kstrtoul(val, 10, &bank)) + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_BANK_NUM, i); + ret = adf_cfg_get_param_value(accel_dev, SEC, key, val); + if (ret) goto err; + + ret = kstrtoul(val, 10, &asym_bank); + if (ret) + goto err; + snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i); - if (adf_cfg_get_param_value(accel_dev, SEC, key, val)) + ret = adf_cfg_get_param_value(accel_dev, SEC, key, val); + if (ret) goto err; - if (kstrtoul(val, 10, &num_msg_sym)) + ret = kstrtoul(val, 10, &num_msg_sym); + if (ret) goto err; num_msg_sym = num_msg_sym >> 1; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i); - if (adf_cfg_get_param_value(accel_dev, SEC, key, val)) + ret = adf_cfg_get_param_value(accel_dev, SEC, key, val); + if (ret) goto err; - if (kstrtoul(val, 10, &num_msg_asym)) + ret = kstrtoul(val, 10, &num_msg_asym); + if (ret) goto err; num_msg_asym = num_msg_asym >> 1; msg_size = ICP_QAT_FW_REQ_DEFAULT_SZ; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i); - if (adf_create_ring(accel_dev, SEC, bank, num_msg_sym, - msg_size, key, NULL, 0, &inst->sym_tx)) + ret = adf_create_ring(accel_dev, SEC, sym_bank, num_msg_sym, + msg_size, key, NULL, 0, &inst->sym_tx); + if (ret) goto err; msg_size = msg_size >> 1; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i); - if (adf_create_ring(accel_dev, SEC, bank, num_msg_asym, - msg_size, key, NULL, 0, &inst->pke_tx)) + ret = adf_create_ring(accel_dev, SEC, asym_bank, num_msg_asym, + msg_size, key, NULL, 0, &inst->pke_tx); + if (ret) goto err; msg_size = ICP_QAT_FW_RESP_DEFAULT_SZ; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i); - if (adf_create_ring(accel_dev, SEC, bank, num_msg_sym, - msg_size, key, qat_alg_callback, 0, - &inst->sym_rx)) + ret = adf_create_ring(accel_dev, SEC, sym_bank, num_msg_sym, + msg_size, key, qat_alg_callback, 0, + &inst->sym_rx); + if (ret) goto err; snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i); - if (adf_create_ring(accel_dev, SEC, bank, num_msg_asym, - msg_size, key, qat_alg_asym_callback, 0, - &inst->pke_rx)) + ret = adf_create_ring(accel_dev, SEC, asym_bank, num_msg_asym, + msg_size, key, qat_alg_asym_callback, 0, + &inst->pke_rx); + if (ret) goto err; } return 0; err: qat_crypto_free_instances(accel_dev); - return -ENOMEM; + return ret; } static int qat_crypto_init(struct adf_accel_dev *accel_dev) diff --git a/drivers/crypto/qat/qat_common/qat_crypto.h b/drivers/crypto/qat/qat_common/qat_crypto.h index 12682d1e9f5f..b6a4c95ae003 100644 --- a/drivers/crypto/qat/qat_common/qat_crypto.h +++ b/drivers/crypto/qat/qat_common/qat_crypto.h @@ -3,6 +3,7 @@ #ifndef _QAT_CRYPTO_INSTANCE_H_ #define _QAT_CRYPTO_INSTANCE_H_ +#include <crypto/aes.h> #include <linux/list.h> #include <linux/slab.h> #include "adf_accel_devices.h" @@ -44,8 +45,29 @@ struct qat_crypto_request { struct qat_crypto_request_buffs buf; void (*cb)(struct icp_qat_fw_la_resp *resp, struct qat_crypto_request *req); - void *iv; - dma_addr_t iv_paddr; + union { + struct { + __be64 iv_hi; + __be64 iv_lo; + }; + u8 iv[AES_BLOCK_SIZE]; + }; + bool encryption; }; +static inline bool adf_hw_dev_has_crypto(struct adf_accel_dev *accel_dev) +{ + struct adf_hw_device_data *hw_device = accel_dev->hw_device; + u32 mask = ~hw_device->accel_capabilities_mask; + + if (mask & ADF_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC) + return false; + if (mask & ADF_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC) + return false; + if (mask & ADF_ACCEL_CAPABILITIES_AUTHENTICATION) + return false; + + return true; +} + #endif diff --git a/drivers/crypto/qat/qat_common/qat_hal.c b/drivers/crypto/qat/qat_common/qat_hal.c index 6b9d47682d04..bd3028126cbe 100644 --- a/drivers/crypto/qat/qat_common/qat_hal.c +++ b/drivers/crypto/qat/qat_common/qat_hal.c @@ -33,7 +33,7 @@ ((((const_val) << 12) & 0x0FF00000ull) | \ (((const_val) << 0) & 0x000000FFull)))) -#define AE(handle, ae) handle->hal_handle->aes[ae] +#define AE(handle, ae) ((handle)->hal_handle->aes[ae]) static const u64 inst_4b[] = { 0x0F0400C0000ull, 0x0F4400C0000ull, 0x0F040000300ull, 0x0F440000300ull, @@ -150,15 +150,15 @@ static int qat_hal_wait_cycles(struct icp_qat_fw_loader_handle *handle, return 0; } -#define CLR_BIT(wrd, bit) (wrd & ~(1 << bit)) -#define SET_BIT(wrd, bit) (wrd | 1 << bit) +#define CLR_BIT(wrd, bit) ((wrd) & ~(1 << (bit))) +#define SET_BIT(wrd, bit) ((wrd) | 1 << (bit)) int qat_hal_set_ae_ctx_mode(struct icp_qat_fw_loader_handle *handle, unsigned char ae, unsigned char mode) { unsigned int csr, new_csr; - if ((mode != 4) && (mode != 8)) { + if (mode != 4 && mode != 8) { pr_err("QAT: bad ctx mode=%d\n", mode); return -EINVAL; } @@ -210,6 +210,16 @@ int qat_hal_set_ae_lm_mode(struct icp_qat_fw_loader_handle *handle, SET_BIT(csr, CE_LMADDR_1_GLOBAL_BITPOS) : CLR_BIT(csr, CE_LMADDR_1_GLOBAL_BITPOS); break; + case ICP_LMEM2: + new_csr = (mode) ? + SET_BIT(csr, CE_LMADDR_2_GLOBAL_BITPOS) : + CLR_BIT(csr, CE_LMADDR_2_GLOBAL_BITPOS); + break; + case ICP_LMEM3: + new_csr = (mode) ? + SET_BIT(csr, CE_LMADDR_3_GLOBAL_BITPOS) : + CLR_BIT(csr, CE_LMADDR_3_GLOBAL_BITPOS); + break; default: pr_err("QAT: lmType = 0x%x\n", lm_type); return -EINVAL; @@ -220,6 +230,20 @@ int qat_hal_set_ae_lm_mode(struct icp_qat_fw_loader_handle *handle, return 0; } +void qat_hal_set_ae_tindex_mode(struct icp_qat_fw_loader_handle *handle, + unsigned char ae, unsigned char mode) +{ + unsigned int csr, new_csr; + + csr = qat_hal_rd_ae_csr(handle, ae, CTX_ENABLES); + csr &= IGNORE_W1C_MASK; + new_csr = (mode) ? + SET_BIT(csr, CE_T_INDEX_GLOBAL_BITPOS) : + CLR_BIT(csr, CE_T_INDEX_GLOBAL_BITPOS); + if (new_csr != csr) + qat_hal_wr_ae_csr(handle, ae, CTX_ENABLES, new_csr); +} + static unsigned short qat_hal_get_reg_addr(unsigned int type, unsigned short reg_num) { @@ -259,6 +283,12 @@ static unsigned short qat_hal_get_reg_addr(unsigned int type, case ICP_LMEM1: reg_addr = 0x220; break; + case ICP_LMEM2: + reg_addr = 0x2c0; + break; + case ICP_LMEM3: + reg_addr = 0x2e0; + break; case ICP_NO_DEST: reg_addr = 0x300 | (reg_num & 0xff); break; @@ -271,12 +301,13 @@ static unsigned short qat_hal_get_reg_addr(unsigned int type, void qat_hal_reset(struct icp_qat_fw_loader_handle *handle) { - unsigned int ae_reset_csr; + unsigned int reset_mask = handle->chip_info->icp_rst_mask; + unsigned int reset_csr = handle->chip_info->icp_rst_csr; + unsigned int csr_val; - ae_reset_csr = GET_GLB_CSR(handle, ICP_RESET); - ae_reset_csr |= handle->hal_handle->ae_mask << RST_CSR_AE_LSB; - ae_reset_csr |= handle->hal_handle->slice_mask << RST_CSR_QAT_LSB; - SET_GLB_CSR(handle, ICP_RESET, ae_reset_csr); + csr_val = GET_CAP_CSR(handle, reset_csr); + csr_val |= reset_mask; + SET_CAP_CSR(handle, reset_csr, csr_val); } static void qat_hal_wr_indr_csr(struct icp_qat_fw_loader_handle *handle, @@ -346,11 +377,12 @@ static void qat_hal_put_wakeup_event(struct icp_qat_fw_loader_handle *handle, static int qat_hal_check_ae_alive(struct icp_qat_fw_loader_handle *handle) { + unsigned long ae_mask = handle->hal_handle->ae_mask; unsigned int base_cnt, cur_cnt; unsigned char ae; int times = MAX_RETRY_TIMES; - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { base_cnt = qat_hal_rd_ae_csr(handle, ae, PROFILE_COUNT); base_cnt &= 0xffff; @@ -384,21 +416,23 @@ int qat_hal_check_ae_active(struct icp_qat_fw_loader_handle *handle, static void qat_hal_reset_timestamp(struct icp_qat_fw_loader_handle *handle) { - unsigned int misc_ctl; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned int misc_ctl_csr, misc_ctl; unsigned char ae; + misc_ctl_csr = handle->chip_info->misc_ctl_csr; /* stop the timestamp timers */ - misc_ctl = GET_GLB_CSR(handle, MISC_CONTROL); + misc_ctl = GET_CAP_CSR(handle, misc_ctl_csr); if (misc_ctl & MC_TIMESTAMP_ENABLE) - SET_GLB_CSR(handle, MISC_CONTROL, misc_ctl & + SET_CAP_CSR(handle, misc_ctl_csr, misc_ctl & (~MC_TIMESTAMP_ENABLE)); - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { qat_hal_wr_ae_csr(handle, ae, TIMESTAMP_LOW, 0); qat_hal_wr_ae_csr(handle, ae, TIMESTAMP_HIGH, 0); } /* start timestamp timers */ - SET_GLB_CSR(handle, MISC_CONTROL, misc_ctl | MC_TIMESTAMP_ENABLE); + SET_CAP_CSR(handle, misc_ctl_csr, misc_ctl | MC_TIMESTAMP_ENABLE); } #define ESRAM_AUTO_TINIT BIT(2) @@ -428,7 +462,7 @@ static int qat_hal_init_esram(struct icp_qat_fw_loader_handle *handle) qat_hal_wait_cycles(handle, 0, ESRAM_AUTO_INIT_USED_CYCLES, 0); csr_val = ADF_CSR_RD(csr_addr, 0); } while (!(csr_val & ESRAM_AUTO_TINIT_DONE) && times--); - if ((times < 0)) { + if (times < 0) { pr_err("QAT: Fail to init eSram!\n"); return -EFAULT; } @@ -438,33 +472,33 @@ static int qat_hal_init_esram(struct icp_qat_fw_loader_handle *handle) #define SHRAM_INIT_CYCLES 2060 int qat_hal_clr_reset(struct icp_qat_fw_loader_handle *handle) { - unsigned int ae_reset_csr; - unsigned char ae; - unsigned int clk_csr; + unsigned int clk_csr = handle->chip_info->glb_clk_enable_csr; + unsigned int reset_mask = handle->chip_info->icp_rst_mask; + unsigned int reset_csr = handle->chip_info->icp_rst_csr; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned char ae = 0; unsigned int times = 100; - unsigned int csr; + unsigned int csr_val; /* write to the reset csr */ - ae_reset_csr = GET_GLB_CSR(handle, ICP_RESET); - ae_reset_csr &= ~(handle->hal_handle->ae_mask << RST_CSR_AE_LSB); - ae_reset_csr &= ~(handle->hal_handle->slice_mask << RST_CSR_QAT_LSB); + csr_val = GET_CAP_CSR(handle, reset_csr); + csr_val &= ~reset_mask; do { - SET_GLB_CSR(handle, ICP_RESET, ae_reset_csr); + SET_CAP_CSR(handle, reset_csr, csr_val); if (!(times--)) goto out_err; - csr = GET_GLB_CSR(handle, ICP_RESET); - } while ((handle->hal_handle->ae_mask | - (handle->hal_handle->slice_mask << RST_CSR_QAT_LSB)) & csr); + csr_val = GET_CAP_CSR(handle, reset_csr); + csr_val &= reset_mask; + } while (csr_val); /* enable clock */ - clk_csr = GET_GLB_CSR(handle, ICP_GLOBAL_CLK_ENABLE); - clk_csr |= handle->hal_handle->ae_mask << 0; - clk_csr |= handle->hal_handle->slice_mask << 20; - SET_GLB_CSR(handle, ICP_GLOBAL_CLK_ENABLE, clk_csr); + csr_val = GET_CAP_CSR(handle, clk_csr); + csr_val |= reset_mask; + SET_CAP_CSR(handle, clk_csr, csr_val); if (qat_hal_check_ae_alive(handle)) goto out_err; /* Set undefined power-up/reset states to reasonable default values */ - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { qat_hal_wr_ae_csr(handle, ae, CTX_ENABLES, INIT_CTX_ENABLE_VALUE); qat_hal_wr_indr_csr(handle, ae, ICP_QAT_UCLO_AE_ALL_CTX, @@ -570,10 +604,11 @@ static void qat_hal_enable_ctx(struct icp_qat_fw_loader_handle *handle, static void qat_hal_clear_xfer(struct icp_qat_fw_loader_handle *handle) { + unsigned long ae_mask = handle->hal_handle->ae_mask; unsigned char ae; unsigned short reg; - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { for (reg = 0; reg < ICP_QAT_UCLO_MAX_GPR_REG; reg++) { qat_hal_init_rd_xfer(handle, ae, 0, ICP_SR_RD_ABS, reg, 0); @@ -585,6 +620,7 @@ static void qat_hal_clear_xfer(struct icp_qat_fw_loader_handle *handle) static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle) { + unsigned long ae_mask = handle->hal_handle->ae_mask; unsigned char ae; unsigned int ctx_mask = ICP_QAT_UCLO_AE_ALL_CTX; int times = MAX_RETRY_TIMES; @@ -592,13 +628,15 @@ static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle) unsigned int savctx = 0; int ret = 0; - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { csr_val = qat_hal_rd_ae_csr(handle, ae, AE_MISC_CONTROL); csr_val &= ~(1 << MMC_SHARE_CS_BITPOS); qat_hal_wr_ae_csr(handle, ae, AE_MISC_CONTROL, csr_val); csr_val = qat_hal_rd_ae_csr(handle, ae, CTX_ENABLES); csr_val &= IGNORE_W1C_MASK; - csr_val |= CE_NN_MODE; + if (handle->chip_info->nn) + csr_val |= CE_NN_MODE; + qat_hal_wr_ae_csr(handle, ae, CTX_ENABLES, csr_val); qat_hal_wr_uwords(handle, ae, 0, ARRAY_SIZE(inst), (u64 *)inst); @@ -613,7 +651,7 @@ static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle) qat_hal_wr_ae_csr(handle, ae, CTX_SIG_EVENTS_ACTIVE, 0); qat_hal_enable_ctx(handle, ae, ctx_mask); } - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { /* wait for AE to finish */ do { ret = qat_hal_wait_cycles(handle, ae, 20, 1); @@ -641,57 +679,143 @@ static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle) return 0; } -#define ICP_QAT_AE_OFFSET 0x20000 -#define ICP_QAT_CAP_OFFSET (ICP_QAT_AE_OFFSET + 0x10000) -#define LOCAL_TO_XFER_REG_OFFSET 0x800 -#define ICP_QAT_EP_OFFSET 0x3a000 -int qat_hal_init(struct adf_accel_dev *accel_dev) +static int qat_hal_chip_init(struct icp_qat_fw_loader_handle *handle, + struct adf_accel_dev *accel_dev) { - unsigned char ae; - unsigned int max_en_ae_id = 0; - struct icp_qat_fw_loader_handle *handle; struct adf_accel_pci *pci_info = &accel_dev->accel_pci_dev; struct adf_hw_device_data *hw_data = accel_dev->hw_device; struct adf_bar *misc_bar = &pci_info->pci_bars[hw_data->get_misc_bar_id(hw_data)]; + unsigned int max_en_ae_id = 0; struct adf_bar *sram_bar; + unsigned int csr_val = 0; + unsigned long ae_mask; + unsigned char ae = 0; + int ret = 0; - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->hal_cap_g_ctl_csr_addr_v = - (void __iomem *)((uintptr_t)misc_bar->virt_addr + - ICP_QAT_CAP_OFFSET); - handle->hal_cap_ae_xfer_csr_addr_v = - (void __iomem *)((uintptr_t)misc_bar->virt_addr + - ICP_QAT_AE_OFFSET); - handle->hal_ep_csr_addr_v = - (void __iomem *)((uintptr_t)misc_bar->virt_addr + - ICP_QAT_EP_OFFSET); - handle->hal_cap_ae_local_csr_addr_v = - (void __iomem *)((uintptr_t)handle->hal_cap_ae_xfer_csr_addr_v + - LOCAL_TO_XFER_REG_OFFSET); handle->pci_dev = pci_info->pci_dev; - if (handle->pci_dev->device == PCI_DEVICE_ID_INTEL_QAT_DH895XCC) { + switch (handle->pci_dev->device) { + case ADF_4XXX_PCI_DEVICE_ID: + handle->chip_info->sram_visible = false; + handle->chip_info->nn = false; + handle->chip_info->lm2lm3 = true; + handle->chip_info->lm_size = ICP_QAT_UCLO_MAX_LMEM_REG_2X; + handle->chip_info->icp_rst_csr = ICP_RESET_CPP0; + handle->chip_info->icp_rst_mask = 0x100015; + handle->chip_info->glb_clk_enable_csr = ICP_GLOBAL_CLK_ENABLE_CPP0; + handle->chip_info->misc_ctl_csr = MISC_CONTROL_C4XXX; + handle->chip_info->wakeup_event_val = 0x80000000; + handle->chip_info->fw_auth = true; + handle->chip_info->css_3k = true; + handle->chip_info->tgroup_share_ustore = true; + handle->chip_info->fcu_ctl_csr = FCU_CONTROL_4XXX; + handle->chip_info->fcu_sts_csr = FCU_STATUS_4XXX; + handle->chip_info->fcu_dram_addr_hi = FCU_DRAM_ADDR_HI_4XXX; + handle->chip_info->fcu_dram_addr_lo = FCU_DRAM_ADDR_LO_4XXX; + handle->chip_info->fcu_loaded_ae_csr = FCU_AE_LOADED_4XXX; + handle->chip_info->fcu_loaded_ae_pos = 0; + + handle->hal_cap_g_ctl_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_CAP_OFFSET_4XXX); + handle->hal_cap_ae_xfer_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_AE_OFFSET_4XXX); + handle->hal_ep_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_EP_OFFSET_4XXX); + handle->hal_cap_ae_local_csr_addr_v = + (void __iomem *)((uintptr_t)handle->hal_cap_ae_xfer_csr_addr_v + + LOCAL_TO_XFER_REG_OFFSET); + break; + case PCI_DEVICE_ID_INTEL_QAT_C62X: + case PCI_DEVICE_ID_INTEL_QAT_C3XXX: + handle->chip_info->sram_visible = false; + handle->chip_info->nn = true; + handle->chip_info->lm2lm3 = false; + handle->chip_info->lm_size = ICP_QAT_UCLO_MAX_LMEM_REG; + handle->chip_info->icp_rst_csr = ICP_RESET; + handle->chip_info->icp_rst_mask = (hw_data->ae_mask << RST_CSR_AE_LSB) | + (hw_data->accel_mask << RST_CSR_QAT_LSB); + handle->chip_info->glb_clk_enable_csr = ICP_GLOBAL_CLK_ENABLE; + handle->chip_info->misc_ctl_csr = MISC_CONTROL; + handle->chip_info->wakeup_event_val = WAKEUP_EVENT; + handle->chip_info->fw_auth = true; + handle->chip_info->css_3k = false; + handle->chip_info->tgroup_share_ustore = false; + handle->chip_info->fcu_ctl_csr = FCU_CONTROL; + handle->chip_info->fcu_sts_csr = FCU_STATUS; + handle->chip_info->fcu_dram_addr_hi = FCU_DRAM_ADDR_HI; + handle->chip_info->fcu_dram_addr_lo = FCU_DRAM_ADDR_LO; + handle->chip_info->fcu_loaded_ae_csr = FCU_STATUS; + handle->chip_info->fcu_loaded_ae_pos = FCU_LOADED_AE_POS; + handle->hal_cap_g_ctl_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_CAP_OFFSET); + handle->hal_cap_ae_xfer_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_AE_OFFSET); + handle->hal_ep_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_EP_OFFSET); + handle->hal_cap_ae_local_csr_addr_v = + (void __iomem *)((uintptr_t)handle->hal_cap_ae_xfer_csr_addr_v + + LOCAL_TO_XFER_REG_OFFSET); + break; + case PCI_DEVICE_ID_INTEL_QAT_DH895XCC: + handle->chip_info->sram_visible = true; + handle->chip_info->nn = true; + handle->chip_info->lm2lm3 = false; + handle->chip_info->lm_size = ICP_QAT_UCLO_MAX_LMEM_REG; + handle->chip_info->icp_rst_csr = ICP_RESET; + handle->chip_info->icp_rst_mask = (hw_data->ae_mask << RST_CSR_AE_LSB) | + (hw_data->accel_mask << RST_CSR_QAT_LSB); + handle->chip_info->glb_clk_enable_csr = ICP_GLOBAL_CLK_ENABLE; + handle->chip_info->misc_ctl_csr = MISC_CONTROL; + handle->chip_info->wakeup_event_val = WAKEUP_EVENT; + handle->chip_info->fw_auth = false; + handle->chip_info->css_3k = false; + handle->chip_info->tgroup_share_ustore = false; + handle->chip_info->fcu_ctl_csr = 0; + handle->chip_info->fcu_sts_csr = 0; + handle->chip_info->fcu_dram_addr_hi = 0; + handle->chip_info->fcu_dram_addr_lo = 0; + handle->chip_info->fcu_loaded_ae_csr = 0; + handle->chip_info->fcu_loaded_ae_pos = 0; + handle->hal_cap_g_ctl_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_CAP_OFFSET); + handle->hal_cap_ae_xfer_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_AE_OFFSET); + handle->hal_ep_csr_addr_v = + (void __iomem *)((uintptr_t)misc_bar->virt_addr + + ICP_QAT_EP_OFFSET); + handle->hal_cap_ae_local_csr_addr_v = + (void __iomem *)((uintptr_t)handle->hal_cap_ae_xfer_csr_addr_v + + LOCAL_TO_XFER_REG_OFFSET); + break; + default: + ret = -EINVAL; + goto out_err; + } + + if (handle->chip_info->sram_visible) { sram_bar = &pci_info->pci_bars[hw_data->get_sram_bar_id(hw_data)]; handle->hal_sram_addr_v = sram_bar->virt_addr; } - handle->fw_auth = (handle->pci_dev->device == - PCI_DEVICE_ID_INTEL_QAT_DH895XCC) ? false : true; - handle->hal_handle = kzalloc(sizeof(*handle->hal_handle), GFP_KERNEL); - if (!handle->hal_handle) - goto out_hal_handle; handle->hal_handle->revision_id = accel_dev->accel_pci_dev.revid; handle->hal_handle->ae_mask = hw_data->ae_mask; + handle->hal_handle->admin_ae_mask = hw_data->admin_ae_mask; handle->hal_handle->slice_mask = hw_data->accel_mask; + handle->cfg_ae_mask = ALL_AE_MASK; /* create AE objects */ handle->hal_handle->upc_mask = 0x1ffff; handle->hal_handle->max_ustore = 0x4000; - for (ae = 0; ae < ICP_QAT_UCLO_MAX_AE; ae++) { - if (!(hw_data->ae_mask & (1 << ae))) - continue; + + ae_mask = handle->hal_handle->ae_mask; + for_each_set_bit(ae, &ae_mask, ICP_QAT_UCLO_MAX_AE) { handle->hal_handle->aes[ae].free_addr = 0; handle->hal_handle->aes[ae].free_size = handle->hal_handle->max_ustore; @@ -702,70 +826,116 @@ int qat_hal_init(struct adf_accel_dev *accel_dev) max_en_ae_id = ae; } handle->hal_handle->ae_max_num = max_en_ae_id + 1; + + /* Set SIGNATURE_ENABLE[0] to 0x1 in order to enable ALU_OUT csr */ + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + csr_val = qat_hal_rd_ae_csr(handle, ae, SIGNATURE_ENABLE); + csr_val |= 0x1; + qat_hal_wr_ae_csr(handle, ae, SIGNATURE_ENABLE, csr_val); + } +out_err: + return ret; +} + +int qat_hal_init(struct adf_accel_dev *accel_dev) +{ + struct icp_qat_fw_loader_handle *handle; + int ret = 0; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->hal_handle = kzalloc(sizeof(*handle->hal_handle), GFP_KERNEL); + if (!handle->hal_handle) { + ret = -ENOMEM; + goto out_hal_handle; + } + + handle->chip_info = kzalloc(sizeof(*handle->chip_info), GFP_KERNEL); + if (!handle->chip_info) { + ret = -ENOMEM; + goto out_chip_info; + } + + ret = qat_hal_chip_init(handle, accel_dev); + if (ret) { + dev_err(&GET_DEV(accel_dev), "qat_hal_chip_init error\n"); + goto out_err; + } + /* take all AEs out of reset */ - if (qat_hal_clr_reset(handle)) { + ret = qat_hal_clr_reset(handle); + if (ret) { dev_err(&GET_DEV(accel_dev), "qat_hal_clr_reset error\n"); goto out_err; } + qat_hal_clear_xfer(handle); - if (!handle->fw_auth) { - if (qat_hal_clear_gpr(handle)) + if (!handle->chip_info->fw_auth) { + ret = qat_hal_clear_gpr(handle); + if (ret) goto out_err; } - /* Set SIGNATURE_ENABLE[0] to 0x1 in order to enable ALU_OUT csr */ - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { - unsigned int csr_val = 0; - - csr_val = qat_hal_rd_ae_csr(handle, ae, SIGNATURE_ENABLE); - csr_val |= 0x1; - qat_hal_wr_ae_csr(handle, ae, SIGNATURE_ENABLE, csr_val); - } accel_dev->fw_loader->fw_loader = handle; return 0; out_err: + kfree(handle->chip_info); +out_chip_info: kfree(handle->hal_handle); out_hal_handle: kfree(handle); - return -EFAULT; + return ret; } void qat_hal_deinit(struct icp_qat_fw_loader_handle *handle) { if (!handle) return; + kfree(handle->chip_info); kfree(handle->hal_handle); kfree(handle); } -void qat_hal_start(struct icp_qat_fw_loader_handle *handle, unsigned char ae, - unsigned int ctx_mask) +int qat_hal_start(struct icp_qat_fw_loader_handle *handle) { + unsigned long ae_mask = handle->hal_handle->ae_mask; + u32 wakeup_val = handle->chip_info->wakeup_event_val; + u32 fcu_ctl_csr, fcu_sts_csr; + unsigned int fcu_sts; + unsigned char ae; + u32 ae_ctr = 0; int retry = 0; - unsigned int fcu_sts = 0; - if (handle->fw_auth) { - SET_CAP_CSR(handle, FCU_CONTROL, FCU_CTRL_CMD_START); + if (handle->chip_info->fw_auth) { + fcu_ctl_csr = handle->chip_info->fcu_ctl_csr; + fcu_sts_csr = handle->chip_info->fcu_sts_csr; + ae_ctr = hweight32(ae_mask); + SET_CAP_CSR(handle, fcu_ctl_csr, FCU_CTRL_CMD_START); do { msleep(FW_AUTH_WAIT_PERIOD); - fcu_sts = GET_CAP_CSR(handle, FCU_STATUS); + fcu_sts = GET_CAP_CSR(handle, fcu_sts_csr); if (((fcu_sts >> FCU_STS_DONE_POS) & 0x1)) - return; + return ae_ctr; } while (retry++ < FW_AUTH_MAX_RETRY); - pr_err("QAT: start error (AE 0x%x FCU_STS = 0x%x)\n", ae, - fcu_sts); + pr_err("QAT: start error (FCU_STS = 0x%x)\n", fcu_sts); + return 0; } else { - qat_hal_put_wakeup_event(handle, ae, (~ctx_mask) & - ICP_QAT_UCLO_AE_ALL_CTX, 0x10000); - qat_hal_enable_ctx(handle, ae, ctx_mask); + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + qat_hal_put_wakeup_event(handle, ae, 0, wakeup_val); + qat_hal_enable_ctx(handle, ae, ICP_QAT_UCLO_AE_ALL_CTX); + ae_ctr++; + } + return ae_ctr; } } void qat_hal_stop(struct icp_qat_fw_loader_handle *handle, unsigned char ae, unsigned int ctx_mask) { - if (!handle->fw_auth) + if (!handle->chip_info->fw_auth) qat_hal_disable_ctx(handle, ae, ctx_mask); } @@ -832,9 +1002,12 @@ static int qat_hal_exec_micro_inst(struct icp_qat_fw_loader_handle *handle, int code_off, unsigned int max_cycle, unsigned int *endpc) { + unsigned int ind_lm_addr_byte0 = 0, ind_lm_addr_byte1 = 0; + unsigned int ind_lm_addr_byte2 = 0, ind_lm_addr_byte3 = 0; + unsigned int ind_t_index = 0, ind_t_index_byte = 0; + unsigned int ind_lm_addr0 = 0, ind_lm_addr1 = 0; + unsigned int ind_lm_addr2 = 0, ind_lm_addr3 = 0; u64 savuwords[MAX_EXEC_INST]; - unsigned int ind_lm_addr0, ind_lm_addr1; - unsigned int ind_lm_addr_byte0, ind_lm_addr_byte1; unsigned int ind_cnt_sig; unsigned int ind_sig, act_sig; unsigned int csr_val = 0, newcsr_val; @@ -853,6 +1026,20 @@ static int qat_hal_exec_micro_inst(struct icp_qat_fw_loader_handle *handle, INDIRECT_LM_ADDR_0_BYTE_INDEX); ind_lm_addr_byte1 = qat_hal_rd_indr_csr(handle, ae, ctx, INDIRECT_LM_ADDR_1_BYTE_INDEX); + if (handle->chip_info->lm2lm3) { + ind_lm_addr2 = qat_hal_rd_indr_csr(handle, ae, ctx, + LM_ADDR_2_INDIRECT); + ind_lm_addr3 = qat_hal_rd_indr_csr(handle, ae, ctx, + LM_ADDR_3_INDIRECT); + ind_lm_addr_byte2 = qat_hal_rd_indr_csr(handle, ae, ctx, + INDIRECT_LM_ADDR_2_BYTE_INDEX); + ind_lm_addr_byte3 = qat_hal_rd_indr_csr(handle, ae, ctx, + INDIRECT_LM_ADDR_3_BYTE_INDEX); + ind_t_index = qat_hal_rd_indr_csr(handle, ae, ctx, + INDIRECT_T_INDEX); + ind_t_index_byte = qat_hal_rd_indr_csr(handle, ae, ctx, + INDIRECT_T_INDEX_BYTE_INDEX); + } if (inst_num <= MAX_EXEC_INST) qat_hal_get_uwords(handle, ae, 0, inst_num, savuwords); qat_hal_get_wakeup_event(handle, ae, ctx, &wakeup_events); @@ -910,6 +1097,23 @@ static int qat_hal_exec_micro_inst(struct icp_qat_fw_loader_handle *handle, INDIRECT_LM_ADDR_0_BYTE_INDEX, ind_lm_addr_byte0); qat_hal_wr_indr_csr(handle, ae, (1 << ctx), INDIRECT_LM_ADDR_1_BYTE_INDEX, ind_lm_addr_byte1); + if (handle->chip_info->lm2lm3) { + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), LM_ADDR_2_INDIRECT, + ind_lm_addr2); + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), LM_ADDR_3_INDIRECT, + ind_lm_addr3); + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), + INDIRECT_LM_ADDR_2_BYTE_INDEX, + ind_lm_addr_byte2); + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), + INDIRECT_LM_ADDR_3_BYTE_INDEX, + ind_lm_addr_byte3); + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), + INDIRECT_T_INDEX, ind_t_index); + qat_hal_wr_indr_csr(handle, ae, BIT(ctx), + INDIRECT_T_INDEX_BYTE_INDEX, + ind_t_index_byte); + } qat_hal_wr_indr_csr(handle, ae, (1 << ctx), FUTURE_COUNT_SIGNAL_INDIRECT, ind_cnt_sig); qat_hal_wr_indr_csr(handle, ae, (1 << ctx), @@ -1125,7 +1329,7 @@ int qat_hal_batch_wr_lm(struct icp_qat_fw_loader_handle *handle, plm_init = plm_init->next; } /* exec micro codes */ - if (micro_inst_arry && (micro_inst_num > 0)) { + if (micro_inst_arry && micro_inst_num > 0) { micro_inst_arry[micro_inst_num++] = 0x0E000010000ull; stat = qat_hal_exec_micro_init_lm(handle, ae, 0, &first_exec, micro_inst_arry, @@ -1146,7 +1350,7 @@ static int qat_hal_put_rel_rd_xfer(struct icp_qat_fw_loader_handle *handle, unsigned short mask; unsigned short dr_offset = 0x10; - status = ctx_enables = qat_hal_rd_ae_csr(handle, ae, CTX_ENABLES); + ctx_enables = qat_hal_rd_ae_csr(handle, ae, CTX_ENABLES); if (CE_INUSE_CONTEXTS & ctx_enables) { if (ctx & 0x1) { pr_err("QAT: bad 4-ctx mode,ctx=0x%x\n", ctx); @@ -1271,7 +1475,7 @@ static int qat_hal_convert_abs_to_rel(struct icp_qat_fw_loader_handle } int qat_hal_init_gpr(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata) { @@ -1291,7 +1495,7 @@ int qat_hal_init_gpr(struct icp_qat_fw_loader_handle *handle, } else { reg = reg_num; type = reg_type; - if (!test_bit(ctx, (unsigned long *)&ctx_mask)) + if (!test_bit(ctx, &ctx_mask)) continue; } stat = qat_hal_wr_rel_reg(handle, ae, ctx, type, reg, regdata); @@ -1305,7 +1509,7 @@ int qat_hal_init_gpr(struct icp_qat_fw_loader_handle *handle, } int qat_hal_init_wr_xfer(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata) { @@ -1325,7 +1529,7 @@ int qat_hal_init_wr_xfer(struct icp_qat_fw_loader_handle *handle, } else { reg = reg_num; type = reg_type; - if (!test_bit(ctx, (unsigned long *)&ctx_mask)) + if (!test_bit(ctx, &ctx_mask)) continue; } stat = qat_hal_put_rel_wr_xfer(handle, ae, ctx, type, reg, @@ -1340,7 +1544,7 @@ int qat_hal_init_wr_xfer(struct icp_qat_fw_loader_handle *handle, } int qat_hal_init_rd_xfer(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, enum icp_qat_uof_regtype reg_type, unsigned short reg_num, unsigned int regdata) { @@ -1360,7 +1564,7 @@ int qat_hal_init_rd_xfer(struct icp_qat_fw_loader_handle *handle, } else { reg = reg_num; type = reg_type; - if (!test_bit(ctx, (unsigned long *)&ctx_mask)) + if (!test_bit(ctx, &ctx_mask)) continue; } stat = qat_hal_put_rel_rd_xfer(handle, ae, ctx, type, reg, @@ -1375,17 +1579,22 @@ int qat_hal_init_rd_xfer(struct icp_qat_fw_loader_handle *handle, } int qat_hal_init_nn(struct icp_qat_fw_loader_handle *handle, - unsigned char ae, unsigned char ctx_mask, + unsigned char ae, unsigned long ctx_mask, unsigned short reg_num, unsigned int regdata) { int stat = 0; unsigned char ctx; + if (!handle->chip_info->nn) { + dev_err(&handle->pci_dev->dev, "QAT: No next neigh in 0x%x\n", + handle->pci_dev->device); + return -EINVAL; + } if (ctx_mask == 0) return -EINVAL; for (ctx = 0; ctx < ICP_QAT_UCLO_MAX_CTX; ctx++) { - if (!test_bit(ctx, (unsigned long *)&ctx_mask)) + if (!test_bit(ctx, &ctx_mask)) continue; stat = qat_hal_put_rel_nn(handle, ae, ctx, reg_num, regdata); if (stat) { diff --git a/drivers/crypto/qat/qat_common/qat_uclo.c b/drivers/crypto/qat/qat_common/qat_uclo.c index 5d1f28cd6680..1fb5fc852f6b 100644 --- a/drivers/crypto/qat/qat_common/qat_uclo.c +++ b/drivers/crypto/qat/qat_common/qat_uclo.c @@ -74,7 +74,7 @@ static int qat_uclo_free_ae_data(struct icp_qat_uclo_aedata *ae_data) static char *qat_uclo_get_string(struct icp_qat_uof_strtable *str_table, unsigned int str_offset) { - if ((!str_table->table_len) || (str_offset > str_table->table_len)) + if (!str_table->table_len || str_offset > str_table->table_len) return NULL; return (char *)(((uintptr_t)(str_table->strings)) + str_offset); } @@ -311,7 +311,7 @@ static int qat_uclo_init_lmem_seg(struct icp_qat_fw_loader_handle *handle, unsigned int ae; if (qat_uclo_fetch_initmem_ae(handle, init_mem, - ICP_QAT_UCLO_MAX_LMEM_REG, &ae)) + handle->chip_info->lm_size, &ae)) return -EINVAL; if (qat_uclo_create_batch_init_list(handle, init_mem, ae, &obj_handle->lm_init_tab[ae])) @@ -324,6 +324,7 @@ static int qat_uclo_init_umem_seg(struct icp_qat_fw_loader_handle *handle, { struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; unsigned int ae, ustore_size, uaddr, i; + struct icp_qat_uclo_aedata *aed; ustore_size = obj_handle->ustore_phy_size; if (qat_uclo_fetch_initmem_ae(handle, init_mem, ustore_size, &ae)) @@ -333,11 +334,10 @@ static int qat_uclo_init_umem_seg(struct icp_qat_fw_loader_handle *handle, return -EINVAL; /* set the highest ustore address referenced */ uaddr = (init_mem->addr + init_mem->num_in_bytes) >> 0x2; - for (i = 0; i < obj_handle->ae_data[ae].slice_num; i++) { - if (obj_handle->ae_data[ae].ae_slices[i]. - encap_image->uwords_num < uaddr) - obj_handle->ae_data[ae].ae_slices[i]. - encap_image->uwords_num = uaddr; + aed = &obj_handle->ae_data[ae]; + for (i = 0; i < aed->slice_num; i++) { + if (aed->ae_slices[i].encap_image->uwords_num < uaddr) + aed->ae_slices[i].encap_image->uwords_num = uaddr; } return 0; } @@ -373,6 +373,8 @@ static int qat_uclo_init_ustore(struct icp_qat_fw_loader_handle *handle, unsigned int ustore_size; unsigned int patt_pos; struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned long cfg_ae_mask = handle->cfg_ae_mask; u64 *fill_data; uof_image = image->img_ptr; @@ -385,9 +387,13 @@ static int qat_uclo_init_ustore(struct icp_qat_fw_loader_handle *handle, sizeof(u64)); page = image->page; - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { if (!test_bit(ae, (unsigned long *)&uof_image->ae_assigned)) continue; + + if (!test_bit(ae, &cfg_ae_mask)) + continue; + ustore_size = obj_handle->ae_data[ae].eff_ustore_size; patt_pos = page->beg_addr_p + page->micro_words_num; @@ -406,6 +412,7 @@ static int qat_uclo_init_memory(struct icp_qat_fw_loader_handle *handle) int i, ae; struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; struct icp_qat_uof_initmem *initmem = obj_handle->init_mem_tab.init_mem; + unsigned long ae_mask = handle->hal_handle->ae_mask; for (i = 0; i < obj_handle->init_mem_tab.entry_num; i++) { if (initmem->num_in_bytes) { @@ -418,7 +425,8 @@ static int qat_uclo_init_memory(struct icp_qat_fw_loader_handle *handle) (sizeof(struct icp_qat_uof_memvar_attr) * initmem->val_attr_num)); } - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { + + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { if (qat_hal_batch_wr_lm(handle, ae, obj_handle->lm_init_tab[ae])) { pr_err("QAT: fail to batch init lmem for AE %d\n", ae); @@ -536,7 +544,7 @@ qat_uclo_check_image_compat(struct icp_qat_uof_encap_obj *encap_uof_obj, (encap_uof_obj->beg_uof + code_page->neigh_reg_tab_offset); if (neigh_reg_tab->entry_num) { - pr_err("QAT: UOF can't contain shared control store feature\n"); + pr_err("QAT: UOF can't contain neighbor register table\n"); return -EINVAL; } if (image->numpages > 1) { @@ -649,11 +657,13 @@ static int qat_uclo_map_ae(struct icp_qat_fw_loader_handle *handle, int max_ae) int i, ae; int mflag = 0; struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned long cfg_ae_mask = handle->cfg_ae_mask; - for (ae = 0; ae < max_ae; ae++) { - if (!test_bit(ae, - (unsigned long *)&handle->hal_handle->ae_mask)) + for_each_set_bit(ae, &ae_mask, max_ae) { + if (!test_bit(ae, &cfg_ae_mask)) continue; + for (i = 0; i < obj_handle->uimage_num; i++) { if (!test_bit(ae, (unsigned long *) &obj_handle->ae_uimage[i].img_ptr->ae_assigned)) @@ -718,6 +728,8 @@ qat_uclo_get_dev_type(struct icp_qat_fw_loader_handle *handle) return ICP_QAT_AC_C62X_DEV_TYPE; case PCI_DEVICE_ID_INTEL_QAT_C3XXX: return ICP_QAT_AC_C3XXX_DEV_TYPE; + case ADF_4XXX_PCI_DEVICE_ID: + return ICP_QAT_AC_4XXX_A_DEV_TYPE; default: pr_err("QAT: unsupported device 0x%x\n", handle->pci_dev->device); @@ -736,8 +748,8 @@ static int qat_uclo_check_uof_compat(struct icp_qat_uclo_objhandle *obj_handle) return -EINVAL; } maj_ver = obj_handle->prod_rev & 0xff; - if ((obj_handle->encap_uof_obj.obj_hdr->max_cpu_ver < maj_ver) || - (obj_handle->encap_uof_obj.obj_hdr->min_cpu_ver > maj_ver)) { + if (obj_handle->encap_uof_obj.obj_hdr->max_cpu_ver < maj_ver || + obj_handle->encap_uof_obj.obj_hdr->min_cpu_ver > maj_ver) { pr_err("QAT: UOF majVer 0x%x out of range\n", maj_ver); return -EINVAL; } @@ -845,6 +857,8 @@ static int qat_uclo_init_reg_sym(struct icp_qat_fw_loader_handle *handle, static int qat_uclo_init_globals(struct icp_qat_fw_loader_handle *handle) { struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; + unsigned long ae_mask = handle->hal_handle->ae_mask; + struct icp_qat_uclo_aedata *aed; unsigned int s, ae; if (obj_handle->global_inited) @@ -855,13 +869,13 @@ static int qat_uclo_init_globals(struct icp_qat_fw_loader_handle *handle) return -EINVAL; } } - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { - for (s = 0; s < obj_handle->ae_data[ae].slice_num; s++) { - if (!obj_handle->ae_data[ae].ae_slices[s].encap_image) + + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + aed = &obj_handle->ae_data[ae]; + for (s = 0; s < aed->slice_num; s++) { + if (!aed->ae_slices[s].encap_image) continue; - if (qat_uclo_init_reg_sym(handle, ae, - obj_handle->ae_data[ae]. - ae_slices[s].encap_image)) + if (qat_uclo_init_reg_sym(handle, ae, aed->ae_slices[s].encap_image)) return -EINVAL; } } @@ -869,46 +883,83 @@ static int qat_uclo_init_globals(struct icp_qat_fw_loader_handle *handle) return 0; } +static int qat_hal_set_modes(struct icp_qat_fw_loader_handle *handle, + struct icp_qat_uclo_objhandle *obj_handle, + unsigned char ae, + struct icp_qat_uof_image *uof_image) +{ + unsigned char mode; + int ret; + + mode = ICP_QAT_CTX_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_ctx_mode(handle, ae, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_ctx_mode error\n"); + return ret; + } + if (handle->chip_info->nn) { + mode = ICP_QAT_NN_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_nn_mode(handle, ae, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_nn_mode error\n"); + return ret; + } + } + mode = ICP_QAT_LOC_MEM0_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM0, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_lm_mode LMEM0 error\n"); + return ret; + } + mode = ICP_QAT_LOC_MEM1_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM1, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_lm_mode LMEM1 error\n"); + return ret; + } + if (handle->chip_info->lm2lm3) { + mode = ICP_QAT_LOC_MEM2_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM2, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_lm_mode LMEM2 error\n"); + return ret; + } + mode = ICP_QAT_LOC_MEM3_MODE(uof_image->ae_mode); + ret = qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM3, mode); + if (ret) { + pr_err("QAT: qat_hal_set_ae_lm_mode LMEM3 error\n"); + return ret; + } + mode = ICP_QAT_LOC_TINDEX_MODE(uof_image->ae_mode); + qat_hal_set_ae_tindex_mode(handle, ae, mode); + } + return 0; +} + static int qat_uclo_set_ae_mode(struct icp_qat_fw_loader_handle *handle) { - unsigned char ae, nn_mode, s; struct icp_qat_uof_image *uof_image; struct icp_qat_uclo_aedata *ae_data; struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned long cfg_ae_mask = handle->cfg_ae_mask; + unsigned char ae, s; + int error; - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { - if (!test_bit(ae, - (unsigned long *)&handle->hal_handle->ae_mask)) + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + if (!test_bit(ae, &cfg_ae_mask)) continue; + ae_data = &obj_handle->ae_data[ae]; for (s = 0; s < min_t(unsigned int, ae_data->slice_num, ICP_QAT_UCLO_MAX_CTX); s++) { if (!obj_handle->ae_data[ae].ae_slices[s].encap_image) continue; uof_image = ae_data->ae_slices[s].encap_image->img_ptr; - if (qat_hal_set_ae_ctx_mode(handle, ae, - (char)ICP_QAT_CTX_MODE - (uof_image->ae_mode))) { - pr_err("QAT: qat_hal_set_ae_ctx_mode error\n"); - return -EFAULT; - } - nn_mode = ICP_QAT_NN_MODE(uof_image->ae_mode); - if (qat_hal_set_ae_nn_mode(handle, ae, nn_mode)) { - pr_err("QAT: qat_hal_set_ae_nn_mode error\n"); - return -EFAULT; - } - if (qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM0, - (char)ICP_QAT_LOC_MEM0_MODE - (uof_image->ae_mode))) { - pr_err("QAT: qat_hal_set_ae_lm_mode LMEM0 error\n"); - return -EFAULT; - } - if (qat_hal_set_ae_lm_mode(handle, ae, ICP_LMEM1, - (char)ICP_QAT_LOC_MEM1_MODE - (uof_image->ae_mode))) { - pr_err("QAT: qat_hal_set_ae_lm_mode LMEM1 error\n"); - return -EFAULT; - } + error = qat_hal_set_modes(handle, obj_handle, ae, + uof_image); + if (error) + return error; } } return 0; @@ -1003,10 +1054,11 @@ static int qat_uclo_map_suof_file_hdr(struct icp_qat_fw_loader_handle *handle, return 0; } -static void qat_uclo_map_simg(struct icp_qat_suof_handle *suof_handle, +static void qat_uclo_map_simg(struct icp_qat_fw_loader_handle *handle, struct icp_qat_suof_img_hdr *suof_img_hdr, struct icp_qat_suof_chunk_hdr *suof_chunk_hdr) { + struct icp_qat_suof_handle *suof_handle = handle->sobj_handle; struct icp_qat_simg_ae_mode *ae_mode; struct icp_qat_suof_objhdr *suof_objhdr; @@ -1021,10 +1073,10 @@ static void qat_uclo_map_simg(struct icp_qat_suof_handle *suof_handle, suof_img_hdr->css_key = (suof_img_hdr->css_header + sizeof(struct icp_qat_css_hdr)); suof_img_hdr->css_signature = suof_img_hdr->css_key + - ICP_QAT_CSS_FWSK_MODULUS_LEN + - ICP_QAT_CSS_FWSK_EXPONENT_LEN; + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) + + ICP_QAT_CSS_FWSK_EXPONENT_LEN(handle); suof_img_hdr->css_simg = suof_img_hdr->css_signature + - ICP_QAT_CSS_SIGNATURE_LEN; + ICP_QAT_CSS_SIGNATURE_LEN(handle); ae_mode = (struct icp_qat_simg_ae_mode *)(suof_img_hdr->css_simg); suof_img_hdr->ae_mask = ae_mode->ae_mask; @@ -1064,8 +1116,8 @@ static int qat_uclo_check_simg_compat(struct icp_qat_fw_loader_handle *handle, return -EINVAL; } maj_ver = prod_rev & 0xff; - if ((maj_ver > img_ae_mode->devmax_ver) || - (maj_ver < img_ae_mode->devmin_ver)) { + if (maj_ver > img_ae_mode->devmax_ver || + maj_ver < img_ae_mode->devmin_ver) { pr_err("QAT: incompatible device majver 0x%x\n", maj_ver); return -EINVAL; } @@ -1108,7 +1160,7 @@ static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle, unsigned int i = 0; struct icp_qat_suof_img_hdr img_header; - if (!suof_ptr || (suof_size == 0)) { + if (!suof_ptr || suof_size == 0) { pr_err("QAT: input parameter SUOF pointer/size is NULL\n"); return -EINVAL; } @@ -1130,20 +1182,24 @@ static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle, if (!suof_img_hdr) return -ENOMEM; suof_handle->img_table.simg_hdr = suof_img_hdr; - } - for (i = 0; i < suof_handle->img_table.num_simgs; i++) { - qat_uclo_map_simg(handle->sobj_handle, &suof_img_hdr[i], - &suof_chunk_hdr[1 + i]); - ret = qat_uclo_check_simg_compat(handle, - &suof_img_hdr[i]); - if (ret) - return ret; - if ((suof_img_hdr[i].ae_mask & 0x1) != 0) - ae0_img = i; + for (i = 0; i < suof_handle->img_table.num_simgs; i++) { + qat_uclo_map_simg(handle, &suof_img_hdr[i], + &suof_chunk_hdr[1 + i]); + ret = qat_uclo_check_simg_compat(handle, + &suof_img_hdr[i]); + if (ret) + return ret; + suof_img_hdr[i].ae_mask &= handle->cfg_ae_mask; + if ((suof_img_hdr[i].ae_mask & 0x1) != 0) + ae0_img = i; + } + + if (!handle->chip_info->tgroup_share_ustore) { + qat_uclo_tail_img(suof_img_hdr, ae0_img, + suof_handle->img_table.num_simgs); + } } - qat_uclo_tail_img(suof_img_hdr, ae0_img, - suof_handle->img_table.num_simgs); return 0; } @@ -1153,18 +1209,26 @@ static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle, static int qat_uclo_auth_fw(struct icp_qat_fw_loader_handle *handle, struct icp_qat_fw_auth_desc *desc) { - unsigned int fcu_sts, retry = 0; + u32 fcu_sts, retry = 0; + u32 fcu_ctl_csr, fcu_sts_csr; + u32 fcu_dram_hi_csr, fcu_dram_lo_csr; u64 bus_addr; bus_addr = ADD_ADDR(desc->css_hdr_high, desc->css_hdr_low) - sizeof(struct icp_qat_auth_chunk); - SET_CAP_CSR(handle, FCU_DRAM_ADDR_HI, (bus_addr >> BITS_IN_DWORD)); - SET_CAP_CSR(handle, FCU_DRAM_ADDR_LO, bus_addr); - SET_CAP_CSR(handle, FCU_CONTROL, FCU_CTRL_CMD_AUTH); + + fcu_ctl_csr = handle->chip_info->fcu_ctl_csr; + fcu_sts_csr = handle->chip_info->fcu_sts_csr; + fcu_dram_hi_csr = handle->chip_info->fcu_dram_addr_hi; + fcu_dram_lo_csr = handle->chip_info->fcu_dram_addr_lo; + + SET_CAP_CSR(handle, fcu_dram_hi_csr, (bus_addr >> BITS_IN_DWORD)); + SET_CAP_CSR(handle, fcu_dram_lo_csr, bus_addr); + SET_CAP_CSR(handle, fcu_ctl_csr, FCU_CTRL_CMD_AUTH); do { msleep(FW_AUTH_WAIT_PERIOD); - fcu_sts = GET_CAP_CSR(handle, FCU_STATUS); + fcu_sts = GET_CAP_CSR(handle, fcu_sts_csr); if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_FAIL) goto auth_fail; if (((fcu_sts >> FCU_STS_AUTHFWLD_POS) & 0x1)) @@ -1177,6 +1241,83 @@ auth_fail: return -EINVAL; } +static bool qat_uclo_is_broadcast(struct icp_qat_fw_loader_handle *handle, + int imgid) +{ + struct icp_qat_suof_handle *sobj_handle; + + if (!handle->chip_info->tgroup_share_ustore) + return false; + + sobj_handle = (struct icp_qat_suof_handle *)handle->sobj_handle; + if (handle->hal_handle->admin_ae_mask & + sobj_handle->img_table.simg_hdr[imgid].ae_mask) + return false; + + return true; +} + +static int qat_uclo_broadcast_load_fw(struct icp_qat_fw_loader_handle *handle, + struct icp_qat_fw_auth_desc *desc) +{ + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned long desc_ae_mask = desc->ae_mask; + u32 fcu_sts, ae_broadcast_mask = 0; + u32 fcu_loaded_csr, ae_loaded; + u32 fcu_sts_csr, fcu_ctl_csr; + unsigned int ae, retry = 0; + + if (handle->chip_info->tgroup_share_ustore) { + fcu_ctl_csr = handle->chip_info->fcu_ctl_csr; + fcu_sts_csr = handle->chip_info->fcu_sts_csr; + fcu_loaded_csr = handle->chip_info->fcu_loaded_ae_csr; + } else { + pr_err("Chip 0x%x doesn't support broadcast load\n", + handle->pci_dev->device); + return -EINVAL; + } + + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + if (qat_hal_check_ae_active(handle, (unsigned char)ae)) { + pr_err("QAT: Broadcast load failed. AE is not enabled or active.\n"); + return -EINVAL; + } + + if (test_bit(ae, &desc_ae_mask)) + ae_broadcast_mask |= 1 << ae; + } + + if (ae_broadcast_mask) { + SET_CAP_CSR(handle, FCU_ME_BROADCAST_MASK_TYPE, + ae_broadcast_mask); + + SET_CAP_CSR(handle, fcu_ctl_csr, FCU_CTRL_CMD_LOAD); + + do { + msleep(FW_AUTH_WAIT_PERIOD); + fcu_sts = GET_CAP_CSR(handle, fcu_sts_csr); + fcu_sts &= FCU_AUTH_STS_MASK; + + if (fcu_sts == FCU_STS_LOAD_FAIL) { + pr_err("Broadcast load failed: 0x%x)\n", fcu_sts); + return -EINVAL; + } else if (fcu_sts == FCU_STS_LOAD_DONE) { + ae_loaded = GET_CAP_CSR(handle, fcu_loaded_csr); + ae_loaded >>= handle->chip_info->fcu_loaded_ae_pos; + + if ((ae_loaded & ae_broadcast_mask) == ae_broadcast_mask) + break; + } + } while (retry++ < FW_AUTH_MAX_RETRY); + + if (retry > FW_AUTH_MAX_RETRY) { + pr_err("QAT: broadcast load failed timeout %d\n", retry); + return -EINVAL; + } + } + return 0; +} + static int qat_uclo_simg_alloc(struct icp_qat_fw_loader_handle *handle, struct icp_firml_dram_desc *dram_desc, unsigned int size) @@ -1197,11 +1338,15 @@ static int qat_uclo_simg_alloc(struct icp_qat_fw_loader_handle *handle, static void qat_uclo_simg_free(struct icp_qat_fw_loader_handle *handle, struct icp_firml_dram_desc *dram_desc) { - dma_free_coherent(&handle->pci_dev->dev, - (size_t)(dram_desc->dram_size), - (dram_desc->dram_base_addr_v), - dram_desc->dram_bus_addr); - memset(dram_desc, 0, sizeof(*dram_desc)); + if (handle && dram_desc && dram_desc->dram_base_addr_v) { + dma_free_coherent(&handle->pci_dev->dev, + (size_t)(dram_desc->dram_size), + dram_desc->dram_base_addr_v, + dram_desc->dram_bus_addr); + } + + if (dram_desc) + memset(dram_desc, 0, sizeof(*dram_desc)); } static void qat_uclo_ummap_auth_fw(struct icp_qat_fw_loader_handle *handle, @@ -1209,12 +1354,14 @@ static void qat_uclo_ummap_auth_fw(struct icp_qat_fw_loader_handle *handle, { struct icp_firml_dram_desc dram_desc; - dram_desc.dram_base_addr_v = *desc; - dram_desc.dram_bus_addr = ((struct icp_qat_auth_chunk *) - (*desc))->chunk_bus_addr; - dram_desc.dram_size = ((struct icp_qat_auth_chunk *) - (*desc))->chunk_size; - qat_uclo_simg_free(handle, &dram_desc); + if (*desc) { + dram_desc.dram_base_addr_v = *desc; + dram_desc.dram_bus_addr = ((struct icp_qat_auth_chunk *) + (*desc))->chunk_bus_addr; + dram_desc.dram_size = ((struct icp_qat_auth_chunk *) + (*desc))->chunk_size; + qat_uclo_simg_free(handle, &dram_desc); + } } static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle, @@ -1226,15 +1373,16 @@ static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle, struct icp_qat_auth_chunk *auth_chunk; u64 virt_addr, bus_addr, virt_base; unsigned int length, simg_offset = sizeof(*auth_chunk); + struct icp_qat_simg_ae_mode *simg_ae_mode; struct icp_firml_dram_desc img_desc; - if (size > (ICP_QAT_AE_IMG_OFFSET + ICP_QAT_CSS_MAX_IMAGE_LEN)) { + if (size > (ICP_QAT_AE_IMG_OFFSET(handle) + ICP_QAT_CSS_MAX_IMAGE_LEN)) { pr_err("QAT: error, input image size overflow %d\n", size); return -EINVAL; } length = (css_hdr->fw_type == CSS_AE_FIRMWARE) ? - ICP_QAT_CSS_AE_SIMG_LEN + simg_offset : - size + ICP_QAT_CSS_FWSK_PAD_LEN + simg_offset; + ICP_QAT_CSS_AE_SIMG_LEN(handle) + simg_offset : + size + ICP_QAT_CSS_FWSK_PAD_LEN(handle) + simg_offset; if (qat_uclo_simg_alloc(handle, &img_desc, length)) { pr_err("QAT: error, allocate continuous dram fail\n"); return -ENOMEM; @@ -1261,42 +1409,42 @@ static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle, memcpy((void *)(uintptr_t)virt_addr, (void *)(image + sizeof(*css_hdr)), - ICP_QAT_CSS_FWSK_MODULUS_LEN); + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle)); /* padding */ - memset((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN), - 0, ICP_QAT_CSS_FWSK_PAD_LEN); + memset((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle)), + 0, ICP_QAT_CSS_FWSK_PAD_LEN(handle)); /* exponent */ - memcpy((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN + - ICP_QAT_CSS_FWSK_PAD_LEN), + memcpy((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) + + ICP_QAT_CSS_FWSK_PAD_LEN(handle)), (void *)(image + sizeof(*css_hdr) + - ICP_QAT_CSS_FWSK_MODULUS_LEN), + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle)), sizeof(unsigned int)); /* signature */ bus_addr = ADD_ADDR(auth_desc->fwsk_pub_high, auth_desc->fwsk_pub_low) + - ICP_QAT_CSS_FWSK_PUB_LEN; - virt_addr = virt_addr + ICP_QAT_CSS_FWSK_PUB_LEN; + ICP_QAT_CSS_FWSK_PUB_LEN(handle); + virt_addr = virt_addr + ICP_QAT_CSS_FWSK_PUB_LEN(handle); auth_desc->signature_high = (unsigned int)(bus_addr >> BITS_IN_DWORD); auth_desc->signature_low = (unsigned int)bus_addr; memcpy((void *)(uintptr_t)virt_addr, (void *)(image + sizeof(*css_hdr) + - ICP_QAT_CSS_FWSK_MODULUS_LEN + - ICP_QAT_CSS_FWSK_EXPONENT_LEN), - ICP_QAT_CSS_SIGNATURE_LEN); + ICP_QAT_CSS_FWSK_MODULUS_LEN(handle) + + ICP_QAT_CSS_FWSK_EXPONENT_LEN(handle)), + ICP_QAT_CSS_SIGNATURE_LEN(handle)); bus_addr = ADD_ADDR(auth_desc->signature_high, auth_desc->signature_low) + - ICP_QAT_CSS_SIGNATURE_LEN; - virt_addr += ICP_QAT_CSS_SIGNATURE_LEN; + ICP_QAT_CSS_SIGNATURE_LEN(handle); + virt_addr += ICP_QAT_CSS_SIGNATURE_LEN(handle); auth_desc->img_high = (unsigned int)(bus_addr >> BITS_IN_DWORD); auth_desc->img_low = (unsigned int)bus_addr; - auth_desc->img_len = size - ICP_QAT_AE_IMG_OFFSET; + auth_desc->img_len = size - ICP_QAT_AE_IMG_OFFSET(handle); memcpy((void *)(uintptr_t)virt_addr, - (void *)(image + ICP_QAT_AE_IMG_OFFSET), + (void *)(image + ICP_QAT_AE_IMG_OFFSET(handle)), auth_desc->img_len); virt_addr = virt_base; /* AE firmware */ @@ -1315,6 +1463,11 @@ static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle, auth_desc->img_ae_insts_high = (unsigned int) (bus_addr >> BITS_IN_DWORD); auth_desc->img_ae_insts_low = (unsigned int)bus_addr; + virt_addr += sizeof(struct icp_qat_css_hdr); + virt_addr += ICP_QAT_CSS_FWSK_PUB_LEN(handle); + virt_addr += ICP_QAT_CSS_SIGNATURE_LEN(handle); + simg_ae_mode = (struct icp_qat_simg_ae_mode *)(uintptr_t)virt_addr; + auth_desc->ae_mask = simg_ae_mode->ae_mask & handle->cfg_ae_mask; } else { auth_desc->img_ae_insts_high = auth_desc->img_high; auth_desc->img_ae_insts_low = auth_desc->img_low; @@ -1326,35 +1479,40 @@ static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle, static int qat_uclo_load_fw(struct icp_qat_fw_loader_handle *handle, struct icp_qat_fw_auth_desc *desc) { + unsigned long ae_mask = handle->hal_handle->ae_mask; + u32 fcu_sts_csr, fcu_ctl_csr; + u32 loaded_aes, loaded_csr; unsigned int i; - unsigned int fcu_sts; - struct icp_qat_simg_ae_mode *virt_addr; - unsigned int fcu_loaded_ae_pos = FCU_LOADED_AE_POS; - - virt_addr = (void *)((uintptr_t)desc + - sizeof(struct icp_qat_auth_chunk) + - sizeof(struct icp_qat_css_hdr) + - ICP_QAT_CSS_FWSK_PUB_LEN + - ICP_QAT_CSS_SIGNATURE_LEN); - for (i = 0; i < handle->hal_handle->ae_max_num; i++) { + u32 fcu_sts; + + fcu_ctl_csr = handle->chip_info->fcu_ctl_csr; + fcu_sts_csr = handle->chip_info->fcu_sts_csr; + loaded_csr = handle->chip_info->fcu_loaded_ae_csr; + + for_each_set_bit(i, &ae_mask, handle->hal_handle->ae_max_num) { int retry = 0; - if (!((virt_addr->ae_mask >> i) & 0x1)) + if (!((desc->ae_mask >> i) & 0x1)) continue; if (qat_hal_check_ae_active(handle, i)) { pr_err("QAT: AE %d is active\n", i); return -EINVAL; } - SET_CAP_CSR(handle, FCU_CONTROL, - (FCU_CTRL_CMD_LOAD | (i << FCU_CTRL_AE_POS))); + SET_CAP_CSR(handle, fcu_ctl_csr, + (FCU_CTRL_CMD_LOAD | + (1 << FCU_CTRL_BROADCAST_POS) | + (i << FCU_CTRL_AE_POS))); do { msleep(FW_AUTH_WAIT_PERIOD); - fcu_sts = GET_CAP_CSR(handle, FCU_STATUS); - if (((fcu_sts & FCU_AUTH_STS_MASK) == - FCU_STS_LOAD_DONE) && - ((fcu_sts >> fcu_loaded_ae_pos) & (1 << i))) - break; + fcu_sts = GET_CAP_CSR(handle, fcu_sts_csr); + if ((fcu_sts & FCU_AUTH_STS_MASK) == + FCU_STS_LOAD_DONE) { + loaded_aes = GET_CAP_CSR(handle, loaded_csr); + loaded_aes >>= handle->chip_info->fcu_loaded_ae_pos; + if (loaded_aes & (1 << i)) + break; + } } while (retry++ < FW_AUTH_MAX_RETRY); if (retry > FW_AUTH_MAX_RETRY) { pr_err("QAT: firmware load failed timeout %x\n", retry); @@ -1387,14 +1545,16 @@ int qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle, struct icp_qat_fw_auth_desc *desc = NULL; int status = 0; - if (handle->fw_auth) { + if (handle->chip_info->fw_auth) { if (!qat_uclo_map_auth_fw(handle, addr_ptr, mem_size, &desc)) status = qat_uclo_auth_fw(handle, desc); qat_uclo_ummap_auth_fw(handle, &desc); } else { - if (handle->pci_dev->device == PCI_DEVICE_ID_INTEL_QAT_C3XXX) { - pr_err("QAT: C3XXX doesn't support unsigned MMP\n"); - return -EINVAL; + if (!handle->chip_info->sram_visible) { + dev_dbg(&handle->pci_dev->dev, + "QAT MMP fw not loaded for device 0x%x", + handle->pci_dev->device); + return status; } qat_uclo_wr_sram_by_words(handle, 0, addr_ptr, mem_size); } @@ -1437,25 +1597,281 @@ out_objbuf_err: return -ENOMEM; } +static int qat_uclo_map_mof_file_hdr(struct icp_qat_fw_loader_handle *handle, + struct icp_qat_mof_file_hdr *mof_ptr, + u32 mof_size) +{ + struct icp_qat_mof_handle *mobj_handle = handle->mobj_handle; + unsigned int min_ver_offset; + unsigned int checksum; + + mobj_handle->file_id = ICP_QAT_MOF_FID; + mobj_handle->mof_buf = (char *)mof_ptr; + mobj_handle->mof_size = mof_size; + + min_ver_offset = mof_size - offsetof(struct icp_qat_mof_file_hdr, + min_ver); + checksum = qat_uclo_calc_str_checksum(&mof_ptr->min_ver, + min_ver_offset); + if (checksum != mof_ptr->checksum) { + pr_err("QAT: incorrect MOF checksum\n"); + return -EINVAL; + } + + mobj_handle->checksum = mof_ptr->checksum; + mobj_handle->min_ver = mof_ptr->min_ver; + mobj_handle->maj_ver = mof_ptr->maj_ver; + return 0; +} + +static void qat_uclo_del_mof(struct icp_qat_fw_loader_handle *handle) +{ + struct icp_qat_mof_handle *mobj_handle = handle->mobj_handle; + + kfree(mobj_handle->obj_table.obj_hdr); + mobj_handle->obj_table.obj_hdr = NULL; + kfree(handle->mobj_handle); + handle->mobj_handle = NULL; +} + +static int qat_uclo_seek_obj_inside_mof(struct icp_qat_mof_handle *mobj_handle, + char *obj_name, char **obj_ptr, + unsigned int *obj_size) +{ + struct icp_qat_mof_objhdr *obj_hdr = mobj_handle->obj_table.obj_hdr; + unsigned int i; + + for (i = 0; i < mobj_handle->obj_table.num_objs; i++) { + if (!strncmp(obj_hdr[i].obj_name, obj_name, + ICP_QAT_SUOF_OBJ_NAME_LEN)) { + *obj_ptr = obj_hdr[i].obj_buf; + *obj_size = obj_hdr[i].obj_size; + return 0; + } + } + + pr_err("QAT: object %s is not found inside MOF\n", obj_name); + return -EINVAL; +} + +static int qat_uclo_map_obj_from_mof(struct icp_qat_mof_handle *mobj_handle, + struct icp_qat_mof_objhdr *mobj_hdr, + struct icp_qat_mof_obj_chunkhdr *obj_chunkhdr) +{ + u8 *obj; + + if (!strncmp(obj_chunkhdr->chunk_id, ICP_QAT_UOF_IMAG, + ICP_QAT_MOF_OBJ_CHUNKID_LEN)) { + obj = mobj_handle->uobjs_hdr + obj_chunkhdr->offset; + } else if (!strncmp(obj_chunkhdr->chunk_id, ICP_QAT_SUOF_IMAG, + ICP_QAT_MOF_OBJ_CHUNKID_LEN)) { + obj = mobj_handle->sobjs_hdr + obj_chunkhdr->offset; + } else { + pr_err("QAT: unsupported chunk id\n"); + return -EINVAL; + } + mobj_hdr->obj_buf = obj; + mobj_hdr->obj_size = (unsigned int)obj_chunkhdr->size; + mobj_hdr->obj_name = obj_chunkhdr->name + mobj_handle->sym_str; + return 0; +} + +static int qat_uclo_map_objs_from_mof(struct icp_qat_mof_handle *mobj_handle) +{ + struct icp_qat_mof_obj_chunkhdr *uobj_chunkhdr; + struct icp_qat_mof_obj_chunkhdr *sobj_chunkhdr; + struct icp_qat_mof_obj_hdr *uobj_hdr; + struct icp_qat_mof_obj_hdr *sobj_hdr; + struct icp_qat_mof_objhdr *mobj_hdr; + unsigned int uobj_chunk_num = 0; + unsigned int sobj_chunk_num = 0; + unsigned int *valid_chunk; + int ret, i; + + uobj_hdr = (struct icp_qat_mof_obj_hdr *)mobj_handle->uobjs_hdr; + sobj_hdr = (struct icp_qat_mof_obj_hdr *)mobj_handle->sobjs_hdr; + if (uobj_hdr) + uobj_chunk_num = uobj_hdr->num_chunks; + if (sobj_hdr) + sobj_chunk_num = sobj_hdr->num_chunks; + + mobj_hdr = kzalloc((uobj_chunk_num + sobj_chunk_num) * + sizeof(*mobj_hdr), GFP_KERNEL); + if (!mobj_hdr) + return -ENOMEM; + + mobj_handle->obj_table.obj_hdr = mobj_hdr; + valid_chunk = &mobj_handle->obj_table.num_objs; + uobj_chunkhdr = (struct icp_qat_mof_obj_chunkhdr *) + ((uintptr_t)uobj_hdr + sizeof(*uobj_hdr)); + sobj_chunkhdr = (struct icp_qat_mof_obj_chunkhdr *) + ((uintptr_t)sobj_hdr + sizeof(*sobj_hdr)); + + /* map uof objects */ + for (i = 0; i < uobj_chunk_num; i++) { + ret = qat_uclo_map_obj_from_mof(mobj_handle, + &mobj_hdr[*valid_chunk], + &uobj_chunkhdr[i]); + if (ret) + return ret; + (*valid_chunk)++; + } + + /* map suof objects */ + for (i = 0; i < sobj_chunk_num; i++) { + ret = qat_uclo_map_obj_from_mof(mobj_handle, + &mobj_hdr[*valid_chunk], + &sobj_chunkhdr[i]); + if (ret) + return ret; + (*valid_chunk)++; + } + + if ((uobj_chunk_num + sobj_chunk_num) != *valid_chunk) { + pr_err("QAT: inconsistent UOF/SUOF chunk amount\n"); + return -EINVAL; + } + return 0; +} + +static void qat_uclo_map_mof_symobjs(struct icp_qat_mof_handle *mobj_handle, + struct icp_qat_mof_chunkhdr *mof_chunkhdr) +{ + char **sym_str = (char **)&mobj_handle->sym_str; + unsigned int *sym_size = &mobj_handle->sym_size; + struct icp_qat_mof_str_table *str_table_obj; + + *sym_size = *(unsigned int *)(uintptr_t) + (mof_chunkhdr->offset + mobj_handle->mof_buf); + *sym_str = (char *)(uintptr_t) + (mobj_handle->mof_buf + mof_chunkhdr->offset + + sizeof(str_table_obj->tab_len)); +} + +static void qat_uclo_map_mof_chunk(struct icp_qat_mof_handle *mobj_handle, + struct icp_qat_mof_chunkhdr *mof_chunkhdr) +{ + char *chunk_id = mof_chunkhdr->chunk_id; + + if (!strncmp(chunk_id, ICP_QAT_MOF_SYM_OBJS, ICP_QAT_MOF_OBJ_ID_LEN)) + qat_uclo_map_mof_symobjs(mobj_handle, mof_chunkhdr); + else if (!strncmp(chunk_id, ICP_QAT_UOF_OBJS, ICP_QAT_MOF_OBJ_ID_LEN)) + mobj_handle->uobjs_hdr = mobj_handle->mof_buf + + mof_chunkhdr->offset; + else if (!strncmp(chunk_id, ICP_QAT_SUOF_OBJS, ICP_QAT_MOF_OBJ_ID_LEN)) + mobj_handle->sobjs_hdr = mobj_handle->mof_buf + + mof_chunkhdr->offset; +} + +static int qat_uclo_check_mof_format(struct icp_qat_mof_file_hdr *mof_hdr) +{ + int maj = mof_hdr->maj_ver & 0xff; + int min = mof_hdr->min_ver & 0xff; + + if (mof_hdr->file_id != ICP_QAT_MOF_FID) { + pr_err("QAT: invalid header 0x%x\n", mof_hdr->file_id); + return -EINVAL; + } + + if (mof_hdr->num_chunks <= 0x1) { + pr_err("QAT: MOF chunk amount is incorrect\n"); + return -EINVAL; + } + if (maj != ICP_QAT_MOF_MAJVER || min != ICP_QAT_MOF_MINVER) { + pr_err("QAT: bad MOF version, major 0x%x, minor 0x%x\n", + maj, min); + return -EINVAL; + } + return 0; +} + +static int qat_uclo_map_mof_obj(struct icp_qat_fw_loader_handle *handle, + struct icp_qat_mof_file_hdr *mof_ptr, + u32 mof_size, char *obj_name, char **obj_ptr, + unsigned int *obj_size) +{ + struct icp_qat_mof_chunkhdr *mof_chunkhdr; + unsigned int file_id = mof_ptr->file_id; + struct icp_qat_mof_handle *mobj_handle; + unsigned short chunks_num; + unsigned int i; + int ret; + + if (file_id == ICP_QAT_UOF_FID || file_id == ICP_QAT_SUOF_FID) { + if (obj_ptr) + *obj_ptr = (char *)mof_ptr; + if (obj_size) + *obj_size = mof_size; + return 0; + } + if (qat_uclo_check_mof_format(mof_ptr)) + return -EINVAL; + + mobj_handle = kzalloc(sizeof(*mobj_handle), GFP_KERNEL); + if (!mobj_handle) + return -ENOMEM; + + handle->mobj_handle = mobj_handle; + ret = qat_uclo_map_mof_file_hdr(handle, mof_ptr, mof_size); + if (ret) + return ret; + + mof_chunkhdr = (void *)mof_ptr + sizeof(*mof_ptr); + chunks_num = mof_ptr->num_chunks; + + /* Parse MOF file chunks */ + for (i = 0; i < chunks_num; i++) + qat_uclo_map_mof_chunk(mobj_handle, &mof_chunkhdr[i]); + + /* All sym_objs uobjs and sobjs should be available */ + if (!mobj_handle->sym_str || + (!mobj_handle->uobjs_hdr && !mobj_handle->sobjs_hdr)) + return -EINVAL; + + ret = qat_uclo_map_objs_from_mof(mobj_handle); + if (ret) + return ret; + + /* Seek specified uof object in MOF */ + return qat_uclo_seek_obj_inside_mof(mobj_handle, obj_name, + obj_ptr, obj_size); +} + int qat_uclo_map_obj(struct icp_qat_fw_loader_handle *handle, - void *addr_ptr, int mem_size) + void *addr_ptr, u32 mem_size, char *obj_name) { + char *obj_addr; + u32 obj_size; + int ret; + BUILD_BUG_ON(ICP_QAT_UCLO_MAX_AE >= (sizeof(handle->hal_handle->ae_mask) * 8)); if (!handle || !addr_ptr || mem_size < 24) return -EINVAL; - return (handle->fw_auth) ? - qat_uclo_map_suof_obj(handle, addr_ptr, mem_size) : - qat_uclo_map_uof_obj(handle, addr_ptr, mem_size); + if (obj_name) { + ret = qat_uclo_map_mof_obj(handle, addr_ptr, mem_size, obj_name, + &obj_addr, &obj_size); + if (ret) + return ret; + } else { + obj_addr = addr_ptr; + obj_size = mem_size; + } + + return (handle->chip_info->fw_auth) ? + qat_uclo_map_suof_obj(handle, obj_addr, obj_size) : + qat_uclo_map_uof_obj(handle, obj_addr, obj_size); } -void qat_uclo_del_uof_obj(struct icp_qat_fw_loader_handle *handle) +void qat_uclo_del_obj(struct icp_qat_fw_loader_handle *handle) { struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; unsigned int a; + if (handle->mobj_handle) + qat_uclo_del_mof(handle); if (handle->sobj_handle) qat_uclo_del_suof(handle); if (!obj_handle) @@ -1479,23 +1895,24 @@ static void qat_uclo_fill_uwords(struct icp_qat_uclo_objhandle *obj_handle, u64 *uword, unsigned int addr_p, unsigned int raddr, u64 fill) { + unsigned int i, addr; u64 uwrd = 0; - unsigned int i; if (!encap_page) { *uword = fill; return; } + addr = (encap_page->page_region) ? raddr : addr_p; for (i = 0; i < encap_page->uwblock_num; i++) { - if (raddr >= encap_page->uwblock[i].start_addr && - raddr <= encap_page->uwblock[i].start_addr + + if (addr >= encap_page->uwblock[i].start_addr && + addr <= encap_page->uwblock[i].start_addr + encap_page->uwblock[i].words_num - 1) { - raddr -= encap_page->uwblock[i].start_addr; - raddr *= obj_handle->uword_in_bytes; + addr -= encap_page->uwblock[i].start_addr; + addr *= obj_handle->uword_in_bytes; memcpy(&uwrd, (void *)(((uintptr_t) - encap_page->uwblock[i].micro_words) + raddr), + encap_page->uwblock[i].micro_words) + addr), obj_handle->uword_in_bytes); - uwrd = uwrd & 0xbffffffffffull; + uwrd = uwrd & GENMASK_ULL(43, 0); } } *uword = uwrd; @@ -1546,6 +1963,10 @@ static void qat_uclo_wr_uimage_page(struct icp_qat_fw_loader_handle *handle, struct icp_qat_uof_image *image) { struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle; + unsigned long ae_mask = handle->hal_handle->ae_mask; + unsigned long cfg_ae_mask = handle->cfg_ae_mask; + unsigned long ae_assigned = image->ae_assigned; + struct icp_qat_uclo_aedata *aed; unsigned int ctx_mask, s; struct icp_qat_uclo_page *page; unsigned char ae; @@ -1557,25 +1978,30 @@ static void qat_uclo_wr_uimage_page(struct icp_qat_fw_loader_handle *handle, ctx_mask = 0x55; /* load the default page and set assigned CTX PC * to the entrypoint address */ - for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) { - if (!test_bit(ae, (unsigned long *)&image->ae_assigned)) + for_each_set_bit(ae, &ae_mask, handle->hal_handle->ae_max_num) { + if (!test_bit(ae, &cfg_ae_mask)) continue; + + if (!test_bit(ae, &ae_assigned)) + continue; + + aed = &obj_handle->ae_data[ae]; /* find the slice to which this image is assigned */ - for (s = 0; s < obj_handle->ae_data[ae].slice_num; s++) { - if (image->ctx_assigned & obj_handle->ae_data[ae]. - ae_slices[s].ctx_mask_assigned) + for (s = 0; s < aed->slice_num; s++) { + if (image->ctx_assigned & + aed->ae_slices[s].ctx_mask_assigned) break; } - if (s >= obj_handle->ae_data[ae].slice_num) + if (s >= aed->slice_num) continue; - page = obj_handle->ae_data[ae].ae_slices[s].page; + page = aed->ae_slices[s].page; if (!page->encap_page->def_page) continue; qat_uclo_wr_uimage_raw_page(handle, page->encap_page, ae); - page = obj_handle->ae_data[ae].ae_slices[s].page; + page = aed->ae_slices[s].page; for (ctx = 0; ctx < ICP_QAT_UCLO_MAX_CTX; ctx++) - obj_handle->ae_data[ae].ae_slices[s].cur_page[ctx] = + aed->ae_slices[s].cur_page[ctx] = (ctx_mask & (1 << ctx)) ? page : NULL; qat_hal_set_live_ctx(handle, (unsigned char)ae, image->ctx_assigned); @@ -1595,13 +2021,18 @@ static int qat_uclo_wr_suof_img(struct icp_qat_fw_loader_handle *handle) if (qat_uclo_map_auth_fw(handle, (char *)simg_hdr[i].simg_buf, (unsigned int) - (simg_hdr[i].simg_len), + simg_hdr[i].simg_len, &desc)) goto wr_err; if (qat_uclo_auth_fw(handle, desc)) goto wr_err; - if (qat_uclo_load_fw(handle, desc)) - goto wr_err; + if (qat_uclo_is_broadcast(handle, i)) { + if (qat_uclo_broadcast_load_fw(handle, desc)) + goto wr_err; + } else { + if (qat_uclo_load_fw(handle, desc)) + goto wr_err; + } qat_uclo_ummap_auth_fw(handle, &desc); } return 0; @@ -1630,6 +2061,16 @@ static int qat_uclo_wr_uof_img(struct icp_qat_fw_loader_handle *handle) int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle) { - return (handle->fw_auth) ? qat_uclo_wr_suof_img(handle) : + return (handle->chip_info->fw_auth) ? qat_uclo_wr_suof_img(handle) : qat_uclo_wr_uof_img(handle); } + +int qat_uclo_set_cfg_ae_mask(struct icp_qat_fw_loader_handle *handle, + unsigned int cfg_ae_mask) +{ + if (!cfg_ae_mask) + return -EINVAL; + + handle->cfg_ae_mask = cfg_ae_mask; + return 0; +} diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c index b975c263446d..1e83d9397b11 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c @@ -3,7 +3,9 @@ #include <adf_accel_devices.h> #include <adf_pf2vf_msg.h> #include <adf_common_drv.h> +#include <adf_gen2_hw_data.h> #include "adf_dh895xcc_hw_data.h" +#include "icp_qat_hw.h" /* Worker thread to service arbiter mappings based on dev SKUs */ static const u32 thrd_to_arb_map_sku4[] = { @@ -24,15 +26,19 @@ static struct adf_hw_device_class dh895xcc_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { - return (~fuse) >> ADF_DH895XCC_ACCELERATORS_REG_OFFSET & - ADF_DH895XCC_ACCELERATORS_MASK; + u32 fuses = self->fuses; + + return ~fuses >> ADF_DH895XCC_ACCELERATORS_REG_OFFSET & + ADF_DH895XCC_ACCELERATORS_MASK; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { - return (~fuse) & ADF_DH895XCC_ACCELENGINES_MASK; + u32 fuses = self->fuses; + + return ~fuses & ADF_DH895XCC_ACCELENGINES_MASK; } static u32 get_num_accels(struct adf_hw_device_data *self) @@ -78,6 +84,29 @@ static u32 get_sram_bar_id(struct adf_hw_device_data *self) return ADF_DH895XCC_SRAM_BAR; } +static u32 get_accel_cap(struct adf_accel_dev *accel_dev) +{ + struct pci_dev *pdev = accel_dev->accel_pci_dev.pci_dev; + u32 capabilities; + u32 legfuses; + + capabilities = ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC | + ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC | + ICP_ACCEL_CAPABILITIES_AUTHENTICATION; + + /* Read accelerator capabilities mask */ + pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, &legfuses); + + if (legfuses & ICP_ACCEL_MASK_CIPHER_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_SYMMETRIC; + if (legfuses & ICP_ACCEL_MASK_PKE_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_CRYPTO_ASYMMETRIC; + if (legfuses & ICP_ACCEL_MASK_AUTH_SLICE) + capabilities &= ~ICP_ACCEL_CAPABILITIES_AUTHENTICATION; + + return capabilities; +} + static enum dev_sku_info get_sku(struct adf_hw_device_data *self) { int sku = (self->fuses & ADF_DH895XCC_FUSECTL_SKU_MASK) @@ -131,11 +160,13 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) { struct adf_hw_device_data *hw_device = accel_dev->hw_device; struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_DH895XCC_PMISC_BAR]; + unsigned long accel_mask = hw_device->accel_mask; + unsigned long ae_mask = hw_device->ae_mask; void __iomem *csr = misc_bar->virt_addr; unsigned int val, i; /* Enable Accel Engine error detection & correction */ - for (i = 0; i < hw_device->get_num_aes(hw_device); i++) { + for_each_set_bit(i, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) { val = ADF_CSR_RD(csr, ADF_DH895XCC_AE_CTX_ENABLES(i)); val |= ADF_DH895XCC_ENABLE_AE_ECC_ERR; ADF_CSR_WR(csr, ADF_DH895XCC_AE_CTX_ENABLES(i), val); @@ -145,7 +176,7 @@ static void adf_enable_error_correction(struct adf_accel_dev *accel_dev) } /* Enable shared memory error detection & correction */ - for (i = 0; i < hw_device->get_num_accels(hw_device); i++) { + for_each_set_bit(i, &accel_mask, ADF_DH895XCC_MAX_ACCELERATORS) { val = ADF_CSR_RD(csr, ADF_DH895XCC_UERRSSMSH(i)); val |= ADF_DH895XCC_ERRSSMSH_EN; ADF_CSR_WR(csr, ADF_DH895XCC_UERRSSMSH(i), val); @@ -164,7 +195,7 @@ static void adf_enable_ints(struct adf_accel_dev *accel_dev) /* Enable bundle and misc interrupts */ ADF_CSR_WR(addr, ADF_DH895XCC_SMIAPF0_MASK_OFFSET, accel_dev->pf.vf_info ? 0 : - GENMASK_ULL(GET_MAX_BANKS(accel_dev) - 1, 0)); + BIT_ULL(GET_MAX_BANKS(accel_dev)) - 1); ADF_CSR_WR(addr, ADF_DH895XCC_SMIAPF1_MASK_OFFSET, ADF_DH895XCC_SMIA1_MASK); } @@ -174,11 +205,19 @@ static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev) return 0; } +static void configure_iov_threads(struct adf_accel_dev *accel_dev, bool enable) +{ + adf_gen2_cfg_iov_thds(accel_dev, enable, + ADF_DH895XCC_AE2FUNC_MAP_GRP_A_NUM_REGS, + ADF_DH895XCC_AE2FUNC_MAP_GRP_B_NUM_REGS); +} + void adf_init_hw_data_dh895xcc(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &dh895xcc_class; hw_data->instance_id = dh895xcc_class.instances++; hw_data->num_banks = ADF_DH895XCC_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_DH895XCC_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_DH895XCC_MAX_ACCELENGINES; @@ -189,18 +228,22 @@ void adf_init_hw_data_dh895xcc(struct adf_hw_device_data *hw_data) hw_data->enable_error_correction = adf_enable_error_correction; hw_data->get_accel_mask = get_accel_mask; hw_data->get_ae_mask = get_ae_mask; + hw_data->get_accel_cap = get_accel_cap; hw_data->get_num_accels = get_num_accels; hw_data->get_num_aes = get_num_aes; hw_data->get_etr_bar_id = get_etr_bar_id; hw_data->get_misc_bar_id = get_misc_bar_id; hw_data->get_pf2vf_offset = get_pf2vf_offset; hw_data->get_vintmsk_offset = get_vintmsk_offset; + hw_data->get_admin_info = adf_gen2_get_admin_info; + hw_data->get_arb_info = adf_gen2_get_arb_info; hw_data->get_sram_bar_id = get_sram_bar_id; hw_data->get_sku = get_sku; hw_data->fw_name = ADF_DH895XCC_FW; hw_data->fw_mmp_name = ADF_DH895XCC_MMP; hw_data->init_admin_comms = adf_init_admin_comms; hw_data->exit_admin_comms = adf_exit_admin_comms; + hw_data->configure_iov_threads = configure_iov_threads; hw_data->disable_iov = adf_disable_sriov; hw_data->send_admin_init = adf_send_admin_init; hw_data->init_arb = adf_init_arb; @@ -210,6 +253,7 @@ void adf_init_hw_data_dh895xcc(struct adf_hw_device_data *hw_data) hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms; hw_data->reset_device = adf_reset_sbr; hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_dh895xcc(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h index 082a04466dca..4d613923d155 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h +++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h @@ -36,6 +36,11 @@ #define ADF_DH895XCC_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04)) #define ADF_DH895XCC_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04)) + +/* AE to function mapping */ +#define ADF_DH895XCC_AE2FUNC_MAP_GRP_A_NUM_REGS 96 +#define ADF_DH895XCC_AE2FUNC_MAP_GRP_B_NUM_REGS 12 + /* FW names */ #define ADF_DH895XCC_FW "qat_895xcc.bin" #define ADF_DH895XCC_MMP "qat_895xcc_mmp.bin" diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c index ecb4f6f20e22..a9ec4357144c 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c @@ -128,8 +128,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) &hw_data->fuses); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* If the device has no acceleration engines then ignore it. */ if (!hw_data->accel_mask || !hw_data->ae_mask || @@ -177,9 +177,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_err_disable; } - /* Read accelerator capabilities mask */ - pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET, - &hw_data->accel_capabilities_mask); + /* Get accelerator capabilities mask */ + hw_data->accel_capabilities_mask = hw_data->get_accel_cap(accel_dev); /* Find and map all the device's BARS */ i = 0; diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c index 5246f0524ca3..f14fb82ed6df 100644 --- a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c +++ b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c @@ -3,6 +3,7 @@ #include <adf_accel_devices.h> #include <adf_pf2vf_msg.h> #include <adf_common_drv.h> +#include <adf_gen2_hw_data.h> #include "adf_dh895xccvf_hw_data.h" static struct adf_hw_device_class dh895xcciov_class = { @@ -11,12 +12,12 @@ static struct adf_hw_device_class dh895xcciov_class = { .instances = 0 }; -static u32 get_accel_mask(u32 fuse) +static u32 get_accel_mask(struct adf_hw_device_data *self) { return ADF_DH895XCCIOV_ACCELERATORS_MASK; } -static u32 get_ae_mask(u32 fuse) +static u32 get_ae_mask(struct adf_hw_device_data *self) { return ADF_DH895XCCIOV_ACCELENGINES_MASK; } @@ -69,6 +70,7 @@ void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data) { hw_data->dev_class = &dh895xcciov_class; hw_data->num_banks = ADF_DH895XCCIOV_ETR_MAX_BANKS; + hw_data->num_rings_per_bank = ADF_ETR_MAX_RINGS_PER_BANK; hw_data->num_accel = ADF_DH895XCCIOV_MAX_ACCELERATORS; hw_data->num_logical_accel = 1; hw_data->num_engines = ADF_DH895XCCIOV_MAX_ACCELENGINES; @@ -97,6 +99,7 @@ void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data) hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION; hw_data->dev_class->instances++; adf_devmgr_update_class_index(hw_data); + adf_gen2_init_hw_csr_ops(&hw_data->csr_ops); } void adf_clean_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data) diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c index 404cf9df6922..c972554a755e 100644 --- a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c +++ b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c @@ -119,8 +119,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adf_init_hw_data_dh895xcciov(accel_dev->hw_device); /* Get Accelerators and Accelerators Engines masks */ - hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses); - hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses); + hw_data->accel_mask = hw_data->get_accel_mask(hw_data); + hw_data->ae_mask = hw_data->get_ae_mask(hw_data); accel_pci_dev->sku = hw_data->get_sku(hw_data); /* Create dev top level debugfs entry */ diff --git a/drivers/crypto/qce/common.c b/drivers/crypto/qce/common.c index 5006e74c40cd..a73db2a5637f 100644 --- a/drivers/crypto/qce/common.c +++ b/drivers/crypto/qce/common.c @@ -7,7 +7,8 @@ #include <linux/interrupt.h> #include <linux/types.h> #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include "cipher.h" #include "common.h" diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c index ea616b7259ae..80b75085c265 100644 --- a/drivers/crypto/qce/core.c +++ b/drivers/crypto/qce/core.c @@ -13,7 +13,6 @@ #include <linux/types.h> #include <crypto/algapi.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> #include "core.h" #include "cipher.h" @@ -160,7 +159,21 @@ static int qce_check_version(struct qce_device *qce) return -ENODEV; qce->burst_size = QCE_BAM_BURST_SIZE; - qce->pipe_pair_id = 1; + + /* + * Rx and tx pipes are treated as a pair inside CE. + * Pipe pair number depends on the actual BAM dma pipe + * that is used for transfers. The BAM dma pipes are passed + * from the device tree and used to derive the pipe pair + * id in the CE driver as follows. + * BAM dma pipes(rx, tx) CE pipe pair id + * 0,1 0 + * 2,3 1 + * 4,5 2 + * 6,7 3 + * ... + */ + qce->pipe_pair_id = qce->dma.rxchan->chan_id >> 1; dev_dbg(qce->dev, "Crypto device found, version %d.%d.%d\n", major, minor, step); @@ -261,6 +274,7 @@ static int qce_crypto_remove(struct platform_device *pdev) static const struct of_device_id qce_crypto_of_match[] = { { .compatible = "qcom,crypto-v5.1", }, + { .compatible = "qcom,crypto-v5.4", }, {} }; MODULE_DEVICE_TABLE(of, qce_crypto_of_match); diff --git a/drivers/crypto/qce/sha.c b/drivers/crypto/qce/sha.c index 87be96a0b0bb..61c418c12345 100644 --- a/drivers/crypto/qce/sha.c +++ b/drivers/crypto/qce/sha.c @@ -48,7 +48,7 @@ static void qce_ahash_done(void *data) dma_unmap_sg(qce->dev, &rctx->result_sg, 1, DMA_FROM_DEVICE); memcpy(rctx->digest, result->auth_iv, digestsize); - if (req->result) + if (req->result && rctx->last_blk) memcpy(req->result, result->auth_iv, digestsize); rctx->byte_count[0] = cpu_to_be32(result->auth_byte_count[0]); diff --git a/drivers/crypto/qce/sha.h b/drivers/crypto/qce/sha.h index d63526e3804d..a22695361f16 100644 --- a/drivers/crypto/qce/sha.h +++ b/drivers/crypto/qce/sha.h @@ -7,7 +7,8 @@ #define _SHA_H_ #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include "common.h" #include "core.h" diff --git a/drivers/crypto/rockchip/rk3288_crypto.h b/drivers/crypto/rockchip/rk3288_crypto.h index 3db595570c9c..97278c2574ff 100644 --- a/drivers/crypto/rockchip/rk3288_crypto.h +++ b/drivers/crypto/rockchip/rk3288_crypto.h @@ -12,7 +12,8 @@ #include <crypto/internal/skcipher.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #define _SBF(v, f) ((v) << (f)) diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 88a6c853ffd7..682c8a450a57 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -30,7 +30,8 @@ #include <crypto/hash.h> #include <crypto/md5.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/internal/hash.h> #define _SBF(s, v) ((v) << (s)) diff --git a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c index eda93fab95fe..f300b0a5958a 100644 --- a/drivers/crypto/sa2ul.c +++ b/drivers/crypto/sa2ul.c @@ -9,8 +9,10 @@ * Tero Kristo */ #include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/dmapool.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> @@ -23,7 +25,8 @@ #include <crypto/internal/hash.h> #include <crypto/internal/skcipher.h> #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include "sa2ul.h" @@ -361,42 +364,45 @@ static void sa_swiz_128(u8 *in, u16 len) } /* Prepare the ipad and opad from key as per SHA algorithm step 1*/ -static void prepare_kiopad(u8 *k_ipad, u8 *k_opad, const u8 *key, u16 key_sz) +static void prepare_kipad(u8 *k_ipad, const u8 *key, u16 key_sz) { int i; - for (i = 0; i < key_sz; i++) { + for (i = 0; i < key_sz; i++) k_ipad[i] = key[i] ^ 0x36; - k_opad[i] = key[i] ^ 0x5c; - } /* Instead of XOR with 0 */ - for (; i < SHA1_BLOCK_SIZE; i++) { + for (; i < SHA1_BLOCK_SIZE; i++) k_ipad[i] = 0x36; +} + +static void prepare_kopad(u8 *k_opad, const u8 *key, u16 key_sz) +{ + int i; + + for (i = 0; i < key_sz; i++) + k_opad[i] = key[i] ^ 0x5c; + + /* Instead of XOR with 0 */ + for (; i < SHA1_BLOCK_SIZE; i++) k_opad[i] = 0x5c; - } } -static void sa_export_shash(struct shash_desc *hash, int block_size, +static void sa_export_shash(void *state, struct shash_desc *hash, int digest_size, __be32 *out) { - union { - struct sha1_state sha1; - struct sha256_state sha256; - struct sha512_state sha512; - } sha; - void *state; + struct sha1_state *sha1; + struct sha256_state *sha256; u32 *result; - int i; switch (digest_size) { case SHA1_DIGEST_SIZE: - state = &sha.sha1; - result = sha.sha1.state; + sha1 = state; + result = sha1->state; break; case SHA256_DIGEST_SIZE: - state = &sha.sha256; - result = sha.sha256.state; + sha256 = state; + result = sha256->state; break; default: dev_err(sa_k3_dev, "%s: bad digest_size=%d\n", __func__, @@ -406,8 +412,7 @@ static void sa_export_shash(struct shash_desc *hash, int block_size, crypto_shash_export(hash, state); - for (i = 0; i < digest_size >> 2; i++) - out[i] = cpu_to_be32(result[i]); + cpu_to_be32_array(out, result, digest_size / 4); } static void sa_prepare_iopads(struct algo_data *data, const u8 *key, @@ -416,24 +421,28 @@ static void sa_prepare_iopads(struct algo_data *data, const u8 *key, SHASH_DESC_ON_STACK(shash, data->ctx->shash); int block_size = crypto_shash_blocksize(data->ctx->shash); int digest_size = crypto_shash_digestsize(data->ctx->shash); - u8 k_ipad[SHA1_BLOCK_SIZE]; - u8 k_opad[SHA1_BLOCK_SIZE]; + union { + struct sha1_state sha1; + struct sha256_state sha256; + u8 k_pad[SHA1_BLOCK_SIZE]; + } sha; shash->tfm = data->ctx->shash; - prepare_kiopad(k_ipad, k_opad, key, key_sz); - - memzero_explicit(ipad, block_size); - memzero_explicit(opad, block_size); + prepare_kipad(sha.k_pad, key, key_sz); crypto_shash_init(shash); - crypto_shash_update(shash, k_ipad, block_size); - sa_export_shash(shash, block_size, digest_size, ipad); + crypto_shash_update(shash, sha.k_pad, block_size); + sa_export_shash(&sha, shash, digest_size, ipad); + + prepare_kopad(sha.k_pad, key, key_sz); crypto_shash_init(shash); - crypto_shash_update(shash, k_opad, block_size); + crypto_shash_update(shash, sha.k_pad, block_size); - sa_export_shash(shash, block_size, digest_size, opad); + sa_export_shash(&sha, shash, digest_size, opad); + + memzero_explicit(&sha, sizeof(sha)); } /* Derive the inverse key used in AES-CBC decryption operation */ @@ -506,7 +515,8 @@ static int sa_set_sc_enc(struct algo_data *ad, const u8 *key, u16 key_sz, static void sa_set_sc_auth(struct algo_data *ad, const u8 *key, u16 key_sz, u8 *sc_buf) { - __be32 ipad[64], opad[64]; + __be32 *ipad = (void *)(sc_buf + 32); + __be32 *opad = (void *)(sc_buf + 64); /* Set Authentication mode selector to hash processing */ sc_buf[0] = SA_HASH_PROCESSING; @@ -515,14 +525,9 @@ static void sa_set_sc_auth(struct algo_data *ad, const u8 *key, u16 key_sz, sc_buf[1] |= ad->auth_ctrl; /* Copy the keys or ipad/opad */ - if (ad->keyed_mac) { + if (ad->keyed_mac) ad->prep_iopad(ad, key, key_sz, ipad, opad); - - /* Copy ipad to AuthKey */ - memcpy(&sc_buf[32], ipad, ad->hash_size); - /* Copy opad to Aux-1 */ - memcpy(&sc_buf[64], opad, ad->hash_size); - } else { + else { /* basic hash */ sc_buf[1] |= SA_BASIC_HASH; } @@ -819,7 +824,7 @@ static void sa_cipher_cra_exit(struct crypto_skcipher *tfm) sa_free_ctx_info(&ctx->enc, data); sa_free_ctx_info(&ctx->dec, data); - crypto_free_sync_skcipher(ctx->fallback.skcipher); + crypto_free_skcipher(ctx->fallback.skcipher); } static int sa_cipher_cra_init(struct crypto_skcipher *tfm) @@ -827,6 +832,7 @@ static int sa_cipher_cra_init(struct crypto_skcipher *tfm) struct sa_tfm_ctx *ctx = crypto_skcipher_ctx(tfm); struct sa_crypto_data *data = dev_get_drvdata(sa_k3_dev); const char *name = crypto_tfm_alg_name(&tfm->base); + struct crypto_skcipher *child; int ret; memzero_explicit(ctx, sizeof(*ctx)); @@ -841,14 +847,17 @@ static int sa_cipher_cra_init(struct crypto_skcipher *tfm) return ret; } - ctx->fallback.skcipher = - crypto_alloc_sync_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK); + child = crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK); - if (IS_ERR(ctx->fallback.skcipher)) { + if (IS_ERR(child)) { dev_err(sa_k3_dev, "Error allocating fallback algo %s\n", name); - return PTR_ERR(ctx->fallback.skcipher); + return PTR_ERR(child); } + ctx->fallback.skcipher = child; + crypto_skcipher_set_reqsize(tfm, crypto_skcipher_reqsize(child) + + sizeof(struct skcipher_request)); + dev_dbg(sa_k3_dev, "%s(0x%p) sc-ids(0x%x(0x%pad), 0x%x(0x%pad))\n", __func__, tfm, ctx->enc.sc_id, &ctx->enc.sc_phys, ctx->dec.sc_id, &ctx->dec.sc_phys); @@ -859,6 +868,7 @@ static int sa_cipher_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen, struct algo_data *ad) { struct sa_tfm_ctx *ctx = crypto_skcipher_ctx(tfm); + struct crypto_skcipher *child = ctx->fallback.skcipher; int cmdl_len; struct sa_cmdl_cfg cfg; int ret; @@ -874,12 +884,10 @@ static int sa_cipher_setkey(struct crypto_skcipher *tfm, const u8 *key, cfg.enc_eng_id = ad->enc_eng.eng_id; cfg.iv_size = crypto_skcipher_ivsize(tfm); - crypto_sync_skcipher_clear_flags(ctx->fallback.skcipher, + crypto_skcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK); + crypto_skcipher_set_flags(child, tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK); - crypto_sync_skcipher_set_flags(ctx->fallback.skcipher, - tfm->base.crt_flags & - CRYPTO_TFM_REQ_MASK); - ret = crypto_sync_skcipher_setkey(ctx->fallback.skcipher, key, keylen); + ret = crypto_skcipher_setkey(child, key, keylen); if (ret) return ret; @@ -1270,7 +1278,6 @@ static int sa_cipher_run(struct skcipher_request *req, u8 *iv, int enc) crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); struct crypto_alg *alg = req->base.tfm->__crt_alg; struct sa_req sa_req = { 0 }; - int ret; if (!req->cryptlen) return 0; @@ -1282,20 +1289,18 @@ static int sa_cipher_run(struct skcipher_request *req, u8 *iv, int enc) if (req->cryptlen > SA_MAX_DATA_SZ || (req->cryptlen >= SA_UNSAFE_DATA_SZ_MIN && req->cryptlen <= SA_UNSAFE_DATA_SZ_MAX)) { - SYNC_SKCIPHER_REQUEST_ON_STACK(subreq, ctx->fallback.skcipher); + struct skcipher_request *subreq = skcipher_request_ctx(req); - skcipher_request_set_sync_tfm(subreq, ctx->fallback.skcipher); + skcipher_request_set_tfm(subreq, ctx->fallback.skcipher); skcipher_request_set_callback(subreq, req->base.flags, - NULL, NULL); + req->base.complete, + req->base.data); skcipher_request_set_crypt(subreq, req->src, req->dst, req->cryptlen, req->iv); if (enc) - ret = crypto_skcipher_encrypt(subreq); + return crypto_skcipher_encrypt(subreq); else - ret = crypto_skcipher_decrypt(subreq); - - skcipher_request_zero(subreq); - return ret; + return crypto_skcipher_decrypt(subreq); } sa_req.size = req->cryptlen; diff --git a/drivers/crypto/sa2ul.h b/drivers/crypto/sa2ul.h index 7f7e3fe60d11..f597ddecde34 100644 --- a/drivers/crypto/sa2ul.h +++ b/drivers/crypto/sa2ul.h @@ -12,10 +12,9 @@ #ifndef _K3_SA2UL_ #define _K3_SA2UL_ -#include <linux/interrupt.h> -#include <linux/skbuff.h> -#include <linux/hw_random.h> #include <crypto/aes.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #define SA_ENGINE_ENABLE_CONTROL 0x1000 @@ -311,7 +310,7 @@ struct sa_tfm_ctx { struct crypto_shash *shash; /* for fallback */ union { - struct crypto_sync_skcipher *skcipher; + struct crypto_skcipher *skcipher; struct crypto_ahash *ahash; struct crypto_aead *aead; } fallback; diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c index d60679c79822..8b5be29cb4dc 100644 --- a/drivers/crypto/sahara.c +++ b/drivers/crypto/sahara.c @@ -15,7 +15,8 @@ #include <crypto/internal/hash.h> #include <crypto/internal/skcipher.h> #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <linux/clk.h> #include <linux/dma-mapping.h> diff --git a/drivers/crypto/stm32/stm32-hash.c b/drivers/crypto/stm32/stm32-hash.c index e3e25278a970..7ac0573ef663 100644 --- a/drivers/crypto/stm32/stm32-hash.c +++ b/drivers/crypto/stm32/stm32-hash.c @@ -25,7 +25,8 @@ #include <crypto/hash.h> #include <crypto/md5.h> #include <crypto/scatterwalk.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/internal/hash.h> #define HASH_CR 0x00 diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index 66773892f665..4fd85f31630a 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -31,7 +31,8 @@ #include <crypto/algapi.h> #include <crypto/aes.h> #include <crypto/internal/des.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/md5.h> #include <crypto/internal/aead.h> #include <crypto/authenc.h> @@ -460,7 +461,7 @@ DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE) /* * locate current (offending) descriptor */ -static u32 current_desc_hdr(struct device *dev, int ch) +static __be32 current_desc_hdr(struct device *dev, int ch) { struct talitos_private *priv = dev_get_drvdata(dev); int tail, iter; @@ -478,7 +479,7 @@ static u32 current_desc_hdr(struct device *dev, int ch) iter = tail; while (priv->chan[ch].fifo[iter].dma_desc != cur_desc && - priv->chan[ch].fifo[iter].desc->next_desc != cur_desc) { + priv->chan[ch].fifo[iter].desc->next_desc != cpu_to_be32(cur_desc)) { iter = (iter + 1) & (priv->fifo_len - 1); if (iter == tail) { dev_err(dev, "couldn't locate current descriptor\n"); @@ -486,7 +487,7 @@ static u32 current_desc_hdr(struct device *dev, int ch) } } - if (priv->chan[ch].fifo[iter].desc->next_desc == cur_desc) { + if (priv->chan[ch].fifo[iter].desc->next_desc == cpu_to_be32(cur_desc)) { struct talitos_edesc *edesc; edesc = container_of(priv->chan[ch].fifo[iter].desc, @@ -501,13 +502,13 @@ static u32 current_desc_hdr(struct device *dev, int ch) /* * user diagnostics; report root cause of error based on execution unit status */ -static void report_eu_error(struct device *dev, int ch, u32 desc_hdr) +static void report_eu_error(struct device *dev, int ch, __be32 desc_hdr) { struct talitos_private *priv = dev_get_drvdata(dev); int i; if (!desc_hdr) - desc_hdr = in_be32(priv->chan[ch].reg + TALITOS_DESCBUF); + desc_hdr = cpu_to_be32(in_be32(priv->chan[ch].reg + TALITOS_DESCBUF)); switch (desc_hdr & DESC_HDR_SEL0_MASK) { case DESC_HDR_SEL0_AFEU: diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index 3d407eebb2ba..da284b0ea1b2 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -31,7 +31,8 @@ #include <linux/bitops.h> #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/scatterwalk.h> #include <crypto/algapi.h> diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 7a47680d6f07..81c42664f21b 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -269,6 +269,15 @@ config EDAC_PND2 first used on the Apollo Lake platform and Denverton micro-server but may appear on others in the future. +config EDAC_IGEN6 + tristate "Intel client SoC Integrated MC" + depends on PCI && X86_64 && PCI_MMCONFIG && ARCH_HAVE_NMI_SAFE_CMPXCHG + help + Support for error detection and correction on the Intel + client SoC Integrated Memory Controller using In-Band ECC IP. + This In-Band ECC is first used on the Elkhart Lake SoC but + may appear on others in the future. + config EDAC_MPC85XX bool "Freescale MPC83xx / MPC85xx" depends on FSL_SOC && EDAC=y @@ -283,13 +292,6 @@ config EDAC_LAYERSCAPE Support for error detection and correction on Freescale memory controllers on Layerscape SoCs. -config EDAC_MV64X60 - tristate "Marvell MV64x60" - depends on MV64X60 - help - Support for error detection and correction on the Marvell - MV64360 and MV64460 chipsets. - config EDAC_PASEMI tristate "PA Semi PWRficient" depends on PPC_PASEMI && PCI @@ -515,10 +517,10 @@ config EDAC_QCOM health, you should probably say 'Y' here. config EDAC_ASPEED - tristate "Aspeed AST 2500 SoC" - depends on MACH_ASPEED_G5 + tristate "Aspeed AST BMC SoC" + depends on ARCH_ASPEED help - Support for error detection and correction on the Aspeed AST 2500 SoC. + Support for error detection and correction on the Aspeed AST BMC SoC. First, ECC must be configured in the bootloader. Then, this driver will expose error counters via the EDAC kernel framework. diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 3a849168780d..464d3d8d850a 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_EDAC_I7300) += i7300_edac.o obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o obj-$(CONFIG_EDAC_PND2) += pnd2_edac.o +obj-$(CONFIG_EDAC_IGEN6) += igen6_edac.o obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o obj-$(CONFIG_EDAC_E752X) += e752x_edac.o obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o @@ -64,7 +65,6 @@ obj-$(CONFIG_EDAC_SKX) += skx_edac.o i10nm_edac-y := skx_common.o i10nm_base.o obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o -obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o obj-$(CONFIG_EDAC_CELL) += cell_edac.o obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 1362274d840b..f7087ddddb90 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -18,6 +18,9 @@ static struct amd64_family_type *fam_type; /* Per-node stuff */ static struct ecc_settings **ecc_stngs; +/* Device for the PCI component */ +static struct device *pci_ctl_dev; + /* * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching- @@ -1133,7 +1136,7 @@ static int k8_early_channel_count(struct amd64_pvt *pvt) /* On F10h and later ErrAddr is MC4_ADDR[47:1] */ static u64 get_error_address(struct amd64_pvt *pvt, struct mce *m) { - u16 mce_nid = amd_get_nb_id(m->extcpu); + u16 mce_nid = topology_die_id(m->extcpu); struct mem_ctl_info *mci; u8 start_bit = 1; u8 end_bit = 47; @@ -2461,14 +2464,11 @@ static int map_err_sym_to_channel(int err_sym, int sym_size) case 0x20: case 0x21: return 0; - break; case 0x22: case 0x23: return 1; - break; default: return err_sym >> 4; - break; } /* x8 symbols */ else @@ -2478,17 +2478,12 @@ static int map_err_sym_to_channel(int err_sym, int sym_size) WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n", err_sym); return -1; - break; - case 0x11: return 0; - break; case 0x12: return 1; - break; default: return err_sym >> 3; - break; } return -1; } @@ -2683,6 +2678,9 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) return -ENODEV; } + if (!pci_ctl_dev) + pci_ctl_dev = &pvt->F0->dev; + edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); @@ -2707,6 +2705,9 @@ reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) return -ENODEV; } + if (!pci_ctl_dev) + pci_ctl_dev = &pvt->F2->dev; + edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); @@ -3046,7 +3047,7 @@ static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, u16 nid) int cpu; for_each_online_cpu(cpu) - if (amd_get_nb_id(cpu) == nid) + if (topology_die_id(cpu) == nid) cpumask_set_cpu(cpu, mask); } @@ -3623,21 +3624,10 @@ static void remove_one_instance(unsigned int nid) static void setup_pci_device(void) { - struct mem_ctl_info *mci; - struct amd64_pvt *pvt; - if (pci_ctl) return; - mci = edac_mc_find(0); - if (!mci) - return; - - pvt = mci->pvt_info; - if (pvt->umc) - pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR); - else - pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); + pci_ctl = edac_pci_create_generic_ctl(pci_ctl_dev, EDAC_MOD_STR); if (!pci_ctl) { pr_warn("%s(): Unable to create PCI control\n", __func__); pr_warn("%s(): PCI error report via EDAC not set\n", __func__); @@ -3716,6 +3706,8 @@ static int __init amd64_edac_init(void) return 0; err_pci: + pci_ctl_dev = NULL; + msrs_free(msrs); msrs = NULL; @@ -3745,6 +3737,8 @@ static void __exit amd64_edac_exit(void) kfree(ecc_stngs); ecc_stngs = NULL; + pci_ctl_dev = NULL; + msrs_free(msrs); msrs = NULL; } diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 9c6e326b4c14..2a49f68a7cf9 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -179,7 +179,6 @@ static int amd76x_process_error_info(struct mem_ctl_info *mci, static void amd76x_check(struct mem_ctl_info *mci) { struct amd76x_error_info info; - edac_dbg(3, "\n"); amd76x_get_error_info(mci, &info); amd76x_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c index fde809efc520..a46da56d6d54 100644 --- a/drivers/edac/aspeed_edac.c +++ b/drivers/edac/aspeed_edac.c @@ -239,7 +239,7 @@ static int init_csrows(struct mem_ctl_info *mci) int rc; /* retrieve info about physical memory from device tree */ - np = of_find_node_by_path("/memory"); + np = of_find_node_by_name(NULL, "memory"); if (!np) { dev_err(mci->pdev, "dt: missing /memory node\n"); return -ENODEV; @@ -375,10 +375,13 @@ static int aspeed_remove(struct platform_device *pdev) static const struct of_device_id aspeed_of_match[] = { + { .compatible = "aspeed,ast2400-sdram-edac" }, { .compatible = "aspeed,ast2500-sdram-edac" }, + { .compatible = "aspeed,ast2600-sdram-edac" }, {}, }; +MODULE_DEVICE_TABLE(of, aspeed_of_match); static struct platform_driver aspeed_driver = { .driver = { @@ -392,5 +395,5 @@ module_platform_driver(aspeed_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Stefan Schaeckeler <sschaeck@cisco.com>"); -MODULE_DESCRIPTION("Aspeed AST2500 EDAC driver"); +MODULE_DESCRIPTION("Aspeed BMC SoC EDAC driver"); MODULE_VERSION("1.0"); diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 313d08018166..ac7c9b42d4c7 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -980,7 +980,6 @@ static void e752x_check(struct mem_ctl_info *mci) { struct e752x_error_info info; - edac_dbg(3, "\n"); e752x_get_error_info(mci, &info); e752x_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index 75d7ce62b3be..497e710fca3d 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -333,7 +333,6 @@ static void e7xxx_check(struct mem_ctl_info *mci) { struct e7xxx_error_info info; - edac_dbg(3, "\n"); e7xxx_get_error_info(mci, &info); e7xxx_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/edac_device.h b/drivers/edac/edac_device.h index c4c0e0bdce14..fc2d2c218064 100644 --- a/drivers/edac/edac_device.h +++ b/drivers/edac/edac_device.h @@ -258,7 +258,7 @@ extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( extern void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info); /** - * edac_device_add_device: Insert the 'edac_dev' structure into the + * edac_device_add_device - Insert the 'edac_dev' structure into the * edac_device global list and create sysfs entries associated with * edac_device structure. * @@ -271,9 +271,8 @@ extern void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info); extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev); /** - * edac_device_del_device: - * Remove sysfs entries for specified edac_device structure and - * then remove edac_device structure from global list + * edac_device_del_device - Remove sysfs entries for specified edac_device + * structure and then remove edac_device structure from global list * * @dev: * Pointer to struct &device representing the edac device @@ -286,7 +285,7 @@ extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev); extern struct edac_device_ctl_info *edac_device_del_device(struct device *dev); /** - * Log correctable errors. + * edac_device_handle_ce_count - Log correctable errors. * * @edac_dev: pointer to struct &edac_device_ctl_info * @inst_nr: number of the instance where the CE error happened @@ -299,7 +298,7 @@ void edac_device_handle_ce_count(struct edac_device_ctl_info *edac_dev, const char *msg); /** - * Log uncorrectable errors. + * edac_device_handle_ue_count - Log uncorrectable errors. * * @edac_dev: pointer to struct &edac_device_ctl_info * @inst_nr: number of the instance where the CE error happened diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 01ff71f7b645..f6d462d0be2d 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -158,10 +158,14 @@ const char * const edac_mem_types[] = { [MEM_DDR3] = "Unbuffered-DDR3", [MEM_RDDR3] = "Registered-DDR3", [MEM_LRDDR3] = "Load-Reduced-DDR3-RAM", + [MEM_LPDDR3] = "Low-Power-DDR3-RAM", [MEM_DDR4] = "Unbuffered-DDR4", [MEM_RDDR4] = "Registered-DDR4", + [MEM_LPDDR4] = "Low-Power-DDR4-RAM", [MEM_LRDDR4] = "Load-Reduced-DDR4-RAM", + [MEM_DDR5] = "Unbuffered-DDR5", [MEM_NVDIMM] = "Non-volatile-RAM", + [MEM_WIO2] = "Wide-IO-2", }; EXPORT_SYMBOL_GPL(edac_mem_types); diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c index c8d11da85bec..238a4ad1e526 100644 --- a/drivers/edac/i10nm_base.c +++ b/drivers/edac/i10nm_base.c @@ -6,27 +6,32 @@ */ #include <linux/kernel.h> +#include <linux/io.h> #include <asm/cpu_device_id.h> #include <asm/intel-family.h> #include <asm/mce.h> #include "edac_module.h" #include "skx_common.h" -#define I10NM_REVISION "v0.0.3" +#define I10NM_REVISION "v0.0.4" #define EDAC_MOD_STR "i10nm_edac" /* Debug macros */ #define i10nm_printk(level, fmt, arg...) \ edac_printk(level, "i10nm", fmt, ##arg) -#define I10NM_GET_SCK_BAR(d, reg) \ +#define I10NM_GET_SCK_BAR(d, reg) \ pci_read_config_dword((d)->uracu, 0xd0, &(reg)) #define I10NM_GET_IMC_BAR(d, i, reg) \ pci_read_config_dword((d)->uracu, 0xd8 + (i) * 4, &(reg)) #define I10NM_GET_DIMMMTR(m, i, j) \ - (*(u32 *)((m)->mbase + 0x2080c + (i) * 0x4000 + (j) * 4)) + readl((m)->mbase + 0x2080c + (i) * (m)->chan_mmio_sz + (j) * 4) #define I10NM_GET_MCDDRTCFG(m, i, j) \ - (*(u32 *)((m)->mbase + 0x20970 + (i) * 0x4000 + (j) * 4)) + readl((m)->mbase + 0x20970 + (i) * (m)->chan_mmio_sz + (j) * 4) +#define I10NM_GET_MCMTR(m, i) \ + readl((m)->mbase + 0x20ef8 + (i) * (m)->chan_mmio_sz) +#define I10NM_GET_AMAP(m, i) \ + readl((m)->mbase + 0x20814 + (i) * (m)->chan_mmio_sz) #define I10NM_GET_SCK_MMIO_BASE(reg) (GET_BITFIELD(reg, 0, 28) << 23) #define I10NM_GET_IMC_MMIO_OFFSET(reg) (GET_BITFIELD(reg, 0, 10) << 12) @@ -126,12 +131,22 @@ static struct res_config i10nm_cfg0 = { .type = I10NM, .decs_did = 0x3452, .busno_cfg_offset = 0xcc, + .ddr_chan_mmio_sz = 0x4000, }; static struct res_config i10nm_cfg1 = { .type = I10NM, .decs_did = 0x3452, .busno_cfg_offset = 0xd0, + .ddr_chan_mmio_sz = 0x4000, +}; + +static struct res_config spr_cfg = { + .type = SPR, + .decs_did = 0x3252, + .busno_cfg_offset = 0xd0, + .ddr_chan_mmio_sz = 0x8000, + .support_ddr5 = true, }; static const struct x86_cpu_id i10nm_cpuids[] = { @@ -140,6 +155,7 @@ static const struct x86_cpu_id i10nm_cpuids[] = { X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x0, 0x3), &i10nm_cfg0), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_X, X86_STEPPINGS(0x4, 0xf), &i10nm_cfg1), X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(ICELAKE_D, X86_STEPPINGS(0x0, 0xf), &i10nm_cfg1), + X86_MATCH_INTEL_FAM6_MODEL_STEPPINGS(SAPPHIRERAPIDS_X, X86_STEPPINGS(0x0, 0xf), &spr_cfg), {} }; MODULE_DEVICE_TABLE(x86cpu, i10nm_cpuids); @@ -148,18 +164,19 @@ static bool i10nm_check_ecc(struct skx_imc *imc, int chan) { u32 mcmtr; - mcmtr = *(u32 *)(imc->mbase + 0x20ef8 + chan * 0x4000); + mcmtr = I10NM_GET_MCMTR(imc, chan); edac_dbg(1, "ch%d mcmtr reg %x\n", chan, mcmtr); return !!GET_BITFIELD(mcmtr, 2, 2); } -static int i10nm_get_dimm_config(struct mem_ctl_info *mci) +static int i10nm_get_dimm_config(struct mem_ctl_info *mci, + struct res_config *cfg) { struct skx_pvt *pvt = mci->pvt_info; struct skx_imc *imc = pvt->imc; + u32 mtr, amap, mcddrtcfg; struct dimm_info *dimm; - u32 mtr, mcddrtcfg; int i, j, ndimms; for (i = 0; i < I10NM_NUM_CHANNELS; i++) { @@ -167,6 +184,7 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci) continue; ndimms = 0; + amap = I10NM_GET_AMAP(imc, i); for (j = 0; j < I10NM_NUM_DIMMS; j++) { dimm = edac_get_dimm(mci, i, j, 0); mtr = I10NM_GET_DIMMMTR(imc, i, j); @@ -175,8 +193,8 @@ static int i10nm_get_dimm_config(struct mem_ctl_info *mci) mtr, mcddrtcfg, imc->mc, i, j); if (IS_DIMM_PRESENT(mtr)) - ndimms += skx_get_dimm_info(mtr, 0, 0, dimm, - imc, i, j); + ndimms += skx_get_dimm_info(mtr, 0, amap, dimm, + imc, i, j, cfg); else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) ndimms += skx_get_nvdimm_info(dimm, imc, i, j, EDAC_MOD_STR); @@ -300,10 +318,11 @@ static int __init i10nm_init(void) d->imc[i].lmc = i; d->imc[i].src_id = src_id; d->imc[i].node_id = node_id; + d->imc[i].chan_mmio_sz = cfg->ddr_chan_mmio_sz; rc = skx_register_mci(&d->imc[i], d->imc[i].mdev, "Intel_10nm Socket", EDAC_MOD_STR, - i10nm_get_dimm_config); + i10nm_get_dimm_config, cfg); if (rc < 0) goto fail; } diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 5c1eea96230c..9065bc4386ff 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -273,7 +273,6 @@ static void i3000_check(struct mem_ctl_info *mci) { struct i3000_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i3000_get_error_info(mci, &info); i3000_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index a8988db6d423..afccdebf5ac1 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -253,7 +253,6 @@ static void i3200_check(struct mem_ctl_info *mci) { struct i3200_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i3200_get_and_clear_error_info(mci, &info); i3200_process_error_info(mci, &info); } diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 1a6f69c859ab..ba46057d4220 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -765,7 +765,7 @@ static void i5000_clear_error(struct mem_ctl_info *mci) static void i5000_check_error(struct mem_ctl_info *mci) { struct i5000_error_info info; - edac_dbg(4, "MC%d\n", mci->mc_idx); + i5000_get_error_info(mci, &info); i5000_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 92d63eb533ae..f76624ee82ef 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -686,7 +686,7 @@ static void i5400_clear_error(struct mem_ctl_info *mci) static void i5400_check_error(struct mem_ctl_info *mci) { struct i5400_error_info info; - edac_dbg(4, "MC%d\n", mci->mc_idx); + i5400_get_error_info(mci, &info); i5400_process_error_info(mci, &info); } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index a2ca929e2168..933dcf3cfdff 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -176,7 +176,6 @@ static void i82443bxgx_edacmc_check(struct mem_ctl_info *mci) { struct i82443bxgx_edacmc_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i82443bxgx_edacmc_get_error_info(mci, &info); i82443bxgx_edacmc_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index 3e3a80ffb322..fbec90d00f1e 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -135,7 +135,6 @@ static void i82860_check(struct mem_ctl_info *mci) { struct i82860_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i82860_get_error_info(mci, &info); i82860_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index ceac925af38c..553880b9fc12 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -262,7 +262,6 @@ static void i82875p_check(struct mem_ctl_info *mci) { struct i82875p_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i82875p_get_error_info(mci, &info); i82875p_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 6be99e0d850d..d99f005832cf 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -330,7 +330,6 @@ static void i82975x_check(struct mem_ctl_info *mci) { struct i82975x_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); i82975x_get_error_info(mci, &info); i82975x_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c index c47963240b65..9a9ff5ad611a 100644 --- a/drivers/edac/ie31200_edac.c +++ b/drivers/edac/ie31200_edac.c @@ -333,7 +333,6 @@ static void ie31200_check(struct mem_ctl_info *mci) { struct ie31200_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); ie31200_get_and_clear_error_info(mci, &info); ie31200_process_error_info(mci, &info); } diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c new file mode 100644 index 000000000000..6be9986fc6bd --- /dev/null +++ b/drivers/edac/igen6_edac.c @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Intel client SoC with integrated memory controller using IBECC + * + * Copyright (C) 2020 Intel Corporation + * + * The In-Band ECC (IBECC) IP provides ECC protection to all or specific + * regions of the physical memory space. It's used for memory controllers + * that don't support the out-of-band ECC which often needs an additional + * storage device to each channel for storing ECC data. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/irq_work.h> +#include <linux/llist.h> +#include <linux/genalloc.h> +#include <linux/edac.h> +#include <linux/bits.h> +#include <linux/io.h> +#include <asm/mach_traps.h> +#include <asm/nmi.h> + +#include "edac_mc.h" +#include "edac_module.h" + +#define IGEN6_REVISION "v2.4" + +#define EDAC_MOD_STR "igen6_edac" +#define IGEN6_NMI_NAME "igen6_ibecc" + +/* Debug macros */ +#define igen6_printk(level, fmt, arg...) \ + edac_printk(level, "igen6", fmt, ##arg) + +#define igen6_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "igen6", fmt, ##arg) + +#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) + +#define NUM_IMC 1 /* Max memory controllers */ +#define NUM_CHANNELS 2 /* Max channels */ +#define NUM_DIMMS 2 /* Max DIMMs per channel */ + +#define _4GB BIT_ULL(32) + +/* Size of physical memory */ +#define TOM_OFFSET 0xa0 +/* Top of low usable DRAM */ +#define TOLUD_OFFSET 0xbc +/* Capability register C */ +#define CAPID_C_OFFSET 0xec +#define CAPID_C_IBECC BIT(15) + +/* Error Status */ +#define ERRSTS_OFFSET 0xc8 +#define ERRSTS_CE BIT_ULL(6) +#define ERRSTS_UE BIT_ULL(7) + +/* Error Command */ +#define ERRCMD_OFFSET 0xca +#define ERRCMD_CE BIT_ULL(6) +#define ERRCMD_UE BIT_ULL(7) + +/* IBECC MMIO base address */ +#define IBECC_BASE (res_cfg->ibecc_base) +#define IBECC_ACTIVATE_OFFSET IBECC_BASE +#define IBECC_ACTIVATE_EN BIT(0) + +/* IBECC error log */ +#define ECC_ERROR_LOG_OFFSET (IBECC_BASE + 0x170) +#define ECC_ERROR_LOG_CE BIT_ULL(62) +#define ECC_ERROR_LOG_UE BIT_ULL(63) +#define ECC_ERROR_LOG_ADDR_SHIFT 5 +#define ECC_ERROR_LOG_ADDR(v) GET_BITFIELD(v, 5, 38) +#define ECC_ERROR_LOG_SYND(v) GET_BITFIELD(v, 46, 61) + +/* Host MMIO base address */ +#define MCHBAR_OFFSET 0x48 +#define MCHBAR_EN BIT_ULL(0) +#define MCHBAR_BASE(v) (GET_BITFIELD(v, 16, 38) << 16) +#define MCHBAR_SIZE 0x10000 + +/* Parameters for the channel decode stage */ +#define MAD_INTER_CHANNEL_OFFSET 0x5000 +#define MAD_INTER_CHANNEL_DDR_TYPE(v) GET_BITFIELD(v, 0, 2) +#define MAD_INTER_CHANNEL_ECHM(v) GET_BITFIELD(v, 3, 3) +#define MAD_INTER_CHANNEL_CH_L_MAP(v) GET_BITFIELD(v, 4, 4) +#define MAD_INTER_CHANNEL_CH_S_SIZE(v) ((u64)GET_BITFIELD(v, 12, 19) << 29) + +/* Parameters for DRAM decode stage */ +#define MAD_INTRA_CH0_OFFSET 0x5004 +#define MAD_INTRA_CH_DIMM_L_MAP(v) GET_BITFIELD(v, 0, 0) + +/* DIMM characteristics */ +#define MAD_DIMM_CH0_OFFSET 0x500c +#define MAD_DIMM_CH_DIMM_L_SIZE(v) ((u64)GET_BITFIELD(v, 0, 6) << 29) +#define MAD_DIMM_CH_DLW(v) GET_BITFIELD(v, 7, 8) +#define MAD_DIMM_CH_DIMM_S_SIZE(v) ((u64)GET_BITFIELD(v, 16, 22) << 29) +#define MAD_DIMM_CH_DSW(v) GET_BITFIELD(v, 24, 25) + +/* Hash for channel selection */ +#define CHANNEL_HASH_OFFSET 0X5024 +/* Hash for enhanced channel selection */ +#define CHANNEL_EHASH_OFFSET 0X5028 +#define CHANNEL_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) +#define CHANNEL_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) +#define CHANNEL_HASH_MODE(v) GET_BITFIELD(v, 28, 28) + +static struct res_config { + int num_imc; + u32 ibecc_base; + bool (*ibecc_available)(struct pci_dev *pdev); + /* Convert error address logged in IBECC to system physical address */ + u64 (*err_addr_to_sys_addr)(u64 eaddr); + /* Convert error address logged in IBECC to integrated memory controller address */ + u64 (*err_addr_to_imc_addr)(u64 eaddr); +} *res_cfg; + +struct igen6_imc { + int mc; + struct mem_ctl_info *mci; + struct pci_dev *pdev; + struct device dev; + void __iomem *window; + u64 ch_s_size; + int ch_l_map; + u64 dimm_s_size[NUM_CHANNELS]; + u64 dimm_l_size[NUM_CHANNELS]; + int dimm_l_map[NUM_CHANNELS]; +}; + +static struct igen6_pvt { + struct igen6_imc imc[NUM_IMC]; +} *igen6_pvt; + +/* The top of low usable DRAM */ +static u32 igen6_tolud; +/* The size of physical memory */ +static u64 igen6_tom; + +struct decoded_addr { + int mc; + u64 imc_addr; + u64 sys_addr; + int channel_idx; + u64 channel_addr; + int sub_channel_idx; + u64 sub_channel_addr; +}; + +struct ecclog_node { + struct llist_node llnode; + int mc; + u64 ecclog; +}; + +/* + * In the NMI handler, the driver uses the lock-less memory allocator + * to allocate memory to store the IBECC error logs and links the logs + * to the lock-less list. Delay printk() and the work of error reporting + * to EDAC core in a worker. + */ +#define ECCLOG_POOL_SIZE PAGE_SIZE +static LLIST_HEAD(ecclog_llist); +static struct gen_pool *ecclog_pool; +static char ecclog_buf[ECCLOG_POOL_SIZE]; +static struct irq_work ecclog_irq_work; +static struct work_struct ecclog_work; + +/* Compute die IDs for Elkhart Lake with IBECC */ +#define DID_EHL_SKU5 0x4514 +#define DID_EHL_SKU6 0x4528 +#define DID_EHL_SKU7 0x452a +#define DID_EHL_SKU8 0x4516 +#define DID_EHL_SKU9 0x452c +#define DID_EHL_SKU10 0x452e +#define DID_EHL_SKU11 0x4532 +#define DID_EHL_SKU12 0x4518 +#define DID_EHL_SKU13 0x451a +#define DID_EHL_SKU14 0x4534 +#define DID_EHL_SKU15 0x4536 + +static bool ehl_ibecc_available(struct pci_dev *pdev) +{ + u32 v; + + if (pci_read_config_dword(pdev, CAPID_C_OFFSET, &v)) + return false; + + return !!(CAPID_C_IBECC & v); +} + +static u64 ehl_err_addr_to_sys_addr(u64 eaddr) +{ + return eaddr; +} + +static u64 ehl_err_addr_to_imc_addr(u64 eaddr) +{ + if (eaddr < igen6_tolud) + return eaddr; + + if (igen6_tom <= _4GB) + return eaddr + igen6_tolud - _4GB; + + if (eaddr < _4GB) + return eaddr + igen6_tolud - igen6_tom; + + return eaddr; +} + +static struct res_config ehl_cfg = { + .num_imc = 1, + .ibecc_base = 0xdc00, + .ibecc_available = ehl_ibecc_available, + .err_addr_to_sys_addr = ehl_err_addr_to_sys_addr, + .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, +}; + +static const struct pci_device_id igen6_pci_tbl[] = { + { PCI_VDEVICE(INTEL, DID_EHL_SKU5), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU6), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU7), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU8), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU9), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU10), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU11), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU12), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU13), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU14), (kernel_ulong_t)&ehl_cfg }, + { PCI_VDEVICE(INTEL, DID_EHL_SKU15), (kernel_ulong_t)&ehl_cfg }, + { }, +}; +MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); + +static enum dev_type get_width(int dimm_l, u32 mad_dimm) +{ + u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : + MAD_DIMM_CH_DSW(mad_dimm); + + switch (w) { + case 0: + return DEV_X8; + case 1: + return DEV_X16; + case 2: + return DEV_X32; + default: + return DEV_UNKNOWN; + } +} + +static enum mem_type get_memory_type(u32 mad_inter) +{ + u32 t = MAD_INTER_CHANNEL_DDR_TYPE(mad_inter); + + switch (t) { + case 0: + return MEM_DDR4; + case 1: + return MEM_DDR3; + case 2: + return MEM_LPDDR3; + case 3: + return MEM_LPDDR4; + case 4: + return MEM_WIO2; + default: + return MEM_UNKNOWN; + } +} + +static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit) +{ + u64 hash_addr = addr & mask, hash = 0; + u64 intlv = (addr >> intlv_bit) & 1; + int i; + + for (i = 6; i < 20; i++) + hash ^= (hash_addr >> i) & 1; + + return (int)hash ^ intlv; +} + +static u64 decode_channel_addr(u64 addr, int intlv_bit) +{ + u64 channel_addr; + + /* Remove the interleave bit and shift upper part down to fill gap */ + channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit; + channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1); + + return channel_addr; +} + +static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map, + int *idx, u64 *sub_addr) +{ + int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6; + + if (addr > 2 * s_size) { + *sub_addr = addr - s_size; + *idx = l_map; + return; + } + + if (CHANNEL_HASH_MODE(hash)) { + *sub_addr = decode_channel_addr(addr, intlv_bit); + *idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit); + } else { + *sub_addr = decode_channel_addr(addr, 6); + *idx = GET_BITFIELD(addr, 6, 6); + } +} + +static int igen6_decode(struct decoded_addr *res) +{ + struct igen6_imc *imc = &igen6_pvt->imc[res->mc]; + u64 addr = res->imc_addr, sub_addr, s_size; + int idx, l_map; + u32 hash; + + if (addr >= igen6_tom) { + edac_dbg(0, "Address 0x%llx out of range\n", addr); + return -EINVAL; + } + + /* Decode channel */ + hash = readl(imc->window + CHANNEL_HASH_OFFSET); + s_size = imc->ch_s_size; + l_map = imc->ch_l_map; + decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr); + res->channel_idx = idx; + res->channel_addr = sub_addr; + + /* Decode sub-channel/DIMM */ + hash = readl(imc->window + CHANNEL_EHASH_OFFSET); + s_size = imc->dimm_s_size[idx]; + l_map = imc->dimm_l_map[idx]; + decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr); + res->sub_channel_idx = idx; + res->sub_channel_addr = sub_addr; + + return 0; +} + +static void igen6_output_error(struct decoded_addr *res, + struct mem_ctl_info *mci, u64 ecclog) +{ + enum hw_event_mc_err_type type = ecclog & ECC_ERROR_LOG_UE ? + HW_EVENT_ERR_UNCORRECTED : + HW_EVENT_ERR_CORRECTED; + + edac_mc_handle_error(type, mci, 1, + res->sys_addr >> PAGE_SHIFT, + res->sys_addr & ~PAGE_MASK, + ECC_ERROR_LOG_SYND(ecclog), + res->channel_idx, res->sub_channel_idx, + -1, "", ""); +} + +static struct gen_pool *ecclog_gen_pool_create(void) +{ + struct gen_pool *pool; + + pool = gen_pool_create(ilog2(sizeof(struct ecclog_node)), -1); + if (!pool) + return NULL; + + if (gen_pool_add(pool, (unsigned long)ecclog_buf, ECCLOG_POOL_SIZE, -1)) { + gen_pool_destroy(pool); + return NULL; + } + + return pool; +} + +static int ecclog_gen_pool_add(int mc, u64 ecclog) +{ + struct ecclog_node *node; + + node = (void *)gen_pool_alloc(ecclog_pool, sizeof(*node)); + if (!node) + return -ENOMEM; + + node->mc = mc; + node->ecclog = ecclog; + llist_add(&node->llnode, &ecclog_llist); + + return 0; +} + +/* + * Either the memory-mapped I/O status register ECC_ERROR_LOG or the PCI + * configuration space status register ERRSTS can indicate whether a + * correctable error or an uncorrectable error occurred. We only use the + * ECC_ERROR_LOG register to check error type, but need to clear both + * registers to enable future error events. + */ +static u64 ecclog_read_and_clear(struct igen6_imc *imc) +{ + u64 ecclog = readq(imc->window + ECC_ERROR_LOG_OFFSET); + + if (ecclog & (ECC_ERROR_LOG_CE | ECC_ERROR_LOG_UE)) { + /* Clear CE/UE bits by writing 1s */ + writeq(ecclog, imc->window + ECC_ERROR_LOG_OFFSET); + return ecclog; + } + + return 0; +} + +static void errsts_clear(struct igen6_imc *imc) +{ + u16 errsts; + + if (pci_read_config_word(imc->pdev, ERRSTS_OFFSET, &errsts)) { + igen6_printk(KERN_ERR, "Failed to read ERRSTS\n"); + return; + } + + /* Clear CE/UE bits by writing 1s */ + if (errsts & (ERRSTS_CE | ERRSTS_UE)) + pci_write_config_word(imc->pdev, ERRSTS_OFFSET, errsts); +} + +static int errcmd_enable_error_reporting(bool enable) +{ + struct igen6_imc *imc = &igen6_pvt->imc[0]; + u16 errcmd; + int rc; + + rc = pci_read_config_word(imc->pdev, ERRCMD_OFFSET, &errcmd); + if (rc) + return rc; + + if (enable) + errcmd |= ERRCMD_CE | ERRSTS_UE; + else + errcmd &= ~(ERRCMD_CE | ERRSTS_UE); + + rc = pci_write_config_word(imc->pdev, ERRCMD_OFFSET, errcmd); + if (rc) + return rc; + + return 0; +} + +static int ecclog_handler(void) +{ + struct igen6_imc *imc; + int i, n = 0; + u64 ecclog; + + for (i = 0; i < res_cfg->num_imc; i++) { + imc = &igen6_pvt->imc[i]; + + /* errsts_clear() isn't NMI-safe. Delay it in the IRQ context */ + + ecclog = ecclog_read_and_clear(imc); + if (!ecclog) + continue; + + if (!ecclog_gen_pool_add(i, ecclog)) + irq_work_queue(&ecclog_irq_work); + + n++; + } + + return n; +} + +static void ecclog_work_cb(struct work_struct *work) +{ + struct ecclog_node *node, *tmp; + struct mem_ctl_info *mci; + struct llist_node *head; + struct decoded_addr res; + u64 eaddr; + + head = llist_del_all(&ecclog_llist); + if (!head) + return; + + llist_for_each_entry_safe(node, tmp, head, llnode) { + memset(&res, 0, sizeof(res)); + eaddr = ECC_ERROR_LOG_ADDR(node->ecclog) << + ECC_ERROR_LOG_ADDR_SHIFT; + res.mc = node->mc; + res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr); + res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr); + + mci = igen6_pvt->imc[res.mc].mci; + + edac_dbg(2, "MC %d, ecclog = 0x%llx\n", node->mc, node->ecclog); + igen6_mc_printk(mci, KERN_DEBUG, "HANDLING IBECC MEMORY ERROR\n"); + igen6_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", res.sys_addr); + + if (!igen6_decode(&res)) + igen6_output_error(&res, mci, node->ecclog); + + gen_pool_free(ecclog_pool, (unsigned long)node, sizeof(*node)); + } +} + +static void ecclog_irq_work_cb(struct irq_work *irq_work) +{ + int i; + + for (i = 0; i < res_cfg->num_imc; i++) + errsts_clear(&igen6_pvt->imc[i]); + + if (!llist_empty(&ecclog_llist)) + schedule_work(&ecclog_work); +} + +static int ecclog_nmi_handler(unsigned int cmd, struct pt_regs *regs) +{ + unsigned char reason; + + if (!ecclog_handler()) + return NMI_DONE; + + /* + * Both In-Band ECC correctable error and uncorrectable error are + * reported by SERR# NMI. The NMI generic code (see pci_serr_error()) + * doesn't clear the bit NMI_REASON_CLEAR_SERR (in port 0x61) to + * re-enable the SERR# NMI after NMI handling. So clear this bit here + * to re-enable SERR# NMI for receiving future In-Band ECC errors. + */ + reason = x86_platform.get_nmi_reason() & NMI_REASON_CLEAR_MASK; + reason |= NMI_REASON_CLEAR_SERR; + outb(reason, NMI_REASON_PORT); + reason &= ~NMI_REASON_CLEAR_SERR; + outb(reason, NMI_REASON_PORT); + + return NMI_HANDLED; +} + +static bool igen6_check_ecc(struct igen6_imc *imc) +{ + u32 activate = readl(imc->window + IBECC_ACTIVATE_OFFSET); + + return !!(activate & IBECC_ACTIVATE_EN); +} + +static int igen6_get_dimm_config(struct mem_ctl_info *mci) +{ + struct igen6_imc *imc = mci->pvt_info; + u32 mad_inter, mad_intra, mad_dimm; + int i, j, ndimms, mc = imc->mc; + struct dimm_info *dimm; + enum mem_type mtype; + enum dev_type dtype; + u64 dsize; + bool ecc; + + edac_dbg(2, "\n"); + + mad_inter = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); + mtype = get_memory_type(mad_inter); + ecc = igen6_check_ecc(imc); + imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(mad_inter); + imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(mad_inter); + + for (i = 0; i < NUM_CHANNELS; i++) { + mad_intra = readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4); + mad_dimm = readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4); + + imc->dimm_l_size[i] = MAD_DIMM_CH_DIMM_L_SIZE(mad_dimm); + imc->dimm_s_size[i] = MAD_DIMM_CH_DIMM_S_SIZE(mad_dimm); + imc->dimm_l_map[i] = MAD_INTRA_CH_DIMM_L_MAP(mad_intra); + ndimms = 0; + + for (j = 0; j < NUM_DIMMS; j++) { + dimm = edac_get_dimm(mci, i, j, 0); + + if (j ^ imc->dimm_l_map[i]) { + dtype = get_width(0, mad_dimm); + dsize = imc->dimm_s_size[i]; + } else { + dtype = get_width(1, mad_dimm); + dsize = imc->dimm_l_size[i]; + } + + if (!dsize) + continue; + + dimm->grain = 64; + dimm->mtype = mtype; + dimm->dtype = dtype; + dimm->nr_pages = MiB_TO_PAGES(dsize >> 20); + dimm->edac_mode = EDAC_SECDED; + snprintf(dimm->label, sizeof(dimm->label), + "MC#%d_Chan#%d_DIMM#%d", mc, i, j); + edac_dbg(0, "MC %d, Channel %d, DIMM %d, Size %llu MiB (%u pages)\n", + mc, i, j, dsize >> 20, dimm->nr_pages); + + ndimms++; + } + + if (ndimms && !ecc) { + igen6_printk(KERN_ERR, "MC%d In-Band ECC is disabled\n", mc); + return -ENODEV; + } + } + + return 0; +} + +#ifdef CONFIG_EDAC_DEBUG +/* Top of upper usable DRAM */ +static u64 igen6_touud; +#define TOUUD_OFFSET 0xa8 + +static void igen6_reg_dump(struct igen6_imc *imc) +{ + int i; + + edac_dbg(2, "CHANNEL_HASH : 0x%x\n", + readl(imc->window + CHANNEL_HASH_OFFSET)); + edac_dbg(2, "CHANNEL_EHASH : 0x%x\n", + readl(imc->window + CHANNEL_EHASH_OFFSET)); + edac_dbg(2, "MAD_INTER_CHANNEL: 0x%x\n", + readl(imc->window + MAD_INTER_CHANNEL_OFFSET)); + edac_dbg(2, "ECC_ERROR_LOG : 0x%llx\n", + readq(imc->window + ECC_ERROR_LOG_OFFSET)); + + for (i = 0; i < NUM_CHANNELS; i++) { + edac_dbg(2, "MAD_INTRA_CH%d : 0x%x\n", i, + readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4)); + edac_dbg(2, "MAD_DIMM_CH%d : 0x%x\n", i, + readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4)); + } + edac_dbg(2, "TOLUD : 0x%x", igen6_tolud); + edac_dbg(2, "TOUUD : 0x%llx", igen6_touud); + edac_dbg(2, "TOM : 0x%llx", igen6_tom); +} + +static struct dentry *igen6_test; + +static int debugfs_u64_set(void *data, u64 val) +{ + u64 ecclog; + + if ((val >= igen6_tolud && val < _4GB) || val >= igen6_touud) { + edac_dbg(0, "Address 0x%llx out of range\n", val); + return 0; + } + + pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); + + val >>= ECC_ERROR_LOG_ADDR_SHIFT; + ecclog = (val << ECC_ERROR_LOG_ADDR_SHIFT) | ECC_ERROR_LOG_CE; + + if (!ecclog_gen_pool_add(0, ecclog)) + irq_work_queue(&ecclog_irq_work); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); + +static void igen6_debug_setup(void) +{ + igen6_test = edac_debugfs_create_dir("igen6_test"); + if (!igen6_test) + return; + + if (!edac_debugfs_create_file("addr", 0200, igen6_test, + NULL, &fops_u64_wo)) { + debugfs_remove(igen6_test); + igen6_test = NULL; + } +} + +static void igen6_debug_teardown(void) +{ + debugfs_remove_recursive(igen6_test); +} +#else +static void igen6_reg_dump(struct igen6_imc *imc) {} +static void igen6_debug_setup(void) {} +static void igen6_debug_teardown(void) {} +#endif + +static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) +{ + union { + u64 v; + struct { + u32 v_lo; + u32 v_hi; + }; + } u; + + edac_dbg(2, "\n"); + + if (!res_cfg->ibecc_available(pdev)) { + edac_dbg(2, "No In-Band ECC IP\n"); + goto fail; + } + + if (pci_read_config_dword(pdev, TOLUD_OFFSET, &igen6_tolud)) { + igen6_printk(KERN_ERR, "Failed to read TOLUD\n"); + goto fail; + } + + igen6_tolud &= GENMASK(31, 20); + + if (pci_read_config_dword(pdev, TOM_OFFSET, &u.v_lo)) { + igen6_printk(KERN_ERR, "Failed to read lower TOM\n"); + goto fail; + } + + if (pci_read_config_dword(pdev, TOM_OFFSET + 4, &u.v_hi)) { + igen6_printk(KERN_ERR, "Failed to read upper TOM\n"); + goto fail; + } + + igen6_tom = u.v & GENMASK_ULL(38, 20); + + if (pci_read_config_dword(pdev, MCHBAR_OFFSET, &u.v_lo)) { + igen6_printk(KERN_ERR, "Failed to read lower MCHBAR\n"); + goto fail; + } + + if (pci_read_config_dword(pdev, MCHBAR_OFFSET + 4, &u.v_hi)) { + igen6_printk(KERN_ERR, "Failed to read upper MCHBAR\n"); + goto fail; + } + + if (!(u.v & MCHBAR_EN)) { + igen6_printk(KERN_ERR, "MCHBAR is disabled\n"); + goto fail; + } + + *mchbar = MCHBAR_BASE(u.v); + +#ifdef CONFIG_EDAC_DEBUG + if (pci_read_config_dword(pdev, TOUUD_OFFSET, &u.v_lo)) + edac_dbg(2, "Failed to read lower TOUUD\n"); + else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi)) + edac_dbg(2, "Failed to read upper TOUUD\n"); + else + igen6_touud = u.v & GENMASK_ULL(38, 20); +#endif + + return 0; +fail: + return -ENODEV; +} + +static int igen6_register_mci(int mc, u64 mchbar, struct pci_dev *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + struct igen6_imc *imc; + void __iomem *window; + int rc; + + edac_dbg(2, "\n"); + + mchbar += mc * MCHBAR_SIZE; + window = ioremap(mchbar, MCHBAR_SIZE); + if (!window) { + igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", mchbar); + return -ENODEV; + } + + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = NUM_CHANNELS; + layers[0].is_virt_csrow = false; + layers[1].type = EDAC_MC_LAYER_SLOT; + layers[1].size = NUM_DIMMS; + layers[1].is_virt_csrow = true; + + mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); + if (!mci) { + rc = -ENOMEM; + goto fail; + } + + mci->ctl_name = kasprintf(GFP_KERNEL, "Intel_client_SoC MC#%d", mc); + if (!mci->ctl_name) { + rc = -ENOMEM; + goto fail2; + } + + mci->mtype_cap = MEM_FLAG_LPDDR4 | MEM_FLAG_DDR4; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = EDAC_MOD_STR; + mci->dev_name = pci_name(pdev); + mci->pvt_info = &igen6_pvt->imc[mc]; + + imc = mci->pvt_info; + device_initialize(&imc->dev); + /* + * EDAC core uses mci->pdev(pointer of structure device) as + * memory controller ID. The client SoCs attach one or more + * memory controllers to single pci_dev (single pci_dev->dev + * can be for multiple memory controllers). + * + * To make mci->pdev unique, assign pci_dev->dev to mci->pdev + * for the first memory controller and assign a unique imc->dev + * to mci->pdev for each non-first memory controller. + */ + mci->pdev = mc ? &imc->dev : &pdev->dev; + imc->mc = mc; + imc->pdev = pdev; + imc->window = window; + + igen6_reg_dump(imc); + + rc = igen6_get_dimm_config(mci); + if (rc) + goto fail3; + + rc = edac_mc_add_mc(mci); + if (rc) { + igen6_printk(KERN_ERR, "Failed to register mci#%d\n", mc); + goto fail3; + } + + imc->mci = mci; + return 0; +fail3: + kfree(mci->ctl_name); +fail2: + edac_mc_free(mci); +fail: + iounmap(window); + return rc; +} + +static void igen6_unregister_mcis(void) +{ + struct mem_ctl_info *mci; + struct igen6_imc *imc; + int i; + + edac_dbg(2, "\n"); + + for (i = 0; i < res_cfg->num_imc; i++) { + imc = &igen6_pvt->imc[i]; + mci = imc->mci; + if (!mci) + continue; + + edac_mc_del_mc(mci->pdev); + kfree(mci->ctl_name); + edac_mc_free(mci); + iounmap(imc->window); + } +} + +static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + u64 mchbar; + int i, rc; + + edac_dbg(2, "\n"); + + igen6_pvt = kzalloc(sizeof(*igen6_pvt), GFP_KERNEL); + if (!igen6_pvt) + return -ENOMEM; + + res_cfg = (struct res_config *)ent->driver_data; + + rc = igen6_pci_setup(pdev, &mchbar); + if (rc) + goto fail; + + for (i = 0; i < res_cfg->num_imc; i++) { + rc = igen6_register_mci(i, mchbar, pdev); + if (rc) + goto fail2; + } + + ecclog_pool = ecclog_gen_pool_create(); + if (!ecclog_pool) { + rc = -ENOMEM; + goto fail2; + } + + INIT_WORK(&ecclog_work, ecclog_work_cb); + init_irq_work(&ecclog_irq_work, ecclog_irq_work_cb); + + /* Check if any pending errors before registering the NMI handler */ + ecclog_handler(); + + rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, + 0, IGEN6_NMI_NAME); + if (rc) { + igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); + goto fail3; + } + + /* Enable error reporting */ + rc = errcmd_enable_error_reporting(true); + if (rc) { + igen6_printk(KERN_ERR, "Failed to enable error reporting\n"); + goto fail4; + } + + igen6_debug_setup(); + return 0; +fail4: + unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME); +fail3: + gen_pool_destroy(ecclog_pool); +fail2: + igen6_unregister_mcis(); +fail: + kfree(igen6_pvt); + return rc; +} + +static void igen6_remove(struct pci_dev *pdev) +{ + edac_dbg(2, "\n"); + + igen6_debug_teardown(); + errcmd_enable_error_reporting(false); + unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME); + irq_work_sync(&ecclog_irq_work); + flush_work(&ecclog_work); + gen_pool_destroy(ecclog_pool); + igen6_unregister_mcis(); + kfree(igen6_pvt); +} + +static struct pci_driver igen6_driver = { + .name = EDAC_MOD_STR, + .probe = igen6_probe, + .remove = igen6_remove, + .id_table = igen6_pci_tbl, +}; + +static int __init igen6_init(void) +{ + const char *owner; + int rc; + + edac_dbg(2, "\n"); + + owner = edac_get_owner(); + if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) + return -ENODEV; + + edac_op_state = EDAC_OPSTATE_NMI; + + rc = pci_register_driver(&igen6_driver); + if (rc) + return rc; + + igen6_printk(KERN_INFO, "%s\n", IGEN6_REVISION); + + return 0; +} + +static void __exit igen6_exit(void) +{ + edac_dbg(2, "\n"); + + pci_unregister_driver(&igen6_driver); +} + +module_init(igen6_init); +module_exit(igen6_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Qiuxu Zhuo"); +MODULE_DESCRIPTION("MC Driver for Intel client SoC using In-Band ECC"); diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 7f28edb070bd..5dd905a3f30c 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -869,7 +869,7 @@ static void decode_mc3_mce(struct mce *m) static void decode_mc4_mce(struct mce *m) { unsigned int fam = x86_family(m->cpuid); - int node_id = amd_get_nb_id(m->extcpu); + int node_id = topology_die_id(m->extcpu); u16 ec = EC(m->status); u8 xec = XEC(m->status, 0x1f); u8 offset = 0; @@ -1003,7 +1003,7 @@ static void decode_smca_error(struct mce *m) pr_cont(", %s.\n", smca_mce_descs[bank_type].descs[xec]); if (bank_type == SMCA_UMC && xec == 0 && decode_dram_ecc) - decode_dram_ecc(cpu_to_node(m->extcpu), m); + decode_dram_ecc(topology_die_id(m->extcpu), m); } static inline void amd_decode_err_code(u16 ec) diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c deleted file mode 100644 index 3c68bb525d5d..000000000000 --- a/drivers/edac/mv64x60_edac.c +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Marvell MV64x60 Memory Controller kernel module for PPC platforms - * - * Author: Dave Jiang <djiang@mvista.com> - * - * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/edac.h> -#include <linux/gfp.h> - -#include "edac_module.h" -#include "mv64x60_edac.h" - -static const char *mv64x60_ctl_name = "MV64x60"; -static int edac_dev_idx; -static int edac_pci_idx; -static int edac_mc_idx; - -/*********************** PCI err device **********************************/ -#ifdef CONFIG_PCI -static void mv64x60_pci_check(struct edac_pci_ctl_info *pci) -{ - struct mv64x60_pci_pdata *pdata = pci->pvt_info; - u32 cause; - - cause = readl(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); - if (!cause) - return; - - printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose); - printk(KERN_ERR "Cause register: 0x%08x\n", cause); - printk(KERN_ERR "Address Low: 0x%08x\n", - readl(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO)); - printk(KERN_ERR "Address High: 0x%08x\n", - readl(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI)); - printk(KERN_ERR "Attribute: 0x%08x\n", - readl(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR)); - printk(KERN_ERR "Command: 0x%08x\n", - readl(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD)); - writel(~cause, pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); - - if (cause & MV64X60_PCI_PE_MASK) - edac_pci_handle_pe(pci, pci->ctl_name); - - if (!(cause & MV64X60_PCI_PE_MASK)) - edac_pci_handle_npe(pci, pci->ctl_name); -} - -static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id) -{ - struct edac_pci_ctl_info *pci = dev_id; - struct mv64x60_pci_pdata *pdata = pci->pvt_info; - u32 val; - - val = readl(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); - if (!val) - return IRQ_NONE; - - mv64x60_pci_check(pci); - - return IRQ_HANDLED; -} - -/* - * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of - * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as - * well. IOW, don't set bit 0. - */ - -/* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */ -static int __init mv64x60_pci_fixup(struct platform_device *pdev) -{ - struct resource *r; - void __iomem *pci_serr; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "PCI err regs\n", __func__); - return -ENOENT; - } - - pci_serr = ioremap(r->start, resource_size(r)); - if (!pci_serr) - return -ENOMEM; - - writel(readl(pci_serr) & ~0x1, pci_serr); - iounmap(pci_serr); - - return 0; -} - -static int mv64x60_pci_err_probe(struct platform_device *pdev) -{ - struct edac_pci_ctl_info *pci; - struct mv64x60_pci_pdata *pdata; - struct resource *r; - int res = 0; - - if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL)) - return -ENOMEM; - - pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err"); - if (!pci) - return -ENOMEM; - - pdata = pci->pvt_info; - - pdata->pci_hose = pdev->id; - pdata->name = "mv64x60_pci_err"; - platform_set_drvdata(pdev, pci); - pci->dev = &pdev->dev; - pci->dev_name = dev_name(&pdev->dev); - pci->mod_name = EDAC_MOD_STR; - pci->ctl_name = pdata->name; - - if (edac_op_state == EDAC_OPSTATE_POLL) - pci->edac_check = mv64x60_pci_check; - - pdata->edac_idx = edac_pci_idx++; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "PCI err regs\n", __func__); - res = -ENOENT; - goto err; - } - - if (!devm_request_mem_region(&pdev->dev, - r->start, - resource_size(r), - pdata->name)) { - printk(KERN_ERR "%s: Error while requesting mem region\n", - __func__); - res = -EBUSY; - goto err; - } - - pdata->pci_vbase = devm_ioremap(&pdev->dev, - r->start, - resource_size(r)); - if (!pdata->pci_vbase) { - printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); - res = -ENOMEM; - goto err; - } - - res = mv64x60_pci_fixup(pdev); - if (res < 0) { - printk(KERN_ERR "%s: PCI fixup failed\n", __func__); - goto err; - } - - writel(0, pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); - writel(0, pdata->pci_vbase + MV64X60_PCI_ERROR_MASK); - writel(MV64X60_PCIx_ERR_MASK_VAL, - pdata->pci_vbase + MV64X60_PCI_ERROR_MASK); - - if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { - edac_dbg(3, "failed edac_pci_add_device()\n"); - goto err; - } - - if (edac_op_state == EDAC_OPSTATE_INT) { - pdata->irq = platform_get_irq(pdev, 0); - res = devm_request_irq(&pdev->dev, - pdata->irq, - mv64x60_pci_isr, - 0, - "[EDAC] PCI err", - pci); - if (res < 0) { - printk(KERN_ERR "%s: Unable to request irq %d for " - "MV64x60 PCI ERR\n", __func__, pdata->irq); - res = -ENODEV; - goto err2; - } - printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", - pdata->irq); - } - - devres_remove_group(&pdev->dev, mv64x60_pci_err_probe); - - /* get this far and it's successful */ - edac_dbg(3, "success\n"); - - return 0; - -err2: - edac_pci_del_device(&pdev->dev); -err: - edac_pci_free_ctl_info(pci); - devres_release_group(&pdev->dev, mv64x60_pci_err_probe); - return res; -} - -static int mv64x60_pci_err_remove(struct platform_device *pdev) -{ - struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); - - edac_dbg(0, "\n"); - - edac_pci_del_device(&pdev->dev); - - edac_pci_free_ctl_info(pci); - - return 0; -} - -static struct platform_driver mv64x60_pci_err_driver = { - .probe = mv64x60_pci_err_probe, - .remove = mv64x60_pci_err_remove, - .driver = { - .name = "mv64x60_pci_err", - } -}; - -#endif /* CONFIG_PCI */ - -/*********************** SRAM err device **********************************/ -static void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev) -{ - struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; - u32 cause; - - cause = readl(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); - if (!cause) - return; - - printk(KERN_ERR "Error in internal SRAM\n"); - printk(KERN_ERR "Cause register: 0x%08x\n", cause); - printk(KERN_ERR "Address Low: 0x%08x\n", - readl(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO)); - printk(KERN_ERR "Address High: 0x%08x\n", - readl(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI)); - printk(KERN_ERR "Data Low: 0x%08x\n", - readl(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO)); - printk(KERN_ERR "Data High: 0x%08x\n", - readl(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI)); - printk(KERN_ERR "Parity: 0x%08x\n", - readl(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY)); - writel(0, pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); - - edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); -} - -static irqreturn_t mv64x60_sram_isr(int irq, void *dev_id) -{ - struct edac_device_ctl_info *edac_dev = dev_id; - struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; - u32 cause; - - cause = readl(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); - if (!cause) - return IRQ_NONE; - - mv64x60_sram_check(edac_dev); - - return IRQ_HANDLED; -} - -static int mv64x60_sram_err_probe(struct platform_device *pdev) -{ - struct edac_device_ctl_info *edac_dev; - struct mv64x60_sram_pdata *pdata; - struct resource *r; - int res = 0; - - if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL)) - return -ENOMEM; - - edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), - "sram", 1, NULL, 0, 0, NULL, 0, - edac_dev_idx); - if (!edac_dev) { - devres_release_group(&pdev->dev, mv64x60_sram_err_probe); - return -ENOMEM; - } - - pdata = edac_dev->pvt_info; - pdata->name = "mv64x60_sram_err"; - edac_dev->dev = &pdev->dev; - platform_set_drvdata(pdev, edac_dev); - edac_dev->dev_name = dev_name(&pdev->dev); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "SRAM err regs\n", __func__); - res = -ENOENT; - goto err; - } - - if (!devm_request_mem_region(&pdev->dev, - r->start, - resource_size(r), - pdata->name)) { - printk(KERN_ERR "%s: Error while request mem region\n", - __func__); - res = -EBUSY; - goto err; - } - - pdata->sram_vbase = devm_ioremap(&pdev->dev, - r->start, - resource_size(r)); - if (!pdata->sram_vbase) { - printk(KERN_ERR "%s: Unable to setup SRAM err regs\n", - __func__); - res = -ENOMEM; - goto err; - } - - /* setup SRAM err registers */ - writel(0, pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); - - edac_dev->mod_name = EDAC_MOD_STR; - edac_dev->ctl_name = pdata->name; - - if (edac_op_state == EDAC_OPSTATE_POLL) - edac_dev->edac_check = mv64x60_sram_check; - - pdata->edac_idx = edac_dev_idx++; - - if (edac_device_add_device(edac_dev) > 0) { - edac_dbg(3, "failed edac_device_add_device()\n"); - goto err; - } - - if (edac_op_state == EDAC_OPSTATE_INT) { - pdata->irq = platform_get_irq(pdev, 0); - res = devm_request_irq(&pdev->dev, - pdata->irq, - mv64x60_sram_isr, - 0, - "[EDAC] SRAM err", - edac_dev); - if (res < 0) { - printk(KERN_ERR - "%s: Unable to request irq %d for " - "MV64x60 SRAM ERR\n", __func__, pdata->irq); - res = -ENODEV; - goto err2; - } - - printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n", - pdata->irq); - } - - devres_remove_group(&pdev->dev, mv64x60_sram_err_probe); - - /* get this far and it's successful */ - edac_dbg(3, "success\n"); - - return 0; - -err2: - edac_device_del_device(&pdev->dev); -err: - devres_release_group(&pdev->dev, mv64x60_sram_err_probe); - edac_device_free_ctl_info(edac_dev); - return res; -} - -static int mv64x60_sram_err_remove(struct platform_device *pdev) -{ - struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); - - edac_dbg(0, "\n"); - - edac_device_del_device(&pdev->dev); - edac_device_free_ctl_info(edac_dev); - - return 0; -} - -static struct platform_driver mv64x60_sram_err_driver = { - .probe = mv64x60_sram_err_probe, - .remove = mv64x60_sram_err_remove, - .driver = { - .name = "mv64x60_sram_err", - } -}; - -/*********************** CPU err device **********************************/ -static void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev) -{ - struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; - u32 cause; - - cause = readl(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & - MV64x60_CPU_CAUSE_MASK; - if (!cause) - return; - - printk(KERN_ERR "Error on CPU interface\n"); - printk(KERN_ERR "Cause register: 0x%08x\n", cause); - printk(KERN_ERR "Address Low: 0x%08x\n", - readl(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO)); - printk(KERN_ERR "Address High: 0x%08x\n", - readl(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI)); - printk(KERN_ERR "Data Low: 0x%08x\n", - readl(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO)); - printk(KERN_ERR "Data High: 0x%08x\n", - readl(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI)); - printk(KERN_ERR "Parity: 0x%08x\n", - readl(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY)); - writel(0, pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE); - - edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); -} - -static irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id) -{ - struct edac_device_ctl_info *edac_dev = dev_id; - struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; - u32 cause; - - cause = readl(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & - MV64x60_CPU_CAUSE_MASK; - if (!cause) - return IRQ_NONE; - - mv64x60_cpu_check(edac_dev); - - return IRQ_HANDLED; -} - -static int mv64x60_cpu_err_probe(struct platform_device *pdev) -{ - struct edac_device_ctl_info *edac_dev; - struct resource *r; - struct mv64x60_cpu_pdata *pdata; - int res = 0; - - if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL)) - return -ENOMEM; - - edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), - "cpu", 1, NULL, 0, 0, NULL, 0, - edac_dev_idx); - if (!edac_dev) { - devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); - return -ENOMEM; - } - - pdata = edac_dev->pvt_info; - pdata->name = "mv64x60_cpu_err"; - edac_dev->dev = &pdev->dev; - platform_set_drvdata(pdev, edac_dev); - edac_dev->dev_name = dev_name(&pdev->dev); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "CPU err regs\n", __func__); - res = -ENOENT; - goto err; - } - - if (!devm_request_mem_region(&pdev->dev, - r->start, - resource_size(r), - pdata->name)) { - printk(KERN_ERR "%s: Error while requesting mem region\n", - __func__); - res = -EBUSY; - goto err; - } - - pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev, - r->start, - resource_size(r)); - if (!pdata->cpu_vbase[0]) { - printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); - res = -ENOMEM; - goto err; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "CPU err regs\n", __func__); - res = -ENOENT; - goto err; - } - - if (!devm_request_mem_region(&pdev->dev, - r->start, - resource_size(r), - pdata->name)) { - printk(KERN_ERR "%s: Error while requesting mem region\n", - __func__); - res = -EBUSY; - goto err; - } - - pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev, - r->start, - resource_size(r)); - if (!pdata->cpu_vbase[1]) { - printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); - res = -ENOMEM; - goto err; - } - - /* setup CPU err registers */ - writel(0, pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE); - writel(0, pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK); - writel(0x000000ff, pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK); - - edac_dev->mod_name = EDAC_MOD_STR; - edac_dev->ctl_name = pdata->name; - if (edac_op_state == EDAC_OPSTATE_POLL) - edac_dev->edac_check = mv64x60_cpu_check; - - pdata->edac_idx = edac_dev_idx++; - - if (edac_device_add_device(edac_dev) > 0) { - edac_dbg(3, "failed edac_device_add_device()\n"); - goto err; - } - - if (edac_op_state == EDAC_OPSTATE_INT) { - pdata->irq = platform_get_irq(pdev, 0); - res = devm_request_irq(&pdev->dev, - pdata->irq, - mv64x60_cpu_isr, - 0, - "[EDAC] CPU err", - edac_dev); - if (res < 0) { - printk(KERN_ERR - "%s: Unable to request irq %d for MV64x60 " - "CPU ERR\n", __func__, pdata->irq); - res = -ENODEV; - goto err2; - } - - printk(KERN_INFO EDAC_MOD_STR - " acquired irq %d for CPU Err\n", pdata->irq); - } - - devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe); - - /* get this far and it's successful */ - edac_dbg(3, "success\n"); - - return 0; - -err2: - edac_device_del_device(&pdev->dev); -err: - devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); - edac_device_free_ctl_info(edac_dev); - return res; -} - -static int mv64x60_cpu_err_remove(struct platform_device *pdev) -{ - struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); - - edac_dbg(0, "\n"); - - edac_device_del_device(&pdev->dev); - edac_device_free_ctl_info(edac_dev); - return 0; -} - -static struct platform_driver mv64x60_cpu_err_driver = { - .probe = mv64x60_cpu_err_probe, - .remove = mv64x60_cpu_err_remove, - .driver = { - .name = "mv64x60_cpu_err", - } -}; - -/*********************** DRAM err device **********************************/ - -static void mv64x60_mc_check(struct mem_ctl_info *mci) -{ - struct mv64x60_mc_pdata *pdata = mci->pvt_info; - u32 reg; - u32 err_addr; - u32 sdram_ecc; - u32 comp_ecc; - u32 syndrome; - - reg = readl(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); - if (!reg) - return; - - err_addr = reg & ~0x3; - sdram_ecc = readl(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD); - comp_ecc = readl(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC); - syndrome = sdram_ecc ^ comp_ecc; - - /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */ - if (!(reg & 0x1)) - edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, - err_addr >> PAGE_SHIFT, - err_addr & PAGE_MASK, syndrome, - 0, 0, -1, - mci->ctl_name, ""); - else /* 2 bit error, UE */ - edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, - err_addr >> PAGE_SHIFT, - err_addr & PAGE_MASK, 0, - 0, 0, -1, - mci->ctl_name, ""); - - /* clear the error */ - writel(0, pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); -} - -static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id) -{ - struct mem_ctl_info *mci = dev_id; - struct mv64x60_mc_pdata *pdata = mci->pvt_info; - u32 reg; - - reg = readl(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); - if (!reg) - return IRQ_NONE; - - /* writing 0's to the ECC err addr in check function clears irq */ - mv64x60_mc_check(mci); - - return IRQ_HANDLED; -} - -static void get_total_mem(struct mv64x60_mc_pdata *pdata) -{ - struct device_node *np = NULL; - const unsigned int *reg; - - np = of_find_node_by_type(NULL, "memory"); - if (!np) - return; - - reg = of_get_property(np, "reg", NULL); - - pdata->total_mem = reg[1]; -} - -static void mv64x60_init_csrows(struct mem_ctl_info *mci, - struct mv64x60_mc_pdata *pdata) -{ - struct csrow_info *csrow; - struct dimm_info *dimm; - - u32 devtype; - u32 ctl; - - get_total_mem(pdata); - - ctl = readl(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); - - csrow = mci->csrows[0]; - dimm = csrow->channels[0]->dimm; - - dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT; - dimm->grain = 8; - - dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR; - - devtype = (ctl >> 20) & 0x3; - switch (devtype) { - case 0x0: - dimm->dtype = DEV_X32; - break; - case 0x2: /* could be X8 too, but no way to tell */ - dimm->dtype = DEV_X16; - break; - case 0x3: - dimm->dtype = DEV_X4; - break; - default: - dimm->dtype = DEV_UNKNOWN; - break; - } - - dimm->edac_mode = EDAC_SECDED; -} - -static int mv64x60_mc_err_probe(struct platform_device *pdev) -{ - struct mem_ctl_info *mci; - struct edac_mc_layer layers[2]; - struct mv64x60_mc_pdata *pdata; - struct resource *r; - u32 ctl; - int res = 0; - - if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL)) - return -ENOMEM; - - layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; - layers[0].size = 1; - layers[0].is_virt_csrow = true; - layers[1].type = EDAC_MC_LAYER_CHANNEL; - layers[1].size = 1; - layers[1].is_virt_csrow = false; - mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers, - sizeof(struct mv64x60_mc_pdata)); - if (!mci) { - printk(KERN_ERR "%s: No memory for CPU err\n", __func__); - devres_release_group(&pdev->dev, mv64x60_mc_err_probe); - return -ENOMEM; - } - - pdata = mci->pvt_info; - mci->pdev = &pdev->dev; - platform_set_drvdata(pdev, mci); - pdata->name = "mv64x60_mc_err"; - mci->dev_name = dev_name(&pdev->dev); - pdata->edac_idx = edac_mc_idx++; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - printk(KERN_ERR "%s: Unable to get resource for " - "MC err regs\n", __func__); - res = -ENOENT; - goto err; - } - - if (!devm_request_mem_region(&pdev->dev, - r->start, - resource_size(r), - pdata->name)) { - printk(KERN_ERR "%s: Error while requesting mem region\n", - __func__); - res = -EBUSY; - goto err; - } - - pdata->mc_vbase = devm_ioremap(&pdev->dev, - r->start, - resource_size(r)); - if (!pdata->mc_vbase) { - printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); - res = -ENOMEM; - goto err; - } - - ctl = readl(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); - if (!(ctl & MV64X60_SDRAM_ECC)) { - /* Non-ECC RAM? */ - printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); - res = -ENODEV; - goto err; - } - - edac_dbg(3, "init mci\n"); - mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; - mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; - mci->edac_cap = EDAC_FLAG_SECDED; - mci->mod_name = EDAC_MOD_STR; - mci->ctl_name = mv64x60_ctl_name; - - if (edac_op_state == EDAC_OPSTATE_POLL) - mci->edac_check = mv64x60_mc_check; - - mci->ctl_page_to_phys = NULL; - - mci->scrub_mode = SCRUB_SW_SRC; - - mv64x60_init_csrows(mci, pdata); - - /* setup MC registers */ - writel(0, pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); - ctl = readl(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); - ctl = (ctl & 0xff00ffff) | 0x10000; - writel(ctl, pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); - - res = edac_mc_add_mc(mci); - if (res) { - edac_dbg(3, "failed edac_mc_add_mc()\n"); - goto err; - } - - if (edac_op_state == EDAC_OPSTATE_INT) { - /* acquire interrupt that reports errors */ - pdata->irq = platform_get_irq(pdev, 0); - res = devm_request_irq(&pdev->dev, - pdata->irq, - mv64x60_mc_isr, - 0, - "[EDAC] MC err", - mci); - if (res < 0) { - printk(KERN_ERR "%s: Unable to request irq %d for " - "MV64x60 DRAM ERR\n", __func__, pdata->irq); - res = -ENODEV; - goto err2; - } - - printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n", - pdata->irq); - } - - /* get this far and it's successful */ - edac_dbg(3, "success\n"); - - return 0; - -err2: - edac_mc_del_mc(&pdev->dev); -err: - devres_release_group(&pdev->dev, mv64x60_mc_err_probe); - edac_mc_free(mci); - return res; -} - -static int mv64x60_mc_err_remove(struct platform_device *pdev) -{ - struct mem_ctl_info *mci = platform_get_drvdata(pdev); - - edac_dbg(0, "\n"); - - edac_mc_del_mc(&pdev->dev); - edac_mc_free(mci); - return 0; -} - -static struct platform_driver mv64x60_mc_err_driver = { - .probe = mv64x60_mc_err_probe, - .remove = mv64x60_mc_err_remove, - .driver = { - .name = "mv64x60_mc_err", - } -}; - -static struct platform_driver * const drivers[] = { - &mv64x60_mc_err_driver, - &mv64x60_cpu_err_driver, - &mv64x60_sram_err_driver, -#ifdef CONFIG_PCI - &mv64x60_pci_err_driver, -#endif -}; - -static int __init mv64x60_edac_init(void) -{ - - printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n"); - printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n"); - - /* make sure error reporting method is sane */ - switch (edac_op_state) { - case EDAC_OPSTATE_POLL: - case EDAC_OPSTATE_INT: - break; - default: - edac_op_state = EDAC_OPSTATE_INT; - break; - } - - return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -} -module_init(mv64x60_edac_init); - -static void __exit mv64x60_edac_exit(void) -{ - platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); -} -module_exit(mv64x60_edac_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Montavista Software, Inc."); -module_param(edac_op_state, int, 0444); -MODULE_PARM_DESC(edac_op_state, - "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h deleted file mode 100644 index c7f209c92a1a..000000000000 --- a/drivers/edac/mv64x60_edac.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * EDAC defs for Marvell MV64x60 bridge chip - * - * Author: Dave Jiang <djiang@mvista.com> - * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - */ -#ifndef _MV64X60_EDAC_H_ -#define _MV64X60_EDAC_H_ - -#define MV64x60_REVISION " Ver: 2.0.0" -#define EDAC_MOD_STR "MV64x60_edac" - -#define mv64x60_printk(level, fmt, arg...) \ - edac_printk(level, "MV64x60", fmt, ##arg) - -#define mv64x60_mc_printk(mci, level, fmt, arg...) \ - edac_mc_chipset_printk(mci, level, "MV64x60", fmt, ##arg) - -/* CPU Error Report Registers */ -#define MV64x60_CPU_ERR_ADDR_LO 0x00 /* 0x0070 */ -#define MV64x60_CPU_ERR_ADDR_HI 0x08 /* 0x0078 */ -#define MV64x60_CPU_ERR_DATA_LO 0x00 /* 0x0128 */ -#define MV64x60_CPU_ERR_DATA_HI 0x08 /* 0x0130 */ -#define MV64x60_CPU_ERR_PARITY 0x10 /* 0x0138 */ -#define MV64x60_CPU_ERR_CAUSE 0x18 /* 0x0140 */ -#define MV64x60_CPU_ERR_MASK 0x20 /* 0x0148 */ - -#define MV64x60_CPU_CAUSE_MASK 0x07ffffff - -/* SRAM Error Report Registers */ -#define MV64X60_SRAM_ERR_CAUSE 0x08 /* 0x0388 */ -#define MV64X60_SRAM_ERR_ADDR_LO 0x10 /* 0x0390 */ -#define MV64X60_SRAM_ERR_ADDR_HI 0x78 /* 0x03f8 */ -#define MV64X60_SRAM_ERR_DATA_LO 0x18 /* 0x0398 */ -#define MV64X60_SRAM_ERR_DATA_HI 0x20 /* 0x03a0 */ -#define MV64X60_SRAM_ERR_PARITY 0x28 /* 0x03a8 */ - -/* SDRAM Controller Registers */ -#define MV64X60_SDRAM_CONFIG 0x00 /* 0x1400 */ -#define MV64X60_SDRAM_ERR_DATA_HI 0x40 /* 0x1440 */ -#define MV64X60_SDRAM_ERR_DATA_LO 0x44 /* 0x1444 */ -#define MV64X60_SDRAM_ERR_ECC_RCVD 0x48 /* 0x1448 */ -#define MV64X60_SDRAM_ERR_ECC_CALC 0x4c /* 0x144c */ -#define MV64X60_SDRAM_ERR_ADDR 0x50 /* 0x1450 */ -#define MV64X60_SDRAM_ERR_ECC_CNTL 0x54 /* 0x1454 */ -#define MV64X60_SDRAM_ERR_ECC_ERR_CNT 0x58 /* 0x1458 */ - -#define MV64X60_SDRAM_REGISTERED 0x20000 -#define MV64X60_SDRAM_ECC 0x40000 - -#ifdef CONFIG_PCI -/* - * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of - * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as - * well. IOW, don't set bit 0. - */ -#define MV64X60_PCIx_ERR_MASK_VAL 0x00a50c24 - -/* Register offsets from PCIx error address low register */ -#define MV64X60_PCI_ERROR_ADDR_LO 0x00 -#define MV64X60_PCI_ERROR_ADDR_HI 0x04 -#define MV64X60_PCI_ERROR_ATTR 0x08 -#define MV64X60_PCI_ERROR_CMD 0x10 -#define MV64X60_PCI_ERROR_CAUSE 0x18 -#define MV64X60_PCI_ERROR_MASK 0x1c - -#define MV64X60_PCI_ERR_SWrPerr 0x0002 -#define MV64X60_PCI_ERR_SRdPerr 0x0004 -#define MV64X60_PCI_ERR_MWrPerr 0x0020 -#define MV64X60_PCI_ERR_MRdPerr 0x0040 - -#define MV64X60_PCI_PE_MASK (MV64X60_PCI_ERR_SWrPerr | \ - MV64X60_PCI_ERR_SRdPerr | \ - MV64X60_PCI_ERR_MWrPerr | \ - MV64X60_PCI_ERR_MRdPerr) - -struct mv64x60_pci_pdata { - int pci_hose; - void __iomem *pci_vbase; - char *name; - int irq; - int edac_idx; -}; - -#endif /* CONFIG_PCI */ - -struct mv64x60_mc_pdata { - void __iomem *mc_vbase; - int total_mem; - char *name; - int irq; - int edac_idx; -}; - -struct mv64x60_cpu_pdata { - void __iomem *cpu_vbase[2]; - char *name; - int irq; - int edac_idx; -}; - -struct mv64x60_sram_pdata { - void __iomem *sram_vbase; - char *name; - int irq; - int edac_idx; -}; - -#endif diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index 851e53e122aa..d0aef83dca2a 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -204,7 +204,6 @@ static void r82600_check(struct mem_ctl_info *mci) { struct r82600_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); r82600_get_error_info(mci, &info); r82600_process_error_info(mci, &info, 1); } diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c index 2c7db95df326..6a4f0b27c654 100644 --- a/drivers/edac/skx_base.c +++ b/drivers/edac/skx_base.c @@ -174,7 +174,7 @@ static bool skx_check_ecc(u32 mcmtr) return !!GET_BITFIELD(mcmtr, 2, 2); } -static int skx_get_dimm_config(struct mem_ctl_info *mci) +static int skx_get_dimm_config(struct mem_ctl_info *mci, struct res_config *cfg) { struct skx_pvt *pvt = mci->pvt_info; u32 mtr, mcmtr, amap, mcddrtcfg; @@ -195,7 +195,7 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci) pci_read_config_dword(imc->chan[i].cdev, 0x80 + 4 * j, &mtr); if (IS_DIMM_PRESENT(mtr)) { - ndimms += skx_get_dimm_info(mtr, mcmtr, amap, dimm, imc, i, j); + ndimms += skx_get_dimm_info(mtr, mcmtr, amap, dimm, imc, i, j, cfg); } else if (IS_NVDIMM_PRESENT(mcddrtcfg, j)) { ndimms += skx_get_nvdimm_info(dimm, imc, i, j, EDAC_MOD_STR); @@ -702,7 +702,7 @@ static int __init skx_init(void) d->imc[i].node_id = node_id; rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev, "Skylake Socket", EDAC_MOD_STR, - skx_get_dimm_config); + skx_get_dimm_config, cfg); if (rc < 0) goto fail; } diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c index 2b4ce8e5ac2f..81c3e2ec6f56 100644 --- a/drivers/edac/skx_common.c +++ b/drivers/edac/skx_common.c @@ -304,15 +304,25 @@ static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add, #define numcol(reg) skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols") int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, - struct skx_imc *imc, int chan, int dimmno) + struct skx_imc *imc, int chan, int dimmno, + struct res_config *cfg) { - int banks = 16, ranks, rows, cols, npages; + int banks, ranks, rows, cols, npages; + enum mem_type mtype; u64 size; ranks = numrank(mtr); rows = numrow(mtr); cols = numcol(mtr); + if (cfg->support_ddr5 && (amap & 0x8)) { + banks = 32; + mtype = MEM_DDR5; + } else { + banks = 16; + mtype = MEM_DDR4; + } + /* * Compute size in 8-byte (2^3) words, then shift to MiB (2^20) */ @@ -332,7 +342,7 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, dimm->nr_pages = npages; dimm->grain = 32; dimm->dtype = get_width(mtr); - dimm->mtype = MEM_DDR4; + dimm->mtype = mtype; dimm->edac_mode = EDAC_SECDED; /* likely better than this */ snprintf(dimm->label, sizeof(dimm->label), "CPU_SrcID#%u_MC#%u_Chan#%u_DIMM#%u", imc->src_id, imc->lmc, chan, dimmno); @@ -390,7 +400,8 @@ unknown_size: int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, const char *ctl_name, const char *mod_str, - get_dimm_config_f get_dimm_config) + get_dimm_config_f get_dimm_config, + struct res_config *cfg) { struct mem_ctl_info *mci; struct edac_mc_layer layers[2]; @@ -425,13 +436,15 @@ int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, } mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_NVDIMM; + if (cfg->support_ddr5) + mci->mtype_cap |= MEM_FLAG_DDR5; mci->edac_ctl_cap = EDAC_FLAG_NONE; mci->edac_cap = EDAC_FLAG_NONE; mci->mod_name = mod_str; mci->dev_name = pci_name(pdev); mci->ctl_page_to_phys = NULL; - rc = get_dimm_config(mci); + rc = get_dimm_config(mci, cfg); if (rc < 0) goto fail; diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h index 78f8c1de0b71..bf56bebff138 100644 --- a/drivers/edac/skx_common.h +++ b/drivers/edac/skx_common.h @@ -59,6 +59,7 @@ struct skx_dev { struct mem_ctl_info *mci; struct pci_dev *mdev; /* for i10nm CPU */ void __iomem *mbase; /* for i10nm CPU */ + int chan_mmio_sz; /* for i10nm CPU */ u8 mc; /* system wide mc# */ u8 lmc; /* socket relative mc# */ u8 src_id, node_id; @@ -82,7 +83,8 @@ struct skx_pvt { enum type { SKX, - I10NM + I10NM, + SPR }; enum { @@ -118,9 +120,13 @@ struct res_config { unsigned int decs_did; /* Default bus number configuration register offset */ int busno_cfg_offset; + /* Per DDR channel memory-mapped I/O size */ + int ddr_chan_mmio_sz; + bool support_ddr5; }; -typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci); +typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci, + struct res_config *cfg); typedef bool (*skx_decode_f)(struct decoded_addr *res); typedef void (*skx_show_retry_log_f)(struct decoded_addr *res, char *msg, int len); @@ -136,14 +142,16 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list); int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm); int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm, - struct skx_imc *imc, int chan, int dimmno); + struct skx_imc *imc, int chan, int dimmno, + struct res_config *cfg); int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, int chan, int dimmno, const char *mod_str); int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, const char *ctl_name, const char *mod_str, - get_dimm_config_f get_dimm_config); + get_dimm_config_f get_dimm_config, + struct res_config *cfg); int skx_mce_check_error(struct notifier_block *nb, unsigned long val, void *data); diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 12211dc040e8..7e7146b22c16 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -1344,7 +1344,8 @@ static int mc_probe(struct platform_device *pdev) #ifdef CONFIG_EDAC_DEBUG if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) { - if (edac_create_sysfs_attributes(mci)) { + rc = edac_create_sysfs_attributes(mci); + if (rc) { edac_printk(KERN_ERR, EDAC_MC, "Failed to create sysfs entries\n"); goto free_edac_mc; diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index a65e2f78a402..49ab5721aab2 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -238,7 +238,6 @@ static void x38_check(struct mem_ctl_info *mci) { struct x38_error_info info; - edac_dbg(1, "MC%d\n", mci->mc_idx); x38_get_and_clear_error_info(mci, &info); x38_process_error_info(mci, &info); } diff --git a/drivers/firmware/efi/cper-x86.c b/drivers/firmware/efi/cper-x86.c index 2531de49f56c..438ed9eff6d0 100644 --- a/drivers/firmware/efi/cper-x86.c +++ b/drivers/firmware/efi/cper-x86.c @@ -2,6 +2,7 @@ // Copyright (C) 2018, Advanced Micro Devices, Inc. #include <linux/cper.h> +#include <linux/acpi.h> /* * We don't need a "CPER_IA" prefix since these are all locally defined. @@ -347,9 +348,13 @@ void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc) ctx_info->mm_reg_addr); } - printk("%sRegister Array:\n", newpfx); - print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, groupsize, - (ctx_info + 1), ctx_info->reg_arr_size, 0); + if (ctx_info->reg_ctx_type != CTX_TYPE_MSR || + arch_apei_report_x86_error(ctx_info, proc->lapic_id)) { + printk("%sRegister Array:\n", newpfx); + print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, + groupsize, (ctx_info + 1), + ctx_info->reg_arr_size, 0); + } ctx_info = (struct cper_ia_proc_ctx *)((long)ctx_info + size); } diff --git a/drivers/firmware/efi/embedded-firmware.c b/drivers/firmware/efi/embedded-firmware.c index 21ae0c48232a..f5be8e22305b 100644 --- a/drivers/firmware/efi/embedded-firmware.c +++ b/drivers/firmware/efi/embedded-firmware.c @@ -12,7 +12,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/vmalloc.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> /* Exported for use by lib/test_firmware.c only */ LIST_HEAD(efi_embedded_fw_list); diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index dc8568ab96f2..56b723d012ac 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -3730,14 +3730,12 @@ static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits) blk_limits_io_opt(limits, chunk_size_bytes * mddev_data_stripes(rs)); /* - * RAID10 personality requires bio splitting, - * RAID0/1/4/5/6 don't and process large discard bios properly. + * RAID1 and RAID10 personalities require bio splitting, + * RAID0/4/5/6 don't and process large discard bios properly. */ - if (rs_is_raid10(rs)) { - limits->discard_granularity = max(chunk_size_bytes, - limits->discard_granularity); - limits->max_discard_sectors = min_not_zero(rs->md.chunk_sectors, - limits->max_discard_sectors); + if (rs_is_raid1(rs) || rs_is_raid10(rs)) { + limits->discard_granularity = chunk_size_bytes; + limits->max_discard_sectors = rs->md.chunk_sectors; } } diff --git a/drivers/md/md.h b/drivers/md/md.h index bb645bc3ba6d..2175a5ac4f7c 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -311,7 +311,7 @@ struct mddev { int external; /* metadata is * managed externally */ char metadata_type[17]; /* externally set*/ - unsigned int chunk_sectors; + int chunk_sectors; time64_t ctime, utime; int level, layout; char clevel[16]; @@ -339,7 +339,7 @@ struct mddev { */ sector_t reshape_position; int delta_disks, new_level, new_layout; - unsigned int new_chunk_sectors; + int new_chunk_sectors; int reshape_backwards; struct md_thread *thread; /* management thread */ diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index a6d073f2e036..6222b3ae220b 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -24,7 +24,6 @@ if MEDIA_SUPPORT config MEDIA_SUPPORT_FILTER bool "Filter media drivers" - depends on MEDIA_SUPPORT default y if !EMBEDDED && !EXPERT help Configuring the media subsystem can be complex, as there are diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index ece236291f35..551689d371a7 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -166,12 +166,12 @@ static void cec_devnode_unregister(struct cec_adapter *adap) mutex_unlock(&devnode->lock); return; } + devnode->registered = false; + devnode->unregistered = true; list_for_each_entry(fh, &devnode->fhs, list) wake_up_interruptible(&fh->wait); - devnode->registered = false; - devnode->unregistered = true; mutex_unlock(&devnode->lock); mutex_lock(&adap->lock); diff --git a/drivers/media/common/cx2341x.c b/drivers/media/common/cx2341x.c index 1f67e021138f..1392bd6b0026 100644 --- a/drivers/media/common/cx2341x.c +++ b/drivers/media/common/cx2341x.c @@ -166,7 +166,7 @@ static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *ty /* Must be sorted from low to high control ID! */ const u32 cx2341x_mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, + V4L2_CID_CODEC_CLASS, V4L2_CID_MPEG_STREAM_TYPE, V4L2_CID_MPEG_STREAM_VBI_FMT, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, @@ -574,7 +574,7 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { - case V4L2_CID_MPEG_CLASS: + case V4L2_CID_CODEC_CLASS: return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c index 21fb16cc5ca1..f2d13b71416c 100644 --- a/drivers/media/common/saa7146/saa7146_core.c +++ b/drivers/media/common/saa7146/saa7146_core.c @@ -177,7 +177,7 @@ void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa goto err_free_slist; pt->nents = pages; - slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE); + slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); if (0 == slen) goto err_free_pgtable; @@ -187,7 +187,7 @@ void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa return mem; err_unmap_sg: - pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE); + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); err_free_pgtable: saa7146_pgtable_free(pci, pt); err_free_slist: @@ -201,7 +201,7 @@ err_null: void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt) { - pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE); + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); saa7146_pgtable_free(pci, pt); kfree(pt->slist); pt->slist = NULL; @@ -212,7 +212,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) { if (NULL == pt->cpu) return; - pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); + dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma); pt->cpu = NULL; } @@ -221,7 +221,7 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) __le32 *cpu; dma_addr_t dma_addr = 0; - cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr); + cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL); if (NULL == cpu) { return -ENOMEM; } @@ -412,18 +412,20 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent err = -ENOMEM; /* get memory for various stuff */ - dev->d_rps0.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM, - &dev->d_rps0.dma_handle); + dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps0.dma_handle, + GFP_KERNEL); if (!dev->d_rps0.cpu_addr) goto err_free_irq; - dev->d_rps1.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM, - &dev->d_rps1.dma_handle); + dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps1.dma_handle, + GFP_KERNEL); if (!dev->d_rps1.cpu_addr) goto err_free_rps0; - dev->d_i2c.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM, - &dev->d_i2c.dma_handle); + dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_i2c.dma_handle, GFP_KERNEL); if (!dev->d_i2c.cpu_addr) goto err_free_rps1; @@ -471,14 +473,14 @@ out: return err; err_free_i2c: - pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, - dev->d_i2c.dma_handle); + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, + dev->d_i2c.dma_handle); err_free_rps1: - pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, - dev->d_rps1.dma_handle); + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, + dev->d_rps1.dma_handle); err_free_rps0: - pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, - dev->d_rps0.dma_handle); + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, + dev->d_rps0.dma_handle); err_free_irq: free_irq(pci->irq, (void *)dev); err_unmap: @@ -519,7 +521,8 @@ static void saa7146_remove_one(struct pci_dev *pdev) free_irq(pdev->irq, dev); for (p = dev_map; p->addr; p++) - pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma); + dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr, + p->dma); iounmap(dev->mem); pci_release_region(pdev, 0); diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index d6531874faa6..baf5772c52a9 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -55,8 +55,6 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); DEB_EE("dev:%p, buf:%p\n", dev, buf); - BUG_ON(in_interrupt()); - videobuf_waiton(q, &buf->vb, 0, 0); videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); @@ -517,8 +515,8 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) dev->ext_vv_data = ext_vv; vv->d_clipping.cpu_addr = - pci_zalloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, - &vv->d_clipping.dma_handle); + dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + &vv->d_clipping.dma_handle, GFP_KERNEL); if( NULL == vv->d_clipping.cpu_addr ) { ERR("out of memory. aborting.\n"); kfree(vv); @@ -576,7 +574,8 @@ int saa7146_vv_release(struct saa7146_dev* dev) DEB_EE("dev:%p\n", dev); v4l2_device_unregister(&dev->v4l2_dev); - pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(vv); dev->vv_data = NULL; diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c index e1d369b976ed..bd442b984423 100644 --- a/drivers/media/common/saa7146/saa7146_vbi.c +++ b/drivers/media/common/saa7146/saa7146_vbi.c @@ -22,7 +22,7 @@ static int vbi_workaround(struct saa7146_dev *dev) as specified. there is this workaround, but please don't let me explain it. ;-) */ - cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr); + cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL); if (NULL == cpu) return -ENOMEM; @@ -123,12 +123,12 @@ static int vbi_workaround(struct saa7146_dev *dev) /* stop rps1 for sure */ saa7146_write(dev, MC1, MASK_29); - pci_free_consistent(dev->pci, 4096, cpu, dma_addr); + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); return -EINTR; } } - pci_free_consistent(dev->pci, 4096, cpu, dma_addr); + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); return 0; } diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c index ccd15b4d4920..7b8795eca589 100644 --- a/drivers/media/common/saa7146/saa7146_video.c +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -771,10 +771,8 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f vv->ov.nclips = f->fmt.win.clipcount; if (vv->ov.nclips > 16) vv->ov.nclips = 16; - if (copy_from_user(vv->ov.clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * vv->ov.nclips)) { - return -EFAULT; - } + memcpy(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips); /* vv->ov.fh is used to indicate that we have valid overlay information, too */ vv->ov.fh = fh; diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index 88f90dfd368b..ae17407e477a 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -1169,12 +1169,15 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, rc = dvb_create_media_graph(&client->adapter, true); if (rc < 0) { pr_err("dvb_create_media_graph failed %d\n", rc); - goto client_error; + goto media_graph_error; } pr_info("DVB interface registered.\n"); return 0; +media_graph_error: + smsdvb_debugfs_release(client); + client_error: dvb_unregister_frontend(&client->frontend); diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 959fa2820259..5ff7bedee247 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -539,6 +539,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, if (IS_ERR(clsdev)) { pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); + dvb_media_device_free(dvbdev); + kfree(dvbdevfops); + kfree(dvbdev); return PTR_ERR(clsdev); } dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", 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/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 237b9d04c076..37b32d9b398d 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2325,7 +2325,6 @@ hi_command(struct i2c_device_addr *dev_addr, const struct drxj_hi_cmd *cmd, u16 default: return -EINVAL; - break; } /* Write command */ @@ -3594,7 +3593,6 @@ static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg break; default: return -EINVAL; - break; } /* switch ( uio_cfg->mode ) */ break; /*====================================================================*/ @@ -3618,7 +3616,6 @@ static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg break; default: return -EINVAL; - break; } /* switch ( uio_cfg->mode ) */ break; /*====================================================================*/ @@ -3642,7 +3639,6 @@ static int ctrl_set_uio_cfg(struct drx_demod_instance *demod, struct drxuio_cfg case DRX_UIO_MODE_FIRMWARE0: default: return -EINVAL; - break; } /* switch ( uio_cfg->mode ) */ break; /*====================================================================*/ @@ -10953,7 +10949,6 @@ ctrl_set_standard(struct drx_demod_instance *demod, enum drx_standard *standard) default: ext_attr->standard = DRX_STANDARD_UNKNOWN; return -EINVAL; - break; } return 0; @@ -11074,7 +11069,6 @@ ctrl_power_mode(struct drx_demod_instance *demod, enum drx_power_mode *mode) default: /* Unknow sleep mode */ return -EINVAL; - break; } /* Check if device needs to be powered up */ @@ -11896,7 +11890,6 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, } default: return -EINVAL; - break; } mc_data += mc_block_nr_bytes; diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 45f982863904..a7eb81df88c2 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1622,7 +1622,6 @@ static int CorrectSysClockDeviation(struct drxd_state *state) break; default: return -1; - break; } /* Compute new sysclock value 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.c b/drivers/media/dvb-frontends/m88ds3103.c index ad6d9d564a87..cfa4cdde99d8 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1793,9 +1793,9 @@ static int m88ds3103_probe(struct i2c_client *client, dev->config.lnb_en_pol = pdata->lnb_en_pol; dev->cfg = &dev->config; /* create regmap */ - dev->regmap_config.reg_bits = 8, - dev->regmap_config.val_bits = 8, - dev->regmap_config.lock_arg = dev, + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.lock_arg = dev; dev->regmap = devm_regmap_init_i2c(client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); 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/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index 35b83b1dd82c..200b6dbc75f8 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -168,7 +168,6 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* break; default: return -EINVAL; - break; } /* set multi register length */ @@ -190,7 +189,6 @@ static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* break; default: return -EINVAL; - break; } pr_warn("Error writing multireg register 0x%02X\n", reg); @@ -216,7 +214,6 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d /* read the actual data */ nxt200x_readbytes(state, reg, data, len); return 0; - break; case NXT2004: /* probably not right, but gives correct values */ attr = 0x02; @@ -239,10 +236,8 @@ static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* d nxt200x_readbytes(state, 0x36 + i, &data[i], 1); } return 0; - break; default: return -EINVAL; - break; } } @@ -374,7 +369,6 @@ static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) break; default: return -EINVAL; - break; } return 0; } @@ -555,7 +549,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } if (fe->ops.tuner_ops.calc_regs) { @@ -580,7 +573,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } nxt200x_writebytes(state, 0x42, buf, 1); @@ -594,7 +586,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } nxt200x_writebytes(state, 0x57, buf, 1); @@ -610,7 +601,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } /* write sdmx input */ @@ -626,7 +616,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } buf[1] = 0x00; switch (state->demod_chip) { @@ -638,7 +627,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } /* write adc power lpf fc */ @@ -664,7 +652,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } /* write kg1 */ @@ -720,7 +707,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } nxt200x_writebytes(state, 0x30, buf, 1); @@ -742,7 +728,6 @@ static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) break; default: return -EINVAL; - break; } /* write agc control reg */ @@ -1114,7 +1099,6 @@ static int nxt200x_init(struct dvb_frontend* fe) break; default: return -EINVAL; - break; } state->initialised = 1; } diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index e5bffaaeed38..01dcc7f1b9b2 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1056,13 +1056,13 @@ static int rtl2832_probe(struct i2c_client *client, dev->sleeping = true; INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); /* create regmap */ - dev->regmap_config.reg_bits = 8, - dev->regmap_config.val_bits = 8, - dev->regmap_config.volatile_reg = rtl2832_volatile_reg, - dev->regmap_config.max_register = 5 * 0x100, - dev->regmap_config.ranges = regmap_range_cfg, - dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg), - dev->regmap_config.cache_type = REGCACHE_NONE, + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.volatile_reg = rtl2832_volatile_reg; + dev->regmap_config.max_register = 5 * 0x100; + dev->regmap_config.ranges = regmap_range_cfg; + dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg); + dev->regmap_config.cache_type = REGCACHE_NONE; dev->regmap = regmap_init_i2c(client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 3fdaef1935ef..ebee230afb7b 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -5,7 +5,7 @@ * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org> * * References: - * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf + * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf */ #include <linux/delay.h> diff --git a/drivers/media/dvb-frontends/si2165.h b/drivers/media/dvb-frontends/si2165.h index 0b19317f3a31..adc5e18754ad 100644 --- a/drivers/media/dvb-frontends/si2165.h +++ b/drivers/media/dvb-frontends/si2165.h @@ -5,7 +5,7 @@ * Copyright (C) 2013-2017 Matthias Schwarzott <zzam@gentoo.org> * * References: - * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf + * https://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf */ #ifndef _DVB_SI2165_H diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index a116eff417f2..e31eb2c5cc4c 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -464,10 +464,8 @@ static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) switch (volt) { case SEC_VOLTAGE_18: return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); - break; case SEC_VOLTAGE_13: return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); - break; default: return -EINVAL; } 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/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 234607b02edb..3e383912bcfd 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -569,11 +569,11 @@ static int ts2020_probe(struct i2c_client *client, /* create regmap */ mutex_init(&dev->regmap_mutex); - dev->regmap_config.reg_bits = 8, - dev->regmap_config.val_bits = 8, - dev->regmap_config.lock = ts2020_regmap_lock, - dev->regmap_config.unlock = ts2020_regmap_unlock, - dev->regmap_config.lock_arg = dev, + dev->regmap_config.reg_bits = 8; + dev->regmap_config.val_bits = 8; + dev->regmap_config.lock = ts2020_regmap_lock; + dev->regmap_config.unlock = ts2020_regmap_unlock; + dev->regmap_config.lock_arg = dev; dev->regmap = regmap_init_i2c(client, &dev->regmap_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); 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 878f66ef2719..2b9d81e4794a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -722,7 +722,7 @@ menu "Camera sensor devices" config VIDEO_APTINA_PLL tristate -config VIDEO_SMIAPP_PLL +config VIDEO_CCS_PLL tristate config VIDEO_HI556 @@ -825,6 +825,19 @@ config VIDEO_IMX355 To compile this driver as a module, choose M here: the module will be called imx355. +config VIDEO_OV02A10 + tristate "OmniVision OV02A10 sensor support" + depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV02A10 camera. + + To compile this driver as a module, choose M here: the + module will be called ov02a10. + config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C @@ -877,6 +890,7 @@ config VIDEO_OV2740 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE + select REGMAP_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV2740 camera. @@ -1050,6 +1064,20 @@ config VIDEO_OV9650 This is a V4L2 sensor driver for the Omnivision OV9650 and OV9652 camera sensors. +config VIDEO_OV9734 + tristate "OmniVision OV9734 sensor support" + depends on VIDEO_V4L2 && I2C + depends on ACPI || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OV9734 camera. + + To compile this driver as a module, choose M here: the + module's name is ov9734. + config VIDEO_OV13858 tristate "OmniVision OV13858 sensor support" depends on I2C && VIDEO_V4L2 @@ -1232,7 +1260,7 @@ config VIDEO_S5K5BAF This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. -source "drivers/media/i2c/smiapp/Kconfig" +source "drivers/media/i2c/ccs/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" config VIDEO_S5C73M3 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index f0a77473979d..a3149dce21bb 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -2,7 +2,7 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ +obj-$(CONFIG_VIDEO_CCS) += ccs/ obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ @@ -64,6 +64,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o @@ -83,6 +84,7 @@ obj-$(CONFIG_VIDEO_OV7740) += ov7740.o obj-$(CONFIG_VIDEO_OV8856) += ov8856.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o +obj-$(CONFIG_VIDEO_OV9734) += ov9734.o obj-$(CONFIG_VIDEO_OV13858) += ov13858.o obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o @@ -104,7 +106,7 @@ obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o -obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o +obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_I2C) += video-i2c.o diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 19c74db0649f..2958a4694461 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -270,8 +270,7 @@ static const struct v4l2_subdev_internal_ops ad5820_internal_ops = { */ static int __maybe_unused ad5820_suspend(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct ad5820_device *coil = to_ad5820_device(subdev); if (!coil->power_count) @@ -282,8 +281,7 @@ static int __maybe_unused ad5820_suspend(struct device *dev) static int __maybe_unused ad5820_resume(struct device *dev) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct ad5820_device *coil = to_ad5820_device(subdev); if (!coil->power_count) diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 694125a59f64..522a0b10e415 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -379,8 +379,7 @@ static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { static int adp1653_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct adp1653_flash *flash = to_adp1653_flash(subdev); if (!flash->power_count) @@ -391,8 +390,7 @@ static int adp1653_suspend(struct device *dev) static int adp1653_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct adp1653_flash *flash = to_adp1653_flash(subdev); if (!flash->power_count) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 4498d14d3429..44bb6fe85644 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1454,8 +1454,7 @@ MODULE_DEVICE_TABLE(i2c, adv7180_id); #ifdef CONFIG_PM_SLEEP static int adv7180_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct adv7180_state *state = to_state(sd); return adv7180_set_power(state, false); @@ -1463,8 +1462,7 @@ static int adv7180_suspend(struct device *dev) static int adv7180_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct adv7180_state *state = to_state(sd); int ret; diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index dbbb1e4d6363..4052cf67bf16 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -154,7 +154,7 @@ static void adv748x_afe_set_video_standard(struct adv748x_state *state, (sdpstd & 0xf) << ADV748X_SDP_VID_SEL_SHIFT); } -static int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input) +int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input) { struct adv748x_state *state = adv748x_afe_to_state(afe); @@ -520,10 +520,6 @@ int adv748x_afe_init(struct adv748x_afe *afe) } } - adv748x_afe_s_input(afe, afe->input); - - adv_dbg(state, "AFE Default input set to %d\n", afe->input); - /* Entity pads and sinks are 0-indexed to match the pads */ for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++) afe->pads[i].flags = MEDIA_PAD_FL_SINK; diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 1fe7f97c6d52..4e54148147b9 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -198,7 +198,7 @@ static int adv748x_initialise_clients(struct adv748x_state *state) return ret; } - return adv748x_set_slave_addresses(state); + return 0; } /** @@ -516,6 +516,10 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; + adv748x_afe_s_input(&state->afe, state->afe.input); + + adv_dbg(state, "AFE Default input set to %d\n", state->afe.input); + /* Reset TXA and TXB */ adv748x_tx_power(&state->txa, 1); adv748x_tx_power(&state->txa, 0); @@ -526,10 +530,14 @@ static int adv748x_reset(struct adv748x_state *state) io_write(state, ADV748X_IO_PD, ADV748X_IO_PD_RX_EN); /* Conditionally enable TXa and TXb. */ - if (is_tx_enabled(&state->txa)) + if (is_tx_enabled(&state->txa)) { regval |= ADV748X_IO_10_CSI4_EN; - if (is_tx_enabled(&state->txb)) + adv748x_csi2_set_virtual_channel(&state->txa, 0); + } + if (is_tx_enabled(&state->txb)) { regval |= ADV748X_IO_10_CSI1_EN; + adv748x_csi2_set_virtual_channel(&state->txb, 0); + } io_write(state, ADV748X_IO_10, regval); /* Use vid_std and v_freq as freerun resolution for CP */ @@ -558,6 +566,18 @@ static int adv748x_identify_chip(struct adv748x_state *state) } /* ----------------------------------------------------------------------------- + * Suspend / Resume + */ + +static int __maybe_unused adv748x_resume_early(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adv748x_state *state = i2c_get_clientdata(client); + + return adv748x_reset(state); +} + +/* ----------------------------------------------------------------------------- * i2c driver */ @@ -589,14 +609,13 @@ static int adv748x_parse_csi2_lanes(struct adv748x_state *state, unsigned int port, struct device_node *ep) { - struct v4l2_fwnode_endpoint vep; + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; unsigned int num_lanes; int ret; if (port != ADV748X_PORT_TXA && port != ADV748X_PORT_TXB) return 0; - vep.bus_type = V4L2_MBUS_CSI2_DPHY; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &vep); if (ret) return ret; @@ -820,10 +839,15 @@ static const struct of_device_id adv748x_of_table[] = { }; MODULE_DEVICE_TABLE(of, adv748x_of_table); +static const struct dev_pm_ops adv748x_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, adv748x_resume_early) +}; + static struct i2c_driver adv748x_driver = { .driver = { .name = "adv748x", .of_match_table = adv748x_of_table, + .pm = &adv748x_pm_ops, }, .probe_new = adv748x_probe, .remove = adv748x_remove, diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 99bb63d05eef..fa9278a08fde 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -14,8 +14,7 @@ #include "adv748x.h" -static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, - unsigned int vc) +int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) { return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT); } @@ -313,9 +312,6 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) if (!is_tx_enabled(tx)) return 0; - /* Initialise the virtual channel */ - adv748x_csi2_set_virtual_channel(tx, 0); - adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, MEDIA_ENT_F_VID_IF_BRIDGE, is_txa(tx) ? "txa" : "txb"); diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 1061f425ece5..56256c1e8b0d 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -435,9 +435,11 @@ int adv748x_tx_power(struct adv748x_csi2 *tx, bool on); int adv748x_afe_init(struct adv748x_afe *afe); void adv748x_afe_cleanup(struct adv748x_afe *afe); +int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input); int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx); void adv748x_csi2_cleanup(struct adv748x_csi2 *tx); +int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc); int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate); int adv748x_hdmi_init(struct adv748x_hdmi *hdmi); diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c index 7b14b11605ca..e1f94ee0f48f 100644 --- a/drivers/media/i2c/ak7375.c +++ b/drivers/media/i2c/ak7375.c @@ -196,9 +196,7 @@ static int ak7375_remove(struct i2c_client *client) */ static int __maybe_unused ak7375_vcm_suspend(struct device *dev) { - - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); int ret, val; @@ -233,8 +231,7 @@ static int __maybe_unused ak7375_vcm_suspend(struct device *dev) */ static int __maybe_unused ak7375_vcm_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); int ret, val; diff --git a/drivers/media/i2c/ccs-pll.c b/drivers/media/i2c/ccs-pll.c new file mode 100644 index 000000000000..eb7b6f01f623 --- /dev/null +++ b/drivers/media/i2c/ccs-pll.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/media/i2c/ccs-pll.c + * + * Generic MIPI CCS/SMIA/SMIA++ PLL calculator + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> + */ + +#include <linux/device.h> +#include <linux/gcd.h> +#include <linux/lcm.h> +#include <linux/module.h> + +#include "ccs-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static inline uint32_t one_or_more(uint32_t a) +{ + return a ?: 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, const char *prefix, + char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_dbg(dev, "%s_%s out of bounds: %d (%d--%d)\n", prefix, + str, val, min, max); + + return -EINVAL; +} + +#define PLL_OP 1 +#define PLL_VT 2 + +static const char *pll_string(unsigned int which) +{ + switch (which) { + case PLL_OP: + return "op"; + case PLL_VT: + return "vt"; + } + + return NULL; +} + +#define PLL_FL(f) CCS_PLL_FLAG_##f + +static void print_pll(struct device *dev, struct ccs_pll *pll) +{ + const struct { + struct ccs_pll_branch_fr *fr; + struct ccs_pll_branch_bk *bk; + unsigned int which; + } branches[] = { + { &pll->vt_fr, &pll->vt_bk, PLL_VT }, + { &pll->op_fr, &pll->op_bk, PLL_OP } + }, *br; + unsigned int i; + + dev_dbg(dev, "ext_clk_freq_hz\t\t%u\n", pll->ext_clk_freq_hz); + + for (i = 0, br = branches; i < ARRAY_SIZE(branches); i++, br++) { + const char *s = pll_string(br->which); + + if (pll->flags & CCS_PLL_FLAG_DUAL_PLL || + br->which == PLL_VT) { + dev_dbg(dev, "%s_pre_pll_clk_div\t\t%u\n", s, + br->fr->pre_pll_clk_div); + dev_dbg(dev, "%s_pll_multiplier\t\t%u\n", s, + br->fr->pll_multiplier); + + dev_dbg(dev, "%s_pll_ip_clk_freq_hz\t%u\n", s, + br->fr->pll_ip_clk_freq_hz); + dev_dbg(dev, "%s_pll_op_clk_freq_hz\t%u\n", s, + br->fr->pll_op_clk_freq_hz); + } + + if (!(pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) || + br->which == PLL_VT) { + dev_dbg(dev, "%s_sys_clk_div\t\t%u\n", s, + br->bk->sys_clk_div); + dev_dbg(dev, "%s_pix_clk_div\t\t%u\n", s, + br->bk->pix_clk_div); + + dev_dbg(dev, "%s_sys_clk_freq_hz\t%u\n", s, + br->bk->sys_clk_freq_hz); + dev_dbg(dev, "%s_pix_clk_freq_hz\t%u\n", s, + br->bk->pix_clk_freq_hz); + } + } + + dev_dbg(dev, "pixel rate in pixel array:\t%u\n", + pll->pixel_rate_pixel_array); + dev_dbg(dev, "pixel rate on CSI-2 bus:\t%u\n", + pll->pixel_rate_csi); + + dev_dbg(dev, "flags%s%s%s%s%s%s%s%s%s\n", + pll->flags & PLL_FL(LANE_SPEED_MODEL) ? " lane-speed" : "", + pll->flags & PLL_FL(LINK_DECOUPLED) ? " link-decoupled" : "", + pll->flags & PLL_FL(EXT_IP_PLL_DIVIDER) ? + " ext-ip-pll-divider" : "", + pll->flags & PLL_FL(FLEXIBLE_OP_PIX_CLK_DIV) ? + " flexible-op-pix-div" : "", + pll->flags & PLL_FL(FIFO_DERATING) ? " fifo-derating" : "", + pll->flags & PLL_FL(FIFO_OVERRATING) ? " fifo-overrating" : "", + pll->flags & PLL_FL(DUAL_PLL) ? " dual-pll" : "", + pll->flags & PLL_FL(OP_SYS_DDR) ? " op-sys-ddr" : "", + pll->flags & PLL_FL(OP_PIX_DDR) ? " op-pix-ddr" : ""); +} + +static uint32_t op_sys_ddr(uint32_t flags) +{ + return flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0; +} + +static uint32_t op_pix_ddr(uint32_t flags) +{ + return flags & CCS_PLL_FLAG_OP_PIX_DDR ? 1 : 0; +} + +static int check_fr_bounds(struct device *dev, + const struct ccs_pll_limits *lim, + struct ccs_pll *pll, unsigned int which) +{ + const struct ccs_pll_branch_limits_fr *lim_fr; + struct ccs_pll_branch_fr *pll_fr; + const char *s = pll_string(which); + int rval; + + if (which == PLL_OP) { + lim_fr = &lim->op_fr; + pll_fr = &pll->op_fr; + } else { + lim_fr = &lim->vt_fr; + pll_fr = &pll->vt_fr; + } + + rval = bounds_check(dev, pll_fr->pre_pll_clk_div, + lim_fr->min_pre_pll_clk_div, + lim_fr->max_pre_pll_clk_div, s, "pre_pll_clk_div"); + + if (!rval) + rval = bounds_check(dev, pll_fr->pll_ip_clk_freq_hz, + lim_fr->min_pll_ip_clk_freq_hz, + lim_fr->max_pll_ip_clk_freq_hz, + s, "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check(dev, pll_fr->pll_multiplier, + lim_fr->min_pll_multiplier, + lim_fr->max_pll_multiplier, + s, "pll_multiplier"); + if (!rval) + rval = bounds_check(dev, pll_fr->pll_op_clk_freq_hz, + lim_fr->min_pll_op_clk_freq_hz, + lim_fr->max_pll_op_clk_freq_hz, + s, "pll_op_clk_freq_hz"); + + return rval; +} + +static int check_bk_bounds(struct device *dev, + const struct ccs_pll_limits *lim, + struct ccs_pll *pll, unsigned int which) +{ + const struct ccs_pll_branch_limits_bk *lim_bk; + struct ccs_pll_branch_bk *pll_bk; + const char *s = pll_string(which); + int rval; + + if (which == PLL_OP) { + if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) + return 0; + + lim_bk = &lim->op_bk; + pll_bk = &pll->op_bk; + } else { + lim_bk = &lim->vt_bk; + pll_bk = &pll->vt_bk; + } + + rval = bounds_check(dev, pll_bk->sys_clk_div, + lim_bk->min_sys_clk_div, + lim_bk->max_sys_clk_div, s, "op_sys_clk_div"); + if (!rval) + rval = bounds_check(dev, pll_bk->sys_clk_freq_hz, + lim_bk->min_sys_clk_freq_hz, + lim_bk->max_sys_clk_freq_hz, + s, "sys_clk_freq_hz"); + if (!rval) + rval = bounds_check(dev, pll_bk->sys_clk_div, + lim_bk->min_sys_clk_div, + lim_bk->max_sys_clk_div, + s, "sys_clk_div"); + if (!rval) + rval = bounds_check(dev, pll_bk->pix_clk_freq_hz, + lim_bk->min_pix_clk_freq_hz, + lim_bk->max_pix_clk_freq_hz, + s, "pix_clk_freq_hz"); + + return rval; +} + +static int check_ext_bounds(struct device *dev, struct ccs_pll *pll) +{ + if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING) && + pll->pixel_rate_pixel_array > pll->pixel_rate_csi) { + dev_dbg(dev, "device does not support derating\n"); + return -EINVAL; + } + + if (!(pll->flags & CCS_PLL_FLAG_FIFO_OVERRATING) && + pll->pixel_rate_pixel_array < pll->pixel_rate_csi) { + dev_dbg(dev, "device does not support overrating\n"); + return -EINVAL; + } + + return 0; +} + +static void +ccs_pll_find_vt_sys_div(struct device *dev, const struct ccs_pll_limits *lim, + struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, + uint16_t min_vt_div, uint16_t max_vt_div, + uint16_t *min_sys_div, uint16_t *max_sys_div) +{ + /* + * Find limits for sys_clk_div. Not all values are possible with all + * values of pix_clk_div. + */ + *min_sys_div = lim->vt_bk.min_sys_clk_div; + dev_dbg(dev, "min_sys_div: %u\n", *min_sys_div); + *min_sys_div = max_t(uint16_t, *min_sys_div, + DIV_ROUND_UP(min_vt_div, + lim->vt_bk.max_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", *min_sys_div); + *min_sys_div = max_t(uint16_t, *min_sys_div, + pll_fr->pll_op_clk_freq_hz + / lim->vt_bk.max_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", *min_sys_div); + *min_sys_div = clk_div_even_up(*min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %u\n", *min_sys_div); + + *max_sys_div = lim->vt_bk.max_sys_clk_div; + dev_dbg(dev, "max_sys_div: %u\n", *max_sys_div); + *max_sys_div = min_t(uint16_t, *max_sys_div, + DIV_ROUND_UP(max_vt_div, + lim->vt_bk.min_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", *max_sys_div); + *max_sys_div = min_t(uint16_t, *max_sys_div, + DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, + lim->vt_bk.min_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", *max_sys_div); +} + +#define CPHY_CONST 7 +#define DPHY_CONST 16 +#define PHY_CONST_DIV 16 + +static inline int +__ccs_pll_calculate_vt_tree(struct device *dev, + const struct ccs_pll_limits *lim, + struct ccs_pll *pll, uint32_t mul, uint32_t div) +{ + const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; + const struct ccs_pll_branch_limits_bk *lim_bk = &lim->vt_bk; + struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; + struct ccs_pll_branch_bk *pll_bk = &pll->vt_bk; + uint32_t more_mul; + uint16_t best_pix_div = SHRT_MAX >> 1, best_div; + uint16_t vt_div, min_sys_div, max_sys_div, sys_div; + + pll_fr->pll_ip_clk_freq_hz = + pll->ext_clk_freq_hz / pll_fr->pre_pll_clk_div; + + dev_dbg(dev, "vt_pll_ip_clk_freq_hz %u\n", pll_fr->pll_ip_clk_freq_hz); + + more_mul = one_or_more(DIV_ROUND_UP(lim_fr->min_pll_op_clk_freq_hz, + pll_fr->pll_ip_clk_freq_hz * mul)); + + dev_dbg(dev, "more_mul: %u\n", more_mul); + more_mul *= DIV_ROUND_UP(lim_fr->min_pll_multiplier, mul * more_mul); + dev_dbg(dev, "more_mul2: %u\n", more_mul); + + pll_fr->pll_multiplier = mul * more_mul; + + if (pll_fr->pll_multiplier * pll_fr->pll_ip_clk_freq_hz > + lim_fr->max_pll_op_clk_freq_hz) + return -EINVAL; + + pll_fr->pll_op_clk_freq_hz = + pll_fr->pll_ip_clk_freq_hz * pll_fr->pll_multiplier; + + vt_div = div * more_mul; + + ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, vt_div, vt_div, + &min_sys_div, &max_sys_div); + + max_sys_div = (vt_div & 1) ? 1 : max_sys_div; + + dev_dbg(dev, "vt min/max_sys_div: %u,%u\n", min_sys_div, max_sys_div); + + for (sys_div = min_sys_div; sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + uint16_t pix_div; + + if (vt_div % sys_div) + continue; + + pix_div = vt_div / sys_div; + + if (pix_div < lim_bk->min_pix_clk_div || + pix_div > lim_bk->max_pix_clk_div) { + dev_dbg(dev, + "pix_div %u too small or too big (%u--%u)\n", + pix_div, + lim_bk->min_pix_clk_div, + lim_bk->max_pix_clk_div); + continue; + } + + dev_dbg(dev, "sys/pix/best_pix: %u,%u,%u\n", sys_div, pix_div, + best_pix_div); + + if (pix_div * sys_div <= best_pix_div) { + best_pix_div = pix_div; + best_div = pix_div * sys_div; + } + } + if (best_pix_div == SHRT_MAX >> 1) + return -EINVAL; + + pll_bk->sys_clk_div = best_div / best_pix_div; + pll_bk->pix_clk_div = best_pix_div; + + pll_bk->sys_clk_freq_hz = + pll_fr->pll_op_clk_freq_hz / pll_bk->sys_clk_div; + pll_bk->pix_clk_freq_hz = + pll_bk->sys_clk_freq_hz / pll_bk->pix_clk_div; + + pll->pixel_rate_pixel_array = + pll_bk->pix_clk_freq_hz * pll->vt_lanes; + + return 0; +} + +static int ccs_pll_calculate_vt_tree(struct device *dev, + const struct ccs_pll_limits *lim, + struct ccs_pll *pll) +{ + const struct ccs_pll_branch_limits_fr *lim_fr = &lim->vt_fr; + struct ccs_pll_branch_fr *pll_fr = &pll->vt_fr; + uint16_t min_pre_pll_clk_div = lim_fr->min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div = lim_fr->max_pre_pll_clk_div; + uint32_t pre_mul, pre_div; + + pre_div = gcd(pll->pixel_rate_csi, + pll->ext_clk_freq_hz * pll->vt_lanes); + pre_mul = pll->pixel_rate_csi / pre_div; + pre_div = pll->ext_clk_freq_hz * pll->vt_lanes / pre_div; + + /* Make sure PLL input frequency is within limits */ + max_pre_pll_clk_div = + min_t(uint16_t, max_pre_pll_clk_div, + DIV_ROUND_UP(pll->ext_clk_freq_hz, + lim_fr->min_pll_ip_clk_freq_hz)); + + min_pre_pll_clk_div = max_t(uint16_t, min_pre_pll_clk_div, + pll->ext_clk_freq_hz / + lim_fr->max_pll_ip_clk_freq_hz); + + dev_dbg(dev, "vt min/max_pre_pll_clk_div: %u,%u\n", + min_pre_pll_clk_div, max_pre_pll_clk_div); + + for (pll_fr->pre_pll_clk_div = min_pre_pll_clk_div; + pll_fr->pre_pll_clk_div <= max_pre_pll_clk_div; + pll_fr->pre_pll_clk_div += + (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 : + 2 - (pll_fr->pre_pll_clk_div & 1)) { + uint32_t mul, div; + int rval; + + div = gcd(pre_mul * pll_fr->pre_pll_clk_div, pre_div); + mul = pre_mul * pll_fr->pre_pll_clk_div / div; + div = pre_div / div; + + dev_dbg(dev, "vt pre-div/mul/div: %u,%u,%u\n", + pll_fr->pre_pll_clk_div, mul, div); + + rval = __ccs_pll_calculate_vt_tree(dev, lim, pll, + mul, div); + if (rval) + continue; + + rval = check_fr_bounds(dev, lim, pll, PLL_VT); + if (rval) + continue; + + rval = check_bk_bounds(dev, lim, pll, PLL_VT); + if (rval) + continue; + + return 0; + } + + return -EINVAL; +} + +static void +ccs_pll_calculate_vt(struct device *dev, const struct ccs_pll_limits *lim, + const struct ccs_pll_branch_limits_bk *op_lim_bk, + struct ccs_pll *pll, struct ccs_pll_branch_fr *pll_fr, + struct ccs_pll_branch_bk *op_pll_bk, bool cphy, + uint32_t phy_const) +{ + uint16_t sys_div; + uint16_t best_pix_div = SHRT_MAX >> 1; + uint16_t vt_op_binning_div; + uint16_t min_vt_div, max_vt_div, vt_div; + uint16_t min_sys_div, max_sys_div; + + if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) + goto out_calc_pixel_rate; + + /* + * Find out whether a sensor supports derating. If it does not, VT and + * OP domains are required to run at the same pixel rate. + */ + if (!(pll->flags & CCS_PLL_FLAG_FIFO_DERATING)) { + min_vt_div = + op_pll_bk->sys_clk_div * op_pll_bk->pix_clk_div + * pll->vt_lanes * phy_const / pll->op_lanes + / (PHY_CONST_DIV << op_pix_ddr(pll->flags)); + } else { + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (lim->min_line_length_pck_bin > lim->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor and number of VT lanes into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %u\n", pll->scale_m); + min_vt_div = + DIV_ROUND_UP(pll->bits_per_pixel + * op_pll_bk->sys_clk_div * pll->scale_n + * pll->vt_lanes * phy_const, + (pll->flags & + CCS_PLL_FLAG_LANE_SPEED_MODEL ? + pll->csi2.lanes : 1) + * vt_op_binning_div * pll->scale_m + * PHY_CONST_DIV << op_pix_ddr(pll->flags)); + } + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); + min_vt_div = max_t(uint16_t, min_vt_div, + DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, + lim->vt_bk.max_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", + min_vt_div); + min_vt_div = max_t(uint16_t, min_vt_div, lim->vt_bk.min_pix_clk_div + * lim->vt_bk.min_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); + + max_vt_div = lim->vt_bk.max_sys_clk_div * lim->vt_bk.max_pix_clk_div; + dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); + max_vt_div = min_t(uint16_t, max_vt_div, + DIV_ROUND_UP(pll_fr->pll_op_clk_freq_hz, + lim->vt_bk.min_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", + max_vt_div); + + ccs_pll_find_vt_sys_div(dev, lim, pll, pll_fr, min_vt_div, + max_vt_div, &min_sys_div, &max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; vt_div++) { + uint16_t __max_sys_div = vt_div & 1 ? 1 : max_sys_div; + + for (sys_div = min_sys_div; sys_div <= __max_sys_div; + sys_div += 2 - (sys_div & 1)) { + uint16_t pix_div; + uint16_t rounded_div; + + pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < lim->vt_bk.min_pix_clk_div + || pix_div > lim->vt_bk.max_pix_clk_div) { + dev_dbg(dev, + "pix_div %u too small or too big (%u--%u)\n", + pix_div, + lim->vt_bk.min_pix_clk_div, + lim->vt_bk.max_pix_clk_div); + continue; + } + + rounded_div = roundup(vt_div, best_pix_div); + + /* Check if this one is better. */ + if (pix_div * sys_div <= rounded_div) + best_pix_div = pix_div; + + /* Bail out if we've already found the best value. */ + if (vt_div == rounded_div) + break; + } + if (best_pix_div < SHRT_MAX >> 1) + break; + } + + pll->vt_bk.sys_clk_div = DIV_ROUND_UP(vt_div, best_pix_div); + pll->vt_bk.pix_clk_div = best_pix_div; + + pll->vt_bk.sys_clk_freq_hz = + pll_fr->pll_op_clk_freq_hz / pll->vt_bk.sys_clk_div; + pll->vt_bk.pix_clk_freq_hz = + pll->vt_bk.sys_clk_freq_hz / pll->vt_bk.pix_clk_div; + +out_calc_pixel_rate: + pll->pixel_rate_pixel_array = + pll->vt_bk.pix_clk_freq_hz * pll->vt_lanes; +} + +/* + * Heuristically guess the PLL tree for a given common multiplier and + * divisor. Begin with the operational timing and continue to video + * timing once operational timing has been verified. + * + * @mul is the PLL multiplier and @div is the common divisor + * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL + * multiplier will be a multiple of @mul. + * + * @return Zero on success, error code on error. + */ +static int +ccs_pll_calculate_op(struct device *dev, const struct ccs_pll_limits *lim, + const struct ccs_pll_branch_limits_fr *op_lim_fr, + const struct ccs_pll_branch_limits_bk *op_lim_bk, + struct ccs_pll *pll, struct ccs_pll_branch_fr *op_pll_fr, + struct ccs_pll_branch_bk *op_pll_bk, uint32_t mul, + uint32_t div, uint32_t op_sys_clk_freq_hz_sdr, uint32_t l, + bool cphy, uint32_t phy_const) +{ + /* + * Higher multipliers (and divisors) are often required than + * necessitated by the external clock and the output clocks. + * There are limits for all values in the clock tree. These + * are the minimum and maximum multiplier for mul. + */ + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t i; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "op_pre_pll_clk_div %u\n", op_pll_fr->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = op_lim_fr->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_op_pll_multiplier check: %u\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(uint32_t, + more_mul_max, + op_lim_fr->max_pll_op_clk_freq_hz + / (pll->ext_clk_freq_hz / + op_pll_fr->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_clk_freq_hz check: %u\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + op_lim_bk->max_sys_clk_div * op_pll_fr->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", + more_mul_max); + /* Ensure we won't go above max_pll_multiplier. */ + more_mul_max = min(more_mul_max, op_lim_fr->max_pll_multiplier / mul); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_clk_freq_hz. */ + more_mul_min = DIV_ROUND_UP(op_lim_fr->min_pll_op_clk_freq_hz, + pll->ext_clk_freq_hz / + op_pll_fr->pre_pll_clk_div * mul); + dev_dbg(dev, "more_mul_min: min_op_pll_op_clk_freq_hz check: %u\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(op_lim_fr->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_op_pll_multiplier check: %u\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_dbg(dev, + "unable to compute more_mul_min and more_mul_max\n"); + return -EINVAL; + } + + more_mul_factor = lcm(div, op_pll_fr->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, op_lim_bk->min_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %u\n", i); + if (i > more_mul_max) { + dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); + return -EINVAL; + } + + op_pll_fr->pll_multiplier = mul * i; + op_pll_bk->sys_clk_div = div * i / op_pll_fr->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll_bk->sys_clk_div); + + op_pll_fr->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / op_pll_fr->pre_pll_clk_div; + + op_pll_fr->pll_op_clk_freq_hz = op_pll_fr->pll_ip_clk_freq_hz + * op_pll_fr->pll_multiplier; + + if (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL) + op_pll_bk->pix_clk_div = + (pll->bits_per_pixel + * pll->op_lanes * (phy_const << op_sys_ddr(pll->flags)) + / PHY_CONST_DIV / pll->csi2.lanes / l) + >> op_pix_ddr(pll->flags); + else + op_pll_bk->pix_clk_div = + (pll->bits_per_pixel + * (phy_const << op_sys_ddr(pll->flags)) + / PHY_CONST_DIV / l) >> op_pix_ddr(pll->flags); + + op_pll_bk->pix_clk_freq_hz = + (op_sys_clk_freq_hz_sdr >> op_pix_ddr(pll->flags)) + / op_pll_bk->pix_clk_div; + op_pll_bk->sys_clk_freq_hz = + op_sys_clk_freq_hz_sdr >> op_sys_ddr(pll->flags); + + dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll_bk->pix_clk_div); + + return 0; +} + +int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim, + struct ccs_pll *pll) +{ + const struct ccs_pll_branch_limits_fr *op_lim_fr; + const struct ccs_pll_branch_limits_bk *op_lim_bk; + struct ccs_pll_branch_fr *op_pll_fr; + struct ccs_pll_branch_bk *op_pll_bk; + bool cphy = pll->bus_type == CCS_PLL_BUS_TYPE_CSI2_CPHY; + uint32_t phy_const = cphy ? CPHY_CONST : DPHY_CONST; + uint32_t op_sys_clk_freq_hz_sdr; + uint16_t min_op_pre_pll_clk_div; + uint16_t max_op_pre_pll_clk_div; + uint32_t mul, div; + uint32_t l = (!pll->op_bits_per_lane || + pll->op_bits_per_lane >= pll->bits_per_pixel) ? 1 : 2; + uint32_t i; + int rval = -EINVAL; + + if (!(pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL)) { + pll->op_lanes = 1; + pll->vt_lanes = 1; + } + + if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) { + op_lim_fr = &lim->op_fr; + op_lim_bk = &lim->op_bk; + op_pll_fr = &pll->op_fr; + op_pll_bk = &pll->op_bk; + } else if (pll->flags & CCS_PLL_FLAG_NO_OP_CLOCKS) { + /* + * If there's no OP PLL at all, use the VT values + * instead. The OP values are ignored for the rest of + * the PLL calculation. + */ + op_lim_fr = &lim->vt_fr; + op_lim_bk = &lim->vt_bk; + op_pll_fr = &pll->vt_fr; + op_pll_bk = &pll->vt_bk; + } else { + op_lim_fr = &lim->vt_fr; + op_lim_bk = &lim->op_bk; + op_pll_fr = &pll->vt_fr; + op_pll_bk = &pll->op_bk; + } + + if (!pll->op_lanes || !pll->vt_lanes || !pll->bits_per_pixel || + !pll->ext_clk_freq_hz || !pll->link_freq || !pll->scale_m || + !op_lim_fr->min_pll_ip_clk_freq_hz || + !op_lim_fr->max_pll_ip_clk_freq_hz || + !op_lim_fr->min_pll_op_clk_freq_hz || + !op_lim_fr->max_pll_op_clk_freq_hz || + !op_lim_bk->max_sys_clk_div || !op_lim_fr->max_pll_multiplier) + return -EINVAL; + + /* + * Make sure op_pix_clk_div will be integer --- unless flexible + * op_pix_clk_div is supported + */ + if (!(pll->flags & CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV) && + (pll->bits_per_pixel * pll->op_lanes) % + (pll->csi2.lanes * l << op_pix_ddr(pll->flags))) { + dev_dbg(dev, "op_pix_clk_div not an integer (bpp %u, op lanes %u, lanes %u, l %u)\n", + pll->bits_per_pixel, pll->op_lanes, pll->csi2.lanes, l); + return -EINVAL; + } + + dev_dbg(dev, "vt_lanes: %u\n", pll->vt_lanes); + dev_dbg(dev, "op_lanes: %u\n", pll->op_lanes); + + dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, + pll->binning_vertical); + + switch (pll->bus_type) { + case CCS_PLL_BUS_TYPE_CSI2_DPHY: + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + op_sys_clk_freq_hz_sdr = pll->link_freq * 2 + * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? + 1 : pll->csi2.lanes); + break; + case CCS_PLL_BUS_TYPE_CSI2_CPHY: + op_sys_clk_freq_hz_sdr = + pll->link_freq + * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? + 1 : pll->csi2.lanes); + break; + default: + return -EINVAL; + } + + pll->pixel_rate_csi = + div_u64((uint64_t)op_sys_clk_freq_hz_sdr + * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? + pll->csi2.lanes : 1) * PHY_CONST_DIV, + phy_const * pll->bits_per_pixel * l); + + /* Figure out limits for OP pre-pll divider based on extclk */ + dev_dbg(dev, "min / max op_pre_pll_clk_div: %u / %u\n", + op_lim_fr->min_pre_pll_clk_div, op_lim_fr->max_pre_pll_clk_div); + max_op_pre_pll_clk_div = + min_t(uint16_t, op_lim_fr->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + op_lim_fr->min_pll_ip_clk_freq_hz)); + min_op_pre_pll_clk_div = + max_t(uint16_t, op_lim_fr->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + op_lim_fr->max_pll_ip_clk_freq_hz))); + dev_dbg(dev, "pre-pll check: min / max op_pre_pll_clk_div: %u / %u\n", + min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); + + i = gcd(op_sys_clk_freq_hz_sdr, + pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)); + mul = op_sys_clk_freq_hz_sdr / i; + div = (pll->ext_clk_freq_hz << op_pix_ddr(pll->flags)) / i; + dev_dbg(dev, "mul %u / div %u\n", mul, div); + + min_op_pre_pll_clk_div = + max_t(uint16_t, min_op_pre_pll_clk_div, + clk_div_even_up( + mul / + one_or_more( + DIV_ROUND_UP(op_lim_fr->max_pll_op_clk_freq_hz, + pll->ext_clk_freq_hz)))); + dev_dbg(dev, "pll_op check: min / max op_pre_pll_clk_div: %u / %u\n", + min_op_pre_pll_clk_div, max_op_pre_pll_clk_div); + + for (op_pll_fr->pre_pll_clk_div = min_op_pre_pll_clk_div; + op_pll_fr->pre_pll_clk_div <= max_op_pre_pll_clk_div; + op_pll_fr->pre_pll_clk_div += + (pll->flags & CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER) ? 1 : + 2 - (op_pll_fr->pre_pll_clk_div & 1)) { + rval = ccs_pll_calculate_op(dev, lim, op_lim_fr, op_lim_bk, pll, + op_pll_fr, op_pll_bk, mul, div, + op_sys_clk_freq_hz_sdr, l, cphy, + phy_const); + if (rval) + continue; + + rval = check_fr_bounds(dev, lim, pll, + pll->flags & CCS_PLL_FLAG_DUAL_PLL ? + PLL_OP : PLL_VT); + if (rval) + continue; + + rval = check_bk_bounds(dev, lim, pll, PLL_OP); + if (rval) + continue; + + if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) + break; + + ccs_pll_calculate_vt(dev, lim, op_lim_bk, pll, op_pll_fr, + op_pll_bk, cphy, phy_const); + + rval = check_bk_bounds(dev, lim, pll, PLL_VT); + if (rval) + continue; + rval = check_ext_bounds(dev, pll); + if (rval) + continue; + + break; + } + + if (rval) { + dev_dbg(dev, "unable to compute pre_pll divisor\n"); + + return rval; + } + + if (pll->flags & CCS_PLL_FLAG_DUAL_PLL) { + rval = ccs_pll_calculate_vt_tree(dev, lim, pll); + + if (rval) + return rval; + } + + print_pll(dev, pll); + + return 0; +} +EXPORT_SYMBOL_GPL(ccs_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); +MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ccs-pll.h b/drivers/media/i2c/ccs-pll.h new file mode 100644 index 000000000000..b97d7ff50ea5 --- /dev/null +++ b/drivers/media/i2c/ccs-pll.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drivers/media/i2c/ccs-pll.h + * + * Generic MIPI CCS/SMIA/SMIA++ PLL calculator + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> + */ + +#ifndef CCS_PLL_H +#define CCS_PLL_H + +#include <linux/bits.h> + +/* CSI-2 or CCP-2 */ +#define CCS_PLL_BUS_TYPE_CSI2_DPHY 0x00 +#define CCS_PLL_BUS_TYPE_CSI2_CPHY 0x01 + +/* Old SMIA and implementation specific flags */ +/* op pix clock is for all lanes in total normally */ +#define CCS_PLL_FLAG_OP_PIX_CLOCK_PER_LANE BIT(0) +#define CCS_PLL_FLAG_NO_OP_CLOCKS BIT(1) +/* CCS PLL flags */ +#define CCS_PLL_FLAG_LANE_SPEED_MODEL BIT(2) +#define CCS_PLL_FLAG_LINK_DECOUPLED BIT(3) +#define CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER BIT(4) +#define CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV BIT(5) +#define CCS_PLL_FLAG_FIFO_DERATING BIT(6) +#define CCS_PLL_FLAG_FIFO_OVERRATING BIT(7) +#define CCS_PLL_FLAG_DUAL_PLL BIT(8) +#define CCS_PLL_FLAG_OP_SYS_DDR BIT(9) +#define CCS_PLL_FLAG_OP_PIX_DDR BIT(10) + +/** + * struct ccs_pll_branch_fr - CCS PLL configuration (front) + * + * A single branch front-end of the CCS PLL tree. + * + * @pre_pll_clk_div: Pre-PLL clock divisor + * @pll_multiplier: PLL multiplier + * @pll_ip_clk_freq_hz: PLL input clock frequency + * @pll_op_clk_freq_hz: PLL output clock frequency + */ +struct ccs_pll_branch_fr { + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; +}; + +/** + * struct ccs_pll_branch_bk - CCS PLL configuration (back) + * + * A single branch back-end of the CCS PLL tree. + * + * @sys_clk_div: System clock divider + * @pix_clk_div: Pixel clock divider + * @sys_clk_freq_hz: System clock frequency + * @pix_clk_freq_hz: Pixel clock frequency + */ +struct ccs_pll_branch_bk { + uint16_t sys_clk_div; + uint16_t pix_clk_div; + uint32_t sys_clk_freq_hz; + uint32_t pix_clk_freq_hz; +}; + +/** + * struct ccs_pll - Full CCS PLL configuration + * + * All information required to calculate CCS PLL configuration. + * + * @bus_type: Type of the data bus, CCS_PLL_BUS_TYPE_* (input) + * @op_lanes: Number of operational lanes (input) + * @vt_lanes: Number of video timing lanes (input) + * @csi2: CSI-2 related parameters + * @csi2.lanes: The number of the CSI-2 data lanes (input) + * @binning_vertical: Vertical binning factor (input) + * @binning_horizontal: Horizontal binning factor (input) + * @scale_m: Downscaling factor, M component, [16, max] (input) + * @scale_n: Downscaling factor, N component, typically 16 (input) + * @bits_per_pixel: Bits per pixel on the output data bus (input) + * @op_bits_per_lane: Number of bits per OP lane (input) + * @flags: CCS_PLL_FLAG_* (input) + * @link_freq: Chosen link frequency (input) + * @ext_clk_freq_hz: External clock frequency, i.e. the sensor's input clock + * (input) + * @vt_fr: Video timing front-end configuration (output) + * @vt_bk: Video timing back-end configuration (output) + * @op_fr: Operational timing front-end configuration (output) + * @op_bk: Operational timing back-end configuration (output) + * @pixel_rate_csi: Pixel rate on the output data bus (output) + * @pixel_rate_pixel_array: Nominal pixel rate in the sensor's pixel array + * (output) + */ +struct ccs_pll { + /* input values */ + uint8_t bus_type; + uint8_t op_lanes; + uint8_t vt_lanes; + struct { + uint8_t lanes; + } csi2; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint8_t op_bits_per_lane; + uint16_t flags; + uint32_t link_freq; + uint32_t ext_clk_freq_hz; + + /* output values */ + struct ccs_pll_branch_fr vt_fr; + struct ccs_pll_branch_bk vt_bk; + struct ccs_pll_branch_fr op_fr; + struct ccs_pll_branch_bk op_bk; + + uint32_t pixel_rate_csi; + uint32_t pixel_rate_pixel_array; +}; + +/** + * struct ccs_pll_branch_limits_fr - CCS PLL front-end limits + * + * @min_pre_pll_clk_div: Minimum pre-PLL clock divider + * @max_pre_pll_clk_div: Maximum pre-PLL clock divider + * @min_pll_ip_clk_freq_hz: Minimum PLL input clock frequency + * @max_pll_ip_clk_freq_hz: Maximum PLL input clock frequency + * @min_pll_multiplier: Minimum PLL multiplier + * @max_pll_multiplier: Maximum PLL multiplier + * @min_pll_op_clk_freq_hz: Minimum PLL output clock frequency + * @max_pll_op_clk_freq_hz: Maximum PLL output clock frequency + */ +struct ccs_pll_branch_limits_fr { + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_clk_freq_hz; + uint32_t max_pll_ip_clk_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_clk_freq_hz; + uint32_t max_pll_op_clk_freq_hz; +}; + +/** + * struct ccs_pll_branch_limits_bk - CCS PLL back-end limits + * + * @min_sys_clk_div: Minimum system clock divider + * @max_sys_clk_div: Maximum system clock divider + * @min_sys_clk_freq_hz: Minimum system clock frequency + * @max_sys_clk_freq_hz: Maximum system clock frequency + * @min_pix_clk_div: Minimum pixel clock divider + * @max_pix_clk_div: Maximum pixel clock divider + * @min_pix_clk_freq_hz: Minimum pixel clock frequency + * @max_pix_clk_freq_hz: Maximum pixel clock frequency + */ +struct ccs_pll_branch_limits_bk { + uint16_t min_sys_clk_div; + uint16_t max_sys_clk_div; + uint32_t min_sys_clk_freq_hz; + uint32_t max_sys_clk_freq_hz; + uint16_t min_pix_clk_div; + uint16_t max_pix_clk_div; + uint32_t min_pix_clk_freq_hz; + uint32_t max_pix_clk_freq_hz; +}; + +/** + * struct ccs_pll_limits - CCS PLL limits + * + * @min_ext_clk_freq_hz: Minimum external clock frequency + * @max_ext_clk_freq_hz: Maximum external clock frequency + * @vt_fr: Video timing front-end limits + * @vt_bk: Video timing back-end limits + * @op_fr: Operational timing front-end limits + * @op_bk: Operational timing back-end limits + * @min_line_length_pck_bin: Minimum line length in pixels, with binning + * @min_line_length_pck: Minimum line length in pixels without binning + */ +struct ccs_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + + struct ccs_pll_branch_limits_fr vt_fr; + struct ccs_pll_branch_limits_bk vt_bk; + struct ccs_pll_branch_limits_fr op_fr; + struct ccs_pll_branch_limits_bk op_bk; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +struct device; + +/** + * ccs_pll_calculate - Calculate CCS PLL configuration based on input parameters + * + * @dev: Device pointer, used for printing messages + * @limits: Limits specific to the sensor + * @pll: Given PLL configuration + * + * Calculate the CCS PLL configuration based on the limits as well as given + * device specific, system specific or user configured input data. + */ +int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *limits, + struct ccs_pll *pll); + +#endif /* CCS_PLL_H */ diff --git a/drivers/media/i2c/ccs/Kconfig b/drivers/media/i2c/ccs/Kconfig new file mode 100644 index 000000000000..59f35b33ddc1 --- /dev/null +++ b/drivers/media/i2c/ccs/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_CCS + tristate "MIPI CCS/SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && HAVE_CLK + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEO_CCS_PLL + select V4L2_FWNODE + help + This is a generic driver for MIPI CCS, SMIA++ and SMIA compliant + camera sensors. diff --git a/drivers/media/i2c/ccs/Makefile b/drivers/media/i2c/ccs/Makefile new file mode 100644 index 000000000000..44601ba8cd53 --- /dev/null +++ b/drivers/media/i2c/ccs/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +ccs-objs += ccs-core.o ccs-reg-access.o \ + ccs-quirk.o ccs-limits.o ccs-data.o +obj-$(CONFIG_VIDEO_CCS) += ccs.o + +ccflags-y += -I $(srctree)/drivers/media/i2c diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c new file mode 100644 index 000000000000..b39ae5f8446b --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -0,0 +1,3479 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/media/i2c/ccs/ccs-core.c + * + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/smiapp.h> +#include <linux/v4l2-mediabus.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-device.h> + +#include "ccs.h" + +#define CCS_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +static struct ccs_limit_offset { + u16 lim; + u16 info; +} ccs_limit_offsets[CCS_L_LAST + 1]; + +/* + * ccs_module_idents - supported camera modules + */ +static const struct ccs_module_ident ccs_module_idents[] = { + CCS_IDENT_L(0x01, 0x022b, -1, "vs6555"), + CCS_IDENT_L(0x01, 0x022e, -1, "vw6558"), + CCS_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + CCS_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + CCS_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + CCS_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + CCS_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + CCS_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + CCS_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + CCS_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + CCS_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +#define CCS_DEVICE_FLAG_IS_SMIA BIT(0) + +struct ccs_device { + unsigned char flags; +}; + +static const char * const ccs_regulators[] = { "vcore", "vio", "vana" }; + +/* + * + * Dynamic Capability Identification + * + */ + +static void ccs_assign_limit(void *ptr, unsigned int width, u32 val) +{ + switch (width) { + case sizeof(u8): + *(u8 *)ptr = val; + break; + case sizeof(u16): + *(u16 *)ptr = val; + break; + case sizeof(u32): + *(u32 *)ptr = val; + break; + } +} + +static int ccs_limit_ptr(struct ccs_sensor *sensor, unsigned int limit, + unsigned int offset, void **__ptr) +{ + const struct ccs_limit *linfo; + + if (WARN_ON(limit >= CCS_L_LAST)) + return -EINVAL; + + linfo = &ccs_limits[ccs_limit_offsets[limit].info]; + + if (WARN_ON(!sensor->ccs_limits) || + WARN_ON(offset + ccs_reg_width(linfo->reg) > + ccs_limit_offsets[limit + 1].lim)) + return -EINVAL; + + *__ptr = sensor->ccs_limits + ccs_limit_offsets[limit].lim + offset; + + return 0; +} + +void ccs_replace_limit(struct ccs_sensor *sensor, + unsigned int limit, unsigned int offset, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct ccs_limit *linfo; + void *ptr; + int ret; + + ret = ccs_limit_ptr(sensor, limit, offset, &ptr); + if (ret) + return; + + linfo = &ccs_limits[ccs_limit_offsets[limit].info]; + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" %u = %d, 0x%x\n", + linfo->reg, linfo->name, offset, val, val); + + ccs_assign_limit(ptr, ccs_reg_width(linfo->reg), val); +} + +u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit, + unsigned int offset) +{ + void *ptr; + u32 val; + int ret; + + ret = ccs_limit_ptr(sensor, limit, offset, &ptr); + if (ret) + return 0; + + switch (ccs_reg_width(ccs_limits[ccs_limit_offsets[limit].info].reg)) { + case sizeof(u8): + val = *(u8 *)ptr; + break; + case sizeof(u16): + val = *(u16 *)ptr; + break; + case sizeof(u32): + val = *(u32 *)ptr; + break; + default: + WARN_ON(1); + return 0; + } + + return ccs_reg_conv(sensor, ccs_limits[limit].reg, val); +} + +static int ccs_read_all_limits(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + void *ptr, *alloc, *end; + unsigned int i, l; + int ret; + + kfree(sensor->ccs_limits); + sensor->ccs_limits = NULL; + + alloc = kzalloc(ccs_limit_offsets[CCS_L_LAST].lim, GFP_KERNEL); + if (!alloc) + return -ENOMEM; + + end = alloc + ccs_limit_offsets[CCS_L_LAST].lim; + + for (i = 0, l = 0, ptr = alloc; ccs_limits[i].size; i++) { + u32 reg = ccs_limits[i].reg; + unsigned int width = ccs_reg_width(reg); + unsigned int j; + + if (l == CCS_L_LAST) { + dev_err(&client->dev, + "internal error --- end of limit array\n"); + ret = -EINVAL; + goto out_err; + } + + for (j = 0; j < ccs_limits[i].size / width; + j++, reg += width, ptr += width) { + u32 val; + + ret = ccs_read_addr_noconv(sensor, reg, &val); + if (ret) + goto out_err; + + if (ptr + width > end) { + dev_err(&client->dev, + "internal error --- no room for regs\n"); + ret = -EINVAL; + goto out_err; + } + + if (!val && j) + break; + + ccs_assign_limit(ptr, width, val); + + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", + reg, ccs_limits[i].name, val, val); + } + + if (ccs_limits[i].flags & CCS_L_FL_SAME_REG) + continue; + + l++; + ptr = alloc + ccs_limit_offsets[l].lim; + } + + if (l != CCS_L_LAST) { + dev_err(&client->dev, + "internal error --- insufficient limits\n"); + ret = -EINVAL; + goto out_err; + } + + sensor->ccs_limits = alloc; + + if (CCS_LIM(sensor, SCALER_N_MIN) < 16) + ccs_replace_limit(sensor, CCS_L_SCALER_N_MIN, 0, 16); + + return 0; + +out_err: + kfree(alloc); + + return ret; +} + +static int ccs_read_frame_fmt(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u8 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int pixel_count = 0; + int line_count = 0; + + fmt_model_type = CCS_LIM(sensor, FRAME_FORMAT_MODEL_TYPE); + fmt_model_subtype = CCS_LIM(sensor, FRAME_FORMAT_MODEL_SUBTYPE); + + ncol_desc = (fmt_model_subtype + & CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_MASK) + >> CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_SHIFT; + nrow_desc = fmt_model_subtype + & CCS_FRAME_FORMAT_MODEL_SUBTYPE_ROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == CCS_FRAME_FORMAT_MODEL_TYPE_2_BYTE + ? "2 byte" : + fmt_model_type == CCS_FRAME_FORMAT_MODEL_TYPE_4_BYTE + ? "4 byte" : "is simply bad"); + + dev_dbg(&client->dev, "%u column and %u row descriptors\n", + ncol_desc, nrow_desc); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == CCS_FRAME_FORMAT_MODEL_TYPE_2_BYTE) { + desc = CCS_LIM_AT(sensor, FRAME_FORMAT_DESCRIPTOR, i); + + pixelcode = + (desc + & CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MASK) + >> CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_SHIFT; + pixels = desc & CCS_FRAME_FORMAT_DESCRIPTOR_PIXELS_MASK; + } else if (fmt_model_type + == CCS_FRAME_FORMAT_MODEL_TYPE_4_BYTE) { + desc = CCS_LIM_AT(sensor, FRAME_FORMAT_DESCRIPTOR_4, i); + + pixelcode = + (desc + & CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MASK) + >> CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_SHIFT; + pixels = desc & + CCS_FRAME_FORMAT_DESCRIPTOR_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_EMBEDDED: + what = "embedded"; + break; + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_DUMMY_PIXEL: + what = "dummy"; + break; + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_BLACK_PIXEL: + what = "black"; + break; + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_DARK_PIXEL: + what = "dark"; + break; + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_VISIBLE_PIXEL: + what = "visible"; + break; + default: + what = "invalid"; + break; + } + + dev_dbg(&client->dev, + "%s pixels: %d %s (pixelcode %u)\n", + what, pixels, which, pixelcode); + + if (i < ncol_desc) { + if (pixelcode == + CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_VISIBLE_PIXEL) + sensor->visible_pixel_start = pixel_count; + pixel_count += pixels; + continue; + } + + /* Handle row descriptors */ + switch (pixelcode) { + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_EMBEDDED: + if (sensor->embedded_end) + break; + sensor->embedded_start = line_count; + sensor->embedded_end = line_count + pixels; + break; + case CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_VISIBLE_PIXEL: + sensor->image_start = line_count; + break; + } + line_count += pixels; + } + + if (sensor->embedded_end > sensor->image_start) { + dev_dbg(&client->dev, + "adjusting image start line to %u (was %u)\n", + sensor->embedded_end, sensor->image_start); + sensor->image_start = sensor->embedded_end; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + sensor->embedded_start, sensor->embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", + sensor->image_start); + + return 0; +} + +static int ccs_pll_configure(struct ccs_sensor *sensor) +{ + struct ccs_pll *pll = &sensor->pll; + int rval; + + rval = ccs_write(sensor, VT_PIX_CLK_DIV, pll->vt_bk.pix_clk_div); + if (rval < 0) + return rval; + + rval = ccs_write(sensor, VT_SYS_CLK_DIV, pll->vt_bk.sys_clk_div); + if (rval < 0) + return rval; + + rval = ccs_write(sensor, PRE_PLL_CLK_DIV, pll->vt_fr.pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = ccs_write(sensor, PLL_MULTIPLIER, pll->vt_fr.pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = ccs_write(sensor, REQUESTED_LINK_RATE, + DIV_ROUND_UP(pll->op_bk.sys_clk_freq_hz, + 1000000 / 256 / 256) * + (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ? + sensor->pll.csi2.lanes : 1) << + (pll->flags & CCS_PLL_FLAG_OP_SYS_DDR ? 1 : 0)); + if (rval < 0 || sensor->pll.flags & CCS_PLL_FLAG_NO_OP_CLOCKS) + return rval; + + rval = ccs_write(sensor, OP_PIX_CLK_DIV, pll->op_bk.pix_clk_div); + if (rval < 0) + return rval; + + rval = ccs_write(sensor, OP_SYS_CLK_DIV, pll->op_bk.sys_clk_div); + if (rval < 0) + return rval; + + if (!(pll->flags & CCS_PLL_FLAG_DUAL_PLL)) + return 0; + + rval = ccs_write(sensor, PLL_MODE, CCS_PLL_MODE_DUAL); + if (rval < 0) + return rval; + + rval = ccs_write(sensor, OP_PRE_PLL_CLK_DIV, + pll->op_fr.pre_pll_clk_div); + if (rval < 0) + return rval; + + return ccs_write(sensor, OP_PLL_MULTIPLIER, pll->op_fr.pll_multiplier); +} + +static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct ccs_pll_limits lim = { + .vt_fr = { + .min_pre_pll_clk_div = CCS_LIM(sensor, MIN_PRE_PLL_CLK_DIV), + .max_pre_pll_clk_div = CCS_LIM(sensor, MAX_PRE_PLL_CLK_DIV), + .min_pll_ip_clk_freq_hz = CCS_LIM(sensor, MIN_PLL_IP_CLK_FREQ_MHZ), + .max_pll_ip_clk_freq_hz = CCS_LIM(sensor, MAX_PLL_IP_CLK_FREQ_MHZ), + .min_pll_multiplier = CCS_LIM(sensor, MIN_PLL_MULTIPLIER), + .max_pll_multiplier = CCS_LIM(sensor, MAX_PLL_MULTIPLIER), + .min_pll_op_clk_freq_hz = CCS_LIM(sensor, MIN_PLL_OP_CLK_FREQ_MHZ), + .max_pll_op_clk_freq_hz = CCS_LIM(sensor, MAX_PLL_OP_CLK_FREQ_MHZ), + }, + .op_fr = { + .min_pre_pll_clk_div = CCS_LIM(sensor, MIN_OP_PRE_PLL_CLK_DIV), + .max_pre_pll_clk_div = CCS_LIM(sensor, MAX_OP_PRE_PLL_CLK_DIV), + .min_pll_ip_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PLL_IP_CLK_FREQ_MHZ), + .max_pll_ip_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PLL_IP_CLK_FREQ_MHZ), + .min_pll_multiplier = CCS_LIM(sensor, MIN_OP_PLL_MULTIPLIER), + .max_pll_multiplier = CCS_LIM(sensor, MAX_OP_PLL_MULTIPLIER), + .min_pll_op_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PLL_OP_CLK_FREQ_MHZ), + .max_pll_op_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PLL_OP_CLK_FREQ_MHZ), + }, + .op_bk = { + .min_sys_clk_div = CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV), + .max_sys_clk_div = CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV), + .min_pix_clk_div = CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV), + .max_pix_clk_div = CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV), + .min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_OP_SYS_CLK_FREQ_MHZ), + .max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_OP_SYS_CLK_FREQ_MHZ), + .min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_OP_PIX_CLK_FREQ_MHZ), + .max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_OP_PIX_CLK_FREQ_MHZ), + }, + .vt_bk = { + .min_sys_clk_div = CCS_LIM(sensor, MIN_VT_SYS_CLK_DIV), + .max_sys_clk_div = CCS_LIM(sensor, MAX_VT_SYS_CLK_DIV), + .min_pix_clk_div = CCS_LIM(sensor, MIN_VT_PIX_CLK_DIV), + .max_pix_clk_div = CCS_LIM(sensor, MAX_VT_PIX_CLK_DIV), + .min_sys_clk_freq_hz = CCS_LIM(sensor, MIN_VT_SYS_CLK_FREQ_MHZ), + .max_sys_clk_freq_hz = CCS_LIM(sensor, MAX_VT_SYS_CLK_FREQ_MHZ), + .min_pix_clk_freq_hz = CCS_LIM(sensor, MIN_VT_PIX_CLK_FREQ_MHZ), + .max_pix_clk_freq_hz = CCS_LIM(sensor, MAX_VT_PIX_CLK_FREQ_MHZ), + }, + .min_line_length_pck_bin = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN), + .min_line_length_pck = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK), + }; + + return ccs_pll_calculate(&client->dev, &lim, pll); +} + +static int ccs_pll_update(struct ccs_sensor *sensor) +{ + struct ccs_pll *pll = &sensor->pll; + int rval; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = ccs_pll_try(sensor, pll); + if (rval < 0) + return rval; + + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray, + pll->pixel_rate_pixel_array); + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi); + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __ccs_update_exposure_limits(struct ccs_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[CCS_PA_PAD_SRC].height + + sensor->vblank->val + - CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); + + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct ccs_csi_data_format ccs_csi_data_formats[] = { + { MEDIA_BUS_FMT_SGRBG16_1X16, 16, 16, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB16_1X16, 16, 16, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR16_1X16, 16, 16, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG16_1X16, 16, 16, CCS_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, 14, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, 14, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, 14, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, 14, CCS_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, CCS_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, CCS_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, CCS_PIXEL_ORDER_GBRG, }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, CCS_PIXEL_ORDER_GRBG, }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, CCS_PIXEL_ORDER_RGGB, }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, CCS_PIXEL_ORDER_BGGR, }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, CCS_PIXEL_ORDER_GBRG, }, +}; + +static const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)ccs_csi_data_formats) \ + / sizeof(*ccs_csi_data_formats)) + +static u32 ccs_pixel_order(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR; + + if (sensor->vflip->val) + flip |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void ccs_update_mbus_formats(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = ccs_pixel_order(sensor); + + if (WARN_ON_ONCE(max(internal_csi_format_idx, csi_format_idx) + + pixel_order >= ARRAY_SIZE(ccs_csi_data_formats))) + return; + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &ccs_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &ccs_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static const char * const ccs_test_patterns[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", +}; + +static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ccs_sensor *sensor = + container_of(ctrl->handler, struct ccs_subdev, ctrl_handler) + ->sensor; + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int pm_status; + u32 orient = 0; + unsigned int i; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR; + + if (sensor->vflip->val) + orient |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; + + orient ^= sensor->hvflip_inv_mask; + + ccs_update_mbus_formats(sensor); + + break; + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __ccs_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = sensor->exposure->maximum; + rval = ccs_set_ctrl(sensor->exposure); + if (rval < 0) + return rval; + } + + break; + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + rval = ccs_pll_update(sensor); + if (rval) + return rval; + + return 0; + case V4L2_CID_TEST_PATTERN: + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) + v4l2_ctrl_activate( + sensor->test_data[i], + ctrl->val == + V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR); + + break; + } + + pm_status = pm_runtime_get_if_active(&client->dev, true); + if (!pm_status) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + rval = ccs_write(sensor, ANALOG_GAIN_CODE_GLOBAL, ctrl->val); + + break; + case V4L2_CID_EXPOSURE: + rval = ccs_write(sensor, COARSE_INTEGRATION_TIME, ctrl->val); + + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + rval = ccs_write(sensor, IMAGE_ORIENTATION, orient); + + break; + case V4L2_CID_VBLANK: + rval = ccs_write(sensor, FRAME_LENGTH_LINES, + sensor->pixel_array->crop[ + CCS_PA_PAD_SRC].height + + ctrl->val); + + break; + case V4L2_CID_HBLANK: + rval = ccs_write(sensor, LINE_LENGTH_PCK, + sensor->pixel_array->crop[CCS_PA_PAD_SRC].width + + ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN: + rval = ccs_write(sensor, TEST_PATTERN_MODE, ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN_RED: + rval = ccs_write(sensor, TEST_DATA_RED, ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN_GREENR: + rval = ccs_write(sensor, TEST_DATA_GREENR, ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN_BLUE: + rval = ccs_write(sensor, TEST_DATA_BLUE, ctrl->val); + + break; + case V4L2_CID_TEST_PATTERN_GREENB: + rval = ccs_write(sensor, TEST_DATA_GREENB, ctrl->val); + + break; + case V4L2_CID_PIXEL_RATE: + /* For v4l2_ctrl_s_ctrl_int64() used internally. */ + rval = 0; + + break; + default: + rval = -EINVAL; + } + + if (pm_status > 0) { + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + return rval; +} + +static const struct v4l2_ctrl_ops ccs_ctrl_ops = { + .s_ctrl = ccs_set_ctrl, +}; + +static int ccs_init_controls(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); + if (rval) + return rval; + + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MAX), + max(CCS_LIM(sensor, ANALOG_GAIN_CODE_STEP), 1U), + CCS_LIM(sensor, ANALOG_GAIN_CODE_MIN)); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); + + v4l2_ctrl_new_std_menu_items(&sensor->pixel_array->ctrl_handler, + &ccs_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ccs_test_patterns) - 1, + 0, 0, ccs_test_patterns); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + return sensor->pixel_array->ctrl_handler.error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + return rval; + + sensor->src->ctrl_handler.lock = &sensor->mutex; + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + return sensor->src->ctrl_handler.error; + } + + sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler; + + return 0; +} + +/* + * For controls that require information on available media bus codes + * and linke frequencies. + */ +static int ccs_init_late_controls(struct ccs_sensor *sensor) +{ + unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ + sensor->csi_format->compressed - sensor->compressed_min_bpp]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { + int max_value = (1 << sensor->csi_format->width) - 1; + + sensor->test_data[i] = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, + &ccs_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, + 0, max_value, 1, max_value); + } + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &ccs_ctrl_ops, + V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), + __ffs(*valid_link_freqs), sensor->hwcfg.op_sys_clock); + + return sensor->src->ctrl_handler.error; +} + +static void ccs_free_controls(struct ccs_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int ccs_get_mbus_formats(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct ccs_pll *pll = &sensor->pll; + u8 compressed_max_bpp = 0; + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + type = CCS_LIM(sensor, DATA_FORMAT_MODEL_TYPE); + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = ccs_read(sensor, PIXEL_ORDER, &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case CCS_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case CCS_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = CCS_LIM_DATA_FORMAT_DESCRIPTOR_MAX_N + 1; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + fmt = CCS_LIM_AT(sensor, DATA_FORMAT_DESCRIPTOR, i); + + dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n", + i, fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(ccs_csi_data_formats); j++) { + const struct ccs_csi_data_format *f = + &ccs_csi_data_formats[j]; + + if (f->pixel_order != CCS_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> + CCS_DATA_FORMAT_DESCRIPTOR_UNCOMPRESSED_SHIFT || + f->compressed != + (fmt & CCS_DATA_FORMAT_DESCRIPTOR_COMPRESSED_MASK)) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + } + } + + /* Figure out which BPP values can be used with which formats. */ + pll->binning_horizontal = 1; + pll->binning_vertical = 1; + pll->scale_m = sensor->scale_m; + + for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { + sensor->compressed_min_bpp = + min(ccs_csi_data_formats[i].compressed, + sensor->compressed_min_bpp); + compressed_max_bpp = + max(ccs_csi_data_formats[i].compressed, + compressed_max_bpp); + } + + sensor->valid_link_freqs = devm_kcalloc( + &client->dev, + compressed_max_bpp - sensor->compressed_min_bpp + 1, + sizeof(*sensor->valid_link_freqs), GFP_KERNEL); + if (!sensor->valid_link_freqs) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { + const struct ccs_csi_data_format *f = + &ccs_csi_data_formats[i]; + unsigned long *valid_link_freqs = + &sensor->valid_link_freqs[ + f->compressed - sensor->compressed_min_bpp]; + unsigned int j; + + if (!(sensor->default_mbus_frame_fmts & 1 << i)) + continue; + + pll->bits_per_pixel = f->compressed; + + for (j = 0; sensor->hwcfg.op_sys_clock[j]; j++) { + pll->link_freq = sensor->hwcfg.op_sys_clock[j]; + + rval = ccs_pll_try(sensor, pll); + dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n", + pll->link_freq, pll->bits_per_pixel, + rval ? "not ok" : "ok"); + if (rval) + continue; + + set_bit(j, valid_link_freqs); + } + + if (!*valid_link_freqs) { + dev_info(&client->dev, + "no valid link frequencies for %u bpp\n", + f->compressed); + sensor->default_mbus_frame_fmts &= ~BIT(i); + continue; + } + + if (!sensor->csi_format + || f->width > sensor->csi_format->width + || (f->width == sensor->csi_format->width + && f->compressed > sensor->csi_format->compressed)) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + ccs_update_mbus_formats(sensor); + + return 0; +} + +static void ccs_update_blanking(struct ccs_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + uint16_t min_fll, max_fll, min_llp, max_llp, min_lbp; + int min, max; + + if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { + min_fll = CCS_LIM(sensor, MIN_FRAME_LENGTH_LINES_BIN); + max_fll = CCS_LIM(sensor, MAX_FRAME_LENGTH_LINES_BIN); + min_llp = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN); + max_llp = CCS_LIM(sensor, MAX_LINE_LENGTH_PCK_BIN); + min_lbp = CCS_LIM(sensor, MIN_LINE_BLANKING_PCK_BIN); + } else { + min_fll = CCS_LIM(sensor, MIN_FRAME_LENGTH_LINES); + max_fll = CCS_LIM(sensor, MAX_FRAME_LENGTH_LINES); + min_llp = CCS_LIM(sensor, MIN_LINE_LENGTH_PCK); + max_llp = CCS_LIM(sensor, MAX_LINE_LENGTH_PCK); + min_lbp = CCS_LIM(sensor, MIN_LINE_BLANKING_PCK); + } + + min = max_t(int, + CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES), + min_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height); + max = max_fll - sensor->pixel_array->crop[CCS_PA_PAD_SRC].height; + + __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); + + min = max_t(int, + min_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width, + min_lbp); + max = max_llp - sensor->pixel_array->crop[CCS_PA_PAD_SRC].width; + + __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); + + __ccs_update_exposure_limits(sensor); +} + +static int ccs_pll_blanking_update(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + rval = ccs_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + ccs_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.pixel_rate_pixel_array / + ((sensor->pixel_array->crop[CCS_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[CCS_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ + +static int ccs_read_nvm_page(struct ccs_sensor *sensor, u32 p, u8 *nvm, + u8 *status) +{ + unsigned int i; + int rval; + u32 s; + + *status = 0; + + rval = ccs_write(sensor, DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + return rval; + + rval = ccs_write(sensor, DATA_TRANSFER_IF_1_CTRL, + CCS_DATA_TRANSFER_IF_1_CTRL_ENABLE); + if (rval) + return rval; + + rval = ccs_read(sensor, DATA_TRANSFER_IF_1_STATUS, &s); + if (rval) + return rval; + + if (s & CCS_DATA_TRANSFER_IF_1_STATUS_IMPROPER_IF_USAGE) { + *status = s; + return -ENODATA; + } + + if (CCS_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & + CCS_DATA_TRANSFER_IF_CAPABILITY_POLLING) { + for (i = 1000; i > 0; i--) { + if (s & CCS_DATA_TRANSFER_IF_1_STATUS_READ_IF_READY) + break; + + rval = ccs_read(sensor, DATA_TRANSFER_IF_1_STATUS, &s); + if (rval) + return rval; + } + + if (!i) + return -ETIMEDOUT; + } + + for (i = 0; i <= CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P; i++) { + u32 v; + + rval = ccs_read(sensor, DATA_TRANSFER_IF_1_DATA(i), &v); + if (rval) + return rval; + + *nvm++ = v; + } + + return 0; +} + +static int ccs_read_nvm(struct ccs_sensor *sensor, unsigned char *nvm, + size_t nvm_size) +{ + u8 status = 0; + u32 p; + int rval = 0, rval2; + + for (p = 0; p < nvm_size / (CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P + 1) + && !rval; p++) { + rval = ccs_read_nvm_page(sensor, p, nvm, &status); + nvm += CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P + 1; + } + + if (rval == -ENODATA && + status & CCS_DATA_TRANSFER_IF_1_STATUS_IMPROPER_IF_USAGE) + rval = 0; + + rval2 = ccs_write(sensor, DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2 ?: p * (CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P + 1); +} + +/* + * + * SMIA++ CCI address control + * + */ +static int ccs_change_cci_addr(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->hwcfg.i2c_addr_dfl; + + rval = ccs_write(sensor, CCI_ADDRESS_CTRL, + sensor->hwcfg.i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->hwcfg.i2c_addr_alt; + + /* verify addr change went ok */ + rval = ccs_read(sensor, CCI_ADDRESS_CTRL, &val); + if (rval) + return rval; + + if (val != sensor->hwcfg.i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int ccs_setup_flash_strobe(struct ccs_sensor *sensor) +{ + struct ccs_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->hwcfg.ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->hwcfg.strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus <sakari.ailus@linux.intel.com> 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = ccs_write(sensor, FLASH_MODE_RS, strobe_setup->mode); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, FLASH_STROBE_ADJUSTMENT, strobe_adjustment); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, FLASH_TRIGGER_RS, strobe_setup->trigger); + +out: + sensor->hwcfg.strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int ccs_write_msr_regs(struct ccs_sensor *sensor) +{ + int rval; + + rval = ccs_write_data_regs(sensor, + sensor->sdata.sensor_manufacturer_regs, + sensor->sdata.num_sensor_manufacturer_regs); + if (rval) + return rval; + + return ccs_write_data_regs(sensor, + sensor->mdata.module_manufacturer_regs, + sensor->mdata.num_module_manufacturer_regs); +} + +static int ccs_power_on(struct device *dev) +{ + struct v4l2_subdev *subdev = dev_get_drvdata(dev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + /* + * The sub-device related to the I2C device is always the + * source one, i.e. ssds[0]. + */ + struct ccs_sensor *sensor = + container_of(ssd, struct ccs_sensor, ssds[0]); + const struct ccs_device *ccsdev = device_get_match_data(dev); + unsigned int sleep; + int rval; + + rval = regulator_bulk_enable(ARRAY_SIZE(ccs_regulators), + sensor->regulators); + if (rval) { + dev_err(dev, "failed to enable vana regulator\n"); + return rval; + } + + rval = clk_prepare_enable(sensor->ext_clk); + if (rval < 0) { + dev_dbg(dev, "failed to enable xclk\n"); + goto out_xclk_fail; + } + + gpiod_set_value(sensor->reset, 0); + gpiod_set_value(sensor->xshutdown, 1); + + if (ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) + sleep = SMIAPP_RESET_DELAY(sensor->hwcfg.ext_clk); + else + sleep = 5000; + + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->hwcfg.i2c_addr_alt) { + rval = ccs_change_cci_addr(sensor); + if (rval) { + dev_err(dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); + if (rval < 0) { + dev_err(dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->hwcfg.i2c_addr_alt) { + rval = ccs_change_cci_addr(sensor); + if (rval) { + dev_err(dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = ccs_write(sensor, COMPRESSION_MODE, + CCS_COMPRESSION_MODE_DPCM_PCM_SIMPLE); + if (rval) { + dev_err(dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = ccs_write(sensor, EXTCLK_FREQUENCY_MHZ, + sensor->hwcfg.ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = ccs_write(sensor, CSI_LANE_MODE, sensor->hwcfg.lanes - 1); + if (rval) { + dev_err(dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = ccs_write(sensor, FAST_STANDBY_CTRL, + CCS_FAST_STANDBY_CTRL_FRAME_TRUNCATION); + if (rval) { + dev_err(dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = ccs_write(sensor, CSI_SIGNALING_MODE, + sensor->hwcfg.csi_signalling_mode); + if (rval) { + dev_err(dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = ccs_write(sensor, PHY_CTRL, CCS_PHY_CTRL_UI); + if (rval < 0) + goto out_cci_addr_fail; + + rval = ccs_write_msr_regs(sensor); + if (rval) + goto out_cci_addr_fail; + + rval = ccs_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + return 0; + +out_cci_addr_fail: + gpiod_set_value(sensor->reset, 1); + gpiod_set_value(sensor->xshutdown, 0); + clk_disable_unprepare(sensor->ext_clk); + +out_xclk_fail: + regulator_bulk_disable(ARRAY_SIZE(ccs_regulators), + sensor->regulators); + + return rval; +} + +static int ccs_power_off(struct device *dev) +{ + struct v4l2_subdev *subdev = dev_get_drvdata(dev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct ccs_sensor *sensor = + container_of(ssd, struct ccs_sensor, ssds[0]); + + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->hwcfg.i2c_addr_alt) + ccs_write(sensor, SOFTWARE_RESET, CCS_SOFTWARE_RESET_ON); + + gpiod_set_value(sensor->reset, 1); + gpiod_set_value(sensor->xshutdown, 0); + clk_disable_unprepare(sensor->ext_clk); + usleep_range(5000, 5000); + regulator_bulk_disable(ARRAY_SIZE(ccs_regulators), + sensor->regulators); + sensor->streaming = false; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int ccs_start_streaming(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + mutex_lock(&sensor->mutex); + + rval = ccs_write(sensor, CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + /* Binning configuration */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = ccs_write(sensor, BINNING_TYPE, binning_type); + if (rval < 0) + goto out; + + binning_mode = 1; + } + rval = ccs_write(sensor, BINNING_MODE, binning_mode); + if (rval < 0) + goto out; + + /* Set up PLL */ + rval = ccs_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = ccs_write(sensor, X_ADDR_START, + sensor->pixel_array->crop[CCS_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, Y_ADDR_START, + sensor->pixel_array->crop[CCS_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = ccs_write( + sensor, X_ADDR_END, + sensor->pixel_array->crop[CCS_PA_PAD_SRC].left + + sensor->pixel_array->crop[CCS_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = ccs_write( + sensor, Y_ADDR_END, + sensor->pixel_array->crop[CCS_PA_PAD_SRC].top + + sensor->pixel_array->crop[CCS_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) + == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = ccs_write( + sensor, DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[CCS_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = ccs_write( + sensor, DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[CCS_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = ccs_write( + sensor, DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[CCS_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = ccs_write( + sensor, DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[CCS_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (CCS_LIM(sensor, SCALING_CAPABILITY) + != CCS_SCALING_CAPABILITY_NONE) { + rval = ccs_write(sensor, SCALING_MODE, sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = ccs_write(sensor, SCALE_M, sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = ccs_write(sensor, X_OUTPUT_SIZE, + sensor->src->crop[CCS_PAD_SRC].width); + if (rval < 0) + goto out; + rval = ccs_write(sensor, Y_OUTPUT_SIZE, + sensor->src->crop[CCS_PAD_SRC].height); + if (rval < 0) + goto out; + + if (CCS_LIM(sensor, FLASH_MODE_CAPABILITY) & + (CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE) && + sensor->hwcfg.strobe_setup != NULL && + sensor->hwcfg.strobe_setup->trigger != 0) { + rval = ccs_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = ccs_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int ccs_stop_streaming(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = ccs_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int ccs_pm_get_init(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + rval = pm_runtime_get_sync(&client->dev); + if (rval < 0) { + pm_runtime_put_noidle(&client->dev); + + return rval; + } else if (!rval) { + rval = v4l2_ctrl_handler_setup(&sensor->pixel_array-> + ctrl_handler); + if (rval) + return rval; + + return v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + } + + return 0; +} + +static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (!enable) { + ccs_stop_streaming(sensor); + sensor->streaming = false; + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return 0; + } + + rval = ccs_pm_get_init(sensor); + if (rval) + return rval; + + sensor->streaming = true; + + rval = ccs_start_streaming(sensor); + if (rval < 0) { + sensor->streaming = false; + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + return rval; +} + +static int ccs_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != CCS_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = ccs_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == CCS_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __ccs_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, + fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + fmt->format.field = V4L2_FIELD_NONE; + } + + return 0; +} + +static int ccs_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __ccs_get_format(subdev, cfg, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void ccs_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = v4l2_subdev_get_try_crop(subdev, + cfg, i); + } + if (comps) + *comps = v4l2_subdev_get_try_compose(subdev, cfg, + CCS_PAD_SINK); + } +} + +/* Changes require propagation only on sink pad. */ +static void ccs_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, int which, + int target) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct v4l2_rect *comp, *crops[CCS_PADS]; + + ccs_get_crop_compose(subdev, cfg, crops, &comp, which); + + switch (target) { + case V4L2_SEL_TGT_CROP: + comp->width = crops[CCS_PAD_SINK]->width; + comp->height = crops[CCS_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN); + sensor->scaling_mode = + CCS_SCALING_MODE_NO_SCALING; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + fallthrough; + case V4L2_SEL_TGT_COMPOSE: + *crops[CCS_PAD_SRC] = *comp; + break; + default: + WARN_ON_ONCE(1); + } +} + +static const struct ccs_csi_data_format +*ccs_validate_csi_data_format(struct ccs_sensor *sensor, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) && + ccs_csi_data_formats[i].code == code) + return &ccs_csi_data_formats[i]; + } + + return sensor->csi_format; +} + +static int ccs_set_format_source(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + const struct ccs_csi_data_format *csi_format, + *old_csi_format = sensor->csi_format; + unsigned long *valid_link_freqs; + u32 code = fmt->format.code; + unsigned int i; + int rval; + + rval = __ccs_get_format(subdev, cfg, fmt); + if (rval) + return rval; + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (subdev != &sensor->src->sd) + return 0; + + csi_format = ccs_validate_csi_data_format(sensor, code); + + fmt->format.code = csi_format->code; + + if (fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return 0; + + sensor->csi_format = csi_format; + + if (csi_format->width != old_csi_format->width) + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) + __v4l2_ctrl_modify_range( + sensor->test_data[i], 0, + (1 << csi_format->width) - 1, 1, 0); + + if (csi_format->compressed == old_csi_format->compressed) + return 0; + + valid_link_freqs = + &sensor->valid_link_freqs[sensor->csi_format->compressed + - sensor->compressed_min_bpp]; + + __v4l2_ctrl_modify_range( + sensor->link_freq, 0, + __fls(*valid_link_freqs), ~*valid_link_freqs, + __ffs(*valid_link_freqs)); + + return ccs_pll_update(sensor); +} + +static int ccs_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct v4l2_rect *crops[CCS_PADS]; + + mutex_lock(&sensor->mutex); + + if (fmt->pad == ssd->source_pad) { + int rval; + + rval = ccs_set_format_source(subdev, cfg, fmt); + + mutex_unlock(&sensor->mutex); + + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + fmt->format.field = V4L2_FIELD_NONE; + + fmt->format.width = + clamp(fmt->format.width, + CCS_LIM(sensor, MIN_X_OUTPUT_SIZE), + CCS_LIM(sensor, MAX_X_OUTPUT_SIZE)); + fmt->format.height = + clamp(fmt->format.height, + CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE), + CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE)); + + ccs_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + ccs_propagate(subdev, cfg, fmt->which, V4L2_SEL_TGT_CROP); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SEL_FLAG_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SEL_FLAG_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < CCS_LIM(sensor, MIN_X_OUTPUT_SIZE)) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_w, h, ask_h, val); + + return val; +} + +static void ccs_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + int best = scaling_goodness( + subdev, + crops[CCS_PAD_SINK]->width, sel->r.width, + crops[CCS_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[CCS_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[CCS_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[CCS_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[CCS_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = CCS_LIM(sensor, SCALER_N_MIN); + int mode = CCS_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[CCS_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[CCS_PAD_SINK]->height); + + a = crops[CCS_PAD_SINK]->width + * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width; + b = crops[CCS_PAD_SINK]->height + * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height; + max_m = crops[CCS_PAD_SINK]->width + * CCS_LIM(sensor, SCALER_N_MIN) + / CCS_LIM(sensor, MIN_X_OUTPUT_SIZE); + + a = clamp(a, CCS_LIM(sensor, SCALER_M_MIN), + CCS_LIM(sensor, SCALER_M_MAX)); + b = clamp(b, CCS_LIM(sensor, SCALER_M_MIN), + CCS_LIM(sensor, SCALER_M_MAX)); + max_m = clamp(max_m, CCS_LIM(sensor, SCALER_M_MIN), + CCS_LIM(sensor, SCALER_M_MAX)); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[CCS_PAD_SINK]->width + / try[i] * CCS_LIM(sensor, SCALER_N_MIN), + sel->r.width, + crops[CCS_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = CCS_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (CCS_LIM(sensor, SCALING_CAPABILITY) + == CCS_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[CCS_PAD_SINK]->width + / try[i] + * CCS_LIM(sensor, SCALER_N_MIN), + sel->r.width, + crops[CCS_PAD_SINK]->height + / try[i] + * CCS_LIM(sensor, SCALER_N_MIN), + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[CCS_PAD_SINK]->width + / scale_m + * CCS_LIM(sensor, SCALER_N_MIN)) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[CCS_PAD_SINK]->height + / scale_m + * CCS_LIM(sensor, SCALER_N_MIN)) + & ~1; + else + sel->r.height = crops[CCS_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int ccs_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct v4l2_rect *comp, *crops[CCS_PADS]; + + ccs_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + ccs_set_compose_binner(subdev, cfg, sel, crops, comp); + else + ccs_set_compose_scaler(subdev, cfg, sel, crops, comp); + + *comp = sel->r; + ccs_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return ccs_pll_blanking_update(sensor); + + return 0; +} + +static int __ccs_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array && sel->pad == CCS_PA_PAD_SRC) + return 0; + if (ssd == sensor->src && sel->pad == CCS_PAD_SRC) + return 0; + if (ssd == sensor->scaler && sel->pad == CCS_PAD_SINK && + CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) + == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SEL_TGT_NATIVE_SIZE: + if (ssd == sensor->pixel_array && sel->pad == CCS_PA_PAD_SRC) + return 0; + return -EINVAL; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler && CCS_LIM(sensor, SCALING_CAPABILITY) + != CCS_SCALING_CAPABILITY_NONE) + return 0; + fallthrough; + default: + return -EINVAL; + } +} + +static int ccs_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct v4l2_rect *src_size, *crops[CCS_PADS]; + struct v4l2_rect _r; + + ccs_get_crop_compose(subdev, cfg, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(subdev, cfg, + sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(subdev, cfg, + sel->pad) + ->height; + src_size = &_r; + } else { + src_size = v4l2_subdev_get_try_compose( + subdev, cfg, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == CCS_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); + sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK) + ccs_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_CROP); + + return 0; +} + +static void ccs_get_native_size(struct ccs_subdev *ssd, struct v4l2_rect *r) +{ + r->top = 0; + r->left = 0; + r->width = CCS_LIM(ssd->sensor, X_ADDR_MAX) + 1; + r->height = CCS_LIM(ssd->sensor, Y_ADDR_MAX) + 1; +} + +static int __ccs_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_subdev *ssd = to_ccs_subdev(subdev); + struct v4l2_rect *comp, *crops[CCS_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __ccs_sel_supported(subdev, sel); + if (ret) + return ret; + + ccs_get_crop_compose(subdev, cfg, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + if (ssd == sensor->pixel_array) + ccs_get_native_size(ssd, &sel->r); + else if (sel->pad == ssd->sink_pad) + sel->r = sink_fmt; + else + sel->r = *comp; + break; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r = *comp; + break; + } + + return 0; +} + +static int ccs_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __ccs_get_selection(subdev, cfg, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int ccs_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int ret; + + ret = __ccs_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = CCS_ALIGN_DIM(sel->r.width, sel->flags); + sel->r.height = CCS_ALIGN_DIM(sel->r.height, sel->flags); + + sel->r.width = max_t(unsigned int, CCS_LIM(sensor, MIN_X_OUTPUT_SIZE), + sel->r.width); + sel->r.height = max_t(unsigned int, CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE), + sel->r.height); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + ret = ccs_set_crop(subdev, cfg, sel); + break; + case V4L2_SEL_TGT_COMPOSE: + ret = ccs_set_compose(subdev, cfg, sel); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int ccs_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +static int ccs_get_skip_top_lines(struct v4l2_subdev *subdev, u32 *lines) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + + *lines = sensor->image_start; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +ccs_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int rval; + + if (!sensor->dev_init_done) + return -EBUSY; + + rval = ccs_pm_get_init(sensor); + if (rval < 0) + return -ENODEV; + + rval = ccs_read_nvm(sensor, buf, PAGE_SIZE); + if (rval < 0) { + pm_runtime_put(&client->dev); + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + return rval; +} +static DEVICE_ATTR(nvm, S_IRUGO, ccs_sysfs_nvm_read, NULL); + +static ssize_t +ccs_sysfs_ident_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct ccs_module_info *minfo = &sensor->minfo; + + if (minfo->mipi_manufacturer_id) + return snprintf(buf, PAGE_SIZE, "%4.4x%4.4x%2.2x\n", + minfo->mipi_manufacturer_id, minfo->model_id, + minfo->revision_number) + 1; + else + return snprintf(buf, PAGE_SIZE, "%2.2x%4.4x%2.2x\n", + minfo->smia_manufacturer_id, minfo->model_id, + minfo->revision_number) + 1; +} + +static DEVICE_ATTR(ident, S_IRUGO, ccs_sysfs_ident_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int ccs_identify_module(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct ccs_module_info *minfo = &sensor->minfo; + unsigned int i; + u32 rev; + int rval = 0; + + /* Module info */ + rval = ccs_read(sensor, MODULE_MANUFACTURER_ID, + &minfo->mipi_manufacturer_id); + if (!rval && !minfo->mipi_manufacturer_id) + rval = ccs_read_addr_8only(sensor, + SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->smia_manufacturer_id); + if (!rval) + rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = ccs_read_addr_8only(sensor, + CCS_R_MODULE_REVISION_NUMBER_MAJOR, + &rev); + if (!rval) { + rval = ccs_read_addr_8only(sensor, + CCS_R_MODULE_REVISION_NUMBER_MINOR, + &minfo->revision_number); + minfo->revision_number |= rev << 8; + } + if (!rval) + rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = ccs_read_addr_8only(sensor, CCS_R_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = ccs_read(sensor, SENSOR_MANUFACTURER_ID, + &minfo->sensor_mipi_manufacturer_id); + if (!rval && !minfo->sensor_mipi_manufacturer_id) + rval = ccs_read_addr_8only(sensor, + CCS_R_SENSOR_MANUFACTURER_ID, + &minfo->sensor_smia_manufacturer_id); + if (!rval) + rval = ccs_read_addr_8only(sensor, + CCS_R_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = ccs_read_addr_8only(sensor, + CCS_R_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = ccs_read_addr_8only(sensor, + CCS_R_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = ccs_read(sensor, MIPI_CCS_VERSION, &minfo->ccs_version); + if (!rval && !minfo->ccs_version) + rval = ccs_read_addr_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval && !minfo->ccs_version) + rval = ccs_read_addr_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + if (minfo->mipi_manufacturer_id) + dev_dbg(&client->dev, "MIPI CCS module 0x%4.4x-0x%4.4x\n", + minfo->mipi_manufacturer_id, minfo->model_id); + else + dev_dbg(&client->dev, "SMIA module 0x%2.2x-0x%4.4x\n", + minfo->smia_manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%4.4x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number, minfo->module_year, minfo->module_month, + minfo->module_day); + + if (minfo->sensor_mipi_manufacturer_id) + dev_dbg(&client->dev, "MIPI CCS sensor 0x%4.4x-0x%4.4x\n", + minfo->sensor_mipi_manufacturer_id, + minfo->sensor_model_id); + else + dev_dbg(&client->dev, "SMIA sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_smia_manufacturer_id, + minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + if (minfo->ccs_version) { + dev_dbg(&client->dev, "MIPI CCS version %u.%u", + (minfo->ccs_version & CCS_MIPI_CCS_VERSION_MAJOR_MASK) + >> CCS_MIPI_CCS_VERSION_MAJOR_SHIFT, + (minfo->ccs_version & CCS_MIPI_CCS_VERSION_MINOR_MASK)); + minfo->name = CCS_NAME; + } else { + dev_dbg(&client->dev, + "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + minfo->name = SMIAPP_NAME; + } + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (minfo->sensor_smia_manufacturer_id && + !minfo->smia_manufacturer_id && !minfo->model_id) { + minfo->smia_manufacturer_id = + minfo->sensor_smia_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(ccs_module_idents); i++) { + if (ccs_module_idents[i].mipi_manufacturer_id && + ccs_module_idents[i].mipi_manufacturer_id + != minfo->mipi_manufacturer_id) + continue; + if (ccs_module_idents[i].smia_manufacturer_id && + ccs_module_idents[i].smia_manufacturer_id + != minfo->smia_manufacturer_id) + continue; + if (ccs_module_idents[i].model_id != minfo->model_id) + continue; + if (ccs_module_idents[i].flags + & CCS_MODULE_IDENT_FLAG_REV_LE) { + if (ccs_module_idents[i].revision_number_major + < (minfo->revision_number >> 8)) + continue; + } else { + if (ccs_module_idents[i].revision_number_major + != (minfo->revision_number >> 8)) + continue; + } + + minfo->name = ccs_module_idents[i].name; + minfo->quirk = ccs_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(ccs_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s\n", minfo->name); + + return 0; +} + +static const struct v4l2_subdev_ops ccs_ops; +static const struct v4l2_subdev_internal_ops ccs_internal_ops; +static const struct media_entity_operations ccs_entity_ops; + +static int ccs_register_subdev(struct ccs_sensor *sensor, + struct ccs_subdev *ssd, + struct ccs_subdev *sink_ssd, + u16 source_pad, u16 sink_pad, u32 link_flags) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + if (!sink_ssd) + return 0; + + rval = media_entity_pads_init(&ssd->sd.entity, ssd->npads, ssd->pads); + if (rval) { + dev_err(&client->dev, "media_entity_pads_init failed\n"); + return rval; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, &ssd->sd); + if (rval) { + dev_err(&client->dev, "v4l2_device_register_subdev failed\n"); + return rval; + } + + rval = media_create_pad_link(&ssd->sd.entity, source_pad, + &sink_ssd->sd.entity, sink_pad, + link_flags); + if (rval) { + dev_err(&client->dev, "media_create_pad_link failed\n"); + v4l2_device_unregister_subdev(&ssd->sd); + return rval; + } + + return 0; +} + +static void ccs_unregistered(struct v4l2_subdev *subdev) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + unsigned int i; + + for (i = 1; i < sensor->ssds_used; i++) + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); +} + +static int ccs_registered(struct v4l2_subdev *subdev) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int rval; + + if (sensor->scaler) { + rval = ccs_register_subdev(sensor, sensor->binner, + sensor->scaler, + CCS_PAD_SRC, CCS_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval < 0) + return rval; + } + + rval = ccs_register_subdev(sensor, sensor->pixel_array, sensor->binner, + CCS_PA_PAD_SRC, CCS_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) + goto out_err; + + return 0; + +out_err: + ccs_unregistered(subdev); + + return rval; +} + +static void ccs_cleanup(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + device_remove_file(&client->dev, &dev_attr_nvm); + device_remove_file(&client->dev, &dev_attr_ident); + + ccs_free_controls(sensor); +} + +static void ccs_create_subdev(struct ccs_sensor *sensor, + struct ccs_subdev *ssd, const char *name, + unsigned short num_pads, u32 function) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + if (!ssd) + return; + + if (ssd != sensor->src) + v4l2_subdev_init(&ssd->sd, &ccs_ops); + + ssd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ssd->sd.entity.function = function; + ssd->sensor = sensor; + + ssd->npads = num_pads; + ssd->source_pad = num_pads - 1; + + v4l2_i2c_subdev_set_name(&ssd->sd, client, sensor->minfo.name, name); + + ccs_get_native_size(ssd, &ssd->sink_fmt); + + ssd->compose.width = ssd->sink_fmt.width; + ssd->compose.height = ssd->sink_fmt.height; + ssd->crop[ssd->source_pad] = ssd->compose; + ssd->pads[ssd->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (ssd != sensor->pixel_array) { + ssd->crop[ssd->sink_pad] = ssd->compose; + ssd->pads[ssd->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + ssd->sd.entity.ops = &ccs_entity_ops; + + if (ssd == sensor->src) + return; + + ssd->sd.internal_ops = &ccs_internal_ops; + ssd->sd.owner = THIS_MODULE; + ssd->sd.dev = &client->dev; + v4l2_set_subdevdata(&ssd->sd, client); +} + +static int ccs_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ccs_subdev *ssd = to_ccs_subdev(sd); + struct ccs_sensor *sensor = ssd->sensor; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, i); + struct v4l2_rect *try_crop = + v4l2_subdev_get_try_crop(sd, fh->pad, i); + struct v4l2_rect *try_comp; + + ccs_get_native_size(ssd, try_crop); + + try_fmt->width = try_crop->width; + try_fmt->height = try_crop->height; + try_fmt->code = sensor->internal_csi_format->code; + try_fmt->field = V4L2_FIELD_NONE; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ccs_video_ops = { + .s_stream = ccs_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ccs_pad_ops = { + .enum_mbus_code = ccs_enum_mbus_code, + .get_fmt = ccs_get_format, + .set_fmt = ccs_set_format, + .get_selection = ccs_get_selection, + .set_selection = ccs_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops ccs_sensor_ops = { + .g_skip_frames = ccs_get_skip_frames, + .g_skip_top_lines = ccs_get_skip_top_lines, +}; + +static const struct v4l2_subdev_ops ccs_ops = { + .video = &ccs_video_ops, + .pad = &ccs_pad_ops, + .sensor = &ccs_sensor_ops, +}; + +static const struct media_entity_operations ccs_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { + .registered = ccs_registered, + .unregistered = ccs_unregistered, + .open = ccs_open, +}; + +static const struct v4l2_subdev_internal_ops ccs_internal_ops = { + .open = ccs_open, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +static int __maybe_unused ccs_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + bool streaming = sensor->streaming; + int rval; + + rval = pm_runtime_get_sync(dev); + if (rval < 0) { + pm_runtime_put_noidle(dev); + + return -EAGAIN; + } + + if (sensor->streaming) + ccs_stop_streaming(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int __maybe_unused ccs_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + int rval = 0; + + pm_runtime_put(dev); + + if (sensor->streaming) + rval = ccs_start_streaming(sensor); + + return rval; +} + +static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) +{ + struct ccs_hwconfig *hwcfg = &sensor->hwcfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + u32 rotation; + int i; + int rval; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENODEV; + + /* + * Note that we do need to rely on detecting the bus type between CSI-2 + * D-PHY and CCP2 as the old bindings did not require it. + */ + rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (rval) + goto out_err; + + switch (bus_cfg.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_DPHY; + hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + break; + case V4L2_MBUS_CSI2_CPHY: + hwcfg->csi_signalling_mode = CCS_CSI_SIGNALING_MODE_CSI_2_CPHY; + hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + break; + case V4L2_MBUS_CSI1: + case V4L2_MBUS_CCP2: + hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ? + SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE : + SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK; + hwcfg->lanes = 1; + break; + default: + dev_err(dev, "unsupported bus %u\n", bus_cfg.bus_type); + rval = -EINVAL; + goto out_err; + } + + dev_dbg(dev, "lanes %u\n", hwcfg->lanes); + + rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); + if (!rval) { + switch (rotation) { + case 180: + hwcfg->module_board_orient = + CCS_MODULE_BOARD_ORIENT_180; + fallthrough; + case 0: + break; + default: + dev_err(dev, "invalid rotation %u\n", rotation); + rval = -EINVAL; + goto out_err; + } + } + + rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &hwcfg->ext_clk); + if (rval) + dev_info(dev, "can't get clock-frequency\n"); + + dev_dbg(dev, "clk %d, mode %d\n", hwcfg->ext_clk, + hwcfg->csi_signalling_mode); + + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined\n"); + rval = -EINVAL; + goto out_err; + } + + hwcfg->op_sys_clock = devm_kcalloc( + dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */, + sizeof(*hwcfg->op_sys_clock), GFP_KERNEL); + if (!hwcfg->op_sys_clock) { + rval = -ENOMEM; + goto out_err; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i]; + dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]); + } + + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + + return 0; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + + return rval; +} + +static int ccs_probe(struct i2c_client *client) +{ + struct ccs_sensor *sensor; + const struct firmware *fw; + char filename[40]; + unsigned int i; + int rval; + + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + rval = ccs_get_hwconfig(sensor, &client->dev); + if (rval) + return rval; + + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &ccs_ops); + sensor->src->sd.internal_ops = &ccs_internal_src_ops; + + sensor->regulators = devm_kcalloc(&client->dev, + ARRAY_SIZE(ccs_regulators), + sizeof(*sensor->regulators), + GFP_KERNEL); + if (!sensor->regulators) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ccs_regulators); i++) + sensor->regulators[i].supply = ccs_regulators[i]; + + rval = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(ccs_regulators), + sensor->regulators); + if (rval) { + dev_err(&client->dev, "could not get regulators\n"); + return rval; + } + + sensor->ext_clk = devm_clk_get(&client->dev, NULL); + if (PTR_ERR(sensor->ext_clk) == -ENOENT) { + dev_info(&client->dev, "no clock defined, continuing...\n"); + sensor->ext_clk = NULL; + } else if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock (%ld)\n", + PTR_ERR(sensor->ext_clk)); + return -EPROBE_DEFER; + } + + if (sensor->ext_clk) { + if (sensor->hwcfg.ext_clk) { + unsigned long rate; + + rval = clk_set_rate(sensor->ext_clk, + sensor->hwcfg.ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock freq to %u\n", + sensor->hwcfg.ext_clk); + return rval; + } + + rate = clk_get_rate(sensor->ext_clk); + if (rate != sensor->hwcfg.ext_clk) { + dev_err(&client->dev, + "can't set clock freq, asked for %u but got %lu\n", + sensor->hwcfg.ext_clk, rate); + return -EINVAL; + } + } else { + sensor->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk); + dev_dbg(&client->dev, "obtained clock freq %u\n", + sensor->hwcfg.ext_clk); + } + } else if (sensor->hwcfg.ext_clk) { + dev_dbg(&client->dev, "assuming clock freq %u\n", + sensor->hwcfg.ext_clk); + } else { + dev_err(&client->dev, "unable to obtain clock freq\n"); + return -EINVAL; + } + + sensor->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset)) + return PTR_ERR(sensor->reset); + /* Support old users that may have used "xshutdown" property. */ + if (!sensor->reset) + sensor->xshutdown = devm_gpiod_get_optional(&client->dev, + "xshutdown", + GPIOD_OUT_LOW); + if (IS_ERR(sensor->xshutdown)) + return PTR_ERR(sensor->xshutdown); + + rval = ccs_power_on(&client->dev); + if (rval < 0) + return rval; + + mutex_init(&sensor->mutex); + + rval = ccs_identify_module(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = snprintf(filename, sizeof(filename), + "ccs/ccs-sensor-%4.4x-%4.4x-%4.4x.fw", + sensor->minfo.sensor_mipi_manufacturer_id, + sensor->minfo.sensor_model_id, + sensor->minfo.sensor_revision_number); + if (rval >= sizeof(filename)) { + rval = -ENOMEM; + goto out_power_off; + } + + rval = request_firmware(&fw, filename, &client->dev); + if (!rval) { + ccs_data_parse(&sensor->sdata, fw->data, fw->size, &client->dev, + true); + release_firmware(fw); + } + + rval = snprintf(filename, sizeof(filename), + "ccs/ccs-module-%4.4x-%4.4x-%4.4x.fw", + sensor->minfo.mipi_manufacturer_id, + sensor->minfo.model_id, + sensor->minfo.revision_number); + if (rval >= sizeof(filename)) { + rval = -ENOMEM; + goto out_release_sdata; + } + + rval = request_firmware(&fw, filename, &client->dev); + if (!rval) { + ccs_data_parse(&sensor->mdata, fw->data, fw->size, &client->dev, + true); + release_firmware(fw); + } + + rval = ccs_read_all_limits(sensor); + if (rval) + goto out_release_mdata; + + rval = ccs_read_frame_fmt(sensor); + if (rval) { + rval = -ENODEV; + goto out_free_ccs_limits; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For CCS_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->hwcfg.module_board_orient == + CCS_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = + CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR | + CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; + + rval = ccs_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_free_ccs_limits; + } + + if (CCS_LIM(sensor, BINNING_CAPABILITY)) { + sensor->nbinning_subtypes = + min_t(u8, CCS_LIM(sensor, BINNING_SUB_TYPES), + CCS_LIM_BINNING_SUB_TYPE_MAX_N); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + sensor->binning_subtypes[i].horizontal = + CCS_LIM_AT(sensor, BINNING_SUB_TYPE, i) >> + CCS_BINNING_SUB_TYPE_COLUMN_SHIFT; + sensor->binning_subtypes[i].vertical = + CCS_LIM_AT(sensor, BINNING_SUB_TYPE, i) & + CCS_BINNING_SUB_TYPE_ROW_MASK; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + if (device_create_file(&client->dev, &dev_attr_ident) != 0) { + dev_err(&client->dev, "sysfs ident entry creation failed\n"); + rval = -ENOENT; + goto out_free_ccs_limits; + } + + if (sensor->minfo.smiapp_version && + CCS_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & + CCS_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) { + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_cleanup; + } + } + + if (!CCS_LIM(sensor, MIN_OP_SYS_CLK_DIV) || + !CCS_LIM(sensor, MAX_OP_SYS_CLK_DIV) || + !CCS_LIM(sensor, MIN_OP_PIX_CLK_DIV) || + !CCS_LIM(sensor, MAX_OP_PIX_CLK_DIV)) { + /* No OP clock branch */ + sensor->pll.flags |= CCS_PLL_FLAG_NO_OP_CLOCKS; + } else if (CCS_LIM(sensor, SCALING_CAPABILITY) + != CCS_SCALING_CAPABILITY_NONE || + CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) + == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + /* We have a scaler or digital crop. */ + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = CCS_LIM(sensor, SCALER_N_MIN); + + /* prepare PLL configuration input values */ + sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; + sensor->pll.csi2.lanes = sensor->hwcfg.lanes; + if (CCS_LIM(sensor, CLOCK_CALCULATION) & + CCS_CLOCK_CALCULATION_LANE_SPEED) { + sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL; + if (CCS_LIM(sensor, CLOCK_CALCULATION) & + CCS_CLOCK_CALCULATION_LINK_DECOUPLED) { + sensor->pll.vt_lanes = + CCS_LIM(sensor, NUM_OF_VT_LANES) + 1; + sensor->pll.op_lanes = + CCS_LIM(sensor, NUM_OF_OP_LANES) + 1; + sensor->pll.flags |= CCS_PLL_FLAG_LINK_DECOUPLED; + } else { + sensor->pll.vt_lanes = sensor->pll.csi2.lanes; + sensor->pll.op_lanes = sensor->pll.csi2.lanes; + } + } + if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) & + CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER) + sensor->pll.flags |= CCS_PLL_FLAG_EXT_IP_PLL_DIVIDER; + if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) & + CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV) + sensor->pll.flags |= CCS_PLL_FLAG_FLEXIBLE_OP_PIX_CLK_DIV; + if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) & + CCS_FIFO_SUPPORT_CAPABILITY_DERATING) + sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING; + if (CCS_LIM(sensor, FIFO_SUPPORT_CAPABILITY) & + CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING) + sensor->pll.flags |= CCS_PLL_FLAG_FIFO_DERATING | + CCS_PLL_FLAG_FIFO_OVERRATING; + if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) & + CCS_CLOCK_TREE_PLL_CAPABILITY_DUAL_PLL) { + if (CCS_LIM(sensor, CLOCK_TREE_PLL_CAPABILITY) & + CCS_CLOCK_TREE_PLL_CAPABILITY_SINGLE_PLL) { + u32 v; + + /* Use sensor default in PLL mode selection */ + rval = ccs_read(sensor, PLL_MODE, &v); + if (rval) + goto out_cleanup; + + if (v == CCS_PLL_MODE_DUAL) + sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL; + } else { + sensor->pll.flags |= CCS_PLL_FLAG_DUAL_PLL; + } + if (CCS_LIM(sensor, CLOCK_CALCULATION) & + CCS_CLOCK_CALCULATION_DUAL_PLL_OP_SYS_DDR) + sensor->pll.flags |= CCS_PLL_FLAG_OP_SYS_DDR; + if (CCS_LIM(sensor, CLOCK_CALCULATION) & + CCS_CLOCK_CALCULATION_DUAL_PLL_OP_PIX_DDR) + sensor->pll.flags |= CCS_PLL_FLAG_OP_PIX_DDR; + } + sensor->pll.op_bits_per_lane = CCS_LIM(sensor, OP_BITS_PER_LANE); + sensor->pll.ext_clk_freq_hz = sensor->hwcfg.ext_clk; + sensor->pll.scale_n = CCS_LIM(sensor, SCALER_N_MIN); + + ccs_create_subdev(sensor, sensor->scaler, " scaler", 2, + MEDIA_ENT_F_CAM_SENSOR); + ccs_create_subdev(sensor, sensor->binner, " binner", 2, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + ccs_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1, + MEDIA_ENT_F_PROC_VIDEO_SCALER); + + rval = ccs_init_controls(sensor); + if (rval < 0) + goto out_cleanup; + + rval = ccs_call_quirk(sensor, init); + if (rval) + goto out_cleanup; + + rval = ccs_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + + rval = ccs_init_late_controls(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + + mutex_lock(&sensor->mutex); + rval = ccs_pll_blanking_update(sensor); + mutex_unlock(&sensor->mutex); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_cleanup; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + rval = media_entity_pads_init(&sensor->src->sd.entity, 2, + sensor->src->pads); + if (rval < 0) + goto out_media_entity_cleanup; + + rval = ccs_write_msr_regs(sensor); + if (rval) + goto out_media_entity_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); + + rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); + if (rval < 0) + goto out_disable_runtime_pm; + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return 0; + +out_disable_runtime_pm: + pm_runtime_put_noidle(&client->dev); + pm_runtime_disable(&client->dev); + +out_media_entity_cleanup: + media_entity_cleanup(&sensor->src->sd.entity); + +out_cleanup: + ccs_cleanup(sensor); + +out_release_mdata: + kvfree(sensor->mdata.backing); + +out_release_sdata: + kvfree(sensor->sdata.backing); + +out_free_ccs_limits: + kfree(sensor->ccs_limits); + +out_power_off: + ccs_power_off(&client->dev); + mutex_destroy(&sensor->mutex); + + return rval; +} + +static int ccs_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + unsigned int i; + + v4l2_async_unregister_subdev(subdev); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ccs_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + for (i = 0; i < sensor->ssds_used; i++) { + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + media_entity_cleanup(&sensor->ssds[i].sd.entity); + } + ccs_cleanup(sensor); + mutex_destroy(&sensor->mutex); + kfree(sensor->ccs_limits); + kvfree(sensor->sdata.backing); + kvfree(sensor->mdata.backing); + + return 0; +} + +static const struct ccs_device smia_device = { + .flags = CCS_DEVICE_FLAG_IS_SMIA, +}; + +static const struct ccs_device ccs_device = {}; + +static const struct acpi_device_id ccs_acpi_table[] = { + { .id = "MIPI0200", .driver_data = (unsigned long)&ccs_device }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, ccs_acpi_table); + +static const struct of_device_id ccs_of_table[] = { + { .compatible = "mipi-ccs-1.1", .data = &ccs_device }, + { .compatible = "mipi-ccs-1.0", .data = &ccs_device }, + { .compatible = "mipi-ccs", .data = &ccs_device }, + { .compatible = "nokia,smia", .data = &smia_device }, + { }, +}; +MODULE_DEVICE_TABLE(of, ccs_of_table); + +static const struct dev_pm_ops ccs_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ccs_suspend, ccs_resume) + SET_RUNTIME_PM_OPS(ccs_power_off, ccs_power_on, NULL) +}; + +static struct i2c_driver ccs_i2c_driver = { + .driver = { + .acpi_match_table = ccs_acpi_table, + .of_match_table = ccs_of_table, + .name = CCS_NAME, + .pm = &ccs_pm_ops, + }, + .probe_new = ccs_probe, + .remove = ccs_remove, +}; + +static int ccs_module_init(void) +{ + unsigned int i, l; + + for (i = 0, l = 0; ccs_limits[i].size && l < CCS_L_LAST; i++) { + if (!(ccs_limits[i].flags & CCS_L_FL_SAME_REG)) { + ccs_limit_offsets[l + 1].lim = + ALIGN(ccs_limit_offsets[l].lim + + ccs_limits[i].size, + ccs_reg_width(ccs_limits[i + 1].reg)); + ccs_limit_offsets[l].info = i; + l++; + } else { + ccs_limit_offsets[l].lim += ccs_limits[i].size; + } + } + + if (WARN_ON(ccs_limits[i].size)) + return -EINVAL; + + if (WARN_ON(l != CCS_L_LAST)) + return -EINVAL; + + return i2c_register_driver(THIS_MODULE, &ccs_i2c_driver); +} + +static void ccs_module_cleanup(void) +{ + i2c_del_driver(&ccs_i2c_driver); +} + +module_init(ccs_module_init); +module_exit(ccs_module_cleanup); + +MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); +MODULE_DESCRIPTION("Generic MIPI CCS/SMIA/SMIA++ camera sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("smiapp"); diff --git a/drivers/media/i2c/ccs/ccs-data-defs.h b/drivers/media/i2c/ccs/ccs-data-defs.h new file mode 100644 index 000000000000..1c9b1d1acd50 --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-data-defs.h @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * CCS static data binary format definitions + * + * Copyright 2019--2020 Intel Corporation + */ + +#ifndef __CCS_DATA_DEFS_H__ +#define __CCS_DATA_DEFS_H__ + +#include "ccs-data.h" + +#define CCS_STATIC_DATA_VERSION 0 + +enum __ccs_data_length_specifier_id { + CCS_DATA_LENGTH_SPECIFIER_1 = 0, + CCS_DATA_LENGTH_SPECIFIER_2 = 1, + CCS_DATA_LENGTH_SPECIFIER_3 = 2 +}; + +#define CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT 6 + +struct __ccs_data_length_specifier { + u8 length; +} __packed; + +struct __ccs_data_length_specifier2 { + u8 length[2]; +} __packed; + +struct __ccs_data_length_specifier3 { + u8 length[3]; +} __packed; + +struct __ccs_data_block { + u8 id; + struct __ccs_data_length_specifier length; +} __packed; + +#define CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT 5 + +struct __ccs_data_block3 { + u8 id; + struct __ccs_data_length_specifier2 length; +} __packed; + +struct __ccs_data_block4 { + u8 id; + struct __ccs_data_length_specifier3 length; +} __packed; + +enum __ccs_data_block_id { + CCS_DATA_BLOCK_ID_DUMMY = 1, + CCS_DATA_BLOCK_ID_DATA_VERSION = 2, + CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS = 3, + CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS = 4, + CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS = 5, + CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS = 6, + CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK = 32, + CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK = 33, + CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION = 36, + CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION = 37, + CCS_DATA_BLOCK_ID_LICENSE = 40, + CCS_DATA_BLOCK_ID_END = 127, +}; + +struct __ccs_data_block_version { + u8 static_data_version_major[2]; + u8 static_data_version_minor[2]; + u8 year[2]; + u8 month; + u8 day; +} __packed; + +struct __ccs_data_block_regs { + u8 reg_len; +} __packed; + +#define CCS_DATA_BLOCK_REGS_ADDR_MASK 0x07 +#define CCS_DATA_BLOCK_REGS_LEN_SHIFT 3 +#define CCS_DATA_BLOCK_REGS_LEN_MASK 0x38 +#define CCS_DATA_BLOCK_REGS_SEL_SHIFT 6 + +enum ccs_data_block_regs_sel { + CCS_DATA_BLOCK_REGS_SEL_REGS = 0, + CCS_DATA_BLOCK_REGS_SEL_REGS2 = 1, + CCS_DATA_BLOCK_REGS_SEL_REGS3 = 2, +}; + +struct __ccs_data_block_regs2 { + u8 reg_len; + u8 addr; +} __packed; + +#define CCS_DATA_BLOCK_REGS_2_ADDR_MASK 0x01 +#define CCS_DATA_BLOCK_REGS_2_LEN_SHIFT 1 +#define CCS_DATA_BLOCK_REGS_2_LEN_MASK 0x3e + +struct __ccs_data_block_regs3 { + u8 reg_len; + u8 addr[2]; +} __packed; + +#define CCS_DATA_BLOCK_REGS_3_LEN_MASK 0x3f + +enum __ccs_data_ffd_pixelcode { + CCS_DATA_BLOCK_FFD_PIXELCODE_EMBEDDED = 1, + CCS_DATA_BLOCK_FFD_PIXELCODE_DUMMY = 2, + CCS_DATA_BLOCK_FFD_PIXELCODE_BLACK = 3, + CCS_DATA_BLOCK_FFD_PIXELCODE_DARK = 4, + CCS_DATA_BLOCK_FFD_PIXELCODE_VISIBLE = 5, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_0 = 8, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_1 = 9, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_2 = 10, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_3 = 11, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_4 = 12, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_5 = 13, + CCS_DATA_BLOCK_FFD_PIXELCODE_MS_6 = 14, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_OB = 16, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_OB = 17, + CCS_DATA_BLOCK_FFD_PIXELCODE_LEFT_OB = 18, + CCS_DATA_BLOCK_FFD_PIXELCODE_RIGHT_OB = 19, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_LEFT_OB = 20, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_RIGHT_OB = 21, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_LEFT_OB = 22, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_RIGHT_OB = 23, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOTAL = 24, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_PDAF = 32, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_PDAF = 33, + CCS_DATA_BLOCK_FFD_PIXELCODE_LEFT_PDAF = 34, + CCS_DATA_BLOCK_FFD_PIXELCODE_RIGHT_PDAF = 35, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_LEFT_PDAF = 36, + CCS_DATA_BLOCK_FFD_PIXELCODE_TOP_RIGHT_PDAF = 37, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_LEFT_PDAF = 38, + CCS_DATA_BLOCK_FFD_PIXELCODE_BOTTOM_RIGHT_PDAF = 39, + CCS_DATA_BLOCK_FFD_PIXELCODE_SEPARATED_PDAF = 40, + CCS_DATA_BLOCK_FFD_PIXELCODE_ORIGINAL_ORDER_PDAF = 41, + CCS_DATA_BLOCK_FFD_PIXELCODE_VENDOR_PDAF = 41, +}; + +struct __ccs_data_block_ffd_entry { + u8 pixelcode; + u8 reserved; + u8 value[2]; +} __packed; + +struct __ccs_data_block_ffd { + u8 num_column_descs; + u8 num_row_descs; +} __packed; + +enum __ccs_data_block_rule_id { + CCS_DATA_BLOCK_RULE_ID_IF = 1, + CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS = 2, + CCS_DATA_BLOCK_RULE_ID_FFD = 3, + CCS_DATA_BLOCK_RULE_ID_MSR = 4, + CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT = 5, +}; + +struct __ccs_data_block_rule_if { + u8 addr[2]; + u8 value; + u8 mask; +} __packed; + +enum __ccs_data_block_pdaf_readout_order { + CCS_DATA_BLOCK_PDAF_READOUT_ORDER_ORIGINAL = 1, + CCS_DATA_BLOCK_PDAF_READOUT_ORDER_SEPARATE_WITHIN_LINE = 2, + CCS_DATA_BLOCK_PDAF_READOUT_ORDER_SEPARATE_TYPES_SEPARATE_LINES = 3, +}; + +struct __ccs_data_block_pdaf_readout { + u8 pdaf_readout_info_reserved; + u8 pdaf_readout_info_order; +} __packed; + +struct __ccs_data_block_pdaf_pix_loc_block_desc { + u8 block_type_id; + u8 repeat_x[2]; +} __packed; + +struct __ccs_data_block_pdaf_pix_loc_block_desc_group { + u8 num_block_descs[2]; + u8 repeat_y; +} __packed; + +enum __ccs_data_block_pdaf_pix_loc_pixel_type { + CCS_DATA_PDAF_PIXEL_TYPE_LEFT_SEPARATED = 0, + CCS_DATA_PDAF_PIXEL_TYPE_RIGHT_SEPARATED = 1, + CCS_DATA_PDAF_PIXEL_TYPE_TOP_SEPARATED = 2, + CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_SEPARATED = 3, + CCS_DATA_PDAF_PIXEL_TYPE_LEFT_SIDE_BY_SIDE = 4, + CCS_DATA_PDAF_PIXEL_TYPE_RIGHT_SIDE_BY_SIDE = 5, + CCS_DATA_PDAF_PIXEL_TYPE_TOP_SIDE_BY_SIDE = 6, + CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_SIDE_BY_SIDE = 7, + CCS_DATA_PDAF_PIXEL_TYPE_TOP_LEFT = 8, + CCS_DATA_PDAF_PIXEL_TYPE_TOP_RIGHT = 9, + CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_LEFT = 10, + CCS_DATA_PDAF_PIXEL_TYPE_BOTTOM_RIGHT = 11, +}; + +struct __ccs_data_block_pdaf_pix_loc_pixel_desc { + u8 pixel_type; + u8 small_offset_x; + u8 small_offset_y; +} __packed; + +struct __ccs_data_block_pdaf_pix_loc { + u8 main_offset_x[2]; + u8 main_offset_y[2]; + u8 global_pdaf_type; + u8 block_width; + u8 block_height; + u8 num_block_desc_groups[2]; +} __packed; + +struct __ccs_data_block_end { + u8 crc[4]; +} __packed; + +#endif /* __CCS_DATA_DEFS_H__ */ diff --git a/drivers/media/i2c/ccs/ccs-data.c b/drivers/media/i2c/ccs/ccs-data.c new file mode 100644 index 000000000000..9a6097b088bd --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-data.c @@ -0,0 +1,953 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * CCS static data binary parser library + * + * Copyright 2019--2020 Intel Corporation + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/limits.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "ccs-data-defs.h" + +struct bin_container { + void *base; + void *now; + void *end; + size_t size; +}; + +static void *bin_alloc(struct bin_container *bin, size_t len) +{ + void *ptr; + + len = ALIGN(len, 8); + + if (bin->end - bin->now < len) + return NULL; + + ptr = bin->now; + bin->now += len; + + return ptr; +} + +static void bin_reserve(struct bin_container *bin, size_t len) +{ + bin->size += ALIGN(len, 8); +} + +static int bin_backing_alloc(struct bin_container *bin) +{ + bin->base = bin->now = kvzalloc(bin->size, GFP_KERNEL); + if (!bin->base) + return -ENOMEM; + + bin->end = bin->base + bin->size; + + return 0; +} + +#define is_contained(var, endp) \ + (sizeof(*var) <= (endp) - (void *)(var)) +#define has_headroom(ptr, headroom, endp) \ + ((headroom) <= (endp) - (void *)(ptr)) +#define is_contained_with_headroom(var, headroom, endp) \ + (sizeof(*var) + (headroom) <= (endp) - (void *)(var)) + +static int +ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len, + size_t *__hlen, size_t *__plen, + const void *endp) +{ + size_t hlen, plen; + + if (!is_contained(__len, endp)) + return -ENODATA; + + switch (__len->length >> CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) { + case CCS_DATA_LENGTH_SPECIFIER_1: + hlen = sizeof(*__len); + plen = __len->length & + ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1); + break; + case CCS_DATA_LENGTH_SPECIFIER_2: { + struct __ccs_data_length_specifier2 *__len2 = (void *)__len; + + if (!is_contained(__len2, endp)) + return -ENODATA; + + hlen = sizeof(*__len2); + plen = ((size_t) + (__len2->length[0] & + ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) + << 8) + __len2->length[1]; + break; + } + case CCS_DATA_LENGTH_SPECIFIER_3: { + struct __ccs_data_length_specifier3 *__len3 = (void *)__len; + + if (!is_contained(__len3, endp)) + return -ENODATA; + + hlen = sizeof(*__len3); + plen = ((size_t) + (__len3->length[0] & + ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) + << 16) + (__len3->length[0] << 8) + __len3->length[1]; + break; + } + default: + return -EINVAL; + } + + if (!has_headroom(__len, hlen + plen, endp)) + return -ENODATA; + + *__hlen = hlen; + *__plen = plen; + + return 0; +} + +static u8 +ccs_data_parse_format_version(const struct __ccs_data_block *block) +{ + return block->id >> CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT; +} + +static u8 ccs_data_parse_block_id(const struct __ccs_data_block *block, + bool is_first) +{ + if (!is_first) + return block->id; + + return block->id & ((1 << CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT) - 1); +} + +static int ccs_data_parse_version(struct bin_container *bin, + struct ccs_data_container *ccsdata, + const void *payload, const void *endp) +{ + const struct __ccs_data_block_version *v = payload; + struct ccs_data_block_version *vv; + + if (v + 1 != endp) + return -ENODATA; + + if (!bin->base) { + bin_reserve(bin, sizeof(*ccsdata->version)); + return 0; + } + + ccsdata->version = bin_alloc(bin, sizeof(*ccsdata->version)); + if (!ccsdata->version) + return -ENOMEM; + + vv = ccsdata->version; + vv->version_major = ((u16)v->static_data_version_major[0] << 8) + + v->static_data_version_major[1]; + vv->version_minor = ((u16)v->static_data_version_minor[0] << 8) + + v->static_data_version_major[1]; + vv->date_year = ((u16)v->year[0] << 8) + v->year[1]; + vv->date_month = v->month; + vv->date_day = v->day; + + return 0; +} + +static void print_ccs_data_version(struct device *dev, + struct ccs_data_block_version *v) +{ + dev_dbg(dev, + "static data version %4.4x.%4.4x, date %4.4u-%2.2u-%2.2u\n", + v->version_major, v->version_minor, + v->date_year, v->date_month, v->date_day); +} + +static int ccs_data_block_parse_header(const struct __ccs_data_block *block, + bool is_first, unsigned int *__block_id, + const void **payload, + const struct __ccs_data_block **next_block, + const void *endp, struct device *dev, + bool verbose) +{ + size_t plen, hlen; + u8 block_id; + int rval; + + if (!is_contained(block, endp)) + return -ENODATA; + + rval = ccs_data_parse_length_specifier(&block->length, &hlen, &plen, + endp); + if (rval < 0) + return rval; + + block_id = ccs_data_parse_block_id(block, is_first); + + if (verbose) + dev_dbg(dev, + "Block ID 0x%2.2x, header length %zu, payload length %zu\n", + block_id, hlen, plen); + + if (!has_headroom(&block->length, hlen + plen, endp)) + return -ENODATA; + + if (__block_id) + *__block_id = block_id; + + if (payload) + *payload = (void *)&block->length + hlen; + + if (next_block) + *next_block = (void *)&block->length + hlen + plen; + + return 0; +} + +static int ccs_data_parse_regs(struct bin_container *bin, + struct ccs_reg **__regs, + size_t *__num_regs, const void *payload, + const void *endp, struct device *dev) +{ + struct ccs_reg *regs_base, *regs; + size_t num_regs = 0; + u16 addr = 0; + + if (bin->base && __regs) { + regs = regs_base = bin_alloc(bin, sizeof(*regs) * *__num_regs); + if (!regs) + return -ENOMEM; + } + + while (payload < endp && num_regs < INT_MAX) { + const struct __ccs_data_block_regs *r = payload; + size_t len; + const void *data; + + if (!is_contained(r, endp)) + return -ENODATA; + + switch (r->reg_len >> CCS_DATA_BLOCK_REGS_SEL_SHIFT) { + case CCS_DATA_BLOCK_REGS_SEL_REGS: + addr += r->reg_len & CCS_DATA_BLOCK_REGS_ADDR_MASK; + len = ((r->reg_len & CCS_DATA_BLOCK_REGS_LEN_MASK) + >> CCS_DATA_BLOCK_REGS_LEN_SHIFT) + 1; + + if (!is_contained_with_headroom(r, len, endp)) + return -ENODATA; + + data = r + 1; + break; + case CCS_DATA_BLOCK_REGS_SEL_REGS2: { + const struct __ccs_data_block_regs2 *r2 = payload; + + if (!is_contained(r2, endp)) + return -ENODATA; + + addr += ((u16)(r2->reg_len & + CCS_DATA_BLOCK_REGS_2_ADDR_MASK) << 8) + + r2->addr; + len = ((r2->reg_len & CCS_DATA_BLOCK_REGS_2_LEN_MASK) + >> CCS_DATA_BLOCK_REGS_2_LEN_SHIFT) + 1; + + if (!is_contained_with_headroom(r2, len, endp)) + return -ENODATA; + + data = r2 + 1; + break; + } + case CCS_DATA_BLOCK_REGS_SEL_REGS3: { + const struct __ccs_data_block_regs3 *r3 = payload; + + if (!is_contained(r3, endp)) + return -ENODATA; + + addr = ((u16)r3->addr[0] << 8) + r3->addr[1]; + len = (r3->reg_len & CCS_DATA_BLOCK_REGS_3_LEN_MASK) + 1; + + if (!is_contained_with_headroom(r3, len, endp)) + return -ENODATA; + + data = r3 + 1; + break; + } + default: + return -EINVAL; + } + + num_regs++; + + if (!bin->base) { + bin_reserve(bin, len); + } else if (__regs) { + regs->addr = addr; + regs->len = len; + regs->value = bin_alloc(bin, len); + if (!regs->value) + return -ENOMEM; + + memcpy(regs->value, data, len); + regs++; + } + + addr += len; + payload = data + len; + } + + if (!bin->base) + bin_reserve(bin, sizeof(*regs) * num_regs); + + if (__num_regs) + *__num_regs = num_regs; + + if (bin->base && __regs) + *__regs = regs_base; + + return 0; +} + +static int ccs_data_parse_reg_rules(struct bin_container *bin, + struct ccs_reg **__regs, + size_t *__num_regs, + const void *payload, + const void *endp, struct device *dev) +{ + int rval; + + if (!bin->base) + return ccs_data_parse_regs(bin, NULL, NULL, payload, endp, dev); + + rval = ccs_data_parse_regs(bin, NULL, __num_regs, payload, endp, dev); + if (rval) + return rval; + + return ccs_data_parse_regs(bin, __regs, __num_regs, payload, endp, + dev); +} + +static void assign_ffd_entry(struct ccs_frame_format_desc *desc, + const struct __ccs_data_block_ffd_entry *ent) +{ + desc->pixelcode = ent->pixelcode; + desc->value = ((u16)ent->value[0] << 8) + ent->value[1]; +} + +static int ccs_data_parse_ffd(struct bin_container *bin, + struct ccs_frame_format_descs **ffd, + const void *payload, + const void *endp, struct device *dev) +{ + const struct __ccs_data_block_ffd *__ffd = payload; + const struct __ccs_data_block_ffd_entry *__entry; + unsigned int i; + + if (!is_contained(__ffd, endp)) + return -ENODATA; + + if ((void *)__ffd + sizeof(*__ffd) + + ((u32)__ffd->num_column_descs + + (u32)__ffd->num_row_descs) * + sizeof(struct __ccs_data_block_ffd_entry) != endp) + return -ENODATA; + + if (!bin->base) { + bin_reserve(bin, sizeof(**ffd)); + bin_reserve(bin, __ffd->num_column_descs * + sizeof(struct ccs_frame_format_desc)); + bin_reserve(bin, __ffd->num_row_descs * + sizeof(struct ccs_frame_format_desc)); + + return 0; + } + + *ffd = bin_alloc(bin, sizeof(**ffd)); + if (!*ffd) + return -ENOMEM; + + (*ffd)->num_column_descs = __ffd->num_column_descs; + (*ffd)->num_row_descs = __ffd->num_row_descs; + __entry = (void *)(__ffd + 1); + + (*ffd)->column_descs = bin_alloc(bin, __ffd->num_column_descs * + sizeof(*(*ffd)->column_descs)); + if (!(*ffd)->column_descs) + return -ENOMEM; + + for (i = 0; i < __ffd->num_column_descs; i++, __entry++) + assign_ffd_entry(&(*ffd)->column_descs[i], __entry); + + (*ffd)->row_descs = bin_alloc(bin, __ffd->num_row_descs * + sizeof(*(*ffd)->row_descs)); + if (!(*ffd)->row_descs) + return -ENOMEM; + + for (i = 0; i < __ffd->num_row_descs; i++, __entry++) + assign_ffd_entry(&(*ffd)->row_descs[i], __entry); + + if (__entry != endp) + return -EPROTO; + + return 0; +} + +static int ccs_data_parse_pdaf_readout(struct bin_container *bin, + struct ccs_pdaf_readout **pdaf_readout, + const void *payload, + const void *endp, struct device *dev) +{ + const struct __ccs_data_block_pdaf_readout *__pdaf = payload; + + if (!is_contained(__pdaf, endp)) + return -ENODATA; + + if (!bin->base) { + bin_reserve(bin, sizeof(**pdaf_readout)); + } else { + *pdaf_readout = bin_alloc(bin, sizeof(**pdaf_readout)); + if (!*pdaf_readout) + return -ENOMEM; + + (*pdaf_readout)->pdaf_readout_info_order = + __pdaf->pdaf_readout_info_order; + } + + return ccs_data_parse_ffd(bin, !bin->base ? NULL : &(*pdaf_readout)->ffd, + __pdaf + 1, endp, dev); +} + +static int ccs_data_parse_rules(struct bin_container *bin, + struct ccs_rule **__rules, + size_t *__num_rules, const void *payload, + const void *endp, struct device *dev) +{ + struct ccs_rule *rules_base, *rules = NULL, *next_rule; + size_t num_rules = 0; + const void *__next_rule = payload; + int rval; + + if (bin->base) { + rules_base = next_rule = + bin_alloc(bin, sizeof(*rules) * *__num_rules); + if (!rules_base) + return -ENOMEM; + } + + while (__next_rule < endp) { + size_t rule_hlen, rule_plen, rule_plen2; + const u8 *__rule_type; + const void *rule_payload; + + /* Size of a single rule */ + rval = ccs_data_parse_length_specifier(__next_rule, &rule_hlen, + &rule_plen, endp); + + if (rval < 0) + return rval; + + __rule_type = __next_rule + rule_hlen; + + if (!is_contained(__rule_type, endp)) + return -ENODATA; + + rule_payload = __rule_type + 1; + rule_plen2 = rule_plen - sizeof(*__rule_type); + + switch (*__rule_type) { + case CCS_DATA_BLOCK_RULE_ID_IF: { + const struct __ccs_data_block_rule_if *__if_rules = + rule_payload; + const size_t __num_if_rules = + rule_plen2 / sizeof(*__if_rules); + struct ccs_if_rule *if_rule; + + if (!has_headroom(__if_rules, + sizeof(*__if_rules) * __num_if_rules, + rule_payload + rule_plen2)) + return -ENODATA; + + /* Also check there is no extra data */ + if (__if_rules + __num_if_rules != + rule_payload + rule_plen2) + return -EINVAL; + + if (!bin->base) { + bin_reserve(bin, + sizeof(*if_rule) * + __num_if_rules); + num_rules++; + } else { + unsigned int i; + + rules = next_rule; + next_rule++; + + if_rule = bin_alloc(bin, + sizeof(*if_rule) * + __num_if_rules); + if (!if_rule) + return -ENOMEM; + + for (i = 0; i < __num_if_rules; i++) { + if_rule[i].addr = + ((u16)__if_rules[i].addr[0] + << 8) + + __if_rules[i].addr[1]; + if_rule[i].value = __if_rules[i].value; + if_rule[i].mask = __if_rules[i].mask; + } + + rules->if_rules = if_rule; + rules->num_if_rules = __num_if_rules; + } + break; + } + case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: + rval = ccs_data_parse_reg_rules(bin, &rules->read_only_regs, + &rules->num_read_only_regs, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_FFD: + rval = ccs_data_parse_ffd(bin, &rules->frame_format, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_MSR: + rval = ccs_data_parse_reg_rules(bin, + &rules->manufacturer_regs, + &rules->num_manufacturer_regs, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: + rval = ccs_data_parse_pdaf_readout(bin, + &rules->pdaf_readout, + rule_payload, + rule_payload + rule_plen2, + dev); + if (rval) + return rval; + break; + default: + dev_dbg(dev, + "Don't know how to handle rule type %u!\n", + *__rule_type); + return -EINVAL; + } + __next_rule = __next_rule + rule_hlen + rule_plen; + } + + if (!bin->base) { + bin_reserve(bin, sizeof(*rules) * num_rules); + *__num_rules = num_rules; + } else { + *__rules = rules_base; + } + + return 0; +} + +static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_loc **pdaf, + const void *payload, const void *endp, + struct device *dev) +{ + const struct __ccs_data_block_pdaf_pix_loc *__pdaf = payload; + const struct __ccs_data_block_pdaf_pix_loc_block_desc_group *__bdesc_group; + const struct __ccs_data_block_pdaf_pix_loc_pixel_desc *__pixel_desc; + unsigned int i; + u16 num_block_desc_groups; + u8 max_block_type_id = 0; + const u8 *__num_pixel_descs; + + if (!is_contained(__pdaf, endp)) + return -ENODATA; + + if (bin->base) { + *pdaf = bin_alloc(bin, sizeof(**pdaf)); + if (!*pdaf) + return -ENOMEM; + } else { + bin_reserve(bin, sizeof(**pdaf)); + } + + num_block_desc_groups = + ((u16)__pdaf->num_block_desc_groups[0] << 8) + + __pdaf->num_block_desc_groups[1]; + + if (bin->base) { + (*pdaf)->main_offset_x = + ((u16)__pdaf->main_offset_x[0] << 8) + + __pdaf->main_offset_x[1]; + (*pdaf)->main_offset_y = + ((u16)__pdaf->main_offset_y[0] << 8) + + __pdaf->main_offset_y[1]; + (*pdaf)->global_pdaf_type = __pdaf->global_pdaf_type; + (*pdaf)->block_width = __pdaf->block_width; + (*pdaf)->block_height = __pdaf->block_height; + (*pdaf)->num_block_desc_groups = num_block_desc_groups; + } + + __bdesc_group = (const void *)(__pdaf + 1); + + if (bin->base) { + (*pdaf)->block_desc_groups = + bin_alloc(bin, + sizeof(struct ccs_pdaf_pix_loc_block_desc_group) * + num_block_desc_groups); + if (!(*pdaf)->block_desc_groups) + return -ENOMEM; + } else { + bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc_group) * + num_block_desc_groups); + } + + for (i = 0; i < num_block_desc_groups; i++) { + const struct __ccs_data_block_pdaf_pix_loc_block_desc *__bdesc; + u16 num_block_descs; + unsigned int j; + + if (!is_contained(__bdesc_group, endp)) + return -ENODATA; + + num_block_descs = + ((u16)__bdesc_group->num_block_descs[0] << 8) + + __bdesc_group->num_block_descs[1]; + + if (bin->base) { + (*pdaf)->block_desc_groups[i].repeat_y = + __bdesc_group->repeat_y; + (*pdaf)->block_desc_groups[i].num_block_descs = + num_block_descs; + } + + __bdesc = (const void *)(__bdesc_group + 1); + + if (bin->base) { + (*pdaf)->block_desc_groups[i].block_descs = + bin_alloc(bin, + sizeof(struct ccs_pdaf_pix_loc_block_desc) * + num_block_descs); + if (!(*pdaf)->block_desc_groups[i].block_descs) + return -ENOMEM; + } else { + bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_block_desc) * + num_block_descs); + } + + for (j = 0; j < num_block_descs; j++, __bdesc++) { + struct ccs_pdaf_pix_loc_block_desc *bdesc; + + if (!is_contained(__bdesc, endp)) + return -ENODATA; + + if (max_block_type_id <= __bdesc->block_type_id) + max_block_type_id = __bdesc->block_type_id + 1; + + if (!bin->base) + continue; + + bdesc = &(*pdaf)->block_desc_groups[i].block_descs[j]; + + bdesc->repeat_x = ((u16)__bdesc->repeat_x[0] << 8) + + __bdesc->repeat_x[1]; + + if (__bdesc->block_type_id >= num_block_descs) + return -EINVAL; + + bdesc->block_type_id = __bdesc->block_type_id; + } + + __bdesc_group = (const void *)__bdesc; + } + + __num_pixel_descs = (const void *)__bdesc_group; + + if (bin->base) { + (*pdaf)->pixel_desc_groups = + bin_alloc(bin, + sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) * + max_block_type_id); + if (!(*pdaf)->pixel_desc_groups) + return -ENOMEM; + (*pdaf)->num_pixel_desc_grups = max_block_type_id; + } else { + bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) * + max_block_type_id); + } + + for (i = 0; i < max_block_type_id; i++) { + struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup; + unsigned int j; + + if (!is_contained(__num_pixel_descs, endp)) + return -ENODATA; + + if (bin->base) { + pdgroup = &(*pdaf)->pixel_desc_groups[i]; + pdgroup->descs = + bin_alloc(bin, + sizeof(struct ccs_pdaf_pix_loc_pixel_desc) * + *__num_pixel_descs); + if (!pdgroup->descs) + return -ENOMEM; + pdgroup->num_descs = *__num_pixel_descs; + } else { + bin_reserve(bin, sizeof(struct ccs_pdaf_pix_loc_pixel_desc) * + *__num_pixel_descs); + } + + __pixel_desc = (const void *)(__num_pixel_descs + 1); + + for (j = 0; j < *__num_pixel_descs; j++, __pixel_desc++) { + struct ccs_pdaf_pix_loc_pixel_desc *pdesc; + + if (!is_contained(__pixel_desc, endp)) + return -ENODATA; + + if (!bin->base) + continue; + + pdesc = &pdgroup->descs[j]; + pdesc->pixel_type = __pixel_desc->pixel_type; + pdesc->small_offset_x = __pixel_desc->small_offset_x; + pdesc->small_offset_y = __pixel_desc->small_offset_y; + } + + __num_pixel_descs = (const void *)(__pixel_desc + 1); + } + + return 0; +} + +static int ccs_data_parse_license(struct bin_container *bin, + char **__license, + size_t *__license_length, + const void *payload, const void *endp) +{ + size_t size = endp - payload; + char *license; + + if (!bin->base) { + bin_reserve(bin, size); + return 0; + } + + license = bin_alloc(bin, size); + if (!license) + return -ENOMEM; + + memcpy(license, payload, size); + + *__license = license; + *__license_length = size; + + return 0; +} + +static int ccs_data_parse_end(bool *end, const void *payload, const void *endp, + struct device *dev) +{ + const struct __ccs_data_block_end *__end = payload; + + if (__end + 1 != endp) { + dev_dbg(dev, "Invalid end block length %u\n", + (unsigned int)(endp - payload)); + return -ENODATA; + } + + *end = true; + + return 0; +} + +static int __ccs_data_parse(struct bin_container *bin, + struct ccs_data_container *ccsdata, + const void *data, size_t len, struct device *dev, + bool verbose) +{ + const struct __ccs_data_block *block = data; + const struct __ccs_data_block *endp = data + len; + unsigned int version; + bool is_first = true; + int rval; + + version = ccs_data_parse_format_version(block); + if (version != CCS_STATIC_DATA_VERSION) { + dev_dbg(dev, "Don't know how to handle version %u\n", version); + return -EINVAL; + } + + if (verbose) + dev_dbg(dev, "Parsing CCS static data version %u\n", version); + + if (!bin->base) + *ccsdata = (struct ccs_data_container){ 0 }; + + while (block < endp) { + const struct __ccs_data_block *next_block; + unsigned int block_id; + const void *payload; + + rval = ccs_data_block_parse_header(block, is_first, &block_id, + &payload, &next_block, endp, + dev, + bin->base ? false : verbose); + + if (rval < 0) + return rval; + + switch (block_id) { + case CCS_DATA_BLOCK_ID_DUMMY: + break; + case CCS_DATA_BLOCK_ID_DATA_VERSION: + rval = ccs_data_parse_version(bin, ccsdata, payload, + next_block); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS: + rval = ccs_data_parse_regs( + bin, &ccsdata->sensor_read_only_regs, + &ccsdata->num_sensor_read_only_regs, payload, + next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS: + rval = ccs_data_parse_regs( + bin, &ccsdata->sensor_manufacturer_regs, + &ccsdata->num_sensor_manufacturer_regs, payload, + next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS: + rval = ccs_data_parse_regs( + bin, &ccsdata->module_read_only_regs, + &ccsdata->num_module_read_only_regs, payload, + next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS: + rval = ccs_data_parse_regs( + bin, &ccsdata->module_manufacturer_regs, + &ccsdata->num_module_manufacturer_regs, payload, + next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION: + rval = ccs_data_parse_pdaf(bin, &ccsdata->sensor_pdaf, + payload, next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION: + rval = ccs_data_parse_pdaf(bin, &ccsdata->module_pdaf, + payload, next_block, dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK: + rval = ccs_data_parse_rules( + bin, &ccsdata->sensor_rules, + &ccsdata->num_sensor_rules, payload, next_block, + dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK: + rval = ccs_data_parse_rules( + bin, &ccsdata->module_rules, + &ccsdata->num_module_rules, payload, next_block, + dev); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_LICENSE: + rval = ccs_data_parse_license(bin, &ccsdata->license, + &ccsdata->license_length, + payload, next_block); + if (rval < 0) + return rval; + break; + case CCS_DATA_BLOCK_ID_END: + rval = ccs_data_parse_end(&ccsdata->end, payload, + next_block, dev); + if (rval < 0) + return rval; + break; + default: + dev_dbg(dev, "WARNING: not handling block ID 0x%2.2x\n", + block_id); + } + + block = next_block; + is_first = false; + } + + return 0; +} + +/** + * ccs_data_parse - Parse a CCS static data file into a usable in-memory + * data structure + * @ccsdata: CCS static data in-memory data structure + * @data: CCS static data binary + * @len: Length of @data + * @dev: Device the data is related to (used for printing debug messages) + * @verbose: Whether to be verbose or not + */ +int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, + size_t len, struct device *dev, bool verbose) +{ + struct bin_container bin = { 0 }; + int rval; + + rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, verbose); + if (rval) + return rval; + + rval = bin_backing_alloc(&bin); + if (rval) + return rval; + + rval = __ccs_data_parse(&bin, ccsdata, data, len, dev, false); + if (rval) + goto out_free; + + if (verbose && ccsdata->version) + print_ccs_data_version(dev, ccsdata->version); + + if (bin.now != bin.end) { + rval = -EPROTO; + dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n", + bin.base, bin.now, bin.end); + goto out_free; + } + + ccsdata->backing = bin.base; + + return 0; + +out_free: + kvfree(bin.base); + + return rval; +} diff --git a/drivers/media/i2c/ccs/ccs-data.h b/drivers/media/i2c/ccs/ccs-data.h new file mode 100644 index 000000000000..50d6508b24f3 --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-data.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * CCS static data in-memory data structure definitions + * + * Copyright 2019--2020 Intel Corporation + */ + +#ifndef __CCS_DATA_H__ +#define __CCS_DATA_H__ + +#include <linux/types.h> + +/** + * struct ccs_data_block_version - CCS static data version + * @version_major: Major version number + * @version_minor: Minor version number + * @date_year: Year + * @date_month: Month + * @date_day: Day + */ +struct ccs_data_block_version { + u16 version_major; + u16 version_minor; + u16 date_year; + u8 date_month; + u8 date_day; +}; + +/** + * struct ccs_reg - CCS register value + * @addr: The 16-bit address of the register + * @len: Length of the data + * @value: Data + */ +struct ccs_reg { + u16 addr; + u16 len; + u8 *value; +}; + +/** + * struct ccs_if_rule - CCS static data if rule + * @addr: Register address + * @value: Register value + * @mask: Value applied to both actual register value and @value + */ +struct ccs_if_rule { + u16 addr; + u8 value; + u8 mask; +}; + +/** + * struct ccs_frame_format_desc - CCS frame format descriptor + * @pixelcode: The pixelcode; CCS_DATA_BLOCK_FFD_PIXELCODE_* + * @value: Value related to the pixelcode + */ +struct ccs_frame_format_desc { + u8 pixelcode; + u16 value; +}; + +/** + * struct ccs_frame_format_descs - A series of CCS frame format descriptors + * @num_column_descs: Number of column descriptors + * @num_row_descs: Number of row descriptors + * @column_descs: Column descriptors + * @row_descs: Row descriptors + */ +struct ccs_frame_format_descs { + u8 num_column_descs; + u8 num_row_descs; + struct ccs_frame_format_desc *column_descs; + struct ccs_frame_format_desc *row_descs; +}; + +/** + * struct ccs_pdaf_readout - CCS PDAF data readout descriptor + * @pdaf_readout_info_order: PDAF readout order + * @ffd: Frame format of PDAF data + */ +struct ccs_pdaf_readout { + u8 pdaf_readout_info_order; + struct ccs_frame_format_descs *ffd; +}; + +/** + * struct ccs_rule - A CCS static data rule + * @num_if_rules: Number of if rules + * @if_rules: If rules + * @num_read_only_regs: Number of read-only registers + * @read_only_regs: Read-only registers + * @num_manufacturer_regs: Number of manufacturer-specific registers + * @manufacturer_regs: Manufacturer-specific registers + * @frame_format: Frame format + * @pdaf_readout: PDAF readout + */ +struct ccs_rule { + size_t num_if_rules; + struct ccs_if_rule *if_rules; + size_t num_read_only_regs; + struct ccs_reg *read_only_regs; + size_t num_manufacturer_regs; + struct ccs_reg *manufacturer_regs; + struct ccs_frame_format_descs *frame_format; + struct ccs_pdaf_readout *pdaf_readout; +}; + +/** + * struct ccs_pdaf_pix_loc_block_desc - PDAF pixel location block descriptor + * @block_type_id: Block type identifier, from 0 to n + * @repeat_x: Number of times this block is repeated to right + */ +struct ccs_pdaf_pix_loc_block_desc { + u8 block_type_id; + u16 repeat_x; +}; + +/** + * struct ccs_pdaf_pix_loc_block_desc_group - PDAF pixel location block + * descriptor group + * @repeat_y: Number of times the group is repeated down + * @num_block_descs: Number of block descriptors in @block_descs + * @block_descs: Block descriptors + */ +struct ccs_pdaf_pix_loc_block_desc_group { + u8 repeat_y; + u16 num_block_descs; + struct ccs_pdaf_pix_loc_block_desc *block_descs; +}; + +/** + * struct ccs_pdaf_pix_loc_block_desc - PDAF pixel location block descriptor + * @pixel_type: Type of the pixel; CCS_DATA_PDAF_PIXEL_TYPE_* + * @small_offset_x: offset X coordinate + * @small_offset_y: offset Y coordinate + */ +struct ccs_pdaf_pix_loc_pixel_desc { + u8 pixel_type; + u8 small_offset_x; + u8 small_offset_y; +}; + +/** + * struct ccs_pdaf_pix_loc_pixel_desc_group - PDAF pixel location pixel + * descriptor group + * @num_descs: Number of descriptors in @descs + * @descs: PDAF pixel location pixel descriptors + */ +struct ccs_pdaf_pix_loc_pixel_desc_group { + u8 num_descs; + struct ccs_pdaf_pix_loc_pixel_desc *descs; +}; + +/** + * struct ccs_pdaf_pix_loc - PDAF pixel locations + * @main_offset_x: Start X coordinate of PDAF pixel blocks + * @main_offset_y: Start Y coordinate of PDAF pixel blocks + * @global_pdaf_type: PDAF pattern type + * @block_width: Width of a block in pixels + * @block_height: Heigth of a block in pixels + * @num_block_desc_groups: Number of block descriptor groups + * @block_desc_groups: Block descriptor groups + * @num_pixel_desc_grups: Number of pixel descriptor groups + * @pixel_desc_groups: Pixel descriptor groups + */ +struct ccs_pdaf_pix_loc { + u16 main_offset_x; + u16 main_offset_y; + u8 global_pdaf_type; + u8 block_width; + u8 block_height; + u16 num_block_desc_groups; + struct ccs_pdaf_pix_loc_block_desc_group *block_desc_groups; + u8 num_pixel_desc_grups; + struct ccs_pdaf_pix_loc_pixel_desc_group *pixel_desc_groups; +}; + +/** + * struct ccs_data_container - In-memory CCS static data + * @version: CCS static data version + * @num_sensor_read_only_regs: Number of the read-only registers for the sensor + * @sensor_read_only_regs: Read-only registers for the sensor + * @num_sensor_manufacturer_regs: Number of the manufacturer-specific registers + * for the sensor + * @sensor_manufacturer_regs: Manufacturer-specific registers for the sensor + * @num_sensor_rules: Number of rules for the sensor + * @sensor_rules: Rules for the sensor + * @num_module_read_only_regs: Number of the read-only registers for the module + * @module_read_only_regs: Read-only registers for the module + * @num_module_manufacturer_regs: Number of the manufacturer-specific registers + * for the module + * @module_manufacturer_regs: Manufacturer-specific registers for the module + * @num_module_rules: Number of rules for the module + * @module_rules: Rules for the module + * @sensor_pdaf: PDAF data for the sensor + * @module_pdaf: PDAF data for the module + * @license_length: Lenght of the license data + * @license: License data + * @end: Whether or not there's an end block + * @backing: Raw data, pointed to from elsewhere so keep it around + */ +struct ccs_data_container { + struct ccs_data_block_version *version; + size_t num_sensor_read_only_regs; + struct ccs_reg *sensor_read_only_regs; + size_t num_sensor_manufacturer_regs; + struct ccs_reg *sensor_manufacturer_regs; + size_t num_sensor_rules; + struct ccs_rule *sensor_rules; + size_t num_module_read_only_regs; + struct ccs_reg *module_read_only_regs; + size_t num_module_manufacturer_regs; + struct ccs_reg *module_manufacturer_regs; + size_t num_module_rules; + struct ccs_rule *module_rules; + struct ccs_pdaf_pix_loc *sensor_pdaf; + struct ccs_pdaf_pix_loc *module_pdaf; + size_t license_length; + char *license; + bool end; + void *backing; +}; + +int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, + size_t len, struct device *dev, bool verbose); + +#endif /* __CCS_DATA_H__ */ diff --git a/drivers/media/i2c/ccs/ccs-limits.c b/drivers/media/i2c/ccs/ccs-limits.c new file mode 100644 index 000000000000..f5511789ac83 --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-limits.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause +/* Copyright (C) 2019--2020 Intel Corporation */ + +#include "ccs-limits.h" +#include "ccs-regs.h" + +const struct ccs_limit ccs_limits[] = { + { CCS_R_FRAME_FORMAT_MODEL_TYPE, 1, 0, "frame_format_model_type" }, + { CCS_R_FRAME_FORMAT_MODEL_SUBTYPE, 1, 0, "frame_format_model_subtype" }, + { CCS_R_FRAME_FORMAT_DESCRIPTOR(0), 30, 0, "frame_format_descriptor" }, + { CCS_R_FRAME_FORMAT_DESCRIPTOR_4(0), 32, 0, "frame_format_descriptor_4" }, + { CCS_R_ANALOG_GAIN_CAPABILITY, 2, 0, "analog_gain_capability" }, + { CCS_R_ANALOG_GAIN_CODE_MIN, 2, 0, "analog_gain_code_min" }, + { CCS_R_ANALOG_GAIN_CODE_MAX, 2, 0, "analog_gain_code_max" }, + { CCS_R_ANALOG_GAIN_CODE_STEP, 2, 0, "analog_gain_code_step" }, + { CCS_R_ANALOG_GAIN_TYPE, 2, 0, "analog_gain_type" }, + { CCS_R_ANALOG_GAIN_M0, 2, 0, "analog_gain_m0" }, + { CCS_R_ANALOG_GAIN_C0, 2, 0, "analog_gain_c0" }, + { CCS_R_ANALOG_GAIN_M1, 2, 0, "analog_gain_m1" }, + { CCS_R_ANALOG_GAIN_C1, 2, 0, "analog_gain_c1" }, + { CCS_R_ANALOG_LINEAR_GAIN_MIN, 2, 0, "analog_linear_gain_min" }, + { CCS_R_ANALOG_LINEAR_GAIN_MAX, 2, 0, "analog_linear_gain_max" }, + { CCS_R_ANALOG_LINEAR_GAIN_STEP_SIZE, 2, 0, "analog_linear_gain_step_size" }, + { CCS_R_ANALOG_EXPONENTIAL_GAIN_MIN, 2, 0, "analog_exponential_gain_min" }, + { CCS_R_ANALOG_EXPONENTIAL_GAIN_MAX, 2, 0, "analog_exponential_gain_max" }, + { CCS_R_ANALOG_EXPONENTIAL_GAIN_STEP_SIZE, 2, 0, "analog_exponential_gain_step_size" }, + { CCS_R_DATA_FORMAT_MODEL_TYPE, 1, 0, "data_format_model_type" }, + { CCS_R_DATA_FORMAT_MODEL_SUBTYPE, 1, 0, "data_format_model_subtype" }, + { CCS_R_DATA_FORMAT_DESCRIPTOR(0), 32, 0, "data_format_descriptor" }, + { CCS_R_INTEGRATION_TIME_CAPABILITY, 2, 0, "integration_time_capability" }, + { CCS_R_COARSE_INTEGRATION_TIME_MIN, 2, 0, "coarse_integration_time_min" }, + { CCS_R_COARSE_INTEGRATION_TIME_MAX_MARGIN, 2, 0, "coarse_integration_time_max_margin" }, + { CCS_R_FINE_INTEGRATION_TIME_MIN, 2, 0, "fine_integration_time_min" }, + { CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN, 2, 0, "fine_integration_time_max_margin" }, + { CCS_R_DIGITAL_GAIN_CAPABILITY, 1, 0, "digital_gain_capability" }, + { CCS_R_DIGITAL_GAIN_MIN, 2, 0, "digital_gain_min" }, + { CCS_R_DIGITAL_GAIN_MAX, 2, 0, "digital_gain_max" }, + { CCS_R_DIGITAL_GAIN_STEP_SIZE, 2, 0, "digital_gain_step_size" }, + { CCS_R_PEDESTAL_CAPABILITY, 1, 0, "Pedestal_capability" }, + { CCS_R_ADC_CAPABILITY, 1, 0, "ADC_capability" }, + { CCS_R_ADC_BIT_DEPTH_CAPABILITY, 4, 0, "ADC_bit_depth_capability" }, + { CCS_R_MIN_EXT_CLK_FREQ_MHZ, 4, 0, "min_ext_clk_freq_mhz" }, + { CCS_R_MAX_EXT_CLK_FREQ_MHZ, 4, 0, "max_ext_clk_freq_mhz" }, + { CCS_R_MIN_PRE_PLL_CLK_DIV, 2, 0, "min_pre_pll_clk_div" }, + { CCS_R_MAX_PRE_PLL_CLK_DIV, 2, 0, "max_pre_pll_clk_div" }, + { CCS_R_MIN_PLL_IP_CLK_FREQ_MHZ, 4, 0, "min_pll_ip_clk_freq_mhz" }, + { CCS_R_MAX_PLL_IP_CLK_FREQ_MHZ, 4, 0, "max_pll_ip_clk_freq_mhz" }, + { CCS_R_MIN_PLL_MULTIPLIER, 2, 0, "min_pll_multiplier" }, + { CCS_R_MAX_PLL_MULTIPLIER, 2, 0, "max_pll_multiplier" }, + { CCS_R_MIN_PLL_OP_CLK_FREQ_MHZ, 4, 0, "min_pll_op_clk_freq_mhz" }, + { CCS_R_MAX_PLL_OP_CLK_FREQ_MHZ, 4, 0, "max_pll_op_clk_freq_mhz" }, + { CCS_R_MIN_VT_SYS_CLK_DIV, 2, 0, "min_vt_sys_clk_div" }, + { CCS_R_MAX_VT_SYS_CLK_DIV, 2, 0, "max_vt_sys_clk_div" }, + { CCS_R_MIN_VT_SYS_CLK_FREQ_MHZ, 4, 0, "min_vt_sys_clk_freq_mhz" }, + { CCS_R_MAX_VT_SYS_CLK_FREQ_MHZ, 4, 0, "max_vt_sys_clk_freq_mhz" }, + { CCS_R_MIN_VT_PIX_CLK_FREQ_MHZ, 4, 0, "min_vt_pix_clk_freq_mhz" }, + { CCS_R_MAX_VT_PIX_CLK_FREQ_MHZ, 4, 0, "max_vt_pix_clk_freq_mhz" }, + { CCS_R_MIN_VT_PIX_CLK_DIV, 2, 0, "min_vt_pix_clk_div" }, + { CCS_R_MAX_VT_PIX_CLK_DIV, 2, 0, "max_vt_pix_clk_div" }, + { CCS_R_CLOCK_CALCULATION, 1, 0, "clock_calculation" }, + { CCS_R_NUM_OF_VT_LANES, 1, 0, "num_of_vt_lanes" }, + { CCS_R_NUM_OF_OP_LANES, 1, 0, "num_of_op_lanes" }, + { CCS_R_OP_BITS_PER_LANE, 1, 0, "op_bits_per_lane" }, + { CCS_R_MIN_FRAME_LENGTH_LINES, 2, 0, "min_frame_length_lines" }, + { CCS_R_MAX_FRAME_LENGTH_LINES, 2, 0, "max_frame_length_lines" }, + { CCS_R_MIN_LINE_LENGTH_PCK, 2, 0, "min_line_length_pck" }, + { CCS_R_MAX_LINE_LENGTH_PCK, 2, 0, "max_line_length_pck" }, + { CCS_R_MIN_LINE_BLANKING_PCK, 2, 0, "min_line_blanking_pck" }, + { CCS_R_MIN_FRAME_BLANKING_LINES, 2, 0, "min_frame_blanking_lines" }, + { CCS_R_MIN_LINE_LENGTH_PCK_STEP_SIZE, 1, 0, "min_line_length_pck_step_size" }, + { CCS_R_TIMING_MODE_CAPABILITY, 1, 0, "timing_mode_capability" }, + { CCS_R_FRAME_MARGIN_MAX_VALUE, 2, 0, "frame_margin_max_value" }, + { CCS_R_FRAME_MARGIN_MIN_VALUE, 1, 0, "frame_margin_min_value" }, + { CCS_R_GAIN_DELAY_TYPE, 1, 0, "gain_delay_type" }, + { CCS_R_MIN_OP_SYS_CLK_DIV, 2, 0, "min_op_sys_clk_div" }, + { CCS_R_MAX_OP_SYS_CLK_DIV, 2, 0, "max_op_sys_clk_div" }, + { CCS_R_MIN_OP_SYS_CLK_FREQ_MHZ, 4, 0, "min_op_sys_clk_freq_mhz" }, + { CCS_R_MAX_OP_SYS_CLK_FREQ_MHZ, 4, 0, "max_op_sys_clk_freq_mhz" }, + { CCS_R_MIN_OP_PIX_CLK_DIV, 2, 0, "min_op_pix_clk_div" }, + { CCS_R_MAX_OP_PIX_CLK_DIV, 2, 0, "max_op_pix_clk_div" }, + { CCS_R_MIN_OP_PIX_CLK_FREQ_MHZ, 4, 0, "min_op_pix_clk_freq_mhz" }, + { CCS_R_MAX_OP_PIX_CLK_FREQ_MHZ, 4, 0, "max_op_pix_clk_freq_mhz" }, + { CCS_R_X_ADDR_MIN, 2, 0, "x_addr_min" }, + { CCS_R_Y_ADDR_MIN, 2, 0, "y_addr_min" }, + { CCS_R_X_ADDR_MAX, 2, 0, "x_addr_max" }, + { CCS_R_Y_ADDR_MAX, 2, 0, "y_addr_max" }, + { CCS_R_MIN_X_OUTPUT_SIZE, 2, 0, "min_x_output_size" }, + { CCS_R_MIN_Y_OUTPUT_SIZE, 2, 0, "min_y_output_size" }, + { CCS_R_MAX_X_OUTPUT_SIZE, 2, 0, "max_x_output_size" }, + { CCS_R_MAX_Y_OUTPUT_SIZE, 2, 0, "max_y_output_size" }, + { CCS_R_X_ADDR_START_DIV_CONSTANT, 1, 0, "x_addr_start_div_constant" }, + { CCS_R_Y_ADDR_START_DIV_CONSTANT, 1, 0, "y_addr_start_div_constant" }, + { CCS_R_X_ADDR_END_DIV_CONSTANT, 1, 0, "x_addr_end_div_constant" }, + { CCS_R_Y_ADDR_END_DIV_CONSTANT, 1, 0, "y_addr_end_div_constant" }, + { CCS_R_X_SIZE_DIV, 1, 0, "x_size_div" }, + { CCS_R_Y_SIZE_DIV, 1, 0, "y_size_div" }, + { CCS_R_X_OUTPUT_DIV, 1, 0, "x_output_div" }, + { CCS_R_Y_OUTPUT_DIV, 1, 0, "y_output_div" }, + { CCS_R_NON_FLEXIBLE_RESOLUTION_SUPPORT, 1, 0, "non_flexible_resolution_support" }, + { CCS_R_MIN_OP_PRE_PLL_CLK_DIV, 2, 0, "min_op_pre_pll_clk_div" }, + { CCS_R_MAX_OP_PRE_PLL_CLK_DIV, 2, 0, "max_op_pre_pll_clk_div" }, + { CCS_R_MIN_OP_PLL_IP_CLK_FREQ_MHZ, 4, 0, "min_op_pll_ip_clk_freq_mhz" }, + { CCS_R_MAX_OP_PLL_IP_CLK_FREQ_MHZ, 4, 0, "max_op_pll_ip_clk_freq_mhz" }, + { CCS_R_MIN_OP_PLL_MULTIPLIER, 2, 0, "min_op_pll_multiplier" }, + { CCS_R_MAX_OP_PLL_MULTIPLIER, 2, 0, "max_op_pll_multiplier" }, + { CCS_R_MIN_OP_PLL_OP_CLK_FREQ_MHZ, 4, 0, "min_op_pll_op_clk_freq_mhz" }, + { CCS_R_MAX_OP_PLL_OP_CLK_FREQ_MHZ, 4, 0, "max_op_pll_op_clk_freq_mhz" }, + { CCS_R_CLOCK_TREE_PLL_CAPABILITY, 1, 0, "clock_tree_pll_capability" }, + { CCS_R_CLOCK_CAPA_TYPE_CAPABILITY, 1, 0, "clock_capa_type_capability" }, + { CCS_R_MIN_EVEN_INC, 2, 0, "min_even_inc" }, + { CCS_R_MIN_ODD_INC, 2, 0, "min_odd_inc" }, + { CCS_R_MAX_EVEN_INC, 2, 0, "max_even_inc" }, + { CCS_R_MAX_ODD_INC, 2, 0, "max_odd_inc" }, + { CCS_R_AUX_SUBSAMP_CAPABILITY, 1, 0, "aux_subsamp_capability" }, + { CCS_R_AUX_SUBSAMP_MONO_CAPABILITY, 1, 0, "aux_subsamp_mono_capability" }, + { CCS_R_MONOCHROME_CAPABILITY, 1, 0, "monochrome_capability" }, + { CCS_R_PIXEL_READOUT_CAPABILITY, 1, 0, "pixel_readout_capability" }, + { CCS_R_MIN_EVEN_INC_MONO, 2, 0, "min_even_inc_mono" }, + { CCS_R_MAX_EVEN_INC_MONO, 2, 0, "max_even_inc_mono" }, + { CCS_R_MIN_ODD_INC_MONO, 2, 0, "min_odd_inc_mono" }, + { CCS_R_MAX_ODD_INC_MONO, 2, 0, "max_odd_inc_mono" }, + { CCS_R_MIN_EVEN_INC_BC2, 2, 0, "min_even_inc_bc2" }, + { CCS_R_MAX_EVEN_INC_BC2, 2, 0, "max_even_inc_bc2" }, + { CCS_R_MIN_ODD_INC_BC2, 2, 0, "min_odd_inc_bc2" }, + { CCS_R_MAX_ODD_INC_BC2, 2, 0, "max_odd_inc_bc2" }, + { CCS_R_MIN_EVEN_INC_MONO_BC2, 2, 0, "min_even_inc_mono_bc2" }, + { CCS_R_MAX_EVEN_INC_MONO_BC2, 2, 0, "max_even_inc_mono_bc2" }, + { CCS_R_MIN_ODD_INC_MONO_BC2, 2, 0, "min_odd_inc_mono_bc2" }, + { CCS_R_MAX_ODD_INC_MONO_BC2, 2, 0, "max_odd_inc_mono_bc2" }, + { CCS_R_SCALING_CAPABILITY, 2, 0, "scaling_capability" }, + { CCS_R_SCALER_M_MIN, 2, 0, "scaler_m_min" }, + { CCS_R_SCALER_M_MAX, 2, 0, "scaler_m_max" }, + { CCS_R_SCALER_N_MIN, 2, 0, "scaler_n_min" }, + { CCS_R_SCALER_N_MAX, 2, 0, "scaler_n_max" }, + { CCS_R_DIGITAL_CROP_CAPABILITY, 1, 0, "digital_crop_capability" }, + { CCS_R_HDR_CAPABILITY_1, 1, 0, "hdr_capability_1" }, + { CCS_R_MIN_HDR_BIT_DEPTH, 1, 0, "min_hdr_bit_depth" }, + { CCS_R_HDR_RESOLUTION_SUB_TYPES, 1, 0, "hdr_resolution_sub_types" }, + { CCS_R_HDR_RESOLUTION_SUB_TYPE(0), 2, 0, "hdr_resolution_sub_type" }, + { CCS_R_HDR_CAPABILITY_2, 1, 0, "hdr_capability_2" }, + { CCS_R_MAX_HDR_BIT_DEPTH, 1, 0, "max_hdr_bit_depth" }, + { CCS_R_USL_SUPPORT_CAPABILITY, 1, 0, "usl_support_capability" }, + { CCS_R_USL_CLOCK_MODE_D_CAPABILITY, 1, 0, "usl_clock_mode_d_capability" }, + { CCS_R_MIN_OP_SYS_CLK_DIV_REV, 1, 0, "min_op_sys_clk_div_rev" }, + { CCS_R_MAX_OP_SYS_CLK_DIV_REV, 1, 0, "max_op_sys_clk_div_rev" }, + { CCS_R_MIN_OP_PIX_CLK_DIV_REV, 1, 0, "min_op_pix_clk_div_rev" }, + { CCS_R_MAX_OP_PIX_CLK_DIV_REV, 1, 0, "max_op_pix_clk_div_rev" }, + { CCS_R_MIN_OP_SYS_CLK_FREQ_REV_MHZ, 4, 0, "min_op_sys_clk_freq_rev_mhz" }, + { CCS_R_MAX_OP_SYS_CLK_FREQ_REV_MHZ, 4, 0, "max_op_sys_clk_freq_rev_mhz" }, + { CCS_R_MIN_OP_PIX_CLK_FREQ_REV_MHZ, 4, 0, "min_op_pix_clk_freq_rev_mhz" }, + { CCS_R_MAX_OP_PIX_CLK_FREQ_REV_MHZ, 4, 0, "max_op_pix_clk_freq_rev_mhz" }, + { CCS_R_MAX_BITRATE_REV_D_MODE_MBPS, 4, 0, "max_bitrate_rev_d_mode_mbps" }, + { CCS_R_MAX_SYMRATE_REV_C_MODE_MSPS, 4, 0, "max_symrate_rev_c_mode_msps" }, + { CCS_R_COMPRESSION_CAPABILITY, 1, 0, "compression_capability" }, + { CCS_R_TEST_MODE_CAPABILITY, 2, 0, "test_mode_capability" }, + { CCS_R_PN9_DATA_FORMAT1, 1, 0, "pn9_data_format1" }, + { CCS_R_PN9_DATA_FORMAT2, 1, 0, "pn9_data_format2" }, + { CCS_R_PN9_DATA_FORMAT3, 1, 0, "pn9_data_format3" }, + { CCS_R_PN9_DATA_FORMAT4, 1, 0, "pn9_data_format4" }, + { CCS_R_PN9_MISC_CAPABILITY, 1, 0, "pn9_misc_capability" }, + { CCS_R_TEST_PATTERN_CAPABILITY, 1, 0, "test_pattern_capability" }, + { CCS_R_PATTERN_SIZE_DIV_M1, 1, 0, "pattern_size_div_m1" }, + { CCS_R_FIFO_SUPPORT_CAPABILITY, 1, 0, "fifo_support_capability" }, + { CCS_R_PHY_CTRL_CAPABILITY, 1, 0, "phy_ctrl_capability" }, + { CCS_R_CSI_DPHY_LANE_MODE_CAPABILITY, 1, 0, "csi_dphy_lane_mode_capability" }, + { CCS_R_CSI_SIGNALING_MODE_CAPABILITY, 1, 0, "csi_signaling_mode_capability" }, + { CCS_R_FAST_STANDBY_CAPABILITY, 1, 0, "fast_standby_capability" }, + { CCS_R_CSI_ADDRESS_CONTROL_CAPABILITY, 1, 0, "csi_address_control_capability" }, + { CCS_R_DATA_TYPE_CAPABILITY, 1, 0, "data_type_capability" }, + { CCS_R_CSI_CPHY_LANE_MODE_CAPABILITY, 1, 0, "csi_cphy_lane_mode_capability" }, + { CCS_R_EMB_DATA_CAPABILITY, 1, 0, "emb_data_capability" }, + { CCS_R_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS(0), 16, 0, "max_per_lane_bitrate_lane_d_mode_mbps 0" }, + { CCS_R_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS(4), 16, CCS_L_FL_SAME_REG, "max_per_lane_bitrate_lane_d_mode_mbps 4" }, + { CCS_R_TEMP_SENSOR_CAPABILITY, 1, 0, "temp_sensor_capability" }, + { CCS_R_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS(0), 16, 0, "max_per_lane_bitrate_lane_c_mode_mbps 0" }, + { CCS_R_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS(4), 16, CCS_L_FL_SAME_REG, "max_per_lane_bitrate_lane_c_mode_mbps 4" }, + { CCS_R_DPHY_EQUALIZATION_CAPABILITY, 1, 0, "dphy_equalization_capability" }, + { CCS_R_CPHY_EQUALIZATION_CAPABILITY, 1, 0, "cphy_equalization_capability" }, + { CCS_R_DPHY_PREAMBLE_CAPABILITY, 1, 0, "dphy_preamble_capability" }, + { CCS_R_DPHY_SSC_CAPABILITY, 1, 0, "dphy_ssc_capability" }, + { CCS_R_CPHY_CALIBRATION_CAPABILITY, 1, 0, "cphy_calibration_capability" }, + { CCS_R_DPHY_CALIBRATION_CAPABILITY, 1, 0, "dphy_calibration_capability" }, + { CCS_R_PHY_CTRL_CAPABILITY_2, 1, 0, "phy_ctrl_capability_2" }, + { CCS_R_LRTE_CPHY_CAPABILITY, 1, 0, "lrte_cphy_capability" }, + { CCS_R_LRTE_DPHY_CAPABILITY, 1, 0, "lrte_dphy_capability" }, + { CCS_R_ALPS_CAPABILITY_DPHY, 1, 0, "alps_capability_dphy" }, + { CCS_R_ALPS_CAPABILITY_CPHY, 1, 0, "alps_capability_cphy" }, + { CCS_R_SCRAMBLING_CAPABILITY, 1, 0, "scrambling_capability" }, + { CCS_R_DPHY_MANUAL_CONSTANT, 1, 0, "dphy_manual_constant" }, + { CCS_R_CPHY_MANUAL_CONSTANT, 1, 0, "cphy_manual_constant" }, + { CCS_R_CSI2_INTERFACE_CAPABILITY_MISC, 1, 0, "CSI2_interface_capability_misc" }, + { CCS_R_PHY_CTRL_CAPABILITY_3, 1, 0, "PHY_ctrl_capability_3" }, + { CCS_R_DPHY_SF, 1, 0, "dphy_sf" }, + { CCS_R_CPHY_SF, 1, 0, "cphy_sf" }, + { CCS_R_DPHY_LIMITS_1, 1, 0, "dphy_limits_1" }, + { CCS_R_DPHY_LIMITS_2, 1, 0, "dphy_limits_2" }, + { CCS_R_DPHY_LIMITS_3, 1, 0, "dphy_limits_3" }, + { CCS_R_DPHY_LIMITS_4, 1, 0, "dphy_limits_4" }, + { CCS_R_DPHY_LIMITS_5, 1, 0, "dphy_limits_5" }, + { CCS_R_DPHY_LIMITS_6, 1, 0, "dphy_limits_6" }, + { CCS_R_CPHY_LIMITS_1, 1, 0, "cphy_limits_1" }, + { CCS_R_CPHY_LIMITS_2, 1, 0, "cphy_limits_2" }, + { CCS_R_CPHY_LIMITS_3, 1, 0, "cphy_limits_3" }, + { CCS_R_MIN_FRAME_LENGTH_LINES_BIN, 2, 0, "min_frame_length_lines_bin" }, + { CCS_R_MAX_FRAME_LENGTH_LINES_BIN, 2, 0, "max_frame_length_lines_bin" }, + { CCS_R_MIN_LINE_LENGTH_PCK_BIN, 2, 0, "min_line_length_pck_bin" }, + { CCS_R_MAX_LINE_LENGTH_PCK_BIN, 2, 0, "max_line_length_pck_bin" }, + { CCS_R_MIN_LINE_BLANKING_PCK_BIN, 2, 0, "min_line_blanking_pck_bin" }, + { CCS_R_FINE_INTEGRATION_TIME_MIN_BIN, 2, 0, "fine_integration_time_min_bin" }, + { CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, 2, 0, "fine_integration_time_max_margin_bin" }, + { CCS_R_BINNING_CAPABILITY, 1, 0, "binning_capability" }, + { CCS_R_BINNING_WEIGHTING_CAPABILITY, 1, 0, "binning_weighting_capability" }, + { CCS_R_BINNING_SUB_TYPES, 1, 0, "binning_sub_types" }, + { CCS_R_BINNING_SUB_TYPE(0), 64, 0, "binning_sub_type" }, + { CCS_R_BINNING_WEIGHTING_MONO_CAPABILITY, 1, 0, "binning_weighting_mono_capability" }, + { CCS_R_BINNING_SUB_TYPES_MONO, 1, 0, "binning_sub_types_mono" }, + { CCS_R_BINNING_SUB_TYPE_MONO(0), 64, 0, "binning_sub_type_mono" }, + { CCS_R_DATA_TRANSFER_IF_CAPABILITY, 1, 0, "data_transfer_if_capability" }, + { CCS_R_SHADING_CORRECTION_CAPABILITY, 1, 0, "shading_correction_capability" }, + { CCS_R_GREEN_IMBALANCE_CAPABILITY, 1, 0, "green_imbalance_capability" }, + { CCS_R_MODULE_SPECIFIC_CORRECTION_CAPABILITY, 1, 0, "module_specific_correction_capability" }, + { CCS_R_DEFECT_CORRECTION_CAPABILITY, 2, 0, "defect_correction_capability" }, + { CCS_R_DEFECT_CORRECTION_CAPABILITY_2, 2, 0, "defect_correction_capability_2" }, + { CCS_R_NF_CAPABILITY, 1, 0, "nf_capability" }, + { CCS_R_OB_READOUT_CAPABILITY, 1, 0, "ob_readout_capability" }, + { CCS_R_COLOR_FEEDBACK_CAPABILITY, 1, 0, "color_feedback_capability" }, + { CCS_R_CFA_PATTERN_CAPABILITY, 1, 0, "CFA_pattern_capability" }, + { CCS_R_CFA_PATTERN_CONVERSION_CAPABILITY, 1, 0, "CFA_pattern_conversion_capability" }, + { CCS_R_FLASH_MODE_CAPABILITY, 1, 0, "flash_mode_capability" }, + { CCS_R_SA_STROBE_MODE_CAPABILITY, 1, 0, "sa_strobe_mode_capability" }, + { CCS_R_RESET_MAX_DELAY, 1, 0, "reset_max_delay" }, + { CCS_R_RESET_MIN_TIME, 1, 0, "reset_min_time" }, + { CCS_R_PDAF_CAPABILITY_1, 1, 0, "pdaf_capability_1" }, + { CCS_R_PDAF_CAPABILITY_2, 1, 0, "pdaf_capability_2" }, + { CCS_R_BRACKETING_LUT_CAPABILITY_1, 1, 0, "bracketing_lut_capability_1" }, + { CCS_R_BRACKETING_LUT_CAPABILITY_2, 1, 0, "bracketing_lut_capability_2" }, + { CCS_R_BRACKETING_LUT_SIZE, 1, 0, "bracketing_lut_size" }, + { 0 } /* Guardian */ +}; diff --git a/drivers/media/i2c/ccs/ccs-limits.h b/drivers/media/i2c/ccs/ccs-limits.h new file mode 100644 index 000000000000..1efa43c23a2e --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-limits.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */ +/* Copyright (C) 2019--2020 Intel Corporation */ + +#ifndef __CCS_LIMITS_H__ +#define __CCS_LIMITS_H__ + +#include <linux/bits.h> +#include <linux/types.h> + +struct ccs_limit { + u32 reg; + u16 size; + u16 flags; + const char *name; +}; + +#define CCS_L_FL_SAME_REG BIT(0) + +extern const struct ccs_limit ccs_limits[]; + +#define CCS_L_FRAME_FORMAT_MODEL_TYPE 0 +#define CCS_L_FRAME_FORMAT_MODEL_SUBTYPE 1 +#define CCS_L_FRAME_FORMAT_DESCRIPTOR 2 +#define CCS_L_FRAME_FORMAT_DESCRIPTOR_OFFSET(n) ((n) * 2) +#define CCS_L_FRAME_FORMAT_DESCRIPTOR_4 3 +#define CCS_L_FRAME_FORMAT_DESCRIPTOR_4_OFFSET(n) ((n) * 4) +#define CCS_L_ANALOG_GAIN_CAPABILITY 4 +#define CCS_L_ANALOG_GAIN_CODE_MIN 5 +#define CCS_L_ANALOG_GAIN_CODE_MAX 6 +#define CCS_L_ANALOG_GAIN_CODE_STEP 7 +#define CCS_L_ANALOG_GAIN_TYPE 8 +#define CCS_L_ANALOG_GAIN_M0 9 +#define CCS_L_ANALOG_GAIN_C0 10 +#define CCS_L_ANALOG_GAIN_M1 11 +#define CCS_L_ANALOG_GAIN_C1 12 +#define CCS_L_ANALOG_LINEAR_GAIN_MIN 13 +#define CCS_L_ANALOG_LINEAR_GAIN_MAX 14 +#define CCS_L_ANALOG_LINEAR_GAIN_STEP_SIZE 15 +#define CCS_L_ANALOG_EXPONENTIAL_GAIN_MIN 16 +#define CCS_L_ANALOG_EXPONENTIAL_GAIN_MAX 17 +#define CCS_L_ANALOG_EXPONENTIAL_GAIN_STEP_SIZE 18 +#define CCS_L_DATA_FORMAT_MODEL_TYPE 19 +#define CCS_L_DATA_FORMAT_MODEL_SUBTYPE 20 +#define CCS_L_DATA_FORMAT_DESCRIPTOR 21 +#define CCS_L_DATA_FORMAT_DESCRIPTOR_OFFSET(n) ((n) * 2) +#define CCS_L_INTEGRATION_TIME_CAPABILITY 22 +#define CCS_L_COARSE_INTEGRATION_TIME_MIN 23 +#define CCS_L_COARSE_INTEGRATION_TIME_MAX_MARGIN 24 +#define CCS_L_FINE_INTEGRATION_TIME_MIN 25 +#define CCS_L_FINE_INTEGRATION_TIME_MAX_MARGIN 26 +#define CCS_L_DIGITAL_GAIN_CAPABILITY 27 +#define CCS_L_DIGITAL_GAIN_MIN 28 +#define CCS_L_DIGITAL_GAIN_MAX 29 +#define CCS_L_DIGITAL_GAIN_STEP_SIZE 30 +#define CCS_L_PEDESTAL_CAPABILITY 31 +#define CCS_L_ADC_CAPABILITY 32 +#define CCS_L_ADC_BIT_DEPTH_CAPABILITY 33 +#define CCS_L_MIN_EXT_CLK_FREQ_MHZ 34 +#define CCS_L_MAX_EXT_CLK_FREQ_MHZ 35 +#define CCS_L_MIN_PRE_PLL_CLK_DIV 36 +#define CCS_L_MAX_PRE_PLL_CLK_DIV 37 +#define CCS_L_MIN_PLL_IP_CLK_FREQ_MHZ 38 +#define CCS_L_MAX_PLL_IP_CLK_FREQ_MHZ 39 +#define CCS_L_MIN_PLL_MULTIPLIER 40 +#define CCS_L_MAX_PLL_MULTIPLIER 41 +#define CCS_L_MIN_PLL_OP_CLK_FREQ_MHZ 42 +#define CCS_L_MAX_PLL_OP_CLK_FREQ_MHZ 43 +#define CCS_L_MIN_VT_SYS_CLK_DIV 44 +#define CCS_L_MAX_VT_SYS_CLK_DIV 45 +#define CCS_L_MIN_VT_SYS_CLK_FREQ_MHZ 46 +#define CCS_L_MAX_VT_SYS_CLK_FREQ_MHZ 47 +#define CCS_L_MIN_VT_PIX_CLK_FREQ_MHZ 48 +#define CCS_L_MAX_VT_PIX_CLK_FREQ_MHZ 49 +#define CCS_L_MIN_VT_PIX_CLK_DIV 50 +#define CCS_L_MAX_VT_PIX_CLK_DIV 51 +#define CCS_L_CLOCK_CALCULATION 52 +#define CCS_L_NUM_OF_VT_LANES 53 +#define CCS_L_NUM_OF_OP_LANES 54 +#define CCS_L_OP_BITS_PER_LANE 55 +#define CCS_L_MIN_FRAME_LENGTH_LINES 56 +#define CCS_L_MAX_FRAME_LENGTH_LINES 57 +#define CCS_L_MIN_LINE_LENGTH_PCK 58 +#define CCS_L_MAX_LINE_LENGTH_PCK 59 +#define CCS_L_MIN_LINE_BLANKING_PCK 60 +#define CCS_L_MIN_FRAME_BLANKING_LINES 61 +#define CCS_L_MIN_LINE_LENGTH_PCK_STEP_SIZE 62 +#define CCS_L_TIMING_MODE_CAPABILITY 63 +#define CCS_L_FRAME_MARGIN_MAX_VALUE 64 +#define CCS_L_FRAME_MARGIN_MIN_VALUE 65 +#define CCS_L_GAIN_DELAY_TYPE 66 +#define CCS_L_MIN_OP_SYS_CLK_DIV 67 +#define CCS_L_MAX_OP_SYS_CLK_DIV 68 +#define CCS_L_MIN_OP_SYS_CLK_FREQ_MHZ 69 +#define CCS_L_MAX_OP_SYS_CLK_FREQ_MHZ 70 +#define CCS_L_MIN_OP_PIX_CLK_DIV 71 +#define CCS_L_MAX_OP_PIX_CLK_DIV 72 +#define CCS_L_MIN_OP_PIX_CLK_FREQ_MHZ 73 +#define CCS_L_MAX_OP_PIX_CLK_FREQ_MHZ 74 +#define CCS_L_X_ADDR_MIN 75 +#define CCS_L_Y_ADDR_MIN 76 +#define CCS_L_X_ADDR_MAX 77 +#define CCS_L_Y_ADDR_MAX 78 +#define CCS_L_MIN_X_OUTPUT_SIZE 79 +#define CCS_L_MIN_Y_OUTPUT_SIZE 80 +#define CCS_L_MAX_X_OUTPUT_SIZE 81 +#define CCS_L_MAX_Y_OUTPUT_SIZE 82 +#define CCS_L_X_ADDR_START_DIV_CONSTANT 83 +#define CCS_L_Y_ADDR_START_DIV_CONSTANT 84 +#define CCS_L_X_ADDR_END_DIV_CONSTANT 85 +#define CCS_L_Y_ADDR_END_DIV_CONSTANT 86 +#define CCS_L_X_SIZE_DIV 87 +#define CCS_L_Y_SIZE_DIV 88 +#define CCS_L_X_OUTPUT_DIV 89 +#define CCS_L_Y_OUTPUT_DIV 90 +#define CCS_L_NON_FLEXIBLE_RESOLUTION_SUPPORT 91 +#define CCS_L_MIN_OP_PRE_PLL_CLK_DIV 92 +#define CCS_L_MAX_OP_PRE_PLL_CLK_DIV 93 +#define CCS_L_MIN_OP_PLL_IP_CLK_FREQ_MHZ 94 +#define CCS_L_MAX_OP_PLL_IP_CLK_FREQ_MHZ 95 +#define CCS_L_MIN_OP_PLL_MULTIPLIER 96 +#define CCS_L_MAX_OP_PLL_MULTIPLIER 97 +#define CCS_L_MIN_OP_PLL_OP_CLK_FREQ_MHZ 98 +#define CCS_L_MAX_OP_PLL_OP_CLK_FREQ_MHZ 99 +#define CCS_L_CLOCK_TREE_PLL_CAPABILITY 100 +#define CCS_L_CLOCK_CAPA_TYPE_CAPABILITY 101 +#define CCS_L_MIN_EVEN_INC 102 +#define CCS_L_MIN_ODD_INC 103 +#define CCS_L_MAX_EVEN_INC 104 +#define CCS_L_MAX_ODD_INC 105 +#define CCS_L_AUX_SUBSAMP_CAPABILITY 106 +#define CCS_L_AUX_SUBSAMP_MONO_CAPABILITY 107 +#define CCS_L_MONOCHROME_CAPABILITY 108 +#define CCS_L_PIXEL_READOUT_CAPABILITY 109 +#define CCS_L_MIN_EVEN_INC_MONO 110 +#define CCS_L_MAX_EVEN_INC_MONO 111 +#define CCS_L_MIN_ODD_INC_MONO 112 +#define CCS_L_MAX_ODD_INC_MONO 113 +#define CCS_L_MIN_EVEN_INC_BC2 114 +#define CCS_L_MAX_EVEN_INC_BC2 115 +#define CCS_L_MIN_ODD_INC_BC2 116 +#define CCS_L_MAX_ODD_INC_BC2 117 +#define CCS_L_MIN_EVEN_INC_MONO_BC2 118 +#define CCS_L_MAX_EVEN_INC_MONO_BC2 119 +#define CCS_L_MIN_ODD_INC_MONO_BC2 120 +#define CCS_L_MAX_ODD_INC_MONO_BC2 121 +#define CCS_L_SCALING_CAPABILITY 122 +#define CCS_L_SCALER_M_MIN 123 +#define CCS_L_SCALER_M_MAX 124 +#define CCS_L_SCALER_N_MIN 125 +#define CCS_L_SCALER_N_MAX 126 +#define CCS_L_DIGITAL_CROP_CAPABILITY 127 +#define CCS_L_HDR_CAPABILITY_1 128 +#define CCS_L_MIN_HDR_BIT_DEPTH 129 +#define CCS_L_HDR_RESOLUTION_SUB_TYPES 130 +#define CCS_L_HDR_RESOLUTION_SUB_TYPE 131 +#define CCS_L_HDR_RESOLUTION_SUB_TYPE_OFFSET(n) (n) +#define CCS_L_HDR_CAPABILITY_2 132 +#define CCS_L_MAX_HDR_BIT_DEPTH 133 +#define CCS_L_USL_SUPPORT_CAPABILITY 134 +#define CCS_L_USL_CLOCK_MODE_D_CAPABILITY 135 +#define CCS_L_MIN_OP_SYS_CLK_DIV_REV 136 +#define CCS_L_MAX_OP_SYS_CLK_DIV_REV 137 +#define CCS_L_MIN_OP_PIX_CLK_DIV_REV 138 +#define CCS_L_MAX_OP_PIX_CLK_DIV_REV 139 +#define CCS_L_MIN_OP_SYS_CLK_FREQ_REV_MHZ 140 +#define CCS_L_MAX_OP_SYS_CLK_FREQ_REV_MHZ 141 +#define CCS_L_MIN_OP_PIX_CLK_FREQ_REV_MHZ 142 +#define CCS_L_MAX_OP_PIX_CLK_FREQ_REV_MHZ 143 +#define CCS_L_MAX_BITRATE_REV_D_MODE_MBPS 144 +#define CCS_L_MAX_SYMRATE_REV_C_MODE_MSPS 145 +#define CCS_L_COMPRESSION_CAPABILITY 146 +#define CCS_L_TEST_MODE_CAPABILITY 147 +#define CCS_L_PN9_DATA_FORMAT1 148 +#define CCS_L_PN9_DATA_FORMAT2 149 +#define CCS_L_PN9_DATA_FORMAT3 150 +#define CCS_L_PN9_DATA_FORMAT4 151 +#define CCS_L_PN9_MISC_CAPABILITY 152 +#define CCS_L_TEST_PATTERN_CAPABILITY 153 +#define CCS_L_PATTERN_SIZE_DIV_M1 154 +#define CCS_L_FIFO_SUPPORT_CAPABILITY 155 +#define CCS_L_PHY_CTRL_CAPABILITY 156 +#define CCS_L_CSI_DPHY_LANE_MODE_CAPABILITY 157 +#define CCS_L_CSI_SIGNALING_MODE_CAPABILITY 158 +#define CCS_L_FAST_STANDBY_CAPABILITY 159 +#define CCS_L_CSI_ADDRESS_CONTROL_CAPABILITY 160 +#define CCS_L_DATA_TYPE_CAPABILITY 161 +#define CCS_L_CSI_CPHY_LANE_MODE_CAPABILITY 162 +#define CCS_L_EMB_DATA_CAPABILITY 163 +#define CCS_L_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS 164 +#define CCS_L_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS_OFFSET(n) ((n) * 4) +#define CCS_L_TEMP_SENSOR_CAPABILITY 165 +#define CCS_L_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS 166 +#define CCS_L_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS_OFFSET(n) ((n) * 4) +#define CCS_L_DPHY_EQUALIZATION_CAPABILITY 167 +#define CCS_L_CPHY_EQUALIZATION_CAPABILITY 168 +#define CCS_L_DPHY_PREAMBLE_CAPABILITY 169 +#define CCS_L_DPHY_SSC_CAPABILITY 170 +#define CCS_L_CPHY_CALIBRATION_CAPABILITY 171 +#define CCS_L_DPHY_CALIBRATION_CAPABILITY 172 +#define CCS_L_PHY_CTRL_CAPABILITY_2 173 +#define CCS_L_LRTE_CPHY_CAPABILITY 174 +#define CCS_L_LRTE_DPHY_CAPABILITY 175 +#define CCS_L_ALPS_CAPABILITY_DPHY 176 +#define CCS_L_ALPS_CAPABILITY_CPHY 177 +#define CCS_L_SCRAMBLING_CAPABILITY 178 +#define CCS_L_DPHY_MANUAL_CONSTANT 179 +#define CCS_L_CPHY_MANUAL_CONSTANT 180 +#define CCS_L_CSI2_INTERFACE_CAPABILITY_MISC 181 +#define CCS_L_PHY_CTRL_CAPABILITY_3 182 +#define CCS_L_DPHY_SF 183 +#define CCS_L_CPHY_SF 184 +#define CCS_L_DPHY_LIMITS_1 185 +#define CCS_L_DPHY_LIMITS_2 186 +#define CCS_L_DPHY_LIMITS_3 187 +#define CCS_L_DPHY_LIMITS_4 188 +#define CCS_L_DPHY_LIMITS_5 189 +#define CCS_L_DPHY_LIMITS_6 190 +#define CCS_L_CPHY_LIMITS_1 191 +#define CCS_L_CPHY_LIMITS_2 192 +#define CCS_L_CPHY_LIMITS_3 193 +#define CCS_L_MIN_FRAME_LENGTH_LINES_BIN 194 +#define CCS_L_MAX_FRAME_LENGTH_LINES_BIN 195 +#define CCS_L_MIN_LINE_LENGTH_PCK_BIN 196 +#define CCS_L_MAX_LINE_LENGTH_PCK_BIN 197 +#define CCS_L_MIN_LINE_BLANKING_PCK_BIN 198 +#define CCS_L_FINE_INTEGRATION_TIME_MIN_BIN 199 +#define CCS_L_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 200 +#define CCS_L_BINNING_CAPABILITY 201 +#define CCS_L_BINNING_WEIGHTING_CAPABILITY 202 +#define CCS_L_BINNING_SUB_TYPES 203 +#define CCS_L_BINNING_SUB_TYPE 204 +#define CCS_L_BINNING_SUB_TYPE_OFFSET(n) (n) +#define CCS_L_BINNING_WEIGHTING_MONO_CAPABILITY 205 +#define CCS_L_BINNING_SUB_TYPES_MONO 206 +#define CCS_L_BINNING_SUB_TYPE_MONO 207 +#define CCS_L_BINNING_SUB_TYPE_MONO_OFFSET(n) (n) +#define CCS_L_DATA_TRANSFER_IF_CAPABILITY 208 +#define CCS_L_SHADING_CORRECTION_CAPABILITY 209 +#define CCS_L_GREEN_IMBALANCE_CAPABILITY 210 +#define CCS_L_MODULE_SPECIFIC_CORRECTION_CAPABILITY 211 +#define CCS_L_DEFECT_CORRECTION_CAPABILITY 212 +#define CCS_L_DEFECT_CORRECTION_CAPABILITY_2 213 +#define CCS_L_NF_CAPABILITY 214 +#define CCS_L_OB_READOUT_CAPABILITY 215 +#define CCS_L_COLOR_FEEDBACK_CAPABILITY 216 +#define CCS_L_CFA_PATTERN_CAPABILITY 217 +#define CCS_L_CFA_PATTERN_CONVERSION_CAPABILITY 218 +#define CCS_L_FLASH_MODE_CAPABILITY 219 +#define CCS_L_SA_STROBE_MODE_CAPABILITY 220 +#define CCS_L_RESET_MAX_DELAY 221 +#define CCS_L_RESET_MIN_TIME 222 +#define CCS_L_PDAF_CAPABILITY_1 223 +#define CCS_L_PDAF_CAPABILITY_2 224 +#define CCS_L_BRACKETING_LUT_CAPABILITY_1 225 +#define CCS_L_BRACKETING_LUT_CAPABILITY_2 226 +#define CCS_L_BRACKETING_LUT_SIZE 227 +#define CCS_L_LAST 228 + +#endif /* __CCS_LIMITS_H__ */ diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/ccs/ccs-quirk.c index ab96d6067fc3..e3d4c7a275bc 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/ccs/ccs-quirk.c @@ -1,30 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * drivers/media/i2c/smiapp/smiapp-quirk.c + * drivers/media/i2c/ccs/ccs-quirk.c * - * Generic driver for SMIA/SMIA++ compliant camera modules + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors * + * Copyright (C) 2020 Intel Corporation * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> */ #include <linux/delay.h> -#include "smiapp.h" +#include "ccs.h" +#include "ccs-limits.h" -static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) -{ - return smiapp_write(sensor, SMIAPP_REG_MK_U8(reg), val); -} - -static int smiapp_write_8s(struct smiapp_sensor *sensor, - const struct smiapp_reg_8 *regs, int len) +static int ccs_write_addr_8s(struct ccs_sensor *sensor, + const struct ccs_reg_8 *regs, int len) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; for (; len > 0; len--, regs++) { - rval = smiapp_write_8(sensor, regs->reg, regs->val); + rval = ccs_write_addr(sensor, regs->reg, regs->val); if (rval < 0) { dev_err(&client->dev, "error %d writing reg 0x%4.4x, val 0x%2.2x", @@ -36,34 +33,22 @@ static int smiapp_write_8s(struct smiapp_sensor *sensor, return 0; } -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", - smiapp_reg_limits[limit].addr, - smiapp_reg_limits[limit].what, val, val); - sensor->limits[limit] = val; -} - -static int jt8ew9_limits(struct smiapp_sensor *sensor) +static int jt8ew9_limits(struct ccs_sensor *sensor) { - if (sensor->minfo.revision_number_major < 0x03) + if (sensor->minfo.revision_number < 0x0300) sensor->frame_skip = 1; /* Below 24 gain doesn't have effect at all, */ /* but ~59 is needed for full dynamic range */ - smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); - smiapp_replace_limit( - sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + ccs_replace_limit(sensor, CCS_L_ANALOG_GAIN_CODE_MIN, 0, 59); + ccs_replace_limit(sensor, CCS_L_ANALOG_GAIN_CODE_MAX, 0, 6000); return 0; } -static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +static int jt8ew9_post_poweron(struct ccs_sensor *sensor) { - static const struct smiapp_reg_8 regs[] = { + static const struct ccs_reg_8 regs[] = { { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ @@ -96,18 +81,18 @@ static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) }; - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + return ccs_write_addr_8s(sensor, regs, ARRAY_SIZE(regs)); } -const struct smiapp_quirk smiapp_jt8ew9_quirk = { +const struct ccs_quirk smiapp_jt8ew9_quirk = { .limits = jt8ew9_limits, .post_poweron = jt8ew9_post_poweron, }; -static int imx125es_post_poweron(struct smiapp_sensor *sensor) +static int imx125es_post_poweron(struct ccs_sensor *sensor) { /* Taken from v02. No idea what the other two are. */ - static const struct smiapp_reg_8 regs[] = { + static const struct ccs_reg_8 regs[] = { /* * 0x3302: clk during frame blanking: * 0x00 - HS mode, 0x01 - LP11 @@ -117,27 +102,26 @@ static int imx125es_post_poweron(struct smiapp_sensor *sensor) { 0x3b08, 0x8c }, }; - return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + return ccs_write_addr_8s(sensor, regs, ARRAY_SIZE(regs)); } -const struct smiapp_quirk smiapp_imx125es_quirk = { +const struct ccs_quirk smiapp_imx125es_quirk = { .post_poweron = imx125es_post_poweron, }; -static int jt8ev1_limits(struct smiapp_sensor *sensor) +static int jt8ev1_limits(struct ccs_sensor *sensor) { - smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); - smiapp_replace_limit(sensor, - SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + ccs_replace_limit(sensor, CCS_L_X_ADDR_MAX, 0, 4271); + ccs_replace_limit(sensor, CCS_L_MIN_LINE_BLANKING_PCK_BIN, 0, 184); return 0; } -static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +static int jt8ev1_post_poweron(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - static const struct smiapp_reg_8 regs[] = { + static const struct ccs_reg_8 regs[] = { { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ @@ -158,38 +142,38 @@ static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) { 0x33cf, 0xec }, /* For Black sun */ { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ }; - static const struct smiapp_reg_8 regs_96[] = { + static const struct ccs_reg_8 regs_96[] = { { 0x30ae, 0x00 }, /* For control of ADC clock */ { 0x30af, 0xd0 }, { 0x30b0, 0x01 }, }; - rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + rval = ccs_write_addr_8s(sensor, regs, ARRAY_SIZE(regs)); if (rval < 0) return rval; - switch (sensor->hwcfg->ext_clk) { + switch (sensor->hwcfg.ext_clk) { case 9600000: - return smiapp_write_8s(sensor, regs_96, + return ccs_write_addr_8s(sensor, regs_96, ARRAY_SIZE(regs_96)); default: dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", - sensor->hwcfg->ext_clk); + sensor->hwcfg.ext_clk); return 0; } } -static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +static int jt8ev1_pre_streamon(struct ccs_sensor *sensor) { - return smiapp_write_8(sensor, 0x3328, 0x00); + return ccs_write_addr(sensor, 0x3328, 0x00); } -static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +static int jt8ev1_post_streamoff(struct ccs_sensor *sensor) { int rval; /* Workaround: allows fast standby to work properly */ - rval = smiapp_write_8(sensor, 0x3205, 0x04); + rval = ccs_write_addr(sensor, 0x3205, 0x04); if (rval < 0) return rval; @@ -197,21 +181,24 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) usleep_range(2000, 2050); /* Restore it */ - rval = smiapp_write_8(sensor, 0x3205, 0x00); + rval = ccs_write_addr(sensor, 0x3205, 0x00); if (rval < 0) return rval; - return smiapp_write_8(sensor, 0x3328, 0x80); + return ccs_write_addr(sensor, 0x3328, 0x80); } -static int jt8ev1_init(struct smiapp_sensor *sensor) +static int jt8ev1_init(struct ccs_sensor *sensor) { - sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + sensor->pll.flags |= CCS_PLL_FLAG_LANE_SPEED_MODEL | + CCS_PLL_FLAG_LINK_DECOUPLED; + sensor->pll.vt_lanes = 1; + sensor->pll.op_lanes = sensor->pll.csi2.lanes; return 0; } -const struct smiapp_quirk smiapp_jt8ev1_quirk = { +const struct ccs_quirk smiapp_jt8ev1_quirk = { .limits = jt8ev1_limits, .post_poweron = jt8ev1_post_poweron, .pre_streamon = jt8ev1_pre_streamon, @@ -219,13 +206,13 @@ const struct smiapp_quirk smiapp_jt8ev1_quirk = { .init = jt8ev1_init, }; -static int tcm8500md_limits(struct smiapp_sensor *sensor) +static int tcm8500md_limits(struct ccs_sensor *sensor) { - smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + ccs_replace_limit(sensor, CCS_L_MIN_PLL_IP_CLK_FREQ_MHZ, 0, 2700000); return 0; } -const struct smiapp_quirk smiapp_tcm8500md_quirk = { +const struct ccs_quirk smiapp_tcm8500md_quirk = { .limits = tcm8500md_limits, }; diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/ccs/ccs-quirk.h index 17505be60c1d..6b4ec4beaba0 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/ccs/ccs-quirk.h @@ -1,20 +1,21 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/media/i2c/smiapp/smiapp-quirk.h + * drivers/media/i2c/ccs/ccs-quirk.h * - * Generic driver for SMIA/SMIA++ compliant camera modules + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors * + * Copyright (C) 2020 Intel Corporation * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> */ -#ifndef __SMIAPP_QUIRK__ -#define __SMIAPP_QUIRK__ +#ifndef __CCS_QUIRK__ +#define __CCS_QUIRK__ -struct smiapp_sensor; +struct ccs_sensor; /** - * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * struct ccs_quirk - quirks for sensors that deviate from SMIA++ standard * * @limits: Replace sensor->limits with values which can't be read from * sensor registers. Called the first time the sensor is powered up. @@ -36,46 +37,43 @@ struct smiapp_sensor; * access may be done by the caller (default read * value is zero), else negative error code on error */ -struct smiapp_quirk { - int (*limits)(struct smiapp_sensor *sensor); - int (*post_poweron)(struct smiapp_sensor *sensor); - int (*pre_streamon)(struct smiapp_sensor *sensor); - int (*post_streamoff)(struct smiapp_sensor *sensor); - unsigned long (*pll_flags)(struct smiapp_sensor *sensor); - int (*init)(struct smiapp_sensor *sensor); - int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg, +struct ccs_quirk { + int (*limits)(struct ccs_sensor *sensor); + int (*post_poweron)(struct ccs_sensor *sensor); + int (*pre_streamon)(struct ccs_sensor *sensor); + int (*post_streamoff)(struct ccs_sensor *sensor); + unsigned long (*pll_flags)(struct ccs_sensor *sensor); + int (*init)(struct ccs_sensor *sensor); + int (*reg_access)(struct ccs_sensor *sensor, bool write, u32 *reg, u32 *val); unsigned long flags; }; -#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 0) +#define CCS_QUIRK_FLAG_8BIT_READ_ONLY (1 << 0) -struct smiapp_reg_8 { +struct ccs_reg_8 { u16 reg; u8 val; }; -void smiapp_replace_limit(struct smiapp_sensor *sensor, - u32 limit, u32 val); - -#define SMIAPP_MK_QUIRK_REG_8(_reg, _val) \ +#define CCS_MK_QUIRK_REG_8(_reg, _val) \ { \ .reg = (u16)_reg, \ .val = _val, \ } -#define smiapp_call_quirk(sensor, _quirk, ...) \ +#define ccs_call_quirk(sensor, _quirk, ...) \ ((sensor)->minfo.quirk && \ (sensor)->minfo.quirk->_quirk ? \ (sensor)->minfo.quirk->_quirk(sensor, ##__VA_ARGS__) : 0) -#define smiapp_needs_quirk(sensor, _quirk) \ +#define ccs_needs_quirk(sensor, _quirk) \ ((sensor)->minfo.quirk ? \ (sensor)->minfo.quirk->flags & _quirk : 0) -extern const struct smiapp_quirk smiapp_jt8ev1_quirk; -extern const struct smiapp_quirk smiapp_imx125es_quirk; -extern const struct smiapp_quirk smiapp_jt8ew9_quirk; -extern const struct smiapp_quirk smiapp_tcm8500md_quirk; +extern const struct ccs_quirk smiapp_jt8ev1_quirk; +extern const struct ccs_quirk smiapp_imx125es_quirk; +extern const struct ccs_quirk smiapp_jt8ew9_quirk; +extern const struct ccs_quirk smiapp_tcm8500md_quirk; -#endif /* __SMIAPP_QUIRK__ */ +#endif /* __CCS_QUIRK__ */ diff --git a/drivers/media/i2c/ccs/ccs-reg-access.c b/drivers/media/i2c/ccs/ccs-reg-access.c new file mode 100644 index 000000000000..b776af2a3c33 --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-reg-access.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/media/i2c/ccs/ccs-reg-access.c + * + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> + */ + +#include <asm/unaligned.h> + +#include <linux/delay.h> +#include <linux/i2c.h> + +#include "ccs.h" +#include "ccs-limits.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int ____ccs_read_addr(struct ccs_sensor *sensor, u16 reg, u16 len, + u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data_buf[sizeof(u32)] = { 0 }; + unsigned char offset_buf[sizeof(u16)]; + int r; + + if (len > sizeof(data_buf)) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = sizeof(offset_buf); + msg.buf = offset_buf; + put_unaligned_be16(reg, offset_buf); + + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + msg.buf = &data_buf[sizeof(data_buf) - len]; + + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = get_unaligned_be32(data_buf); + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); + + return r; +} + +/* Read a register using 8-bit access only. */ +static int ____ccs_read_addr_8only(struct ccs_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + unsigned int i; + int rval; + + *val = 0; + + for (i = 0; i < len; i++) { + u32 val8; + + rval = ____ccs_read_addr(sensor, reg + i, 1, &val8); + if (rval < 0) + return rval; + *val |= val8 << ((len - i - 1) << 3); + } + + return 0; +} + +unsigned int ccs_reg_width(u32 reg) +{ + if (reg & CCS_FL_16BIT) + return sizeof(uint16_t); + if (reg & CCS_FL_32BIT) + return sizeof(uint32_t); + + return sizeof(uint8_t); +} + +static u32 ireal32_to_u32_mul_1000000(struct i2c_client *client, u32 val) +{ + if (val >> 10 > U32_MAX / 15625) { + dev_warn(&client->dev, "value %u overflows!\n", val); + return U32_MAX; + } + + return ((val >> 10) * 15625) + + (val & GENMASK(9, 0)) * 15625 / 1024; +} + +u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + if (reg & CCS_FL_FLOAT_IREAL) { + if (CCS_LIM(sensor, CLOCK_CAPA_TYPE_CAPABILITY) & + CCS_CLOCK_CAPA_TYPE_CAPABILITY_IREAL) + val = ireal32_to_u32_mul_1000000(client, val); + else + val = float_to_u32_mul_1000000(client, val); + } else if (reg & CCS_FL_IREAL) { + val = ireal32_to_u32_mul_1000000(client, val); + } + + return val; +} + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int __ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val, + bool only8, bool conv) +{ + unsigned int len = ccs_reg_width(reg); + int rval; + + if (!only8) + rval = ____ccs_read_addr(sensor, CCS_REG_ADDR(reg), len, val); + else + rval = ____ccs_read_addr_8only(sensor, CCS_REG_ADDR(reg), len, + val); + if (rval < 0) + return rval; + + if (!conv) + return 0; + + *val = ccs_reg_conv(sensor, reg, *val); + + return 0; +} + +static int __ccs_read_data(struct ccs_reg *regs, size_t num_regs, + u32 reg, u32 *val) +{ + unsigned int width = ccs_reg_width(reg); + size_t i; + + for (i = 0; i < num_regs; i++, regs++) { + uint8_t *data; + + if (regs->addr + regs->len < CCS_REG_ADDR(reg) + width) + continue; + + if (regs->addr > CCS_REG_ADDR(reg)) + break; + + data = ®s->value[CCS_REG_ADDR(reg) - regs->addr]; + + switch (width) { + case sizeof(uint8_t): + *val = *data; + break; + case sizeof(uint16_t): + *val = get_unaligned_be16(data); + break; + case sizeof(uint32_t): + *val = get_unaligned_be32(data); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + return 0; + } + + return -ENOENT; +} + +static int ccs_read_data(struct ccs_sensor *sensor, u32 reg, u32 *val) +{ + if (!__ccs_read_data(sensor->sdata.sensor_read_only_regs, + sensor->sdata.num_sensor_read_only_regs, + reg, val)) + return 0; + + return __ccs_read_data(sensor->mdata.module_read_only_regs, + sensor->mdata.num_module_read_only_regs, + reg, val); +} + +static int ccs_read_addr_raw(struct ccs_sensor *sensor, u32 reg, u32 *val, + bool force8, bool quirk, bool conv, bool data) +{ + int rval; + + if (data) { + rval = ccs_read_data(sensor, reg, val); + if (!rval) + return 0; + } + + if (quirk) { + *val = 0; + rval = ccs_call_quirk(sensor, reg_access, false, ®, val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + if (force8) + return __ccs_read_addr(sensor, reg, val, true, conv); + } + + return __ccs_read_addr(sensor, reg, val, + ccs_needs_quirk(sensor, + CCS_QUIRK_FLAG_8BIT_READ_ONLY), + conv); +} + +int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val) +{ + return ccs_read_addr_raw(sensor, reg, val, false, true, true, true); +} + +int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val) +{ + return ccs_read_addr_raw(sensor, reg, val, true, true, true, true); +} + +int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val) +{ + return ccs_read_addr_raw(sensor, reg, val, false, true, false, true); +} + +static int ccs_write_retry(struct i2c_client *client, struct i2c_msg *msg) +{ + unsigned int retries; + int r; + + for (retries = 0; retries < 10; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, msg, 1); + if (r != 1) { + usleep_range(1000, 2000); + continue; + } + + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. retries: %d\n", + retries); + return 0; + } + + return r; +} + +int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[6]; + unsigned int len = ccs_reg_width(reg); + int r; + + if (len > sizeof(data) - 2) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + put_unaligned_be16(CCS_REG_ADDR(reg), data); + put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2); + + dev_dbg(&client->dev, "writing reg 0x%4.4x value 0x%*.*x (%u)\n", + CCS_REG_ADDR(reg), ccs_reg_width(reg) << 1, + ccs_reg_width(reg) << 1, val, val); + + r = ccs_write_retry(client, &msg); + if (r) + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, + CCS_REG_ADDR(reg), r); + + return r; +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val) +{ + int rval; + + rval = ccs_call_quirk(sensor, reg_access, true, ®, &val); + if (rval == -ENOIOCTLCMD) + return 0; + if (rval < 0) + return rval; + + return ccs_write_addr_no_quirk(sensor, reg, val); +} + +#define MAX_WRITE_LEN 32U + +int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs, + size_t num_regs) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned char buf[2 + MAX_WRITE_LEN]; + struct i2c_msg msg = { + .addr = client->addr, + .buf = buf, + }; + size_t i; + + for (i = 0; i < num_regs; i++, regs++) { + unsigned char *regdata = regs->value; + unsigned int j; + + for (j = 0; j < regs->len; + j += msg.len - 2, regdata += msg.len - 2) { + int rval; + + msg.len = min(regs->len - j, MAX_WRITE_LEN); + + put_unaligned_be16(regs->addr + j, buf); + memcpy(buf + 2, regdata, msg.len); + msg.len += 2; + + rval = ccs_write_retry(client, &msg); + if (rval) { + dev_err(&client->dev, + "error writing %u octets to address 0x%4.4x\n", + msg.len, regs->addr + j); + return rval; + } + } + } + + return 0; +} diff --git a/drivers/media/i2c/ccs/ccs-reg-access.h b/drivers/media/i2c/ccs/ccs-reg-access.h new file mode 100644 index 000000000000..78c43f92d99a --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-reg-access.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * include/media/ccs/ccs-reg-access.h + * + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> + */ + +#ifndef SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include <linux/i2c.h> +#include <linux/types.h> + +#include "ccs-regs.h" + +#define CCS_REG_ADDR(reg) ((u16)reg) + +struct ccs_sensor; + +int ccs_read_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 *val); +int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val); +int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val); +int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val); +int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val); +int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val); +int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs, + size_t num_regs); + +unsigned int ccs_reg_width(u32 reg); +u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val); + +#define ccs_read(sensor, reg_name, val) \ + ccs_read_addr(sensor, CCS_R_##reg_name, val) + +#define ccs_write(sensor, reg_name, val) \ + ccs_write_addr(sensor, CCS_R_##reg_name, val) + +#endif diff --git a/drivers/media/i2c/ccs/ccs-regs.h b/drivers/media/i2c/ccs/ccs-regs.h new file mode 100644 index 000000000000..4b3e5df2121f --- /dev/null +++ b/drivers/media/i2c/ccs/ccs-regs.h @@ -0,0 +1,954 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause */ +/* Copyright (C) 2019--2020 Intel Corporation */ + +#ifndef __CCS_REGS_H__ +#define __CCS_REGS_H__ + +#include <linux/bits.h> + +#define CCS_FL_BASE 16 +#define CCS_FL_16BIT BIT(CCS_FL_BASE) +#define CCS_FL_32BIT BIT(CCS_FL_BASE + 1) +#define CCS_FL_FLOAT_IREAL BIT(CCS_FL_BASE + 2) +#define CCS_FL_IREAL BIT(CCS_FL_BASE + 3) +#define CCS_R_ADDR(r) ((r) & 0xffff) + +#define CCS_R_MODULE_MODEL_ID (0x0000 | CCS_FL_16BIT) +#define CCS_R_MODULE_REVISION_NUMBER_MAJOR 0x0002 +#define CCS_R_FRAME_COUNT 0x0005 +#define CCS_R_PIXEL_ORDER 0x0006 +#define CCS_PIXEL_ORDER_GRBG 0U +#define CCS_PIXEL_ORDER_RGGB 1U +#define CCS_PIXEL_ORDER_BGGR 2U +#define CCS_PIXEL_ORDER_GBRG 3U +#define CCS_R_MIPI_CCS_VERSION 0x0007 +#define CCS_MIPI_CCS_VERSION_V1_0 0x10 +#define CCS_MIPI_CCS_VERSION_V1_1 0x11 +#define CCS_MIPI_CCS_VERSION_MAJOR_SHIFT 4U +#define CCS_MIPI_CCS_VERSION_MAJOR_MASK 0xf0 +#define CCS_MIPI_CCS_VERSION_MINOR_SHIFT 0U +#define CCS_MIPI_CCS_VERSION_MINOR_MASK 0xf +#define CCS_R_DATA_PEDESTAL (0x0008 | CCS_FL_16BIT) +#define CCS_R_MODULE_MANUFACTURER_ID (0x000e | CCS_FL_16BIT) +#define CCS_R_MODULE_REVISION_NUMBER_MINOR 0x0010 +#define CCS_R_MODULE_DATE_YEAR 0x0012 +#define CCS_R_MODULE_DATE_MONTH 0x0013 +#define CCS_R_MODULE_DATE_DAY 0x0014 +#define CCS_R_MODULE_DATE_PHASE 0x0015 +#define CCS_MODULE_DATE_PHASE_SHIFT 0U +#define CCS_MODULE_DATE_PHASE_MASK 0x7 +#define CCS_MODULE_DATE_PHASE_TS 0U +#define CCS_MODULE_DATE_PHASE_ES 1U +#define CCS_MODULE_DATE_PHASE_CS 2U +#define CCS_MODULE_DATE_PHASE_MP 3U +#define CCS_R_SENSOR_MODEL_ID (0x0016 | CCS_FL_16BIT) +#define CCS_R_SENSOR_REVISION_NUMBER 0x0018 +#define CCS_R_SENSOR_FIRMWARE_VERSION 0x001a +#define CCS_R_SERIAL_NUMBER (0x001c | CCS_FL_32BIT) +#define CCS_R_SENSOR_MANUFACTURER_ID (0x0020 | CCS_FL_16BIT) +#define CCS_R_SENSOR_REVISION_NUMBER_16 (0x0022 | CCS_FL_16BIT) +#define CCS_R_FRAME_FORMAT_MODEL_TYPE 0x0040 +#define CCS_FRAME_FORMAT_MODEL_TYPE_2_BYTE 1U +#define CCS_FRAME_FORMAT_MODEL_TYPE_4_BYTE 2U +#define CCS_R_FRAME_FORMAT_MODEL_SUBTYPE 0x0041 +#define CCS_FRAME_FORMAT_MODEL_SUBTYPE_ROWS_SHIFT 0U +#define CCS_FRAME_FORMAT_MODEL_SUBTYPE_ROWS_MASK 0xf +#define CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_SHIFT 4U +#define CCS_FRAME_FORMAT_MODEL_SUBTYPE_COLUMNS_MASK 0xf0 +#define CCS_R_FRAME_FORMAT_DESCRIPTOR(n) ((0x0042 | CCS_FL_16BIT) + (n) * 2) +#define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_MIN_N 0U +#define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_MAX_N 14U +#define CCS_R_FRAME_FORMAT_DESCRIPTOR_4(n) ((0x0060 | CCS_FL_32BIT) + (n) * 4) +#define CCS_FRAME_FORMAT_DESCRIPTOR_PIXELS_SHIFT 0U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PIXELS_MASK 0xfff +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_SHIFT 12U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MASK 0xf000 +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_EMBEDDED 1U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_DUMMY_PIXEL 2U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_BLACK_PIXEL 3U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_DARK_PIXEL 4U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_VISIBLE_PIXEL 5U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_0 8U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_1 9U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_2 10U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_3 11U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_4 12U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_5 13U +#define CCS_FRAME_FORMAT_DESCRIPTOR_PCODE_MANUF_SPECIFIC_6 14U +#define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_4_MIN_N 0U +#define CCS_LIM_FRAME_FORMAT_DESCRIPTOR_4_MAX_N 7U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PIXELS_SHIFT 0U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PIXELS_MASK 0xffff +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_SHIFT 28U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MASK 0xf0000000 +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_EMBEDDED 1U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_DUMMY_PIXEL 2U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_BLACK_PIXEL 3U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_DARK_PIXEL 4U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_VISIBLE_PIXEL 5U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_0 8U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_1 9U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_2 10U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_3 11U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_4 12U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_5 13U +#define CCS_FRAME_FORMAT_DESCRIPTOR_4_PCODE_MANUF_SPECIFIC_6 14U +#define CCS_R_ANALOG_GAIN_CAPABILITY (0x0080 | CCS_FL_16BIT) +#define CCS_ANALOG_GAIN_CAPABILITY_GLOBAL 0U +#define CCS_ANALOG_GAIN_CAPABILITY_ALTERNATE_GLOBAL 2U +#define CCS_R_ANALOG_GAIN_CODE_MIN (0x0084 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_CODE_MAX (0x0086 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_CODE_STEP (0x0088 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_TYPE (0x008a | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_M0 (0x008c | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_C0 (0x008e | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_M1 (0x0090 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_C1 (0x0092 | CCS_FL_16BIT) +#define CCS_R_ANALOG_LINEAR_GAIN_MIN (0x0094 | CCS_FL_16BIT) +#define CCS_R_ANALOG_LINEAR_GAIN_MAX (0x0096 | CCS_FL_16BIT) +#define CCS_R_ANALOG_LINEAR_GAIN_STEP_SIZE (0x0098 | CCS_FL_16BIT) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MIN (0x009a | CCS_FL_16BIT) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_MAX (0x009c | CCS_FL_16BIT) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_STEP_SIZE (0x009e | CCS_FL_16BIT) +#define CCS_R_DATA_FORMAT_MODEL_TYPE 0x00c0 +#define CCS_DATA_FORMAT_MODEL_TYPE_NORMAL 1U +#define CCS_DATA_FORMAT_MODEL_TYPE_EXTENDED 2U +#define CCS_R_DATA_FORMAT_MODEL_SUBTYPE 0x00c1 +#define CCS_DATA_FORMAT_MODEL_SUBTYPE_ROWS_SHIFT 0U +#define CCS_DATA_FORMAT_MODEL_SUBTYPE_ROWS_MASK 0xf +#define CCS_DATA_FORMAT_MODEL_SUBTYPE_COLUMNS_SHIFT 4U +#define CCS_DATA_FORMAT_MODEL_SUBTYPE_COLUMNS_MASK 0xf0 +#define CCS_R_DATA_FORMAT_DESCRIPTOR(n) ((0x00c2 | CCS_FL_16BIT) + (n) * 2) +#define CCS_LIM_DATA_FORMAT_DESCRIPTOR_MIN_N 0U +#define CCS_LIM_DATA_FORMAT_DESCRIPTOR_MAX_N 15U +#define CCS_DATA_FORMAT_DESCRIPTOR_COMPRESSED_SHIFT 0U +#define CCS_DATA_FORMAT_DESCRIPTOR_COMPRESSED_MASK 0xff +#define CCS_DATA_FORMAT_DESCRIPTOR_UNCOMPRESSED_SHIFT 8U +#define CCS_DATA_FORMAT_DESCRIPTOR_UNCOMPRESSED_MASK 0xff00 +#define CCS_R_MODE_SELECT 0x0100 +#define CCS_MODE_SELECT_SOFTWARE_STANDBY 0U +#define CCS_MODE_SELECT_STREAMING 1U +#define CCS_R_IMAGE_ORIENTATION 0x0101 +#define CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR BIT(0) +#define CCS_IMAGE_ORIENTATION_VERTICAL_FLIP BIT(1) +#define CCS_R_SOFTWARE_RESET 0x0103 +#define CCS_SOFTWARE_RESET_OFF 0U +#define CCS_SOFTWARE_RESET_ON 1U +#define CCS_R_GROUPED_PARAMETER_HOLD 0x0104 +#define CCS_R_MASK_CORRUPTED_FRAMES 0x0105 +#define CCS_MASK_CORRUPTED_FRAMES_ALLOW 0U +#define CCS_MASK_CORRUPTED_FRAMES_MASK 1U +#define CCS_R_FAST_STANDBY_CTRL 0x0106 +#define CCS_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0U +#define CCS_FAST_STANDBY_CTRL_FRAME_TRUNCATION 1U +#define CCS_R_CCI_ADDRESS_CTRL 0x0107 +#define CCS_R_2ND_CCI_IF_CTRL 0x0108 +#define CCS_2ND_CCI_IF_CTRL_ENABLE BIT(0) +#define CCS_2ND_CCI_IF_CTRL_ACK BIT(1) +#define CCS_R_2ND_CCI_ADDRESS_CTRL 0x0109 +#define CCS_R_CSI_CHANNEL_IDENTIFIER 0x0110 +#define CCS_R_CSI_SIGNALING_MODE 0x0111 +#define CCS_CSI_SIGNALING_MODE_CSI_2_DPHY 2U +#define CCS_CSI_SIGNALING_MODE_CSI_2_CPHY 3U +#define CCS_R_CSI_DATA_FORMAT (0x0112 | CCS_FL_16BIT) +#define CCS_R_CSI_LANE_MODE 0x0114 +#define CCS_R_DPCM_FRAME_DT 0x011d +#define CCS_R_BOTTOM_EMBEDDED_DATA_DT 0x011e +#define CCS_R_BOTTOM_EMBEDDED_DATA_VC 0x011f +#define CCS_R_GAIN_MODE 0x0120 +#define CCS_GAIN_MODE_GLOBAL 0U +#define CCS_GAIN_MODE_ALTERNATE 1U +#define CCS_R_ADC_BIT_DEPTH 0x0121 +#define CCS_R_EMB_DATA_CTRL 0x0122 +#define CCS_EMB_DATA_CTRL_RAW8_PACKING_FOR_RAW16 BIT(0) +#define CCS_EMB_DATA_CTRL_RAW10_PACKING_FOR_RAW20 BIT(1) +#define CCS_EMB_DATA_CTRL_RAW12_PACKING_FOR_RAW24 BIT(2) +#define CCS_R_GPIO_TRIG_MODE 0x0130 +#define CCS_R_EXTCLK_FREQUENCY_MHZ (0x0136 | (CCS_FL_16BIT | CCS_FL_IREAL)) +#define CCS_R_TEMP_SENSOR_CTRL 0x0138 +#define CCS_TEMP_SENSOR_CTRL_ENABLE BIT(0) +#define CCS_R_TEMP_SENSOR_MODE 0x0139 +#define CCS_R_TEMP_SENSOR_OUTPUT 0x013a +#define CCS_R_FINE_INTEGRATION_TIME (0x0200 | CCS_FL_16BIT) +#define CCS_R_COARSE_INTEGRATION_TIME (0x0202 | CCS_FL_16BIT) +#define CCS_R_ANALOG_GAIN_CODE_GLOBAL (0x0204 | CCS_FL_16BIT) +#define CCS_R_ANALOG_LINEAR_GAIN_GLOBAL (0x0206 | CCS_FL_16BIT) +#define CCS_R_ANALOG_EXPONENTIAL_GAIN_GLOBAL (0x0208 | CCS_FL_16BIT) +#define CCS_R_DIGITAL_GAIN_GLOBAL (0x020e | CCS_FL_16BIT) +#define CCS_R_SHORT_ANALOG_GAIN_GLOBAL (0x0216 | CCS_FL_16BIT) +#define CCS_R_SHORT_DIGITAL_GAIN_GLOBAL (0x0218 | CCS_FL_16BIT) +#define CCS_R_HDR_MODE 0x0220 +#define CCS_HDR_MODE_ENABLED BIT(0) +#define CCS_HDR_MODE_SEPARATE_ANALOG_GAIN BIT(1) +#define CCS_HDR_MODE_UPSCALING BIT(2) +#define CCS_HDR_MODE_RESET_SYNC BIT(3) +#define CCS_HDR_MODE_TIMING_MODE BIT(4) +#define CCS_HDR_MODE_EXPOSURE_CTRL_DIRECT BIT(5) +#define CCS_HDR_MODE_SEPARATE_DIGITAL_GAIN BIT(6) +#define CCS_R_HDR_RESOLUTION_REDUCTION 0x0221 +#define CCS_HDR_RESOLUTION_REDUCTION_ROW_SHIFT 0U +#define CCS_HDR_RESOLUTION_REDUCTION_ROW_MASK 0xf +#define CCS_HDR_RESOLUTION_REDUCTION_COLUMN_SHIFT 4U +#define CCS_HDR_RESOLUTION_REDUCTION_COLUMN_MASK 0xf0 +#define CCS_R_EXPOSURE_RATIO 0x0222 +#define CCS_R_HDR_INTERNAL_BIT_DEPTH 0x0223 +#define CCS_R_DIRECT_SHORT_INTEGRATION_TIME (0x0224 | CCS_FL_16BIT) +#define CCS_R_SHORT_ANALOG_LINEAR_GAIN_GLOBAL (0x0226 | CCS_FL_16BIT) +#define CCS_R_SHORT_ANALOG_EXPONENTIAL_GAIN_GLOBAL (0x0228 | CCS_FL_16BIT) +#define CCS_R_VT_PIX_CLK_DIV (0x0300 | CCS_FL_16BIT) +#define CCS_R_VT_SYS_CLK_DIV (0x0302 | CCS_FL_16BIT) +#define CCS_R_PRE_PLL_CLK_DIV (0x0304 | CCS_FL_16BIT) +#define CCS_R_PLL_MULTIPLIER (0x0306 | CCS_FL_16BIT) +#define CCS_R_OP_PIX_CLK_DIV (0x0308 | CCS_FL_16BIT) +#define CCS_R_OP_SYS_CLK_DIV (0x030a | CCS_FL_16BIT) +#define CCS_R_OP_PRE_PLL_CLK_DIV (0x030c | CCS_FL_16BIT) +#define CCS_R_OP_PLL_MULTIPLIER (0x031e | CCS_FL_16BIT) +#define CCS_R_PLL_MODE 0x0310 +#define CCS_PLL_MODE_SHIFT 0U +#define CCS_PLL_MODE_MASK 0x1 +#define CCS_PLL_MODE_SINGLE 0U +#define CCS_PLL_MODE_DUAL 1U +#define CCS_R_OP_PIX_CLK_DIV_REV (0x0312 | CCS_FL_16BIT) +#define CCS_R_OP_SYS_CLK_DIV_REV (0x0314 | CCS_FL_16BIT) +#define CCS_R_FRAME_LENGTH_LINES (0x0340 | CCS_FL_16BIT) +#define CCS_R_LINE_LENGTH_PCK (0x0342 | CCS_FL_16BIT) +#define CCS_R_X_ADDR_START (0x0344 | CCS_FL_16BIT) +#define CCS_R_Y_ADDR_START (0x0346 | CCS_FL_16BIT) +#define CCS_R_X_ADDR_END (0x0348 | CCS_FL_16BIT) +#define CCS_R_Y_ADDR_END (0x034a | CCS_FL_16BIT) +#define CCS_R_X_OUTPUT_SIZE (0x034c | CCS_FL_16BIT) +#define CCS_R_Y_OUTPUT_SIZE (0x034e | CCS_FL_16BIT) +#define CCS_R_FRAME_LENGTH_CTRL 0x0350 +#define CCS_FRAME_LENGTH_CTRL_AUTOMATIC BIT(0) +#define CCS_R_TIMING_MODE_CTRL 0x0352 +#define CCS_TIMING_MODE_CTRL_MANUAL_READOUT BIT(0) +#define CCS_TIMING_MODE_CTRL_DELAYED_EXPOSURE BIT(1) +#define CCS_R_START_READOUT_RS 0x0353 +#define CCS_START_READOUT_RS_MANUAL_READOUT_START BIT(0) +#define CCS_R_FRAME_MARGIN (0x0354 | CCS_FL_16BIT) +#define CCS_R_X_EVEN_INC (0x0380 | CCS_FL_16BIT) +#define CCS_R_X_ODD_INC (0x0382 | CCS_FL_16BIT) +#define CCS_R_Y_EVEN_INC (0x0384 | CCS_FL_16BIT) +#define CCS_R_Y_ODD_INC (0x0386 | CCS_FL_16BIT) +#define CCS_R_MONOCHROME_EN 0x0390 +#define CCS_MONOCHROME_EN_ENABLED 0U +#define CCS_R_SCALING_MODE (0x0400 | CCS_FL_16BIT) +#define CCS_SCALING_MODE_NO_SCALING 0U +#define CCS_SCALING_MODE_HORIZONTAL 1U +#define CCS_R_SCALE_M (0x0404 | CCS_FL_16BIT) +#define CCS_R_SCALE_N (0x0406 | CCS_FL_16BIT) +#define CCS_R_DIGITAL_CROP_X_OFFSET (0x0408 | CCS_FL_16BIT) +#define CCS_R_DIGITAL_CROP_Y_OFFSET (0x040a | CCS_FL_16BIT) +#define CCS_R_DIGITAL_CROP_IMAGE_WIDTH (0x040c | CCS_FL_16BIT) +#define CCS_R_DIGITAL_CROP_IMAGE_HEIGHT (0x040e | CCS_FL_16BIT) +#define CCS_R_COMPRESSION_MODE (0x0500 | CCS_FL_16BIT) +#define CCS_COMPRESSION_MODE_NONE 0U +#define CCS_COMPRESSION_MODE_DPCM_PCM_SIMPLE 1U +#define CCS_R_TEST_PATTERN_MODE (0x0600 | CCS_FL_16BIT) +#define CCS_TEST_PATTERN_MODE_NONE 0U +#define CCS_TEST_PATTERN_MODE_SOLID_COLOR 1U +#define CCS_TEST_PATTERN_MODE_COLOR_BARS 2U +#define CCS_TEST_PATTERN_MODE_FADE_TO_GREY 3U +#define CCS_TEST_PATTERN_MODE_PN9 4U +#define CCS_TEST_PATTERN_MODE_COLOR_TILE 5U +#define CCS_R_TEST_DATA_RED (0x0602 | CCS_FL_16BIT) +#define CCS_R_TEST_DATA_GREENR (0x0604 | CCS_FL_16BIT) +#define CCS_R_TEST_DATA_BLUE (0x0606 | CCS_FL_16BIT) +#define CCS_R_TEST_DATA_GREENB (0x0608 | CCS_FL_16BIT) +#define CCS_R_VALUE_STEP_SIZE_SMOOTH 0x060a +#define CCS_R_VALUE_STEP_SIZE_QUANTISED 0x060b +#define CCS_R_TCLK_POST 0x0800 +#define CCS_R_THS_PREPARE 0x0801 +#define CCS_R_THS_ZERO_MIN 0x0802 +#define CCS_R_THS_TRAIL 0x0803 +#define CCS_R_TCLK_TRAIL_MIN 0x0804 +#define CCS_R_TCLK_PREPARE 0x0805 +#define CCS_R_TCLK_ZERO 0x0806 +#define CCS_R_TLPX 0x0807 +#define CCS_R_PHY_CTRL 0x0808 +#define CCS_PHY_CTRL_AUTO 0U +#define CCS_PHY_CTRL_UI 1U +#define CCS_PHY_CTRL_MANUAL 2U +#define CCS_R_TCLK_POST_EX (0x080a | CCS_FL_16BIT) +#define CCS_R_THS_PREPARE_EX (0x080c | CCS_FL_16BIT) +#define CCS_R_THS_ZERO_MIN_EX (0x080e | CCS_FL_16BIT) +#define CCS_R_THS_TRAIL_EX (0x0810 | CCS_FL_16BIT) +#define CCS_R_TCLK_TRAIL_MIN_EX (0x0812 | CCS_FL_16BIT) +#define CCS_R_TCLK_PREPARE_EX (0x0814 | CCS_FL_16BIT) +#define CCS_R_TCLK_ZERO_EX (0x0816 | CCS_FL_16BIT) +#define CCS_R_TLPX_EX (0x0818 | CCS_FL_16BIT) +#define CCS_R_REQUESTED_LINK_RATE (0x0820 | CCS_FL_32BIT) +#define CCS_R_DPHY_EQUALIZATION_MODE 0x0824 +#define CCS_DPHY_EQUALIZATION_MODE_EQ2 BIT(0) +#define CCS_R_PHY_EQUALIZATION_CTRL 0x0825 +#define CCS_PHY_EQUALIZATION_CTRL_ENABLE BIT(0) +#define CCS_R_DPHY_PREAMBLE_CTRL 0x0826 +#define CCS_DPHY_PREAMBLE_CTRL_ENABLE BIT(0) +#define CCS_R_DPHY_PREAMBLE_LENGTH 0x0826 +#define CCS_R_PHY_SSC_CTRL 0x0828 +#define CCS_PHY_SSC_CTRL_ENABLE BIT(0) +#define CCS_R_MANUAL_LP_CTRL 0x0829 +#define CCS_MANUAL_LP_CTRL_ENABLE BIT(0) +#define CCS_R_TWAKEUP 0x082a +#define CCS_R_TINIT 0x082b +#define CCS_R_THS_EXIT 0x082c +#define CCS_R_THS_EXIT_EX (0x082e | CCS_FL_16BIT) +#define CCS_R_PHY_PERIODIC_CALIBRATION_CTRL 0x0830 +#define CCS_PHY_PERIODIC_CALIBRATION_CTRL_FRAME_BLANKING BIT(0) +#define CCS_R_PHY_PERIODIC_CALIBRATION_INTERVAL 0x0831 +#define CCS_R_PHY_INIT_CALIBRATION_CTRL 0x0832 +#define CCS_PHY_INIT_CALIBRATION_CTRL_STREAM_START BIT(0) +#define CCS_R_DPHY_CALIBRATION_MODE 0x0833 +#define CCS_DPHY_CALIBRATION_MODE_ALSO_ALTERNATE BIT(0) +#define CCS_R_CPHY_CALIBRATION_MODE 0x0834 +#define CCS_CPHY_CALIBRATION_MODE_FORMAT_1 0U +#define CCS_CPHY_CALIBRATION_MODE_FORMAT_2 1U +#define CCS_CPHY_CALIBRATION_MODE_FORMAT_3 2U +#define CCS_R_T3_CALPREAMBLE_LENGTH 0x0835 +#define CCS_R_T3_CALPREAMBLE_LENGTH_PER 0x0836 +#define CCS_R_T3_CALALTSEQ_LENGTH 0x0837 +#define CCS_R_T3_CALALTSEQ_LENGTH_PER 0x0838 +#define CCS_R_FM2_INIT_SEED (0x083a | CCS_FL_16BIT) +#define CCS_R_T3_CALUDEFSEQ_LENGTH (0x083c | CCS_FL_16BIT) +#define CCS_R_T3_CALUDEFSEQ_LENGTH_PER (0x083e | CCS_FL_16BIT) +#define CCS_R_TGR_PREAMBLE_LENGTH 0x0841 +#define CCS_TGR_PREAMBLE_LENGTH_PREAMABLE_PROG_SEQ BIT(7) +#define CCS_TGR_PREAMBLE_LENGTH_BEGIN_PREAMBLE_LENGTH_SHIFT 0U +#define CCS_TGR_PREAMBLE_LENGTH_BEGIN_PREAMBLE_LENGTH_MASK 0x3f +#define CCS_R_TGR_POST_LENGTH 0x0842 +#define CCS_TGR_POST_LENGTH_POST_LENGTH_SHIFT 0U +#define CCS_TGR_POST_LENGTH_POST_LENGTH_MASK 0x1f +#define CCS_R_TGR_PREAMBLE_PROG_SEQUENCE(n2) (0x0843 + (n2)) +#define CCS_LIM_TGR_PREAMBLE_PROG_SEQUENCE_MIN_N2 0U +#define CCS_LIM_TGR_PREAMBLE_PROG_SEQUENCE_MAX_N2 6U +#define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_1_SHIFT 3U +#define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_1_MASK 0x38 +#define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_SHIFT 0U +#define CCS_TGR_PREAMBLE_PROG_SEQUENCE_SYMBOL_N_MASK 0x7 +#define CCS_R_T3_PREPARE (0x084e | CCS_FL_16BIT) +#define CCS_R_T3_LPX (0x0850 | CCS_FL_16BIT) +#define CCS_R_ALPS_CTRL 0x085a +#define CCS_ALPS_CTRL_LVLP_DPHY BIT(0) +#define CCS_ALPS_CTRL_LVLP_CPHY BIT(1) +#define CCS_ALPS_CTRL_ALP_CPHY BIT(2) +#define CCS_R_TX_REG_CSI_EPD_EN_SSP_CPHY (0x0860 | CCS_FL_16BIT) +#define CCS_R_TX_REG_CSI_EPD_OP_SLP_CPHY (0x0862 | CCS_FL_16BIT) +#define CCS_R_TX_REG_CSI_EPD_EN_SSP_DPHY (0x0864 | CCS_FL_16BIT) +#define CCS_R_TX_REG_CSI_EPD_OP_SLP_DPHY (0x0866 | CCS_FL_16BIT) +#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_CPHY 0x0868 +#define CCS_R_TX_REG_CSI_EPD_MISC_OPTION_DPHY 0x0869 +#define CCS_R_SCRAMBLING_CTRL 0x0870 +#define CCS_SCRAMBLING_CTRL_ENABLED BIT(0) +#define CCS_SCRAMBLING_CTRL_SHIFT 2U +#define CCS_SCRAMBLING_CTRL_MASK 0xc +#define CCS_SCRAMBLING_CTRL_1_SEED_CPHY 0U +#define CCS_SCRAMBLING_CTRL_4_SEED_CPHY 3U +#define CCS_R_LANE_SEED_VALUE(seed, lane) ((0x0872 | CCS_FL_16BIT) + (seed) * 16 + (lane) * 2) +#define CCS_LIM_LANE_SEED_VALUE_MIN_SEED 0U +#define CCS_LIM_LANE_SEED_VALUE_MAX_SEED 3U +#define CCS_LIM_LANE_SEED_VALUE_MIN_LANE 0U +#define CCS_LIM_LANE_SEED_VALUE_MAX_LANE 7U +#define CCS_R_TX_USL_REV_ENTRY (0x08c0 | CCS_FL_16BIT) +#define CCS_R_TX_USL_REV_CLOCK_COUNTER (0x08c2 | CCS_FL_16BIT) +#define CCS_R_TX_USL_REV_LP_COUNTER (0x08c4 | CCS_FL_16BIT) +#define CCS_R_TX_USL_REV_FRAME_COUNTER (0x08c6 | CCS_FL_16BIT) +#define CCS_R_TX_USL_REV_CHRONOLOGICAL_TIMER (0x08c8 | CCS_FL_16BIT) +#define CCS_R_TX_USL_FWD_ENTRY (0x08ca | CCS_FL_16BIT) +#define CCS_R_TX_USL_GPIO (0x08cc | CCS_FL_16BIT) +#define CCS_R_TX_USL_OPERATION (0x08ce | CCS_FL_16BIT) +#define CCS_TX_USL_OPERATION_RESET BIT(0) +#define CCS_R_TX_USL_ALP_CTRL (0x08d0 | CCS_FL_16BIT) +#define CCS_TX_USL_ALP_CTRL_CLOCK_PAUSE BIT(0) +#define CCS_R_TX_USL_APP_BTA_ACK_TIMEOUT (0x08d2 | CCS_FL_16BIT) +#define CCS_R_TX_USL_SNS_BTA_ACK_TIMEOUT (0x08d2 | CCS_FL_16BIT) +#define CCS_R_USL_CLOCK_MODE_D_CTRL 0x08d2 +#define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_STANDBY BIT(0) +#define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_VBLANK BIT(1) +#define CCS_USL_CLOCK_MODE_D_CTRL_CONT_CLOCK_HBLANK BIT(2) +#define CCS_R_BINNING_MODE 0x0900 +#define CCS_R_BINNING_TYPE 0x0901 +#define CCS_R_BINNING_WEIGHTING 0x0902 +#define CCS_R_DATA_TRANSFER_IF_1_CTRL 0x0a00 +#define CCS_DATA_TRANSFER_IF_1_CTRL_ENABLE BIT(0) +#define CCS_DATA_TRANSFER_IF_1_CTRL_WRITE BIT(1) +#define CCS_DATA_TRANSFER_IF_1_CTRL_CLEAR_ERROR BIT(2) +#define CCS_R_DATA_TRANSFER_IF_1_STATUS 0x0a01 +#define CCS_DATA_TRANSFER_IF_1_STATUS_READ_IF_READY BIT(0) +#define CCS_DATA_TRANSFER_IF_1_STATUS_WRITE_IF_READY BIT(1) +#define CCS_DATA_TRANSFER_IF_1_STATUS_DATA_CORRUPTED BIT(2) +#define CCS_DATA_TRANSFER_IF_1_STATUS_IMPROPER_IF_USAGE BIT(3) +#define CCS_R_DATA_TRANSFER_IF_1_PAGE_SELECT 0x0a02 +#define CCS_R_DATA_TRANSFER_IF_1_DATA(p) (0x0a04 + (p)) +#define CCS_LIM_DATA_TRANSFER_IF_1_DATA_MIN_P 0U +#define CCS_LIM_DATA_TRANSFER_IF_1_DATA_MAX_P 63U +#define CCS_R_SHADING_CORRECTION_EN 0x0b00 +#define CCS_SHADING_CORRECTION_EN_ENABLE BIT(0) +#define CCS_R_LUMINANCE_CORRECTION_LEVEL 0x0b01 +#define CCS_R_GREEN_IMBALANCE_FILTER_EN 0x0b02 +#define CCS_GREEN_IMBALANCE_FILTER_EN_ENABLE BIT(0) +#define CCS_R_MAPPED_DEFECT_CORRECT_EN 0x0b05 +#define CCS_MAPPED_DEFECT_CORRECT_EN_ENABLE BIT(0) +#define CCS_R_SINGLE_DEFECT_CORRECT_EN 0x0b06 +#define CCS_SINGLE_DEFECT_CORRECT_EN_ENABLE BIT(0) +#define CCS_R_DYNAMIC_COUPLET_CORRECT_EN 0x0b08 +#define CCS_DYNAMIC_COUPLET_CORRECT_EN_ENABLE BIT(0) +#define CCS_R_COMBINED_DEFECT_CORRECT_EN 0x0b0a +#define CCS_COMBINED_DEFECT_CORRECT_EN_ENABLE BIT(0) +#define CCS_R_MODULE_SPECIFIC_CORRECTION_EN 0x0b0c +#define CCS_MODULE_SPECIFIC_CORRECTION_EN_ENABLE BIT(0) +#define CCS_R_DYNAMIC_TRIPLET_DEFECT_CORRECT_EN 0x0b13 +#define CCS_DYNAMIC_TRIPLET_DEFECT_CORRECT_EN_ENABLE BIT(0) +#define CCS_R_NF_CTRL 0x0b15 +#define CCS_NF_CTRL_LUMA BIT(0) +#define CCS_NF_CTRL_CHROMA BIT(1) +#define CCS_NF_CTRL_COMBINED BIT(2) +#define CCS_R_OB_READOUT_CONTROL 0x0b30 +#define CCS_OB_READOUT_CONTROL_ENABLE BIT(0) +#define CCS_OB_READOUT_CONTROL_INTERLEAVING BIT(1) +#define CCS_R_OB_VIRTUAL_CHANNEL 0x0b31 +#define CCS_R_OB_DT 0x0b32 +#define CCS_R_OB_DATA_FORMAT 0x0b33 +#define CCS_R_COLOR_TEMPERATURE (0x0b8c | CCS_FL_16BIT) +#define CCS_R_ABSOLUTE_GAIN_GREENR (0x0b8e | CCS_FL_16BIT) +#define CCS_R_ABSOLUTE_GAIN_RED (0x0b90 | CCS_FL_16BIT) +#define CCS_R_ABSOLUTE_GAIN_BLUE (0x0b92 | CCS_FL_16BIT) +#define CCS_R_ABSOLUTE_GAIN_GREENB (0x0b94 | CCS_FL_16BIT) +#define CCS_R_CFA_CONVERSION_CTRL 0x0ba0 +#define CCS_CFA_CONVERSION_CTRL_BAYER_CONVERSION_ENABLE BIT(0) +#define CCS_R_FLASH_STROBE_ADJUSTMENT 0x0c12 +#define CCS_R_FLASH_STROBE_START_POINT (0x0c14 | CCS_FL_16BIT) +#define CCS_R_TFLASH_STROBE_DELAY_RS_CTRL (0x0c16 | CCS_FL_16BIT) +#define CCS_R_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL (0x0c18 | CCS_FL_16BIT) +#define CCS_R_FLASH_MODE_RS 0x0c1a +#define CCS_FLASH_MODE_RS_CONTINUOUS BIT(0) +#define CCS_FLASH_MODE_RS_TRUNCATE BIT(1) +#define CCS_FLASH_MODE_RS_ASYNC BIT(3) +#define CCS_R_FLASH_TRIGGER_RS 0x0c1b +#define CCS_R_FLASH_STATUS 0x0c1c +#define CCS_FLASH_STATUS_RETIMED BIT(0) +#define CCS_R_SA_STROBE_MODE 0x0c1d +#define CCS_SA_STROBE_MODE_CONTINUOUS BIT(0) +#define CCS_SA_STROBE_MODE_TRUNCATE BIT(1) +#define CCS_SA_STROBE_MODE_ASYNC BIT(3) +#define CCS_SA_STROBE_MODE_ADJUST_EDGE BIT(4) +#define CCS_R_SA_STROBE_START_POINT (0x0c1e | CCS_FL_16BIT) +#define CCS_R_TSA_STROBE_DELAY_CTRL (0x0c20 | CCS_FL_16BIT) +#define CCS_R_TSA_STROBE_WIDTH_CTRL (0x0c22 | CCS_FL_16BIT) +#define CCS_R_SA_STROBE_TRIGGER 0x0c24 +#define CCS_R_SA_STROBE_STATUS 0x0c25 +#define CCS_SA_STROBE_STATUS_RETIMED BIT(0) +#define CCS_R_TSA_STROBE_RE_DELAY_CTRL (0x0c30 | CCS_FL_16BIT) +#define CCS_R_TSA_STROBE_FE_DELAY_CTRL (0x0c32 | CCS_FL_16BIT) +#define CCS_R_PDAF_CTRL (0x0d00 | CCS_FL_16BIT) +#define CCS_PDAF_CTRL_ENABLE BIT(0) +#define CCS_PDAF_CTRL_PROCESSED BIT(1) +#define CCS_PDAF_CTRL_INTERLEAVED BIT(2) +#define CCS_PDAF_CTRL_VISIBLE_PDAF_CORRECTION BIT(3) +#define CCS_R_PDAF_VC 0x0d02 +#define CCS_R_PDAF_DT 0x0d03 +#define CCS_R_PD_X_ADDR_START (0x0d04 | CCS_FL_16BIT) +#define CCS_R_PD_Y_ADDR_START (0x0d06 | CCS_FL_16BIT) +#define CCS_R_PD_X_ADDR_END (0x0d08 | CCS_FL_16BIT) +#define CCS_R_PD_Y_ADDR_END (0x0d0a | CCS_FL_16BIT) +#define CCS_R_BRACKETING_LUT_CTRL 0x0e00 +#define CCS_R_BRACKETING_LUT_MODE 0x0e01 +#define CCS_BRACKETING_LUT_MODE_CONTINUE_STREAMING BIT(0) +#define CCS_BRACKETING_LUT_MODE_LOOP_MODE BIT(1) +#define CCS_R_BRACKETING_LUT_ENTRY_CTRL 0x0e02 +#define CCS_R_BRACKETING_LUT_FRAME(n) (0x0e10 + (n)) +#define CCS_LIM_BRACKETING_LUT_FRAME_MIN_N 0U +#define CCS_LIM_BRACKETING_LUT_FRAME_MAX_N 239U +#define CCS_R_INTEGRATION_TIME_CAPABILITY (0x1000 | CCS_FL_16BIT) +#define CCS_INTEGRATION_TIME_CAPABILITY_FINE BIT(0) +#define CCS_R_COARSE_INTEGRATION_TIME_MIN (0x1004 | CCS_FL_16BIT) +#define CCS_R_COARSE_INTEGRATION_TIME_MAX_MARGIN (0x1006 | CCS_FL_16BIT) +#define CCS_R_FINE_INTEGRATION_TIME_MIN (0x1008 | CCS_FL_16BIT) +#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN (0x100a | CCS_FL_16BIT) +#define CCS_R_DIGITAL_GAIN_CAPABILITY 0x1081 +#define CCS_DIGITAL_GAIN_CAPABILITY_NONE 0U +#define CCS_DIGITAL_GAIN_CAPABILITY_GLOBAL 2U +#define CCS_R_DIGITAL_GAIN_MIN (0x1084 | CCS_FL_16BIT) +#define CCS_R_DIGITAL_GAIN_MAX (0x1086 | CCS_FL_16BIT) +#define CCS_R_DIGITAL_GAIN_STEP_SIZE (0x1088 | CCS_FL_16BIT) +#define CCS_R_PEDESTAL_CAPABILITY 0x10e0 +#define CCS_R_ADC_CAPABILITY 0x10f0 +#define CCS_ADC_CAPABILITY_BIT_DEPTH_CTRL BIT(0) +#define CCS_R_ADC_BIT_DEPTH_CAPABILITY (0x10f4 | CCS_FL_32BIT) +#define CCS_R_MIN_EXT_CLK_FREQ_MHZ (0x1100 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_EXT_CLK_FREQ_MHZ (0x1104 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_PRE_PLL_CLK_DIV (0x1108 | CCS_FL_16BIT) +#define CCS_R_MAX_PRE_PLL_CLK_DIV (0x110a | CCS_FL_16BIT) +#define CCS_R_MIN_PLL_IP_CLK_FREQ_MHZ (0x110c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_PLL_IP_CLK_FREQ_MHZ (0x1110 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_PLL_MULTIPLIER (0x1114 | CCS_FL_16BIT) +#define CCS_R_MAX_PLL_MULTIPLIER (0x1116 | CCS_FL_16BIT) +#define CCS_R_MIN_PLL_OP_CLK_FREQ_MHZ (0x1118 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_PLL_OP_CLK_FREQ_MHZ (0x111c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_VT_SYS_CLK_DIV (0x1120 | CCS_FL_16BIT) +#define CCS_R_MAX_VT_SYS_CLK_DIV (0x1122 | CCS_FL_16BIT) +#define CCS_R_MIN_VT_SYS_CLK_FREQ_MHZ (0x1124 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_VT_SYS_CLK_FREQ_MHZ (0x1128 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_VT_PIX_CLK_FREQ_MHZ (0x112c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_VT_PIX_CLK_FREQ_MHZ (0x1130 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_VT_PIX_CLK_DIV (0x1134 | CCS_FL_16BIT) +#define CCS_R_MAX_VT_PIX_CLK_DIV (0x1136 | CCS_FL_16BIT) +#define CCS_R_CLOCK_CALCULATION 0x1138 +#define CCS_CLOCK_CALCULATION_LANE_SPEED BIT(0) +#define CCS_CLOCK_CALCULATION_LINK_DECOUPLED BIT(1) +#define CCS_CLOCK_CALCULATION_DUAL_PLL_OP_SYS_DDR BIT(2) +#define CCS_CLOCK_CALCULATION_DUAL_PLL_OP_PIX_DDR BIT(3) +#define CCS_R_NUM_OF_VT_LANES 0x1139 +#define CCS_R_NUM_OF_OP_LANES 0x113a +#define CCS_R_OP_BITS_PER_LANE 0x113b +#define CCS_R_MIN_FRAME_LENGTH_LINES (0x1140 | CCS_FL_16BIT) +#define CCS_R_MAX_FRAME_LENGTH_LINES (0x1142 | CCS_FL_16BIT) +#define CCS_R_MIN_LINE_LENGTH_PCK (0x1144 | CCS_FL_16BIT) +#define CCS_R_MAX_LINE_LENGTH_PCK (0x1146 | CCS_FL_16BIT) +#define CCS_R_MIN_LINE_BLANKING_PCK (0x1148 | CCS_FL_16BIT) +#define CCS_R_MIN_FRAME_BLANKING_LINES (0x114a | CCS_FL_16BIT) +#define CCS_R_MIN_LINE_LENGTH_PCK_STEP_SIZE 0x114c +#define CCS_R_TIMING_MODE_CAPABILITY 0x114d +#define CCS_TIMING_MODE_CAPABILITY_AUTO_FRAME_LENGTH BIT(0) +#define CCS_TIMING_MODE_CAPABILITY_ROLLING_SHUTTER_MANUAL_READOUT BIT(2) +#define CCS_TIMING_MODE_CAPABILITY_DELAYED_EXPOSURE_START BIT(3) +#define CCS_TIMING_MODE_CAPABILITY_MANUAL_EXPOSURE_EMBEDDED_DATA BIT(4) +#define CCS_R_FRAME_MARGIN_MAX_VALUE (0x114e | CCS_FL_16BIT) +#define CCS_R_FRAME_MARGIN_MIN_VALUE 0x1150 +#define CCS_R_GAIN_DELAY_TYPE 0x1151 +#define CCS_GAIN_DELAY_TYPE_FIXED 0U +#define CCS_GAIN_DELAY_TYPE_VARIABLE 1U +#define CCS_R_MIN_OP_SYS_CLK_DIV (0x1160 | CCS_FL_16BIT) +#define CCS_R_MAX_OP_SYS_CLK_DIV (0x1162 | CCS_FL_16BIT) +#define CCS_R_MIN_OP_SYS_CLK_FREQ_MHZ (0x1164 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_SYS_CLK_FREQ_MHZ (0x1168 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_OP_PIX_CLK_DIV (0x116c | CCS_FL_16BIT) +#define CCS_R_MAX_OP_PIX_CLK_DIV (0x116e | CCS_FL_16BIT) +#define CCS_R_MIN_OP_PIX_CLK_FREQ_MHZ (0x1170 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_PIX_CLK_FREQ_MHZ (0x1174 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_X_ADDR_MIN (0x1180 | CCS_FL_16BIT) +#define CCS_R_Y_ADDR_MIN (0x1182 | CCS_FL_16BIT) +#define CCS_R_X_ADDR_MAX (0x1184 | CCS_FL_16BIT) +#define CCS_R_Y_ADDR_MAX (0x1186 | CCS_FL_16BIT) +#define CCS_R_MIN_X_OUTPUT_SIZE (0x1188 | CCS_FL_16BIT) +#define CCS_R_MIN_Y_OUTPUT_SIZE (0x118a | CCS_FL_16BIT) +#define CCS_R_MAX_X_OUTPUT_SIZE (0x118c | CCS_FL_16BIT) +#define CCS_R_MAX_Y_OUTPUT_SIZE (0x118e | CCS_FL_16BIT) +#define CCS_R_X_ADDR_START_DIV_CONSTANT 0x1190 +#define CCS_R_Y_ADDR_START_DIV_CONSTANT 0x1191 +#define CCS_R_X_ADDR_END_DIV_CONSTANT 0x1192 +#define CCS_R_Y_ADDR_END_DIV_CONSTANT 0x1193 +#define CCS_R_X_SIZE_DIV 0x1194 +#define CCS_R_Y_SIZE_DIV 0x1195 +#define CCS_R_X_OUTPUT_DIV 0x1196 +#define CCS_R_Y_OUTPUT_DIV 0x1197 +#define CCS_R_NON_FLEXIBLE_RESOLUTION_SUPPORT 0x1198 +#define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_NEW_PIX_ADDR BIT(0) +#define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_NEW_OUTPUT_RES BIT(1) +#define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_OUTPUT_CROP_NO_PAD BIT(2) +#define CCS_NON_FLEXIBLE_RESOLUTION_SUPPORT_OUTPUT_SIZE_LANE_DEP BIT(3) +#define CCS_R_MIN_OP_PRE_PLL_CLK_DIV (0x11a0 | CCS_FL_16BIT) +#define CCS_R_MAX_OP_PRE_PLL_CLK_DIV (0x11a2 | CCS_FL_16BIT) +#define CCS_R_MIN_OP_PLL_IP_CLK_FREQ_MHZ (0x11a4 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_PLL_IP_CLK_FREQ_MHZ (0x11a8 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_OP_PLL_MULTIPLIER (0x11ac | CCS_FL_16BIT) +#define CCS_R_MAX_OP_PLL_MULTIPLIER (0x11ae | CCS_FL_16BIT) +#define CCS_R_MIN_OP_PLL_OP_CLK_FREQ_MHZ (0x11b0 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_PLL_OP_CLK_FREQ_MHZ (0x11b4 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_CLOCK_TREE_PLL_CAPABILITY 0x11b8 +#define CCS_CLOCK_TREE_PLL_CAPABILITY_DUAL_PLL BIT(0) +#define CCS_CLOCK_TREE_PLL_CAPABILITY_SINGLE_PLL BIT(1) +#define CCS_CLOCK_TREE_PLL_CAPABILITY_EXT_DIVIDER BIT(2) +#define CCS_CLOCK_TREE_PLL_CAPABILITY_FLEXIBLE_OP_PIX_CLK_DIV BIT(3) +#define CCS_R_CLOCK_CAPA_TYPE_CAPABILITY 0x11b9 +#define CCS_CLOCK_CAPA_TYPE_CAPABILITY_IREAL BIT(0) +#define CCS_R_MIN_EVEN_INC (0x11c0 | CCS_FL_16BIT) +#define CCS_R_MIN_ODD_INC (0x11c2 | CCS_FL_16BIT) +#define CCS_R_MAX_EVEN_INC (0x11c4 | CCS_FL_16BIT) +#define CCS_R_MAX_ODD_INC (0x11c6 | CCS_FL_16BIT) +#define CCS_R_AUX_SUBSAMP_CAPABILITY 0x11c8 +#define CCS_AUX_SUBSAMP_CAPABILITY_FACTOR_POWER_OF_2 BIT(1) +#define CCS_R_AUX_SUBSAMP_MONO_CAPABILITY 0x11c9 +#define CCS_AUX_SUBSAMP_MONO_CAPABILITY_FACTOR_POWER_OF_2 BIT(1) +#define CCS_R_MONOCHROME_CAPABILITY 0x11ca +#define CCS_MONOCHROME_CAPABILITY_INC_ODD 0U +#define CCS_MONOCHROME_CAPABILITY_INC_EVEN 1U +#define CCS_R_PIXEL_READOUT_CAPABILITY 0x11cb +#define CCS_PIXEL_READOUT_CAPABILITY_BAYER 0U +#define CCS_PIXEL_READOUT_CAPABILITY_MONOCHROME 1U +#define CCS_PIXEL_READOUT_CAPABILITY_BAYER_AND_MONO 2U +#define CCS_R_MIN_EVEN_INC_MONO (0x11cc | CCS_FL_16BIT) +#define CCS_R_MAX_EVEN_INC_MONO (0x11ce | CCS_FL_16BIT) +#define CCS_R_MIN_ODD_INC_MONO (0x11d0 | CCS_FL_16BIT) +#define CCS_R_MAX_ODD_INC_MONO (0x11d2 | CCS_FL_16BIT) +#define CCS_R_MIN_EVEN_INC_BC2 (0x11d4 | CCS_FL_16BIT) +#define CCS_R_MAX_EVEN_INC_BC2 (0x11d6 | CCS_FL_16BIT) +#define CCS_R_MIN_ODD_INC_BC2 (0x11d8 | CCS_FL_16BIT) +#define CCS_R_MAX_ODD_INC_BC2 (0x11da | CCS_FL_16BIT) +#define CCS_R_MIN_EVEN_INC_MONO_BC2 (0x11dc | CCS_FL_16BIT) +#define CCS_R_MAX_EVEN_INC_MONO_BC2 (0x11de | CCS_FL_16BIT) +#define CCS_R_MIN_ODD_INC_MONO_BC2 (0x11f0 | CCS_FL_16BIT) +#define CCS_R_MAX_ODD_INC_MONO_BC2 (0x11f2 | CCS_FL_16BIT) +#define CCS_R_SCALING_CAPABILITY (0x1200 | CCS_FL_16BIT) +#define CCS_SCALING_CAPABILITY_NONE 0U +#define CCS_SCALING_CAPABILITY_HORIZONTAL 1U +#define CCS_SCALING_CAPABILITY_RESERVED 2U +#define CCS_R_SCALER_M_MIN (0x1204 | CCS_FL_16BIT) +#define CCS_R_SCALER_M_MAX (0x1206 | CCS_FL_16BIT) +#define CCS_R_SCALER_N_MIN (0x1208 | CCS_FL_16BIT) +#define CCS_R_SCALER_N_MAX (0x120a | CCS_FL_16BIT) +#define CCS_R_DIGITAL_CROP_CAPABILITY 0x120e +#define CCS_DIGITAL_CROP_CAPABILITY_NONE 0U +#define CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1U +#define CCS_R_HDR_CAPABILITY_1 0x1210 +#define CCS_HDR_CAPABILITY_1_2X2_BINNING BIT(0) +#define CCS_HDR_CAPABILITY_1_COMBINED_ANALOG_GAIN BIT(1) +#define CCS_HDR_CAPABILITY_1_SEPARATE_ANALOG_GAIN BIT(2) +#define CCS_HDR_CAPABILITY_1_UPSCALING BIT(3) +#define CCS_HDR_CAPABILITY_1_RESET_SYNC BIT(4) +#define CCS_HDR_CAPABILITY_1_DIRECT_SHORT_EXP_TIMING BIT(5) +#define CCS_HDR_CAPABILITY_1_DIRECT_SHORT_EXP_SYNTHESIS BIT(6) +#define CCS_R_MIN_HDR_BIT_DEPTH 0x1211 +#define CCS_R_HDR_RESOLUTION_SUB_TYPES 0x1212 +#define CCS_R_HDR_RESOLUTION_SUB_TYPE(n) (0x1213 + (n)) +#define CCS_LIM_HDR_RESOLUTION_SUB_TYPE_MIN_N 0U +#define CCS_LIM_HDR_RESOLUTION_SUB_TYPE_MAX_N 1U +#define CCS_HDR_RESOLUTION_SUB_TYPE_ROW_SHIFT 0U +#define CCS_HDR_RESOLUTION_SUB_TYPE_ROW_MASK 0xf +#define CCS_HDR_RESOLUTION_SUB_TYPE_COLUMN_SHIFT 4U +#define CCS_HDR_RESOLUTION_SUB_TYPE_COLUMN_MASK 0xf0 +#define CCS_R_HDR_CAPABILITY_2 0x121b +#define CCS_HDR_CAPABILITY_2_COMBINED_DIGITAL_GAIN BIT(0) +#define CCS_HDR_CAPABILITY_2_SEPARATE_DIGITAL_GAIN BIT(1) +#define CCS_HDR_CAPABILITY_2_TIMING_MODE BIT(3) +#define CCS_HDR_CAPABILITY_2_SYNTHESIS_MODE BIT(4) +#define CCS_R_MAX_HDR_BIT_DEPTH 0x121c +#define CCS_R_USL_SUPPORT_CAPABILITY 0x1230 +#define CCS_USL_SUPPORT_CAPABILITY_CLOCK_TREE BIT(0) +#define CCS_USL_SUPPORT_CAPABILITY_REV_CLOCK_TREE BIT(1) +#define CCS_USL_SUPPORT_CAPABILITY_REV_CLOCK_CALC BIT(2) +#define CCS_R_USL_CLOCK_MODE_D_CAPABILITY 0x1231 +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_STANDBY BIT(0) +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_VBLANK BIT(1) +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_CONT_CLOCK_HBLANK BIT(2) +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_STANDBY BIT(3) +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_VBLANK BIT(4) +#define CCS_USL_CLOCK_MODE_D_CAPABILITY_NONCONT_CLOCK_HBLANK BIT(5) +#define CCS_R_MIN_OP_SYS_CLK_DIV_REV 0x1234 +#define CCS_R_MAX_OP_SYS_CLK_DIV_REV 0x1236 +#define CCS_R_MIN_OP_PIX_CLK_DIV_REV 0x1238 +#define CCS_R_MAX_OP_PIX_CLK_DIV_REV 0x123a +#define CCS_R_MIN_OP_SYS_CLK_FREQ_REV_MHZ (0x123c | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_SYS_CLK_FREQ_REV_MHZ (0x1240 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MIN_OP_PIX_CLK_FREQ_REV_MHZ (0x1244 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_OP_PIX_CLK_FREQ_REV_MHZ (0x1248 | (CCS_FL_32BIT | CCS_FL_FLOAT_IREAL)) +#define CCS_R_MAX_BITRATE_REV_D_MODE_MBPS (0x124c | (CCS_FL_32BIT | CCS_FL_IREAL)) +#define CCS_R_MAX_SYMRATE_REV_C_MODE_MSPS (0x1250 | (CCS_FL_32BIT | CCS_FL_IREAL)) +#define CCS_R_COMPRESSION_CAPABILITY 0x1300 +#define CCS_COMPRESSION_CAPABILITY_DPCM_PCM_SIMPLE BIT(0) +#define CCS_R_TEST_MODE_CAPABILITY (0x1310 | CCS_FL_16BIT) +#define CCS_TEST_MODE_CAPABILITY_SOLID_COLOR BIT(0) +#define CCS_TEST_MODE_CAPABILITY_COLOR_BARS BIT(1) +#define CCS_TEST_MODE_CAPABILITY_FADE_TO_GREY BIT(2) +#define CCS_TEST_MODE_CAPABILITY_PN9 BIT(3) +#define CCS_TEST_MODE_CAPABILITY_COLOR_TILE BIT(5) +#define CCS_R_PN9_DATA_FORMAT1 0x1312 +#define CCS_R_PN9_DATA_FORMAT2 0x1313 +#define CCS_R_PN9_DATA_FORMAT3 0x1314 +#define CCS_R_PN9_DATA_FORMAT4 0x1315 +#define CCS_R_PN9_MISC_CAPABILITY 0x1316 +#define CCS_PN9_MISC_CAPABILITY_NUM_PIXELS_SHIFT 0U +#define CCS_PN9_MISC_CAPABILITY_NUM_PIXELS_MASK 0x7 +#define CCS_PN9_MISC_CAPABILITY_COMPRESSION BIT(3) +#define CCS_R_TEST_PATTERN_CAPABILITY 0x1317 +#define CCS_TEST_PATTERN_CAPABILITY_NO_REPEAT BIT(1) +#define CCS_R_PATTERN_SIZE_DIV_M1 0x1318 +#define CCS_R_FIFO_SUPPORT_CAPABILITY 0x1502 +#define CCS_FIFO_SUPPORT_CAPABILITY_NONE 0U +#define CCS_FIFO_SUPPORT_CAPABILITY_DERATING 1U +#define CCS_FIFO_SUPPORT_CAPABILITY_DERATING_OVERRATING 2U +#define CCS_R_PHY_CTRL_CAPABILITY 0x1600 +#define CCS_PHY_CTRL_CAPABILITY_AUTO_PHY_CTL BIT(0) +#define CCS_PHY_CTRL_CAPABILITY_UI_PHY_CTL BIT(1) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_TIME_UI_REG_1_CTL BIT(2) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_TIME_UI_REG_2_CTL BIT(3) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_TIME_CTL BIT(4) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_UI_REG_1_CTL BIT(5) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_UI_REG_2_CTL BIT(6) +#define CCS_PHY_CTRL_CAPABILITY_DPHY_EXT_TIME_CTL BIT(7) +#define CCS_R_CSI_DPHY_LANE_MODE_CAPABILITY 0x1601 +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_1_LANE BIT(0) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_2_LANE BIT(1) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_3_LANE BIT(2) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_4_LANE BIT(3) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_5_LANE BIT(4) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_6_LANE BIT(5) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_7_LANE BIT(6) +#define CCS_CSI_DPHY_LANE_MODE_CAPABILITY_8_LANE BIT(7) +#define CCS_R_CSI_SIGNALING_MODE_CAPABILITY 0x1602 +#define CCS_CSI_SIGNALING_MODE_CAPABILITY_CSI_DPHY BIT(2) +#define CCS_CSI_SIGNALING_MODE_CAPABILITY_CSI_CPHY BIT(3) +#define CCS_R_FAST_STANDBY_CAPABILITY 0x1603 +#define CCS_FAST_STANDBY_CAPABILITY_NO_FRAME_TRUNCATION 0U +#define CCS_FAST_STANDBY_CAPABILITY_FRAME_TRUNCATION 1U +#define CCS_R_CSI_ADDRESS_CONTROL_CAPABILITY 0x1604 +#define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_CCI_ADDR_CHANGE BIT(0) +#define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_2ND_CCI_ADDR BIT(1) +#define CCS_CSI_ADDRESS_CONTROL_CAPABILITY_SW_CHANGEABLE_2ND_CCI_ADDR BIT(2) +#define CCS_R_DATA_TYPE_CAPABILITY 0x1605 +#define CCS_DATA_TYPE_CAPABILITY_DPCM_PROGRAMMABLE BIT(0) +#define CCS_DATA_TYPE_CAPABILITY_BOTTOM_EMBEDDED_DT_PROGRAMMABLE BIT(1) +#define CCS_DATA_TYPE_CAPABILITY_BOTTOM_EMBEDDED_VC_PROGRAMMABLE BIT(2) +#define CCS_DATA_TYPE_CAPABILITY_EXT_VC_RANGE BIT(3) +#define CCS_R_CSI_CPHY_LANE_MODE_CAPABILITY 0x1606 +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_1_LANE BIT(0) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_2_LANE BIT(1) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_3_LANE BIT(2) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_4_LANE BIT(3) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_5_LANE BIT(4) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_6_LANE BIT(5) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_7_LANE BIT(6) +#define CCS_CSI_CPHY_LANE_MODE_CAPABILITY_8_LANE BIT(7) +#define CCS_R_EMB_DATA_CAPABILITY 0x1607 +#define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW16 BIT(0) +#define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW20 BIT(1) +#define CCS_EMB_DATA_CAPABILITY_TWO_BYTES_PER_RAW24 BIT(2) +#define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW16 BIT(3) +#define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW20 BIT(4) +#define CCS_EMB_DATA_CAPABILITY_NO_ONE_BYTE_PER_RAW24 BIT(5) +#define CCS_R_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS(n) ((0x1608 | (CCS_FL_32BIT | CCS_FL_IREAL)) + ((n) < 4 ? (n) * 4 : 0x32 + ((n) - 4) * 4)) +#define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS_MIN_N 0U +#define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_D_MODE_MBPS_MAX_N 7U +#define CCS_R_TEMP_SENSOR_CAPABILITY 0x1618 +#define CCS_TEMP_SENSOR_CAPABILITY_SUPPORTED BIT(0) +#define CCS_TEMP_SENSOR_CAPABILITY_CCS_FORMAT BIT(1) +#define CCS_TEMP_SENSOR_CAPABILITY_RESET_0X80 BIT(2) +#define CCS_R_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS(n) ((0x161a | (CCS_FL_32BIT | CCS_FL_IREAL)) + ((n) < 4 ? (n) * 4 : 0x30 + ((n) - 4) * 4)) +#define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS_MIN_N 0U +#define CCS_LIM_MAX_PER_LANE_BITRATE_LANE_C_MODE_MBPS_MAX_N 7U +#define CCS_R_DPHY_EQUALIZATION_CAPABILITY 0x162b +#define CCS_DPHY_EQUALIZATION_CAPABILITY_EQUALIZATION_CTRL BIT(0) +#define CCS_DPHY_EQUALIZATION_CAPABILITY_EQ1 BIT(1) +#define CCS_DPHY_EQUALIZATION_CAPABILITY_EQ2 BIT(2) +#define CCS_R_CPHY_EQUALIZATION_CAPABILITY 0x162c +#define CCS_CPHY_EQUALIZATION_CAPABILITY_EQUALIZATION_CTRL BIT(0) +#define CCS_R_DPHY_PREAMBLE_CAPABILITY 0x162d +#define CCS_DPHY_PREAMBLE_CAPABILITY_PREAMBLE_SEQ_CTRL BIT(0) +#define CCS_R_DPHY_SSC_CAPABILITY 0x162e +#define CCS_DPHY_SSC_CAPABILITY_SUPPORTED BIT(0) +#define CCS_R_CPHY_CALIBRATION_CAPABILITY 0x162f +#define CCS_CPHY_CALIBRATION_CAPABILITY_MANUAL BIT(0) +#define CCS_CPHY_CALIBRATION_CAPABILITY_MANUAL_STREAMING BIT(1) +#define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_1_CTRL BIT(2) +#define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_2_CTRL BIT(3) +#define CCS_CPHY_CALIBRATION_CAPABILITY_FORMAT_3_CTRL BIT(4) +#define CCS_R_DPHY_CALIBRATION_CAPABILITY 0x1630 +#define CCS_DPHY_CALIBRATION_CAPABILITY_MANUAL BIT(0) +#define CCS_DPHY_CALIBRATION_CAPABILITY_MANUAL_STREAMING BIT(1) +#define CCS_DPHY_CALIBRATION_CAPABILITY_ALTERNATE_SEQ BIT(2) +#define CCS_R_PHY_CTRL_CAPABILITY_2 0x1631 +#define CCS_PHY_CTRL_CAPABILITY_2_TGR_LENGTH BIT(0) +#define CCS_PHY_CTRL_CAPABILITY_2_TGR_PREAMBLE_PROG_SEQ BIT(1) +#define CCS_PHY_CTRL_CAPABILITY_2_EXTRA_CPHY_MANUAL_TIMING BIT(2) +#define CCS_PHY_CTRL_CAPABILITY_2_CLOCK_BASED_MANUAL_CDPHY BIT(3) +#define CCS_PHY_CTRL_CAPABILITY_2_CLOCK_BASED_MANUAL_DPHY BIT(4) +#define CCS_PHY_CTRL_CAPABILITY_2_CLOCK_BASED_MANUAL_CPHY BIT(5) +#define CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_DPHY BIT(6) +#define CCS_PHY_CTRL_CAPABILITY_2_MANUAL_LP_CPHY BIT(7) +#define CCS_R_LRTE_CPHY_CAPABILITY 0x1632 +#define CCS_LRTE_CPHY_CAPABILITY_PDQ_SHORT BIT(0) +#define CCS_LRTE_CPHY_CAPABILITY_SPACER_SHORT BIT(1) +#define CCS_LRTE_CPHY_CAPABILITY_PDQ_LONG BIT(2) +#define CCS_LRTE_CPHY_CAPABILITY_SPACER_LONG BIT(3) +#define CCS_LRTE_CPHY_CAPABILITY_SPACER_NO_PDQ BIT(4) +#define CCS_R_LRTE_DPHY_CAPABILITY 0x1633 +#define CCS_LRTE_DPHY_CAPABILITY_PDQ_SHORT_OPT1 BIT(0) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_SHORT_OPT1 BIT(1) +#define CCS_LRTE_DPHY_CAPABILITY_PDQ_LONG_OPT1 BIT(2) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_LONG_OPT1 BIT(3) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_SHORT_OPT2 BIT(4) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_LONG_OPT2 BIT(5) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_NO_PDQ_OPT1 BIT(6) +#define CCS_LRTE_DPHY_CAPABILITY_SPACER_VARIABLE_OPT2 BIT(7) +#define CCS_R_ALPS_CAPABILITY_DPHY 0x1634 +#define CCS_ALPS_CAPABILITY_DPHY_LVLP_NOT_SUPPORTED 0U +#define CCS_ALPS_CAPABILITY_DPHY_LVLP_SUPPORTED 1U +#define CCS_ALPS_CAPABILITY_DPHY_CONTROLLABLE_LVLP 2U +#define CCS_R_ALPS_CAPABILITY_CPHY 0x1635 +#define CCS_ALPS_CAPABILITY_CPHY_LVLP_NOT_SUPPORTED 0U +#define CCS_ALPS_CAPABILITY_CPHY_LVLP_SUPPORTED 1U +#define CCS_ALPS_CAPABILITY_CPHY_CONTROLLABLE_LVLP 2U +#define CCS_ALPS_CAPABILITY_CPHY_ALP_NOT_SUPPORTED 0xc +#define CCS_ALPS_CAPABILITY_CPHY_ALP_SUPPORTED 0xd +#define CCS_ALPS_CAPABILITY_CPHY_CONTROLLABLE_ALP 0xe +#define CCS_R_SCRAMBLING_CAPABILITY 0x1636 +#define CCS_SCRAMBLING_CAPABILITY_SCRAMBLING_SUPPORTED BIT(0) +#define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_SHIFT 1U +#define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_MASK 0x6 +#define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_1 0U +#define CCS_SCRAMBLING_CAPABILITY_MAX_SEEDS_PER_LANE_C_4 3U +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_SHIFT 3U +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_MASK 0x38 +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_0 0U +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_1 1U +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_REGS_4 4U +#define CCS_SCRAMBLING_CAPABILITY_NUM_SEED_PER_LANE BIT(6) +#define CCS_R_DPHY_MANUAL_CONSTANT 0x1637 +#define CCS_R_CPHY_MANUAL_CONSTANT 0x1638 +#define CCS_R_CSI2_INTERFACE_CAPABILITY_MISC 0x1639 +#define CCS_CSI2_INTERFACE_CAPABILITY_MISC_EOTP_SHORT_PKT_OPT2 BIT(0) +#define CCS_R_PHY_CTRL_CAPABILITY_3 0x165c +#define CCS_PHY_CTRL_CAPABILITY_3_DPHY_TIMING_NOT_MULTIPLE BIT(0) +#define CCS_PHY_CTRL_CAPABILITY_3_DPHY_MIN_TIMING_VALUE_1 BIT(1) +#define CCS_PHY_CTRL_CAPABILITY_3_TWAKEUP_SUPPORTED BIT(2) +#define CCS_PHY_CTRL_CAPABILITY_3_TINIT_SUPPORTED BIT(3) +#define CCS_PHY_CTRL_CAPABILITY_3_THS_EXIT_SUPPORTED BIT(4) +#define CCS_PHY_CTRL_CAPABILITY_3_CPHY_TIMING_NOT_MULTIPLE BIT(5) +#define CCS_PHY_CTRL_CAPABILITY_3_CPHY_MIN_TIMING_VALUE_1 BIT(6) +#define CCS_R_DPHY_SF 0x165d +#define CCS_R_CPHY_SF 0x165e +#define CCS_CPHY_SF_TWAKEUP_SHIFT 0U +#define CCS_CPHY_SF_TWAKEUP_MASK 0xf +#define CCS_CPHY_SF_TINIT_SHIFT 4U +#define CCS_CPHY_SF_TINIT_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_1 0x165f +#define CCS_DPHY_LIMITS_1_THS_PREPARE_SHIFT 0U +#define CCS_DPHY_LIMITS_1_THS_PREPARE_MASK 0xf +#define CCS_DPHY_LIMITS_1_THS_ZERO_SHIFT 4U +#define CCS_DPHY_LIMITS_1_THS_ZERO_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_2 0x1660 +#define CCS_DPHY_LIMITS_2_THS_TRAIL_SHIFT 0U +#define CCS_DPHY_LIMITS_2_THS_TRAIL_MASK 0xf +#define CCS_DPHY_LIMITS_2_TCLK_TRAIL_MIN_SHIFT 4U +#define CCS_DPHY_LIMITS_2_TCLK_TRAIL_MIN_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_3 0x1661 +#define CCS_DPHY_LIMITS_3_TCLK_PREPARE_SHIFT 0U +#define CCS_DPHY_LIMITS_3_TCLK_PREPARE_MASK 0xf +#define CCS_DPHY_LIMITS_3_TCLK_ZERO_SHIFT 4U +#define CCS_DPHY_LIMITS_3_TCLK_ZERO_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_4 0x1662 +#define CCS_DPHY_LIMITS_4_TCLK_POST_SHIFT 0U +#define CCS_DPHY_LIMITS_4_TCLK_POST_MASK 0xf +#define CCS_DPHY_LIMITS_4_TLPX_SHIFT 4U +#define CCS_DPHY_LIMITS_4_TLPX_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_5 0x1663 +#define CCS_DPHY_LIMITS_5_THS_EXIT_SHIFT 0U +#define CCS_DPHY_LIMITS_5_THS_EXIT_MASK 0xf +#define CCS_DPHY_LIMITS_5_TWAKEUP_SHIFT 4U +#define CCS_DPHY_LIMITS_5_TWAKEUP_MASK 0xf0 +#define CCS_R_DPHY_LIMITS_6 0x1664 +#define CCS_DPHY_LIMITS_6_TINIT_SHIFT 0U +#define CCS_DPHY_LIMITS_6_TINIT_MASK 0xf +#define CCS_R_CPHY_LIMITS_1 0x1665 +#define CCS_CPHY_LIMITS_1_T3_PREPARE_MAX_SHIFT 0U +#define CCS_CPHY_LIMITS_1_T3_PREPARE_MAX_MASK 0xf +#define CCS_CPHY_LIMITS_1_T3_LPX_MAX_SHIFT 4U +#define CCS_CPHY_LIMITS_1_T3_LPX_MAX_MASK 0xf0 +#define CCS_R_CPHY_LIMITS_2 0x1666 +#define CCS_CPHY_LIMITS_2_THS_EXIT_MAX_SHIFT 0U +#define CCS_CPHY_LIMITS_2_THS_EXIT_MAX_MASK 0xf +#define CCS_CPHY_LIMITS_2_TWAKEUP_MAX_SHIFT 4U +#define CCS_CPHY_LIMITS_2_TWAKEUP_MAX_MASK 0xf0 +#define CCS_R_CPHY_LIMITS_3 0x1667 +#define CCS_CPHY_LIMITS_3_TINIT_MAX_SHIFT 0U +#define CCS_CPHY_LIMITS_3_TINIT_MAX_MASK 0xf +#define CCS_R_MIN_FRAME_LENGTH_LINES_BIN (0x1700 | CCS_FL_16BIT) +#define CCS_R_MAX_FRAME_LENGTH_LINES_BIN (0x1702 | CCS_FL_16BIT) +#define CCS_R_MIN_LINE_LENGTH_PCK_BIN (0x1704 | CCS_FL_16BIT) +#define CCS_R_MAX_LINE_LENGTH_PCK_BIN (0x1706 | CCS_FL_16BIT) +#define CCS_R_MIN_LINE_BLANKING_PCK_BIN (0x1708 | CCS_FL_16BIT) +#define CCS_R_FINE_INTEGRATION_TIME_MIN_BIN (0x170a | CCS_FL_16BIT) +#define CCS_R_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN (0x170c | CCS_FL_16BIT) +#define CCS_R_BINNING_CAPABILITY 0x1710 +#define CCS_BINNING_CAPABILITY_UNSUPPORTED 0U +#define CCS_BINNING_CAPABILITY_BINNING_THEN_SUBSAMPLING 1U +#define CCS_BINNING_CAPABILITY_SUBSAMPLING_THEN_BINNING 2U +#define CCS_R_BINNING_WEIGHTING_CAPABILITY 0x1711 +#define CCS_BINNING_WEIGHTING_CAPABILITY_AVERAGED BIT(0) +#define CCS_BINNING_WEIGHTING_CAPABILITY_SUMMED BIT(1) +#define CCS_BINNING_WEIGHTING_CAPABILITY_BAYER_CORRECTED BIT(2) +#define CCS_BINNING_WEIGHTING_CAPABILITY_MODULE_SPECIFIC_WEIGHT BIT(3) +#define CCS_R_BINNING_SUB_TYPES 0x1712 +#define CCS_R_BINNING_SUB_TYPE(n) (0x1713 + (n)) +#define CCS_LIM_BINNING_SUB_TYPE_MIN_N 0U +#define CCS_LIM_BINNING_SUB_TYPE_MAX_N 63U +#define CCS_BINNING_SUB_TYPE_ROW_SHIFT 0U +#define CCS_BINNING_SUB_TYPE_ROW_MASK 0xf +#define CCS_BINNING_SUB_TYPE_COLUMN_SHIFT 4U +#define CCS_BINNING_SUB_TYPE_COLUMN_MASK 0xf0 +#define CCS_R_BINNING_WEIGHTING_MONO_CAPABILITY 0x1771 +#define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_AVERAGED BIT(0) +#define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_SUMMED BIT(1) +#define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_BAYER_CORRECTED BIT(2) +#define CCS_BINNING_WEIGHTING_MONO_CAPABILITY_MODULE_SPECIFIC_WEIGHT BIT(3) +#define CCS_R_BINNING_SUB_TYPES_MONO 0x1772 +#define CCS_R_BINNING_SUB_TYPE_MONO(n) (0x1773 + (n)) +#define CCS_LIM_BINNING_SUB_TYPE_MONO_MIN_N 0U +#define CCS_LIM_BINNING_SUB_TYPE_MONO_MAX_N 63U +#define CCS_R_DATA_TRANSFER_IF_CAPABILITY 0x1800 +#define CCS_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) +#define CCS_DATA_TRANSFER_IF_CAPABILITY_POLLING BIT(2) +#define CCS_R_SHADING_CORRECTION_CAPABILITY 0x1900 +#define CCS_SHADING_CORRECTION_CAPABILITY_COLOR_SHADING BIT(0) +#define CCS_SHADING_CORRECTION_CAPABILITY_LUMINANCE_CORRECTION BIT(1) +#define CCS_R_GREEN_IMBALANCE_CAPABILITY 0x1901 +#define CCS_GREEN_IMBALANCE_CAPABILITY_SUPPORTED BIT(0) +#define CCS_R_MODULE_SPECIFIC_CORRECTION_CAPABILITY 0x1903 +#define CCS_R_DEFECT_CORRECTION_CAPABILITY (0x1904 | CCS_FL_16BIT) +#define CCS_DEFECT_CORRECTION_CAPABILITY_MAPPED_DEFECT BIT(0) +#define CCS_DEFECT_CORRECTION_CAPABILITY_DYNAMIC_COUPLET BIT(2) +#define CCS_DEFECT_CORRECTION_CAPABILITY_DYNAMIC_SINGLE BIT(5) +#define CCS_DEFECT_CORRECTION_CAPABILITY_COMBINED_DYNAMIC BIT(8) +#define CCS_R_DEFECT_CORRECTION_CAPABILITY_2 (0x1906 | CCS_FL_16BIT) +#define CCS_DEFECT_CORRECTION_CAPABILITY_2_DYNAMIC_TRIPLET BIT(3) +#define CCS_R_NF_CAPABILITY 0x1908 +#define CCS_NF_CAPABILITY_LUMA BIT(0) +#define CCS_NF_CAPABILITY_CHROMA BIT(1) +#define CCS_NF_CAPABILITY_COMBINED BIT(2) +#define CCS_R_OB_READOUT_CAPABILITY 0x1980 +#define CCS_OB_READOUT_CAPABILITY_CONTROLLABLE_READOUT BIT(0) +#define CCS_OB_READOUT_CAPABILITY_VISIBLE_PIXEL_READOUT BIT(1) +#define CCS_OB_READOUT_CAPABILITY_DIFFERENT_VC_READOUT BIT(2) +#define CCS_OB_READOUT_CAPABILITY_DIFFERENT_DT_READOUT BIT(3) +#define CCS_OB_READOUT_CAPABILITY_PROG_DATA_FORMAT BIT(4) +#define CCS_R_COLOR_FEEDBACK_CAPABILITY 0x1987 +#define CCS_COLOR_FEEDBACK_CAPABILITY_KELVIN BIT(0) +#define CCS_COLOR_FEEDBACK_CAPABILITY_AWB_GAIN BIT(1) +#define CCS_R_CFA_PATTERN_CAPABILITY 0x1990 +#define CCS_CFA_PATTERN_CAPABILITY_BAYER 0U +#define CCS_CFA_PATTERN_CAPABILITY_MONOCHROME 1U +#define CCS_CFA_PATTERN_CAPABILITY_4X4_QUAD_BAYER 2U +#define CCS_CFA_PATTERN_CAPABILITY_VENDOR_SPECIFIC 3U +#define CCS_R_CFA_PATTERN_CONVERSION_CAPABILITY 0x1991 +#define CCS_CFA_PATTERN_CONVERSION_CAPABILITY_BAYER BIT(0) +#define CCS_R_FLASH_MODE_CAPABILITY 0x1a02 +#define CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) +#define CCS_R_SA_STROBE_MODE_CAPABILITY 0x1a03 +#define CCS_SA_STROBE_MODE_CAPABILITY_FIXED_WIDTH BIT(0) +#define CCS_SA_STROBE_MODE_CAPABILITY_EDGE_CTRL BIT(1) +#define CCS_R_RESET_MAX_DELAY 0x1a10 +#define CCS_R_RESET_MIN_TIME 0x1a11 +#define CCS_R_PDAF_CAPABILITY_1 0x1b80 +#define CCS_PDAF_CAPABILITY_1_SUPPORTED BIT(0) +#define CCS_PDAF_CAPABILITY_1_PROCESSED_BOTTOM_EMBEDDED BIT(1) +#define CCS_PDAF_CAPABILITY_1_PROCESSED_INTERLEAVED BIT(2) +#define CCS_PDAF_CAPABILITY_1_RAW_BOTTOM_EMBEDDED BIT(3) +#define CCS_PDAF_CAPABILITY_1_RAW_INTERLEAVED BIT(4) +#define CCS_PDAF_CAPABILITY_1_VISIBLE_PDAF_CORRECTION BIT(5) +#define CCS_PDAF_CAPABILITY_1_VC_INTERLEAVING BIT(6) +#define CCS_PDAF_CAPABILITY_1_DT_INTERLEAVING BIT(7) +#define CCS_R_PDAF_CAPABILITY_2 0x1b81 +#define CCS_PDAF_CAPABILITY_2_ROI BIT(0) +#define CCS_PDAF_CAPABILITY_2_AFTER_DIGITAL_CROP BIT(1) +#define CCS_PDAF_CAPABILITY_2_CTRL_RETIMED BIT(2) +#define CCS_R_BRACKETING_LUT_CAPABILITY_1 0x1c00 +#define CCS_BRACKETING_LUT_CAPABILITY_1_COARSE_INTEGRATION BIT(0) +#define CCS_BRACKETING_LUT_CAPABILITY_1_GLOBAL_ANALOG_GAIN BIT(1) +#define CCS_BRACKETING_LUT_CAPABILITY_1_FLASH BIT(4) +#define CCS_BRACKETING_LUT_CAPABILITY_1_GLOBAL_DIGITAL_GAIN BIT(5) +#define CCS_BRACKETING_LUT_CAPABILITY_1_ALTERNATE_GLOBAL_ANALOG_GAIN BIT(6) +#define CCS_R_BRACKETING_LUT_CAPABILITY_2 0x1c01 +#define CCS_BRACKETING_LUT_CAPABILITY_2_SINGLE_BRACKETING_MODE BIT(0) +#define CCS_BRACKETING_LUT_CAPABILITY_2_LOOPED_BRACKETING_MODE BIT(1) +#define CCS_R_BRACKETING_LUT_SIZE 0x1c02 + +#endif /* __CCS_REGS_H__ */ diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/ccs/ccs.h index 6f469934f9e3..356b87c33405 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -1,24 +1,28 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * drivers/media/i2c/smiapp/smiapp.h + * drivers/media/i2c/smiapp/ccs.h * - * Generic driver for SMIA/SMIA++ compliant camera modules + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors * + * Copyright (C) 2020 Intel Corporation * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> + * Contact: Sakari Ailus <sakari.ailus@linux.intel.com> */ -#ifndef __SMIAPP_PRIV_H_ -#define __SMIAPP_PRIV_H_ +#ifndef __CCS_H__ +#define __CCS_H__ #include <linux/mutex.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> -#include "smiapp-pll.h" -#include "smiapp-reg.h" -#include "smiapp-regs.h" -#include "smiapp-quirk.h" +#include "ccs-data.h" +#include "ccs-limits.h" +#include "ccs-quirk.h" +#include "ccs-regs.h" +#include "ccs-reg-access.h" +#include "../ccs-pll.h" +#include "smiapp-reg-defs.h" /* * Standard SMIA++ constants @@ -39,12 +43,19 @@ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + (clk) / 1000 - 1) / ((clk) / 1000)) -#define SMIAPP_COLOUR_COMPONENTS 4 +#define CCS_COLOUR_COMPONENTS 4 -#define SMIAPP_NAME "smiapp" +#define SMIAPP_NAME "smiapp" +#define CCS_NAME "ccs" -#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ -#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ +#define CCS_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ +#define CCS_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ + +#define CCS_LIM(sensor, limit) \ + ccs_get_limit(sensor, CCS_L_##limit, 0) + +#define CCS_LIM_AT(sensor, limit, offset) \ + ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset)) /* * Sometimes due to board layout considerations the camera module can be @@ -52,12 +63,12 @@ * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. * FIXME: rotation also changes the bayer pattern. */ -enum smiapp_module_board_orient { - SMIAPP_MODULE_BOARD_ORIENT_0 = 0, - SMIAPP_MODULE_BOARD_ORIENT_180, +enum ccs_module_board_orient { + CCS_MODULE_BOARD_ORIENT_0 = 0, + CCS_MODULE_BOARD_ORIENT_180, }; -struct smiapp_flash_strobe_parms { +struct ccs_flash_strobe_parms { u8 mode; u32 strobe_width_high_us; u16 strobe_delay; @@ -65,7 +76,7 @@ struct smiapp_flash_strobe_parms { u8 trigger; }; -struct smiapp_hwconfig { +struct ccs_hwconfig { /* * Change the cci address if i2c_addr_alt is set. * Both default and alternate cci addr need to be present @@ -76,136 +87,128 @@ struct smiapp_hwconfig { uint32_t ext_clk; /* sensor external clk */ unsigned int lanes; /* Number of CSI-2 lanes */ - uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + uint32_t csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ uint64_t *op_sys_clock; - enum smiapp_module_board_orient module_board_orient; + enum ccs_module_board_orient module_board_orient; - struct smiapp_flash_strobe_parms *strobe_setup; + struct ccs_flash_strobe_parms *strobe_setup; }; -#include "smiapp-limits.h" +struct ccs_quirk; -struct smiapp_quirk; +#define CCS_MODULE_IDENT_FLAG_REV_LE (1 << 0) -#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) - -struct smiapp_module_ident { - u8 manufacturer_id; +struct ccs_module_ident { + u16 mipi_manufacturer_id; u16 model_id; + u8 smia_manufacturer_id; u8 revision_number_major; u8 flags; char *name; - const struct smiapp_quirk *quirk; + const struct ccs_quirk *quirk; }; -struct smiapp_module_info { - u32 manufacturer_id; +struct ccs_module_info { + u32 smia_manufacturer_id; + u32 mipi_manufacturer_id; u32 model_id; - u32 revision_number_major; - u32 revision_number_minor; + u32 revision_number; u32 module_year; u32 module_month; u32 module_day; - u32 sensor_manufacturer_id; + u32 sensor_smia_manufacturer_id; + u32 sensor_mipi_manufacturer_id; u32 sensor_model_id; u32 sensor_revision_number; u32 sensor_firmware_version; u32 smia_version; u32 smiapp_version; - - u32 smiapp_profile; + u32 ccs_version; char *name; - const struct smiapp_quirk *quirk; + const struct ccs_quirk *quirk; }; -#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ +#define CCS_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .smia_manufacturer_id = manufacturer, \ .model_id = model, \ .revision_number_major = rev, \ .flags = fl, \ .name = _name, \ .quirk = _quirk, } -#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ +#define CCS_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .smia_manufacturer_id = manufacturer, \ .model_id = model, \ .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .flags = CCS_MODULE_IDENT_FLAG_REV_LE, \ .name = _name, \ .quirk = _quirk, } -#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ +#define CCS_IDENT_L(manufacturer, model, rev, _name) \ + { .smia_manufacturer_id = manufacturer, \ .model_id = model, \ .revision_number_major = rev, \ - .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .flags = CCS_MODULE_IDENT_FLAG_REV_LE, \ .name = _name, } -#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ - { .manufacturer_id = manufacturer, \ +#define CCS_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .smia_manufacturer_id = manufacturer, \ .model_id = model, \ .revision_number_major = rev, \ .flags = 0, \ .name = _name, \ .quirk = _quirk, } -#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ - { .manufacturer_id = manufacturer, \ +#define CCS_IDENT(manufacturer, model, rev, _name) \ + { .smia_manufacturer_id = manufacturer, \ .model_id = model, \ .revision_number_major = rev, \ .flags = 0, \ .name = _name, } -struct smiapp_reg_limits { - u32 addr; - char *what; -}; - -extern struct smiapp_reg_limits smiapp_reg_limits[]; - -struct smiapp_csi_data_format { +struct ccs_csi_data_format { u32 code; u8 width; u8 compressed; u8 pixel_order; }; -#define SMIAPP_SUBDEVS 3 +#define CCS_SUBDEVS 3 -#define SMIAPP_PA_PAD_SRC 0 -#define SMIAPP_PAD_SINK 0 -#define SMIAPP_PAD_SRC 1 -#define SMIAPP_PADS 2 +#define CCS_PA_PAD_SRC 0 +#define CCS_PAD_SINK 0 +#define CCS_PAD_SRC 1 +#define CCS_PADS 2 -struct smiapp_binning_subtype { +struct ccs_binning_subtype { u8 horizontal:4; u8 vertical:4; } __packed; -struct smiapp_subdev { +struct ccs_subdev { struct v4l2_subdev sd; - struct media_pad pads[SMIAPP_PADS]; + struct media_pad pads[CCS_PADS]; struct v4l2_rect sink_fmt; - struct v4l2_rect crop[SMIAPP_PADS]; + struct v4l2_rect crop[CCS_PADS]; struct v4l2_rect compose; /* compose on sink */ unsigned short sink_pad; unsigned short source_pad; int npads; - struct smiapp_sensor *sensor; + struct ccs_sensor *sensor; struct v4l2_ctrl_handler ctrl_handler; }; /* - * struct smiapp_sensor - Main device structure + * struct ccs_sensor - Main device structure */ -struct smiapp_sensor { +struct ccs_sensor { /* * "mutex" is used to serialise access to all fields here * except v4l2_ctrls at the end of the struct. "mutex" is also @@ -213,24 +216,26 @@ struct smiapp_sensor { * information. */ struct mutex mutex; - struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + struct ccs_subdev ssds[CCS_SUBDEVS]; u32 ssds_used; - struct smiapp_subdev *src; - struct smiapp_subdev *binner; - struct smiapp_subdev *scaler; - struct smiapp_subdev *pixel_array; - struct smiapp_hwconfig *hwcfg; - struct regulator *vana; + struct ccs_subdev *src; + struct ccs_subdev *binner; + struct ccs_subdev *scaler; + struct ccs_subdev *pixel_array; + struct ccs_hwconfig hwcfg; + struct regulator_bulk_data *regulators; struct clk *ext_clk; struct gpio_desc *xshutdown; - u32 limits[SMIAPP_LIMIT_LAST]; + struct gpio_desc *reset; + void *ccs_limits; u8 nbinning_subtypes; - struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + struct ccs_binning_subtype binning_subtypes[CCS_LIM_BINNING_SUB_TYPE_MAX_N + 1]; u32 mbus_frame_fmts; - const struct smiapp_csi_data_format *csi_format; - const struct smiapp_csi_data_format *internal_csi_format; + const struct ccs_csi_data_format *csi_format; + const struct ccs_csi_data_format *internal_csi_format; u32 default_mbus_frame_fmts; int default_pixel_order; + struct ccs_data_container sdata, mdata; u8 binning_horizontal; u8 binning_vertical; @@ -249,9 +254,9 @@ struct smiapp_sensor { bool dev_init_done; u8 compressed_min_bpp; - struct smiapp_module_info minfo; + struct ccs_module_info minfo; - struct smiapp_pll pll; + struct ccs_pll pll; /* Is a default format supported for a given BPP? */ unsigned long *valid_link_freqs; @@ -268,13 +273,18 @@ struct smiapp_sensor { struct v4l2_ctrl *link_freq; struct v4l2_ctrl *pixel_rate_csi; /* test pattern colour components */ - struct v4l2_ctrl *test_data[SMIAPP_COLOUR_COMPONENTS]; + struct v4l2_ctrl *test_data[CCS_COLOUR_COMPONENTS]; }; -#define to_smiapp_subdev(_sd) \ - container_of(_sd, struct smiapp_subdev, sd) +#define to_ccs_subdev(_sd) \ + container_of(_sd, struct ccs_subdev, sd) + +#define to_ccs_sensor(_sd) \ + (to_ccs_subdev(_sd)->sensor) -#define to_smiapp_sensor(_sd) \ - (to_smiapp_subdev(_sd)->sensor) +void ccs_replace_limit(struct ccs_sensor *sensor, + unsigned int limit, unsigned int offset, u32 val); +u32 ccs_get_limit(struct ccs_sensor *sensor, unsigned int limit, + unsigned int offset); -#endif /* __SMIAPP_PRIV_H_ */ +#endif /* __CCS_H__ */ diff --git a/drivers/media/i2c/ccs/smiapp-reg-defs.h b/drivers/media/i2c/ccs/smiapp-reg-defs.h new file mode 100644 index 000000000000..e80c110ebf3a --- /dev/null +++ b/drivers/media/i2c/ccs/smiapp-reg-defs.h @@ -0,0 +1,580 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * drivers/media/i2c/smiapp/smiapp-reg-defs.h + * + * Generic driver for MIPI CCS/SMIA/SMIA++ compliant camera sensors + * + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus <sakari.ailus@iki.fi> + */ + +#ifndef __SMIAPP_REG_DEFS_H__ +#define __SMIAPP_REG_DEFS_H__ + +/* Register addresses */ +#define SMIAPP_REG_U16_MODEL_ID (0x0000 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR 0x0002 +#define SMIAPP_REG_U8_MANUFACTURER_ID 0x0003 +#define SMIAPP_REG_U8_SMIA_VERSION 0x0004 +#define SMIAPP_REG_U8_FRAME_COUNT 0x0005 +#define SMIAPP_REG_U8_PIXEL_ORDER 0x0006 +#define SMIAPP_REG_U16_DATA_PEDESTAL (0x0008 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_PIXEL_DEPTH 0x000c +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR 0x0010 +#define SMIAPP_REG_U8_SMIAPP_VERSION 0x0011 +#define SMIAPP_REG_U8_MODULE_DATE_YEAR 0x0012 +#define SMIAPP_REG_U8_MODULE_DATE_MONTH 0x0013 +#define SMIAPP_REG_U8_MODULE_DATE_DAY 0x0014 +#define SMIAPP_REG_U8_MODULE_DATE_PHASE 0x0015 +#define SMIAPP_REG_U16_SENSOR_MODEL_ID (0x0016 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER 0x0018 +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID 0x0019 +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION 0x001a +#define SMIAPP_REG_U32_SERIAL_NUMBER (0x001c | CCS_FL_32BIT) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE 0x0040 +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE 0x0041 +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) ((0x0042 + ((n) << 1)) | CCS_FL_16BIT) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) ((0x0060 + ((n) << 2)) | CCS_FL_32BIT) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY (0x0080 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN (0x0084 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX (0x0086 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP (0x0088 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE (0x008a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 (0x008c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 (0x008e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 (0x0090 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 (0x0092 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE 0x00c0 +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE 0x00c1 +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) ((0x00c2 + ((n) << 1)) | CCS_FL_16BIT) +#define SMIAPP_REG_U8_MODE_SELECT 0x0100 +#define SMIAPP_REG_U8_IMAGE_ORIENTATION 0x0101 +#define SMIAPP_REG_U8_SOFTWARE_RESET 0x0103 +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD 0x0104 +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES 0x0105 +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL 0x0106 +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL 0x0107 +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL 0x0108 +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL 0x0109 +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER 0x0110 +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE 0x0111 +#define SMIAPP_REG_U16_CSI_DATA_FORMAT (0x0112 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_CSI_LANE_MODE 0x0114 +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT 0x0115 +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT 0x0116 +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT 0x0117 +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT 0x0118 +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT 0x0119 +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT 0x011a +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT 0x011b +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT 0x011c +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT 0x011d +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT 0x011e +#define SMIAPP_REG_U8_GAIN_MODE 0x0120 +#define SMIAPP_REG_U16_VANA_VOLTAGE (0x0130 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VDIG_VOLTAGE (0x0132 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VIO_VOLTAGE (0x0134 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ (0x0136 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL 0x0138 +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE 0x0139 +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT 0x013a +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME (0x0200 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME (0x0202 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL (0x0204 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR (0x0206 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED (0x0208 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE (0x020a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB (0x020c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR (0x020e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED (0x0210 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE (0x0212 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB (0x0214 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV (0x0300 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV (0x0302 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV (0x0304 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_PLL_MULTIPLIER (0x0306 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV (0x0308 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV (0x030a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES (0x0340 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK (0x0342 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_ADDR_START (0x0344 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_ADDR_START (0x0346 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_ADDR_END (0x0348 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_ADDR_END (0x034a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE (0x034c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE (0x034e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_EVEN_INC (0x0380 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_ODD_INC (0x0382 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_EVEN_INC (0x0384 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_ODD_INC (0x0386 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALING_MODE (0x0400 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING (0x0402 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALE_M (0x0404 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALE_N (0x0406 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET (0x0408 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET (0x040a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH (0x040c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT (0x040e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_COMPRESSION_MODE (0x0500 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE (0x0600 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TEST_DATA_RED (0x0602 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TEST_DATA_GREENR (0x0604 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TEST_DATA_BLUE (0x0606 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TEST_DATA_GREENB (0x0608 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH (0x060a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION (0x060c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH (0x060e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION (0x0610 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS (0x0700 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_TCLK_POST 0x0800 +#define SMIAPP_REG_U8_THS_PREPARE 0x0801 +#define SMIAPP_REG_U8_THS_ZERO_MIN 0x0802 +#define SMIAPP_REG_U8_THS_TRAIL 0x0803 +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN 0x0804 +#define SMIAPP_REG_U8_TCLK_PREPARE 0x0805 +#define SMIAPP_REG_U8_TCLK_ZERO 0x0806 +#define SMIAPP_REG_U8_TLPX 0x0807 +#define SMIAPP_REG_U8_DPHY_CTRL 0x0808 +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS (0x0820 | CCS_FL_32BIT) +#define SMIAPP_REG_U8_BINNING_MODE 0x0900 +#define SMIAPP_REG_U8_BINNING_TYPE 0x0901 +#define SMIAPP_REG_U8_BINNING_WEIGHTING 0x0902 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL 0x0a00 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS 0x0a01 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT 0x0a02 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 0x0a04 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 0x0a05 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 0x0a06 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 0x0a07 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 0x0a08 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 0x0a09 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 0x0a10 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 0x0a11 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 0x0a12 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 0x0a13 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 0x0a14 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 0x0a15 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 0x0a16 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 0x0a17 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 0x0a18 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 0x0a19 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 0x0a1a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 0x0a1b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 0x0a1c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 0x0a1d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 0x0a1e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 0x0a1f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 0x0a20 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 0x0a21 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 0x0a22 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 0x0a23 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 0x0a24 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 0x0a25 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 0x0a26 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 0x0a27 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 0x0a28 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 0x0a29 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 0x0a2a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 0x0a2b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 0x0a2c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 0x0a2d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 0x0a2e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 0x0a2f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 0x0a30 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 0x0a31 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 0x0a32 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 0x0a33 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 0x0a34 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 0x0a35 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 0x0a36 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 0x0a37 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 0x0a38 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 0x0a39 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 0x0a3a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 0x0a3b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 0x0a3c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 0x0a3d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 0x0a3e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 0x0a3f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 0x0a40 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 0x0a41 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 0x0a42 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 0x0a43 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL 0x0a44 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS 0x0a45 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT 0x0a46 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 0x0a48 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 0x0a49 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 0x0a4a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 0x0a4b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 0x0a4c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 0x0a4d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 0x0a4e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 0x0a4f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 0x0a50 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 0x0a51 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 0x0a52 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 0x0a53 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 0x0a54 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 0x0a55 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 0x0a56 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 0x0a57 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 0x0a58 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 0x0a59 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 0x0a5a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 0x0a5b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 0x0a5c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 0x0a5d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 0x0a5e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 0x0a5f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 0x0a60 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 0x0a61 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 0x0a62 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 0x0a63 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 0x0a64 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 0x0a65 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 0x0a66 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 0x0a67 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 0x0a68 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 0x0a69 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 0x0a6a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 0x0a6b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 0x0a6c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 0x0a6d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 0x0a6e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 0x0a6f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 0x0a70 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 0x0a71 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 0x0a72 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 0x0a73 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 0x0a74 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 0x0a75 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 0x0a76 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 0x0a77 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 0x0a78 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 0x0a79 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 0x0a7a +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 0x0a7b +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 0x0a7c +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 0x0a7d +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 0x0a7e +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 0x0a7f +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 0x0a80 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 0x0a81 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 0x0a82 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 0x0a83 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 0x0a84 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 0x0a85 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 0x0a86 +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 0x0a87 +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE 0x0b00 +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL 0x0b01 +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE 0x0b02 +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT 0x0b03 +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE 0x0b04 +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE 0x0b05 +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE 0x0b06 +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT 0x0b07 +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE 0x0b08 +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT 0x0b09 +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE 0x0b0a +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT 0x0b0b +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE 0x0b0c +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT 0x0b0d +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE 0x0b0e +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST 0x0b0f +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST 0x0b10 +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE 0x0b11 +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST 0x0b12 +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE 0x0b13 +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST 0x0b14 +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE 0x0b15 +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST 0x0b16 +#define SMIAPP_REG_U8_EDOF_MODE 0x0b80 +#define SMIAPP_REG_U8_SHARPNESS 0x0b83 +#define SMIAPP_REG_U8_DENOISING 0x0b84 +#define SMIAPP_REG_U8_MODULE_SPECIFIC 0x0b85 +#define SMIAPP_REG_U16_DEPTH_OF_FIELD (0x0b86 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FOCUS_DISTANCE (0x0b88 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL 0x0b8a +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE (0x0b8c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR (0x0b8e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED (0x0b90 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE (0x0b92 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB (0x0b94 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE 0x0bc0 +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING (0x0bc2 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START (0x0bc4 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START (0x0bc6 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH (0x0bc8 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT (0x0bca | CCS_FL_16BIT) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 0x0c00 +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 0x0c01 +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 0x0c02 +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 0x0c03 +#define SMIAPP_REG_U16_TRDY_CTRL (0x0c04 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TRDOUT_CTRL (0x0c06 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL (0x0c08 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL (0x0c0a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL (0x0c0c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL (0x0c0e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL (0x0c10 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT 0x0c12 +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT (0x0c14 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL (0x0c16 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL (0x0c18 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_FLASH_MODE_RS 0x0c1a +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS 0x0c1b +#define SMIAPP_REG_U8_FLASH_STATUS 0x0c1c +#define SMIAPP_REG_U8_SA_STROBE_MODE 0x0c1d +#define SMIAPP_REG_U16_SA_STROBE_START_POINT (0x0c1e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL (0x0c20 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL (0x0c22 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER 0x0c24 +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS 0x0c25 +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL (0x0c26 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL (0x0c28 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL 0x0c2a +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL 0x0c2b +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL (0x0c2c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL (0x0c2e | CCS_FL_16BIT) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL 0x0c80 +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT (0x0c82 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 (0x0c84 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT 0x0c86 +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 (0x0c88 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT 0x0c8a +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 (0x0c8c | CCS_FL_16BIT) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT 0x0c8e +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL 0x0d00 +#define SMIAPP_REG_U8_OPERATION_MODE 0x0d01 +#define SMIAPP_REG_U8_ACT_STATE1 0x0d02 +#define SMIAPP_REG_U8_ACT_STATE2 0x0d03 +#define SMIAPP_REG_U16_FOCUS_CHANGE (0x0d80 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL (0x0d82 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 (0x0d84 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 (0x0d86 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 0x0d88 +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 0x0d89 +#define SMIAPP_REG_U8_POSITION 0x0d8a +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL 0x0e00 +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE 0x0e01 +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL 0x0e02 +#define SMIAPP_REG_U8_LUT_PARAMETERS_START 0x0e10 +#define SMIAPP_REG_U8_LUT_PARAMETERS_END 0x0eff +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY (0x1000 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN (0x1004 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN (0x1006 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN (0x1008 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN (0x100a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY (0x1080 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN (0x1084 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX (0x1086 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE (0x1088 | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ (0x1100 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ (0x1104 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV (0x1108 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV (0x110a | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ (0x110c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ (0x1110 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER (0x1114 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER (0x1116 | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ (0x1118 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ (0x111c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV (0x1120 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV (0x1122 | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ (0x1124 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ (0x1128 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ (0x112c | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ (0x1130 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV (0x1134 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV (0x1136 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES (0x1140 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES (0x1142 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK (0x1144 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK (0x1146 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK (0x1148 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES (0x114a | CCS_FL_16BIT) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE 0x114c +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV (0x1160 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV (0x1162 | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ (0x1164 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ (0x1168 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV (0x116c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV (0x116e | CCS_FL_16BIT) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ (0x1170 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ (0x1174 | CCS_FL_FLOAT_IREAL | CCS_FL_32BIT) +#define SMIAPP_REG_U16_X_ADDR_MIN (0x1180 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_ADDR_MIN (0x1182 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_X_ADDR_MAX (0x1184 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_Y_ADDR_MAX (0x1186 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE (0x1188 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE (0x118a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE (0x118c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE (0x118e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_EVEN_INC (0x11c0 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_EVEN_INC (0x11c2 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_ODD_INC (0x11c4 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_ODD_INC (0x11c6 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALING_CAPABILITY (0x1200 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALER_M_MIN (0x1204 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALER_M_MAX (0x1206 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALER_N_MIN (0x1208 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SCALER_N_MAX (0x120a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY (0x120c | CCS_FL_16BIT) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY 0x120e +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY (0x1300 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED (0x1400 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED (0x1402 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED (0x1404 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN (0x1406 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN (0x1408 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN (0x140a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE (0x140c | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE (0x140e | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE (0x1410 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS (0x1500 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY 0x1502 +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY 0x1600 +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY 0x1601 +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY 0x1602 +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY 0x1603 +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY 0x1604 +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS (0x1608 | CCS_FL_32BIT) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS (0x160c | CCS_FL_32BIT) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS (0x1610 | CCS_FL_32BIT) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS (0x1614 | CCS_FL_32BIT) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY 0x1618 +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN (0x1700 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN (0x1702 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN (0x1704 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN (0x1706 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN (0x1708 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN (0x170a | CCS_FL_16BIT) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN (0x170c | CCS_FL_16BIT) +#define SMIAPP_REG_U8_BINNING_CAPABILITY 0x1710 +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY 0x1711 +#define SMIAPP_REG_U8_BINNING_SUBTYPES 0x1712 +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) (0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY 0x1800 +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY 0x1900 +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY 0x1901 +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY 0x1902 +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY 0x1903 +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY (0x1904 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 (0x1906 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_EDOF_CAPABILITY 0x1980 +#define SMIAPP_REG_U8_ESTIMATION_FRAMES 0x1981 +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ 0x1982 +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ 0x1983 +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ 0x1984 +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ 0x1985 +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ 0x1986 +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY 0x1987 +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM 0x1988 +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY 0x19c0 +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY 0x19c1 +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD (0x19c2 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE (0x19c4 | CCS_FL_16BIT) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN (0x1a00 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY 0x1a02 +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR (0x1b02 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY 0x1b04 +#define SMIAPP_REG_U16_ACTUATOR_TYPE (0x1b40 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS 0x1b42 +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS (0x1b44 | CCS_FL_16BIT) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 0x1c00 +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 0x1c01 +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE 0x1c02 + +/* Register bit definitions */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP BIT(0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP BIT(1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY BIT(1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA BIT(2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE BIT(3) + +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) +#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL BIT(2) + +#define SMIAPP_SOFTWARE_RESET BIT(0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) + +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 +#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +#endif /* __SMIAPP_REG_DEFS_H__ */ diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c index 45cdd924b565..8b8cb4b077b5 100644 --- a/drivers/media/i2c/dw9768.c +++ b/drivers/media/i2c/dw9768.c @@ -315,8 +315,7 @@ static int dw9768_release(struct dw9768 *dw9768) static int dw9768_runtime_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct dw9768 *dw9768 = sd_to_dw9768(sd); dw9768_release(dw9768); @@ -328,8 +327,7 @@ static int dw9768_runtime_suspend(struct device *dev) static int dw9768_runtime_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct dw9768 *dw9768 = sd_to_dw9768(sd); int ret; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 256acf73d5ea..122af761c8e3 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1237,7 +1237,7 @@ static ssize_t et8ek8_priv_mem_read(struct device *dev, struct device_attribute *attr, char *buf) { - struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); #if PAGE_SIZE < ET8EK8_PRIV_MEM_SIZE @@ -1374,8 +1374,7 @@ static const struct v4l2_subdev_internal_ops et8ek8_internal_ops = { */ static int __maybe_unused et8ek8_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); if (!sensor->power_count) @@ -1386,8 +1385,7 @@ static int __maybe_unused et8ek8_suspend(struct device *dev) static int __maybe_unused et8ek8_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct v4l2_subdev *subdev = dev_get_drvdata(dev); struct et8ek8_sensor *sensor = to_et8ek8_sensor(subdev); if (!sensor->power_count) diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index c66cd1446c0f..c74736845d7a 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -839,8 +839,7 @@ static int hi556_set_stream(struct v4l2_subdev *sd, int enable) static int __maybe_unused hi556_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct hi556 *hi556 = to_hi556(sd); mutex_lock(&hi556->mutex); @@ -854,8 +853,7 @@ static int __maybe_unused hi556_suspend(struct device *dev) static int __maybe_unused hi556_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct hi556 *hi556 = to_hi556(sd); int ret; diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index 1ef5af9a8c8b..cee1a4817af9 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -786,7 +786,7 @@ static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) if (ret < 0) goto err_rpm_put; } else { - ret = imx214_start_streaming(imx214); + ret = imx214_stop_streaming(imx214); if (ret < 0) goto err_rpm_put; pm_runtime_put(imx214->dev); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 1cee45e35355..e7791a0848b3 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -262,8 +262,6 @@ static const struct imx219_reg mode_1920_1080_regs[] = { {0x4793, 0x10}, {0x4797, 0x0e}, {0x479b, 0x0e}, - {0x0162, 0x0d}, - {0x0163, 0x78}, }; static const struct imx219_reg mode_1640_1232_regs[] = { @@ -473,8 +471,8 @@ static const struct imx219_mode supported_modes[] = { .width = 3280, .height = 2464, .crop = { - .left = 0, - .top = 0, + .left = IMX219_PIXEL_ARRAY_LEFT, + .top = IMX219_PIXEL_ARRAY_TOP, .width = 3280, .height = 2464 }, @@ -489,8 +487,8 @@ static const struct imx219_mode supported_modes[] = { .width = 1920, .height = 1080, .crop = { - .left = 680, - .top = 692, + .left = 688, + .top = 700, .width = 1920, .height = 1080 }, @@ -505,8 +503,8 @@ static const struct imx219_mode supported_modes[] = { .width = 1640, .height = 1232, .crop = { - .left = 0, - .top = 0, + .left = IMX219_PIXEL_ARRAY_LEFT, + .top = IMX219_PIXEL_ARRAY_TOP, .width = 3280, .height = 2464 }, @@ -521,8 +519,8 @@ static const struct imx219_mode supported_modes[] = { .width = 640, .height = 480, .crop = { - .left = 1000, - .top = 752, + .left = 1008, + .top = 760, .width = 1280, .height = 960 }, @@ -1008,6 +1006,7 @@ static int imx219_get_selection(struct v4l2_subdev *sd, return 0; case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.top = IMX219_PIXEL_ARRAY_TOP; sel->r.left = IMX219_PIXEL_ARRAY_LEFT; sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; @@ -1114,22 +1113,21 @@ err_unlock: /* Power/clock management functions */ static int imx219_power_on(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); int ret; ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, imx219->supplies); if (ret) { - dev_err(&client->dev, "%s: failed to enable regulators\n", + dev_err(dev, "%s: failed to enable regulators\n", __func__); return ret; } ret = clk_prepare_enable(imx219->xclk); if (ret) { - dev_err(&client->dev, "%s: failed to enable clock\n", + dev_err(dev, "%s: failed to enable clock\n", __func__); goto reg_off; } @@ -1148,8 +1146,7 @@ reg_off: static int imx219_power_off(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); gpiod_set_value_cansleep(imx219->reset_gpio, 0); @@ -1161,8 +1158,7 @@ static int imx219_power_off(struct device *dev) static int __maybe_unused imx219_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); if (imx219->streaming) @@ -1173,8 +1169,7 @@ static int __maybe_unused imx219_suspend(struct device *dev) static int __maybe_unused imx219_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx219 *imx219 = to_imx219(sd); int ret; @@ -1498,7 +1493,8 @@ static int imx219_probe(struct i2c_client *client) /* Initialize subdev */ imx219->sd.internal_ops = &imx219_internal_ops; - imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pad */ diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index ccb55fd1d506..df62c69a48c0 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1305,6 +1305,6 @@ module_i2c_driver(imx258_i2c_driver); MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>"); MODULE_AUTHOR("Chiang, Alan"); -MODULE_AUTHOR("Chen, Jason <jasonx.z.chen@intel.com>"); +MODULE_AUTHOR("Chen, Jason"); MODULE_DESCRIPTION("Sony IMX258 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index e6aa9f32b6a8..54642d5f2d5b 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -18,7 +18,9 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_gpio.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> @@ -65,7 +67,6 @@ */ #define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72) -#define IMX274_DEFAULT_BINNING IMX274_BINNING_OFF #define IMX274_MAX_WIDTH (3840) #define IMX274_MAX_HEIGHT (2160) #define IMX274_MAX_FRAME_RATE (120) @@ -131,6 +132,15 @@ #define IMX274_TABLE_WAIT_MS 0 #define IMX274_TABLE_END 1 +/* regulator supplies */ +static const char * const imx274_supply_names[] = { + "vddl", /* IF (1.2V) supply */ + "vdig", /* Digital Core (1.8V) supply */ + "vana", /* Analog (2.8V) supply */ +}; + +#define IMX274_NUM_SUPPLIES ARRAY_SIZE(imx274_supply_names) + /* * imx274 I2C operation related structure */ @@ -145,12 +155,6 @@ static const struct regmap_config imx274_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -enum imx274_binning { - IMX274_BINNING_OFF, - IMX274_BINNING_2_1, - IMX274_BINNING_3_1, -}; - /* * Parameters for each imx274 readout mode. * @@ -158,7 +162,8 @@ enum imx274_binning { * implemented modes. * * @init_regs: registers to initialize the mode - * @bin_ratio: downscale factor (e.g. 3 for 3:1 binning) + * @wbin_ratio: width downscale factor (e.g. 3 for 1280; 3 = 3840/1280) + * @hbin_ratio: height downscale factor (e.g. 3 for 720; 3 = 2160/720) * @min_frame_len: Minimum frame length for each mode (see "Frame Rate * Adjustment (CSI-2)" in the datasheet) * @min_SHR: Minimum SHR register value (see "Shutter Setting (CSI-2)" in the @@ -169,7 +174,8 @@ enum imx274_binning { */ struct imx274_mode { const struct reg_8 *init_regs; - unsigned int bin_ratio; + u8 wbin_ratio; + u8 hbin_ratio; int min_frame_len; int min_SHR; int max_fps; @@ -333,6 +339,46 @@ static const struct reg_8 imx274_mode5_1280x720_raw10[] = { }; /* + * Vertical 2/8 subsampling horizontal 3 binning + * imx274 mode6(refer to datasheet) register configuration with + * 1280x540 resolution, raw10 data and mipi four lane output + */ +static const struct reg_8 imx274_mode6_1280x540_raw10[] = { + {0x3004, 0x04}, /* mode setting */ + {0x3005, 0x31}, + {0x3006, 0x00}, + {0x3007, 0x02}, /* mode setting */ + + {0x3018, 0xA2}, /* output XVS, HVS */ + + {0x306B, 0x05}, + {0x30E2, 0x04}, /* mode setting */ + + {0x30EE, 0x01}, + {0x3342, 0x0A}, + {0x3343, 0x00}, + {0x3344, 0x16}, + {0x3345, 0x00}, + {0x33A6, 0x01}, + {0x3528, 0x0E}, + {0x3554, 0x1F}, + {0x3555, 0x01}, + {0x3556, 0x01}, + {0x3557, 0x01}, + {0x3558, 0x01}, + {0x3559, 0x00}, + {0x355A, 0x00}, + {0x35BA, 0x0E}, + {0x366A, 0x1B}, + {0x366B, 0x1A}, + {0x366C, 0x19}, + {0x366D, 0x17}, + {0x3A41, 0x04}, + + {IMX274_TABLE_END, 0x00} +}; + +/* * imx274 first step register configuration for * starting stream */ @@ -445,7 +491,8 @@ static const struct reg_8 imx274_tp_regs[] = { static const struct imx274_mode imx274_modes[] = { { /* mode 1, 4K */ - .bin_ratio = 1, + .wbin_ratio = 1, /* 3840 */ + .hbin_ratio = 1, /* 2160 */ .init_regs = imx274_mode1_3840x2160_raw10, .min_frame_len = 4550, .min_SHR = 12, @@ -454,7 +501,8 @@ static const struct imx274_mode imx274_modes[] = { }, { /* mode 3, 1080p */ - .bin_ratio = 2, + .wbin_ratio = 2, /* 1920 */ + .hbin_ratio = 2, /* 1080 */ .init_regs = imx274_mode3_1920x1080_raw10, .min_frame_len = 2310, .min_SHR = 8, @@ -463,13 +511,24 @@ static const struct imx274_mode imx274_modes[] = { }, { /* mode 5, 720p */ - .bin_ratio = 3, + .wbin_ratio = 3, /* 1280 */ + .hbin_ratio = 3, /* 720 */ .init_regs = imx274_mode5_1280x720_raw10, .min_frame_len = 2310, .min_SHR = 8, .max_fps = 120, .nocpiop = 112, }, + { + /* mode 6, 540p */ + .wbin_ratio = 3, /* 1280 */ + .hbin_ratio = 4, /* 540 */ + .init_regs = imx274_mode6_1280x540_raw10, + .min_frame_len = 2310, + .min_SHR = 4, + .max_fps = 120, + .nocpiop = 112, + }, }; /* @@ -501,6 +560,8 @@ struct imx274_ctrls { * @frame_rate: V4L2 frame rate structure * @regmap: Pointer to regmap structure * @reset_gpio: Pointer to reset gpio + * @supplies: List of analog and digital supply regulators + * @inck: Pointer to sensor input clock * @lock: Mutex structure * @mode: Parameters for the selected readout mode */ @@ -514,6 +575,8 @@ struct stimx274 { struct v4l2_fract frame_interval; struct regmap *regmap; struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[IMX274_NUM_SUPPLIES]; + struct clk *inck; struct mutex lock; /* mutex lock for operations */ const struct imx274_mode *mode; }; @@ -726,6 +789,12 @@ static int imx274_start_stream(struct stimx274 *priv) { int err = 0; + err = __v4l2_ctrl_handler_setup(&priv->ctrls.handler); + if (err) { + dev_err(&priv->client->dev, "Error %d setup controls\n", err); + return err; + } + /* * Refer to "Standby Cancel Sequence when using CSI-2" in * imx274 datasheet, it should wait 10ms or more here. @@ -767,6 +836,66 @@ static void imx274_reset(struct stimx274 *priv, int rst) usleep_range(IMX274_RESET_DELAY1, IMX274_RESET_DELAY2); } +static int imx274_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct stimx274 *imx274 = to_imx274(sd); + int ret; + + /* keep sensor in reset before power on */ + imx274_reset(imx274, 0); + + ret = clk_prepare_enable(imx274->inck); + if (ret) { + dev_err(&imx274->client->dev, + "Failed to enable input clock: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(IMX274_NUM_SUPPLIES, imx274->supplies); + if (ret) { + dev_err(&imx274->client->dev, + "Failed to enable regulators: %d\n", ret); + goto fail_reg; + } + + udelay(2); + imx274_reset(imx274, 1); + + return 0; + +fail_reg: + clk_disable_unprepare(imx274->inck); + return ret; +} + +static int imx274_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct stimx274 *imx274 = to_imx274(sd); + + imx274_reset(imx274, 0); + + regulator_bulk_disable(IMX274_NUM_SUPPLIES, imx274->supplies); + + clk_disable_unprepare(imx274->inck); + + return 0; +} + +static int imx274_regulators_get(struct device *dev, struct stimx274 *imx274) +{ + unsigned int i; + + for (i = 0; i < IMX274_NUM_SUPPLIES; i++) + imx274->supplies[i].supply = imx274_supply_names[i]; + + return devm_regulator_bulk_get(dev, IMX274_NUM_SUPPLIES, + imx274->supplies); +} + /** * imx274_s_ctrl - This is used to set the imx274 V4L2 controls * @ctrl: V4L2 control to be set @@ -781,6 +910,9 @@ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) struct stimx274 *imx274 = to_imx274(sd); int ret = -EINVAL; + if (!pm_runtime_get_if_in_use(&imx274->client->dev)) + return 0; + dev_dbg(&imx274->client->dev, "%s : s_ctrl: %s, value: %d\n", __func__, ctrl->name, ctrl->val); @@ -811,6 +943,8 @@ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) break; } + pm_runtime_put(&imx274->client->dev); + return ret; } @@ -892,12 +1026,13 @@ static int __imx274_change_compose(struct stimx274 *imx274, } for (i = 0; i < ARRAY_SIZE(imx274_modes); i++) { - unsigned int ratio = imx274_modes[i].bin_ratio; + u8 wratio = imx274_modes[i].wbin_ratio; + u8 hratio = imx274_modes[i].hbin_ratio; int goodness = imx274_binning_goodness( imx274, - cur_crop->width / ratio, *width, - cur_crop->height / ratio, *height, + cur_crop->width / wratio, *width, + cur_crop->height / hratio, *height, flags); if (goodness >= best_goodness) { @@ -906,14 +1041,14 @@ static int __imx274_change_compose(struct stimx274 *imx274, } } - *width = cur_crop->width / best_mode->bin_ratio; - *height = cur_crop->height / best_mode->bin_ratio; + *width = cur_crop->width / best_mode->wbin_ratio; + *height = cur_crop->height / best_mode->hbin_ratio; if (which == V4L2_SUBDEV_FORMAT_ACTIVE) imx274->mode = best_mode; - dev_dbg(dev, "%s: selected %u:1 binning\n", - __func__, best_mode->bin_ratio); + dev_dbg(dev, "%s: selected %ux%u binning\n", + __func__, best_mode->wbin_ratio, best_mode->hbin_ratio); tgt_fmt->width = *width; tgt_fmt->height = *height; @@ -1163,7 +1298,7 @@ static int imx274_apply_trimming(struct stimx274 *imx274) (-imx274->crop.top / 2) : (imx274->crop.top / 2); v_cut = (IMX274_MAX_HEIGHT - imx274->crop.height) / 2; write_v_size = imx274->crop.height + 22; - y_out_size = imx274->crop.height + 14; + y_out_size = imx274->crop.height; err = imx274_write_mbreg(imx274, IMX274_HMAX_REG_LSB, hmax, 2); if (!err) @@ -1271,10 +1406,8 @@ unlock: * * Return: 0 on success, errors otherwise */ -static int imx274_load_default(struct stimx274 *priv) +static void imx274_load_default(struct stimx274 *priv) { - int ret; - /* load default control values */ priv->frame_interval.numerator = 1; priv->frame_interval.denominator = IMX274_DEF_FRAME_RATE; @@ -1282,29 +1415,6 @@ static int imx274_load_default(struct stimx274 *priv) priv->ctrls.gain->val = IMX274_DEF_GAIN; priv->ctrls.vflip->val = 0; priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED; - - /* update frame rate */ - ret = imx274_set_frame_interval(priv, - priv->frame_interval); - if (ret) - return ret; - - /* update exposure time */ - ret = v4l2_ctrl_s_ctrl(priv->ctrls.exposure, priv->ctrls.exposure->val); - if (ret) - return ret; - - /* update gain */ - ret = v4l2_ctrl_s_ctrl(priv->ctrls.gain, priv->ctrls.gain->val); - if (ret) - return ret; - - /* update vflip */ - ret = v4l2_ctrl_s_ctrl(priv->ctrls.vflip, priv->ctrls.vflip->val); - if (ret) - return ret; - - return 0; } /** @@ -1329,6 +1439,13 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) mutex_lock(&imx274->lock); if (on) { + ret = pm_runtime_get_sync(&imx274->client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&imx274->client->dev); + mutex_unlock(&imx274->lock); + return ret; + } + /* load mode registers */ ret = imx274_mode_regs(imx274); if (ret) @@ -1349,12 +1466,6 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) if (ret) goto fail; - /* update exposure time */ - ret = __v4l2_ctrl_s_ctrl(imx274->ctrls.exposure, - imx274->ctrls.exposure->val); - if (ret) - goto fail; - /* start stream */ ret = imx274_start_stream(imx274); if (ret) @@ -1364,6 +1475,8 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) ret = imx274_write_table(imx274, imx274_stop); if (ret) goto fail; + + pm_runtime_put(&imx274->client->dev); } mutex_unlock(&imx274->lock); @@ -1371,6 +1484,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) return 0; fail: + pm_runtime_put(&imx274->client->dev); mutex_unlock(&imx274->lock); dev_err(&imx274->client->dev, "s_stream failed\n"); return ret; @@ -1836,12 +1950,23 @@ static int imx274_probe(struct i2c_client *client) mutex_init(&imx274->lock); + imx274->inck = devm_clk_get_optional(&client->dev, "inck"); + if (IS_ERR(imx274->inck)) + return PTR_ERR(imx274->inck); + + ret = imx274_regulators_get(&client->dev, imx274); + if (ret) { + dev_err(&client->dev, + "Failed to get power regulators, err: %d\n", ret); + return ret; + } + /* initialize format */ - imx274->mode = &imx274_modes[IMX274_DEFAULT_BINNING]; + imx274->mode = &imx274_modes[0]; imx274->crop.width = IMX274_MAX_WIDTH; imx274->crop.height = IMX274_MAX_HEIGHT; - imx274->format.width = imx274->crop.width / imx274->mode->bin_ratio; - imx274->format.height = imx274->crop.height / imx274->mode->bin_ratio; + imx274->format.width = imx274->crop.width / imx274->mode->wbin_ratio; + imx274->format.height = imx274->crop.height / imx274->mode->hbin_ratio; imx274->format.field = V4L2_FIELD_NONE; imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; imx274->format.colorspace = V4L2_COLORSPACE_SRGB; @@ -1883,15 +2008,20 @@ static int imx274_probe(struct i2c_client *client) goto err_me; } - /* pull sensor out of reset */ - imx274_reset(imx274, 1); + /* power on the sensor */ + ret = imx274_power_on(&client->dev); + if (ret < 0) { + dev_err(&client->dev, + "%s : imx274 power on failed\n", __func__); + goto err_me; + } /* initialize controls */ ret = v4l2_ctrl_handler_init(&imx274->ctrls.handler, 4); if (ret < 0) { dev_err(&client->dev, "%s : ctrl handler init Failed\n", __func__); - goto err_me; + goto err_power_off; } imx274->ctrls.handler.lock = &imx274->lock; @@ -1927,22 +2057,8 @@ static int imx274_probe(struct i2c_client *client) goto err_ctrls; } - /* setup default controls */ - ret = v4l2_ctrl_handler_setup(&imx274->ctrls.handler); - if (ret) { - dev_err(&client->dev, - "Error %d setup default controls\n", ret); - goto err_ctrls; - } - /* load default control values */ - ret = imx274_load_default(imx274); - if (ret) { - dev_err(&client->dev, - "%s : imx274_load_default failed %d\n", - __func__, ret); - goto err_ctrls; - } + imx274_load_default(imx274); /* register subdevice */ ret = v4l2_async_register_subdev(sd); @@ -1953,11 +2069,17 @@ static int imx274_probe(struct i2c_client *client) goto err_ctrls; } + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + dev_info(&client->dev, "imx274 : imx274 probe success !\n"); return 0; err_ctrls: v4l2_ctrl_handler_free(&imx274->ctrls.handler); +err_power_off: + imx274_power_off(&client->dev); err_me: media_entity_cleanup(&sd->entity); err_regmap: @@ -1970,19 +2092,27 @@ static int imx274_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct stimx274 *imx274 = to_imx274(sd); - /* stop stream */ - imx274_write_table(imx274, imx274_stop); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx274_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&imx274->ctrls.handler); + media_entity_cleanup(&sd->entity); mutex_destroy(&imx274->lock); return 0; } +static const struct dev_pm_ops imx274_pm_ops = { + SET_RUNTIME_PM_OPS(imx274_power_off, imx274_power_on, NULL) +}; + static struct i2c_driver imx274_i2c_driver = { .driver = { .name = DRIVER_NAME, + .pm = &imx274_pm_ops, .of_match_table = imx274_of_id_table, }, .probe_new = imx274_probe, diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index adcddf3204f7..6319a42057d2 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -842,20 +842,19 @@ exit: static int imx290_power_on(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx290 *imx290 = to_imx290(sd); int ret; ret = clk_prepare_enable(imx290->xclk); if (ret) { - dev_err(imx290->dev, "Failed to enable clock\n"); + dev_err(dev, "Failed to enable clock\n"); return ret; } ret = regulator_bulk_enable(IMX290_NUM_SUPPLIES, imx290->supplies); if (ret) { - dev_err(imx290->dev, "Failed to enable regulators\n"); + dev_err(dev, "Failed to enable regulators\n"); clk_disable_unprepare(imx290->xclk); return ret; } @@ -872,8 +871,7 @@ static int imx290_power_on(struct device *dev) static int imx290_power_off(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx290 *imx290 = to_imx290(sd); clk_disable_unprepare(imx290->xclk); diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index 17c2e4b41221..8473c0bbb35d 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -2179,8 +2179,7 @@ err_unlock: static int __maybe_unused imx319_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx319 *imx319 = to_imx319(sd); if (imx319->streaming) @@ -2191,8 +2190,7 @@ static int __maybe_unused imx319_suspend(struct device *dev) static int __maybe_unused imx319_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx319 *imx319 = to_imx319(sd); int ret; @@ -2535,7 +2533,7 @@ static const struct dev_pm_ops imx319_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(imx319_suspend, imx319_resume) }; -static const struct acpi_device_id imx319_acpi_ids[] = { +static const struct acpi_device_id imx319_acpi_ids[] __maybe_unused = { { "SONY319A" }, { /* sentinel */ } }; diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index bed293b60e50..700f7467fb31 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1480,8 +1480,7 @@ err_unlock: static int __maybe_unused imx355_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx355 *imx355 = to_imx355(sd); if (imx355->streaming) @@ -1492,8 +1491,7 @@ static int __maybe_unused imx355_suspend(struct device *dev) static int __maybe_unused imx355_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx355 *imx355 = to_imx355(sd); int ret; @@ -1835,7 +1833,7 @@ static const struct dev_pm_ops imx355_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(imx355_suspend, imx355_resume) }; -static const struct acpi_device_id imx355_acpi_ids[] = { +static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { { "SONY355A" }, { /* sentinel */ } }; diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 03b4ed3a61b8..661208c9bfc5 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -503,7 +503,7 @@ static void max2175_set_bbfilter(struct max2175 *ctx) } } -static bool max2175_set_csm_mode(struct max2175 *ctx, +static int max2175_set_csm_mode(struct max2175 *ctx, enum max2175_csm_mode new_mode) { int ret = max2175_poll_csm_ready(ctx); diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c index 0f6f7a092a46..c247db569bab 100644 --- a/drivers/media/i2c/max9271.c +++ b/drivers/media/i2c/max9271.c @@ -223,12 +223,12 @@ int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask) { int ret; - ret = max9271_read(dev, 0x0f); + ret = max9271_read(dev, 0x0e); if (ret < 0) return 0; /* BIT(0) reserved: GPO is always enabled. */ - ret |= gpio_mask | BIT(0); + ret |= (gpio_mask & ~BIT(0)); ret = max9271_write(dev, 0x0e, ret); if (ret < 0) { dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret); @@ -245,12 +245,12 @@ int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask) { int ret; - ret = max9271_read(dev, 0x0f); + ret = max9271_read(dev, 0x0e); if (ret < 0) return 0; /* BIT(0) reserved: GPO cannot be disabled */ - ret &= (~gpio_mask | BIT(0)); + ret &= ~(gpio_mask | BIT(0)); ret = max9271_write(dev, 0x0e, ret); if (ret < 0) { dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret); diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c index 52e506f86de5..ecabc0e1d32e 100644 --- a/drivers/media/i2c/msp3400-kthreads.c +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -549,8 +549,10 @@ restart: val = msp_read_dsp(client, 0x1b); if (val > 32767) val -= 65536; - if (val1 < val) - val1 = val, max1 = i; + if (val1 < val) { + val1 = val; + max1 = i; + } dev_dbg_lvl(&client->dev, 1, msp_debug, "carrier1 val: %5d / %s\n", val, cd[i].name); } @@ -586,8 +588,10 @@ restart: val = msp_read_dsp(client, 0x1b); if (val > 32767) val -= 65536; - if (val2 < val) - val2 = val, max2 = i; + if (val2 < val) { + val2 = val; + max2 = i; + } dev_dbg_lvl(&client->dev, 1, msp_debug, "carrier2 val: %5d / %s\n", val, cd[i].name); } diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index dc23b9ed510a..a633b934d93e 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -346,8 +346,7 @@ static void mt9p031_power_off(struct mt9p031 *mt9p031) regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), mt9p031->regulators); - if (mt9p031->clk) - clk_disable_unprepare(mt9p031->clk); + clk_disable_unprepare(mt9p031->clk); } static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c new file mode 100644 index 000000000000..8683ffd3287a --- /dev/null +++ b/drivers/media/i2c/ov02a10.c @@ -0,0 +1,1015 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 MediaTek Inc. + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <media/media-entity.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define OV02A10_ID 0x2509 +#define OV02A10_ID_MASK GENMASK(15, 0) + +#define OV02A10_REG_CHIP_ID 0x02 + +/* Bit[1] vertical upside down */ +/* Bit[0] horizontal mirror */ +#define REG_MIRROR_FLIP_CONTROL 0x3f + +/* Orientation */ +#define REG_MIRROR_FLIP_ENABLE 0x03 + +/* Bit[2:0] MIPI transmission speed select */ +#define TX_SPEED_AREA_SEL 0xa1 +#define OV02A10_MIPI_TX_SPEED_DEFAULT 0x04 + +#define REG_PAGE_SWITCH 0xfd +#define REG_GLOBAL_EFFECTIVE 0x01 +#define REG_ENABLE BIT(0) + +#define REG_SC_CTRL_MODE 0xac +#define SC_CTRL_MODE_STANDBY 0x00 +#define SC_CTRL_MODE_STREAMING 0x01 + +/* Exposure control */ +#define OV02A10_EXP_SHIFT 8 +#define OV02A10_REG_EXPOSURE_H 0x03 +#define OV02A10_REG_EXPOSURE_L 0x04 +#define OV02A10_EXPOSURE_MIN 4 +#define OV02A10_EXPOSURE_MAX_MARGIN 4 +#define OV02A10_EXPOSURE_STEP 1 + +/* Vblanking control */ +#define OV02A10_VTS_SHIFT 8 +#define OV02A10_REG_VTS_H 0x05 +#define OV02A10_REG_VTS_L 0x06 +#define OV02A10_VTS_MAX 0x209f +#define OV02A10_BASE_LINES 1224 + +/* Analog gain control */ +#define OV02A10_REG_GAIN 0x24 +#define OV02A10_GAIN_MIN 0x10 +#define OV02A10_GAIN_MAX 0xf8 +#define OV02A10_GAIN_STEP 0x01 +#define OV02A10_GAIN_DEFAULT 0x40 + +/* Test pattern control */ +#define OV02A10_REG_TEST_PATTERN 0xb6 + +#define HZ_PER_MHZ 1000000L +#define OV02A10_LINK_FREQ_390MHZ (390 * HZ_PER_MHZ) +#define OV02A10_ECLK_FREQ (24 * HZ_PER_MHZ) + +/* Number of lanes supported by this driver */ +#define OV02A10_DATA_LANES 1 + +/* Bits per sample of sensor output */ +#define OV02A10_BITS_PER_SAMPLE 10 + +static const char * const ov02a10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov02a10_reg { + u8 addr; + u8 val; +}; + +struct ov02a10_reg_list { + u32 num_of_regs; + const struct ov02a10_reg *regs; +}; + +struct ov02a10_mode { + u32 width; + u32 height; + u32 exp_def; + u32 hts_def; + u32 vts_def; + const struct ov02a10_reg_list reg_list; +}; + +struct ov02a10 { + u32 eclk_freq; + /* Indication of MIPI transmission speed select */ + u32 mipi_clock_voltage; + + struct clk *eclk; + struct gpio_desc *pd_gpio; + struct gpio_desc *rst_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov02a10_supply_names)]; + + bool streaming; + bool upside_down; + + /* + * Serialize control access, get/set format, get selection + * and start streaming. + */ + struct mutex mutex; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_mbus_framefmt fmt; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + + const struct ov02a10_mode *cur_mode; +}; + +static inline struct ov02a10 *to_ov02a10(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov02a10, subdev); +} + +/* + * eclk 24Mhz + * pclk 39Mhz + * linelength 934(0x3a6) + * framelength 1390(0x56E) + * grabwindow_width 1600 + * grabwindow_height 1200 + * max_framerate 30fps + * mipi_datarate per lane 780Mbps + */ +static const struct ov02a10_reg ov02a10_1600x1200_regs[] = { + {0xfd, 0x01}, + {0xac, 0x00}, + {0xfd, 0x00}, + {0x2f, 0x29}, + {0x34, 0x00}, + {0x35, 0x21}, + {0x30, 0x15}, + {0x33, 0x01}, + {0xfd, 0x01}, + {0x44, 0x00}, + {0x2a, 0x4c}, + {0x2b, 0x1e}, + {0x2c, 0x60}, + {0x25, 0x11}, + {0x03, 0x01}, + {0x04, 0xae}, + {0x09, 0x00}, + {0x0a, 0x02}, + {0x06, 0xa6}, + {0x31, 0x00}, + {0x24, 0x40}, + {0x01, 0x01}, + {0xfb, 0x73}, + {0xfd, 0x01}, + {0x16, 0x04}, + {0x1c, 0x09}, + {0x21, 0x42}, + {0x12, 0x04}, + {0x13, 0x10}, + {0x11, 0x40}, + {0x33, 0x81}, + {0xd0, 0x00}, + {0xd1, 0x01}, + {0xd2, 0x00}, + {0x50, 0x10}, + {0x51, 0x23}, + {0x52, 0x20}, + {0x53, 0x10}, + {0x54, 0x02}, + {0x55, 0x20}, + {0x56, 0x02}, + {0x58, 0x48}, + {0x5d, 0x15}, + {0x5e, 0x05}, + {0x66, 0x66}, + {0x68, 0x68}, + {0x6b, 0x00}, + {0x6c, 0x00}, + {0x6f, 0x40}, + {0x70, 0x40}, + {0x71, 0x0a}, + {0x72, 0xf0}, + {0x73, 0x10}, + {0x75, 0x80}, + {0x76, 0x10}, + {0x84, 0x00}, + {0x85, 0x10}, + {0x86, 0x10}, + {0x87, 0x00}, + {0x8a, 0x22}, + {0x8b, 0x22}, + {0x19, 0xf1}, + {0x29, 0x01}, + {0xfd, 0x01}, + {0x9d, 0x16}, + {0xa0, 0x29}, + {0xa1, 0x04}, + {0xad, 0x62}, + {0xae, 0x00}, + {0xaf, 0x85}, + {0xb1, 0x01}, + {0x8e, 0x06}, + {0x8f, 0x40}, + {0x90, 0x04}, + {0x91, 0xb0}, + {0x45, 0x01}, + {0x46, 0x00}, + {0x47, 0x6c}, + {0x48, 0x03}, + {0x49, 0x8b}, + {0x4a, 0x00}, + {0x4b, 0x07}, + {0x4c, 0x04}, + {0x4d, 0xb7}, + {0xf0, 0x40}, + {0xf1, 0x40}, + {0xf2, 0x40}, + {0xf3, 0x40}, + {0x3f, 0x00}, + {0xfd, 0x01}, + {0x05, 0x00}, + {0x06, 0xa6}, + {0xfd, 0x01}, +}; + +static const char * const ov02a10_test_pattern_menu[] = { + "Disabled", + "Eight Vertical Colour Bars", +}; + +static const s64 link_freq_menu_items[] = { + OV02A10_LINK_FREQ_390MHZ, +}; + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV02A10_DATA_LANES; + + do_div(pixel_rate, OV02A10_BITS_PER_SAMPLE); + + return pixel_rate; +} + +static const struct ov02a10_mode supported_modes[] = { + { + .width = 1600, + .height = 1200, + .exp_def = 0x01ae, + .hts_def = 0x03a6, + .vts_def = 0x056e, + .reg_list = { + .num_of_regs = ARRAY_SIZE(ov02a10_1600x1200_regs), + .regs = ov02a10_1600x1200_regs, + }, + }, +}; + +static int ov02a10_write_array(struct ov02a10 *ov02a10, + const struct ov02a10_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = i2c_smbus_write_byte_data(client, r_list->regs[i].addr, + r_list->regs[i].val); + if (ret < 0) + return ret; + } + + return 0; +} + +static void ov02a10_fill_fmt(const struct ov02a10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov02a10_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov02a10 *ov02a10 = to_ov02a10(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + struct v4l2_mbus_framefmt *frame_fmt; + int ret = 0; + + mutex_lock(&ov02a10->mutex); + + if (ov02a10->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = -EBUSY; + goto out_unlock; + } + + /* Only one sensor mode supported */ + mbus_fmt->code = ov02a10->fmt.code; + ov02a10_fill_fmt(ov02a10->cur_mode, mbus_fmt); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + frame_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + else + frame_fmt = &ov02a10->fmt; + + *frame_fmt = *mbus_fmt; + +out_unlock: + mutex_unlock(&ov02a10->mutex); + return ret; +} + +static int ov02a10_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov02a10 *ov02a10 = to_ov02a10(sd); + struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format; + + mutex_lock(&ov02a10->mutex); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + } else { + fmt->format = ov02a10->fmt; + mbus_fmt->code = ov02a10->fmt.code; + ov02a10_fill_fmt(ov02a10->cur_mode, mbus_fmt); + } + + mutex_unlock(&ov02a10->mutex); + + return 0; +} + +static int ov02a10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov02a10 *ov02a10 = to_ov02a10(sd); + + if (code->index != 0) + return -EINVAL; + + code->code = ov02a10->fmt.code; + + return 0; +} + +static int ov02a10_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov02a10_check_sensor_id(struct ov02a10 *ov02a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + u16 chip_id; + int ret; + + /* Validate the chip ID */ + ret = i2c_smbus_read_word_swapped(client, OV02A10_REG_CHIP_ID); + if (ret < 0) + return ret; + + chip_id = le16_to_cpu(ret); + + if ((chip_id & OV02A10_ID_MASK) != OV02A10_ID) { + dev_err(&client->dev, "unexpected sensor id(0x%04x)\n", chip_id); + return -EINVAL; + } + + return 0; +} + +static int ov02a10_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02a10 *ov02a10 = to_ov02a10(sd); + int ret; + + gpiod_set_value_cansleep(ov02a10->rst_gpio, 1); + gpiod_set_value_cansleep(ov02a10->pd_gpio, 1); + + ret = clk_prepare_enable(ov02a10->eclk); + if (ret < 0) { + dev_err(dev, "failed to enable eclk\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov02a10_supply_names), + ov02a10->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators\n"); + goto disable_clk; + } + usleep_range(5000, 6000); + + gpiod_set_value_cansleep(ov02a10->pd_gpio, 0); + usleep_range(5000, 6000); + + gpiod_set_value_cansleep(ov02a10->rst_gpio, 0); + usleep_range(5000, 6000); + + ret = ov02a10_check_sensor_id(ov02a10); + if (ret) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_bulk_disable(ARRAY_SIZE(ov02a10_supply_names), + ov02a10->supplies); +disable_clk: + clk_disable_unprepare(ov02a10->eclk); + + return ret; +} + +static int ov02a10_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02a10 *ov02a10 = to_ov02a10(sd); + + gpiod_set_value_cansleep(ov02a10->rst_gpio, 1); + clk_disable_unprepare(ov02a10->eclk); + gpiod_set_value_cansleep(ov02a10->pd_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(ov02a10_supply_names), + ov02a10->supplies); + + return 0; +} + +static int __ov02a10_start_stream(struct ov02a10 *ov02a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + const struct ov02a10_reg_list *reg_list; + int ret; + + /* Apply default values of current mode */ + reg_list = &ov02a10->cur_mode->reg_list; + ret = ov02a10_write_array(ov02a10, reg_list); + if (ret) + return ret; + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(ov02a10->subdev.ctrl_handler); + if (ret) + return ret; + + /* Set orientation to 180 degree */ + if (ov02a10->upside_down) { + ret = i2c_smbus_write_byte_data(client, REG_MIRROR_FLIP_CONTROL, + REG_MIRROR_FLIP_ENABLE); + if (ret < 0) { + dev_err(&client->dev, "failed to set orientation\n"); + return ret; + } + ret = i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, + REG_ENABLE); + if (ret < 0) + return ret; + } + + /* Set MIPI TX speed according to DT property */ + if (ov02a10->mipi_clock_voltage != OV02A10_MIPI_TX_SPEED_DEFAULT) { + ret = i2c_smbus_write_byte_data(client, TX_SPEED_AREA_SEL, + ov02a10->mipi_clock_voltage); + if (ret < 0) + return ret; + } + + /* Set stream on register */ + return i2c_smbus_write_byte_data(client, REG_SC_CTRL_MODE, + SC_CTRL_MODE_STREAMING); +} + +static int __ov02a10_stop_stream(struct ov02a10 *ov02a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + + return i2c_smbus_write_byte_data(client, REG_SC_CTRL_MODE, + SC_CTRL_MODE_STANDBY); +} + +static int ov02a10_entity_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .width = 1600, + .height = 1200, + } + }; + + ov02a10_set_fmt(sd, cfg, &fmt); + + return 0; +} + +static int ov02a10_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov02a10 *ov02a10 = to_ov02a10(sd); + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + int ret; + + mutex_lock(&ov02a10->mutex); + + if (ov02a10->streaming == on) { + ret = 0; + goto unlock_and_return; + } + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov02a10_start_stream(ov02a10); + if (ret) { + __ov02a10_stop_stream(ov02a10); + ov02a10->streaming = !on; + goto err_rpm_put; + } + } else { + __ov02a10_stop_stream(ov02a10); + pm_runtime_put(&client->dev); + } + + ov02a10->streaming = on; + mutex_unlock(&ov02a10->mutex); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); +unlock_and_return: + mutex_unlock(&ov02a10->mutex); + + return ret; +} + +static const struct dev_pm_ops ov02a10_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ov02a10_power_off, ov02a10_power_on, NULL) +}; + +static int ov02a10_set_exposure(struct ov02a10 *ov02a10, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + int ret; + + ret = i2c_smbus_write_byte_data(client, REG_PAGE_SWITCH, REG_ENABLE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_EXPOSURE_H, + val >> OV02A10_EXP_SHIFT); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_EXPOSURE_L, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, + REG_ENABLE); +} + +static int ov02a10_set_gain(struct ov02a10 *ov02a10, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + int ret; + + ret = i2c_smbus_write_byte_data(client, REG_PAGE_SWITCH, REG_ENABLE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_GAIN, val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, + REG_ENABLE); +} + +static int ov02a10_set_vblank(struct ov02a10 *ov02a10, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + u32 vts = val + ov02a10->cur_mode->height - OV02A10_BASE_LINES; + int ret; + + ret = i2c_smbus_write_byte_data(client, REG_PAGE_SWITCH, REG_ENABLE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_VTS_H, + vts >> OV02A10_VTS_SHIFT); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_VTS_L, vts); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, + REG_ENABLE); +} + +static int ov02a10_set_test_pattern(struct ov02a10 *ov02a10, int pattern) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + int ret; + + ret = i2c_smbus_write_byte_data(client, REG_PAGE_SWITCH, REG_ENABLE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, OV02A10_REG_TEST_PATTERN, + pattern); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, REG_GLOBAL_EFFECTIVE, + REG_ENABLE); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, REG_SC_CTRL_MODE, + SC_CTRL_MODE_STREAMING); +} + +static int ov02a10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov02a10 *ov02a10 = container_of(ctrl->handler, + struct ov02a10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + s64 max_expo; + int ret; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + max_expo = ov02a10->cur_mode->height + ctrl->val - + OV02A10_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov02a10->exposure, + ov02a10->exposure->minimum, max_expo, + ov02a10->exposure->step, + ov02a10->exposure->default_value); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ov02a10_set_exposure(ov02a10, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = ov02a10_set_gain(ov02a10, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = ov02a10_set_vblank(ov02a10, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov02a10_set_test_pattern(ov02a10, ctrl->val); + break; + default: + ret = -EINVAL; + break; + }; + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops ov02a10_video_ops = { + .s_stream = ov02a10_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov02a10_pad_ops = { + .init_cfg = ov02a10_entity_init_cfg, + .enum_mbus_code = ov02a10_enum_mbus_code, + .enum_frame_size = ov02a10_enum_frame_sizes, + .get_fmt = ov02a10_get_fmt, + .set_fmt = ov02a10_set_fmt, +}; + +static const struct v4l2_subdev_ops ov02a10_subdev_ops = { + .video = &ov02a10_video_ops, + .pad = &ov02a10_pad_ops, +}; + +static const struct media_entity_operations ov02a10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_ctrl_ops ov02a10_ctrl_ops = { + .s_ctrl = ov02a10_set_ctrl, +}; + +static int ov02a10_initialize_controls(struct ov02a10 *ov02a10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov02a10->subdev); + const struct ov02a10_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max; + s64 vblank_def; + s64 pixel_rate; + s64 h_blank; + int ret; + + handler = &ov02a10->ctrl_handler; + mode = ov02a10->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 7); + if (ret) + return ret; + + handler->lock = &ov02a10->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, + link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(0); + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, pixel_rate, 1, + pixel_rate); + + h_blank = mode->hts_def - mode->width; + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + + vblank_def = mode->vts_def - mode->height; + v4l2_ctrl_new_std(handler, &ov02a10_ctrl_ops, V4L2_CID_VBLANK, + vblank_def, OV02A10_VTS_MAX - mode->height, 1, + vblank_def); + + exposure_max = mode->vts_def - 4; + ov02a10->exposure = v4l2_ctrl_new_std(handler, &ov02a10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV02A10_EXPOSURE_MIN, + exposure_max, + OV02A10_EXPOSURE_STEP, + mode->exp_def); + + v4l2_ctrl_new_std(handler, &ov02a10_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV02A10_GAIN_MIN, + OV02A10_GAIN_MAX, OV02A10_GAIN_STEP, + OV02A10_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(handler, &ov02a10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov02a10_test_pattern_menu) - 1, + 0, 0, ov02a10_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&client->dev, "failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov02a10->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned int i, j; + u32 clk_volt; + int ret; + + if (!fwnode) + return -EINVAL; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + /* Optional indication of MIPI clock voltage unit */ + ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", + &clk_volt); + + if (!ret) + ov02a10->mipi_clock_voltage = clk_volt; + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported\n", + link_freq_menu_items[i]); + ret = -EINVAL; + break; + } + } + + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov02a10_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ov02a10 *ov02a10; + unsigned int i; + unsigned int rotation; + int ret; + + ov02a10 = devm_kzalloc(dev, sizeof(*ov02a10), GFP_KERNEL); + if (!ov02a10) + return -ENOMEM; + + ret = ov02a10_check_hwcfg(dev, ov02a10); + if (ret) + return dev_err_probe(dev, ret, + "failed to check HW configuration\n"); + + v4l2_i2c_subdev_init(&ov02a10->subdev, client, &ov02a10_subdev_ops); + + ov02a10->mipi_clock_voltage = OV02A10_MIPI_TX_SPEED_DEFAULT; + ov02a10->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; + + /* Optional indication of physical rotation of sensor */ + rotation = 0; + device_property_read_u32(dev, "rotation", &rotation); + if (rotation == 180) { + ov02a10->upside_down = true; + ov02a10->fmt.code = MEDIA_BUS_FMT_SRGGB10_1X10; + } + + ov02a10->eclk = devm_clk_get(dev, "eclk"); + if (IS_ERR(ov02a10->eclk)) + return dev_err_probe(dev, PTR_ERR(ov02a10->eclk), + "failed to get eclk\n"); + + ret = device_property_read_u32(dev, "clock-frequency", + &ov02a10->eclk_freq); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get eclk frequency\n"); + + ret = clk_set_rate(ov02a10->eclk, ov02a10->eclk_freq); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to set eclk frequency (24MHz)\n"); + + if (clk_get_rate(ov02a10->eclk) != OV02A10_ECLK_FREQ) + dev_warn(dev, "eclk mismatched, mode is based on 24MHz\n"); + + ov02a10->pd_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(ov02a10->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(ov02a10->pd_gpio), + "failed to get powerdown-gpios\n"); + + ov02a10->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov02a10->rst_gpio)) + return dev_err_probe(dev, PTR_ERR(ov02a10->rst_gpio), + "failed to get reset-gpios\n"); + + for (i = 0; i < ARRAY_SIZE(ov02a10_supply_names); i++) + ov02a10->supplies[i].supply = ov02a10_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov02a10_supply_names), + ov02a10->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + mutex_init(&ov02a10->mutex); + + /* Set default mode */ + ov02a10->cur_mode = &supported_modes[0]; + + ret = ov02a10_initialize_controls(ov02a10); + if (ret) { + dev_err_probe(dev, ret, "failed to initialize controls\n"); + goto err_destroy_mutex; + } + + /* Initialize subdev */ + ov02a10->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov02a10->subdev.entity.ops = &ov02a10_subdev_entity_ops; + ov02a10->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov02a10->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&ov02a10->subdev.entity, 1, &ov02a10->pad); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to initialize entity pads\n"); + goto err_free_handler; + } + + pm_runtime_enable(dev); + if (!pm_runtime_enabled(dev)) { + ret = ov02a10_power_on(dev); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to power on\n"); + goto err_clean_entity; + } + } + + ret = v4l2_async_register_subdev(&ov02a10->subdev); + if (ret) { + dev_err_probe(dev, ret, "failed to register V4L2 subdev\n"); + goto err_power_off; + } + + return 0; + +err_power_off: + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); + else + ov02a10_power_off(dev); +err_clean_entity: + media_entity_cleanup(&ov02a10->subdev.entity); +err_free_handler: + v4l2_ctrl_handler_free(ov02a10->subdev.ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov02a10->mutex); + + return ret; +} + +static int ov02a10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov02a10 *ov02a10 = to_ov02a10(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov02a10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + mutex_destroy(&ov02a10->mutex); + + return 0; +} + +static const struct of_device_id ov02a10_of_match[] = { + { .compatible = "ovti,ov02a10" }, + {} +}; +MODULE_DEVICE_TABLE(of, ov02a10_of_match); + +static struct i2c_driver ov02a10_i2c_driver = { + .driver = { + .name = "ov02a10", + .pm = &ov02a10_pm_ops, + .of_match_table = ov02a10_of_match, + }, + .probe_new = &ov02a10_probe, + .remove = &ov02a10_remove, +}; +module_i2c_driver(ov02a10_i2c_driver); + +MODULE_AUTHOR("Dongchun Zhu <dongchun.zhu@mediatek.com>"); +MODULE_DESCRIPTION("OmniVision OV02A10 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 236ad2c816b7..2f3be7a80cef 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1505,8 +1505,7 @@ err_unlock: static int __maybe_unused ov13858_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13858 *ov13858 = to_ov13858(sd); if (ov13858->streaming) @@ -1517,8 +1516,7 @@ static int __maybe_unused ov13858_suspend(struct device *dev) static int __maybe_unused ov13858_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov13858 *ov13858 = to_ov13858(sd); int ret; diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 59cdbc33658c..178dfe985a25 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -1111,8 +1111,7 @@ static int ov2680_remove(struct i2c_client *client) static int __maybe_unused ov2680_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); if (sensor->is_streaming) @@ -1123,8 +1122,7 @@ static int __maybe_unused ov2680_suspend(struct device *dev) static int __maybe_unused ov2680_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2680_dev *sensor = to_ov2680_dev(sd); int ret; diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 6814583d9606..49a2dcedb347 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -506,8 +506,7 @@ static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static int __maybe_unused ov2685_runtime_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2685 *ov2685 = to_ov2685(sd); return __ov2685_power_on(ov2685); @@ -515,8 +514,7 @@ static int __maybe_unused ov2685_runtime_resume(struct device *dev) static int __maybe_unused ov2685_runtime_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2685 *ov2685 = to_ov2685(sd); __ov2685_power_off(ov2685); diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index bd0d45b0d43f..b41a90c2aed5 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -37,7 +37,7 @@ /* Exposure controls from sensor */ #define OV2740_REG_EXPOSURE 0x3500 -#define OV2740_EXPOSURE_MIN 8 +#define OV2740_EXPOSURE_MIN 4 #define OV2740_EXPOSURE_MAX_MARGIN 8 #define OV2740_EXPOSURE_STEP 1 @@ -71,9 +71,10 @@ #define OV2740_REG_OTP_CUSTOMER 0x7010 struct nvm_data { - char *nvm_buffer; + struct i2c_client *client; struct nvmem_device *nvmem; struct regmap *regmap; + char *nvm_buffer; }; enum { @@ -335,6 +336,9 @@ struct ov2740 { /* Streaming on/off */ bool streaming; + + /* NVM data inforamtion */ + struct nvm_data *nvm; }; static inline struct ov2740 *to_ov2740(struct v4l2_subdev *subdev) @@ -594,13 +598,112 @@ static void ov2740_update_pad_format(const struct ov2740_mode *mode, fmt->field = V4L2_FIELD_NONE; } +static int ov2740_load_otp_data(struct nvm_data *nvm) +{ + struct i2c_client *client; + struct ov2740 *ov2740; + u32 isp_ctrl00 = 0; + u32 isp_ctrl01 = 0; + int ret; + + if (!nvm) + return -EINVAL; + + if (nvm->nvm_buffer) + return 0; + + client = nvm->client; + ov2740 = to_ov2740(i2c_get_clientdata(client)); + + nvm->nvm_buffer = kzalloc(CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); + if (!nvm->nvm_buffer) + return -ENOMEM; + + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL00\n"); + goto err; + } + + ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); + if (ret) { + dev_err(&client->dev, "failed to read ISP CTRL01\n"); + goto err; + } + + /* Clear bit 5 of ISP CTRL00 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, + isp_ctrl00 & ~BIT(5)); + if (ret) { + dev_err(&client->dev, "failed to set ISP CTRL00\n"); + goto err; + } + + /* Clear bit 7 of ISP CTRL01 */ + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, + isp_ctrl01 & ~BIT(7)); + if (ret) { + dev_err(&client->dev, "failed to set ISP CTRL01\n"); + goto err; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STREAMING); + if (ret) { + dev_err(&client->dev, "failed to set streaming mode\n"); + goto err; + } + + /* + * Users are not allowed to access OTP-related registers and memory + * during the 20 ms period after streaming starts (0x100 = 0x01). + */ + msleep(20); + + ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, + nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); + if (ret) { + dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); + goto err; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, + OV2740_MODE_STANDBY); + if (ret) { + dev_err(&client->dev, "failed to set streaming mode\n"); + goto err; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); + if (ret) { + dev_err(&client->dev, "failed to set ISP CTRL01\n"); + goto err; + } + + ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); + if (ret) { + dev_err(&client->dev, "failed to set ISP CTRL00\n"); + goto err; + } + + return 0; +err: + kfree(nvm->nvm_buffer); + nvm->nvm_buffer = NULL; + + return ret; +} + static int ov2740_start_streaming(struct ov2740 *ov2740) { struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd); + struct nvm_data *nvm = ov2740->nvm; const struct ov2740_reg_list *reg_list; int link_freq_index; int ret = 0; + ov2740_load_otp_data(nvm); + link_freq_index = ov2740->cur_mode->link_freq_index; reg_list = &link_freq_configs[link_freq_index].reg_list; ret = ov2740_write_reg_list(ov2740, reg_list); @@ -674,8 +777,7 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable) static int __maybe_unused ov2740_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2740 *ov2740 = to_ov2740(sd); mutex_lock(&ov2740->mutex); @@ -689,8 +791,7 @@ static int __maybe_unused ov2740_suspend(struct device *dev) static int __maybe_unused ov2740_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov2740 *ov2740 = to_ov2740(sd); int ret = 0; @@ -932,96 +1033,52 @@ static int ov2740_remove(struct i2c_client *client) return 0; } -static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm) +static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, + size_t count) { - struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client)); - u32 isp_ctrl00 = 0; - u32 isp_ctrl01 = 0; - int ret; - - ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00); - if (ret) { - dev_err(&client->dev, "failed to read ISP CTRL00\n"); - goto exit; - } - ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01); - if (ret) { - dev_err(&client->dev, "failed to read ISP CTRL01\n"); - goto exit; - } - - /* Clear bit 5 of ISP CTRL00 */ - ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, - isp_ctrl00 & ~BIT(5)); - if (ret) { - dev_err(&client->dev, "failed to write ISP CTRL00\n"); - goto exit; - } + struct nvm_data *nvm = priv; + struct v4l2_subdev *sd = i2c_get_clientdata(nvm->client); + struct device *dev = &nvm->client->dev; + struct ov2740 *ov2740 = to_ov2740(sd); + int ret = 0; - /* Clear bit 7 of ISP CTRL01 */ - ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, - isp_ctrl01 & ~BIT(7)); - if (ret) { - dev_err(&client->dev, "failed to write ISP CTRL01\n"); - goto exit; - } + mutex_lock(&ov2740->mutex); - ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, - OV2740_MODE_STREAMING); - if (ret) { - dev_err(&client->dev, "failed to start streaming\n"); + if (nvm->nvm_buffer) { + memcpy(val, nvm->nvm_buffer + off, count); goto exit; } - /* - * Users are not allowed to access OTP-related registers and memory - * during the 20 ms period after streaming starts (0x100 = 0x01). - */ - msleep(20); - - ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER, - nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE); - if (ret) { - dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); goto exit; } - ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1, - OV2740_MODE_STANDBY); - ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01); - ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00); + ret = ov2740_load_otp_data(nvm); + if (!ret) + memcpy(val, nvm->nvm_buffer + off, count); + pm_runtime_put(dev); exit: + mutex_unlock(&ov2740->mutex); return ret; } -static int ov2740_nvmem_read(void *priv, unsigned int off, void *val, - size_t count) -{ - struct nvm_data *nvm = priv; - - memcpy(val, nvm->nvm_buffer + off, count); - - return 0; -} - -static int ov2740_register_nvmem(struct i2c_client *client) +static int ov2740_register_nvmem(struct i2c_client *client, + struct ov2740 *ov2740) { struct nvm_data *nvm; struct regmap_config regmap_config = { }; struct nvmem_config nvmem_config = { }; struct regmap *regmap; struct device *dev = &client->dev; - int ret = 0; + int ret; nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); if (!nvm) return -ENOMEM; - nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); - if (!nvm->nvm_buffer) - return -ENOMEM; - regmap_config.val_bits = 8; regmap_config.reg_bits = 16; regmap_config.disable_locking = true; @@ -1030,12 +1087,7 @@ static int ov2740_register_nvmem(struct i2c_client *client) return PTR_ERR(regmap); nvm->regmap = regmap; - - ret = ov2740_load_otp_data(client, nvm); - if (ret) { - dev_err(dev, "failed to load OTP data, ret %d\n", ret); - return ret; - } + nvm->client = client; nvmem_config.name = dev_name(dev); nvmem_config.dev = dev; @@ -1053,7 +1105,11 @@ static int ov2740_register_nvmem(struct i2c_client *client) nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); - return PTR_ERR_OR_ZERO(nvm->nvmem); + ret = PTR_ERR_OR_ZERO(nvm->nvmem); + if (!ret) + ov2740->nvm = nvm; + + return ret; } static int ov2740_probe(struct i2c_client *client) @@ -1105,7 +1161,7 @@ static int ov2740_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } - ret = ov2740_register_nvmem(client); + ret = ov2740_register_nvmem(client, ov2740); if (ret) dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 8d0254d0e5ea..14f3afa7721a 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -98,7 +98,8 @@ #define OV5640_REG_AVG_READOUT 0x56a1 enum ov5640_mode_id { - OV5640_MODE_QCIF_176_144 = 0, + OV5640_MODE_QQVGA_160_120 = 0, + OV5640_MODE_QCIF_176_144, OV5640_MODE_QVGA_320_240, OV5640_MODE_VGA_640_480, OV5640_MODE_NTSC_720_480, @@ -416,6 +417,24 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; +static const struct reg_value ov5640_setting_QQVGA_160_120[] = { + {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, + {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, + {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, + {0x3810, 0x00, 0, 0}, + {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, + {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, + {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, +}; + static const struct reg_value ov5640_setting_QCIF_176_144[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, @@ -552,6 +571,11 @@ static const struct ov5640_mode_info ov5640_mode_init_data = { static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = { + {OV5640_MODE_QQVGA_160_120, SUBSAMPLING, + 160, 1896, 120, 984, + ov5640_setting_QQVGA_160_120, + ARRAY_SIZE(ov5640_setting_QQVGA_160_120), + OV5640_30_FPS}, {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 1896, 144, 984, ov5640_setting_QCIF_176_144, @@ -1216,20 +1240,6 @@ static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on) BIT(1), on ? 0 : BIT(1)); } -static int ov5640_set_stream_bt656(struct ov5640_dev *sensor, bool on) -{ - int ret; - - ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, - on ? 0x1 : 0x00); - if (ret) - return ret; - - return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ? - OV5640_REG_SYS_CTRL0_SW_PWUP : - OV5640_REG_SYS_CTRL0_SW_PWDN); -} - static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) { return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ? @@ -1994,13 +2004,13 @@ static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on) static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on) { unsigned int flags = sensor->ep.bus.parallel.flags; - u8 pclk_pol = 0; - u8 hsync_pol = 0; - u8 vsync_pol = 0; + bool bt656 = sensor->ep.bus_type == V4L2_MBUS_BT656; + u8 polarities = 0; int ret; if (!on) { /* Reset settings to their default values. */ + ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, 0x00); ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58); ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, 0x20); ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, 0x00); @@ -2024,7 +2034,35 @@ static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on) * - VSYNC: active high * - HREF: active low * - PCLK: active low + * + * VSYNC & HREF are not configured if BT656 bus mode is selected */ + + /* + * BT656 embedded synchronization configuration + * + * CCIR656 CTRL00 + * - [7]: SYNC code selection (0: auto generate sync code, + * 1: sync code from regs 0x4732-0x4735) + * - [6]: f value in CCIR656 SYNC code when fixed f value + * - [5]: Fixed f value + * - [4:3]: Blank toggle data options (00: data=1'h040/1'h200, + * 01: data from regs 0x4736-0x4738, 10: always keep 0) + * - [1]: Clip data disable + * - [0]: CCIR656 mode enable + * + * Default CCIR656 SAV/EAV mode with default codes + * SAV=0xff000080 & EAV=0xff00009d is enabled here with settings: + * - CCIR656 mode enable + * - auto generation of sync codes + * - blank toggle data 1'h040/1'h200 + * - clip reserved data (0x00 & 0xff changed to 0x01 & 0xfe) + */ + ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, + bt656 ? 0x01 : 0x00); + if (ret) + return ret; + /* * configure parallel port control lines polarity * @@ -2035,29 +2073,26 @@ static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on) * datasheet and hardware, 0 is active high * and 1 is active low...) */ - if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) { - if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - pclk_pol = 1; + if (!bt656) { if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - hsync_pol = 1; + polarities |= BIT(1); if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - vsync_pol = 1; - - ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, - (pclk_pol << 5) | (hsync_pol << 1) | - vsync_pol); - - if (ret) - return ret; + polarities |= BIT(0); } + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + polarities |= BIT(5); + + ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, polarities); + if (ret) + return ret; /* - * powerdown MIPI TX/RX PHY & disable MIPI + * powerdown MIPI TX/RX PHY & enable DVP * * MIPI CONTROL 00 - * 4: PWDN PHY TX - * 3: PWDN PHY RX - * 2: MIPI enable + * [4] = 1 : Power down MIPI HS Tx + * [3] = 1 : Power down MIPI LS Rx + * [2] = 0 : DVP enable (MIPI disable) */ ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x18); if (ret) @@ -2074,8 +2109,7 @@ static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on) * - [3:0]: D[9:6] output enable */ ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, - sensor->ep.bus_type == V4L2_MBUS_PARALLEL ? - 0x7f : 0x1f); + bt656 ? 0x1f : 0x7f); if (ret) return ret; @@ -2925,8 +2959,6 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) ret = ov5640_set_stream_mipi(sensor, enable); - else if (sensor->ep.bus_type == V4L2_MBUS_BT656) - ret = ov5640_set_stream_bt656(sensor, enable); else ret = ov5640_set_stream_dvp(sensor, enable); diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index f26252e35e08..148fd4e05029 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2373,8 +2373,7 @@ unlock_and_return: static int __maybe_unused ov5670_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5670 *ov5670 = to_ov5670(sd); if (ov5670->streaming) @@ -2385,8 +2384,7 @@ static int __maybe_unused ov5670_suspend(struct device *dev) static int __maybe_unused ov5670_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5670 *ov5670 = to_ov5670(sd); int ret; diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 9540ce8918f0..5e35808037ad 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -889,8 +889,7 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) static int __maybe_unused ov5675_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5675 *ov5675 = to_ov5675(sd); mutex_lock(&ov5675->mutex); @@ -904,8 +903,7 @@ static int __maybe_unused ov5675_suspend(struct device *dev) static int __maybe_unused ov5675_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5675 *ov5675 = to_ov5675(sd); int ret; diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index cc678d9d2e0d..bbccb6f9582f 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1033,8 +1033,7 @@ static void __ov5695_power_off(struct ov5695 *ov5695) static int __maybe_unused ov5695_runtime_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5695 *ov5695 = to_ov5695(sd); return __ov5695_power_on(ov5695); @@ -1042,8 +1041,7 @@ static int __maybe_unused ov5695_runtime_resume(struct device *dev) static int __maybe_unused ov5695_runtime_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5695 *ov5695 = to_ov5695(sd); __ov5695_power_off(ov5695); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index b42b289faaef..d2df811b1a40 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -561,6 +561,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, unsigned char *value) { struct ov7670_info *info = to_state(sd); + if (info->use_smbus) return ov7670_read_smbus(sd, reg, value); else @@ -571,6 +572,7 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, unsigned char value) { struct ov7670_info *info = to_state(sd); + if (info->use_smbus) return ov7670_write_smbus(sd, reg, value); else @@ -597,6 +599,7 @@ static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) { while (vals->reg_num != 0xff || vals->value != 0xff) { int ret = ov7670_write(sd, vals->reg_num, vals->value); + if (ret < 0) return ret; vals++; @@ -921,27 +924,38 @@ static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, { int ret; unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); - ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); - ret += ov7670_read(sd, REG_HREF, &v); + /* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + if (ret) + return ret; + ret = ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + if (ret) + return ret; + ret = ov7670_read(sd, REG_HREF, &v); + if (ret) + return ret; v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); msleep(10); - ret += ov7670_write(sd, REG_HREF, v); -/* - * Vertical: similar arrangement, but only 10 bits. - */ - ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); - ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); - ret += ov7670_read(sd, REG_VREF, &v); + ret = ov7670_write(sd, REG_HREF, v); + if (ret) + return ret; + /* Vertical: similar arrangement, but only 10 bits. */ + ret = ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); + if (ret) + return ret; + ret = ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); + if (ret) + return ret; + ret = ov7670_read(sd, REG_VREF, &v); + if (ret) + return ret; v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); msleep(10); - ret += ov7670_write(sd, REG_VREF, v); - return ret; + return ov7670_write(sd, REG_VREF, v); } @@ -1245,6 +1259,7 @@ static int ov7670_enum_frame_size(struct v4l2_subdev *sd, */ for (i = 0; i < n_win_sizes; i++) { struct ov7670_win_size *win = &info->devtype->win_sizes[i]; + if (info->min_width && win->width < info->min_width) continue; if (info->min_height && win->height < info->min_height) @@ -1285,17 +1300,17 @@ static int ov7670_store_cmatrix(struct v4l2_subdev *sd, raw = 0xff; else raw = (-1 * matrix[i]) & 0xff; - } - else { + } else { if (matrix[i] > 255) raw = 0xff; else raw = matrix[i] & 0xff; } - ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); + ret = ov7670_write(sd, REG_CMATRIX_BASE + i, raw); + if (ret) + return ret; } - ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); - return ret; + return ov7670_write(sd, REG_CMATRIX_SIGN, signbits); } @@ -1381,11 +1396,9 @@ static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) { struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; - int ret; ov7670_calc_cmatrix(info, matrix, sat, hue); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; + return ov7670_store_cmatrix(sd, matrix); } @@ -1403,14 +1416,12 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) { unsigned char com8 = 0, v; - int ret; ov7670_read(sd, REG_COM8, &com8); com8 &= ~COM8_AEC; ov7670_write(sd, REG_COM8, com8); v = ov7670_abs_to_sm(value); - ret = ov7670_write(sd, REG_BRIGHT, v); - return ret; + return ov7670_write(sd, REG_BRIGHT, v); } static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) @@ -1424,13 +1435,14 @@ static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) int ret; ret = ov7670_read(sd, REG_MVFP, &v); + if (ret) + return ret; if (value) v |= MVFP_MIRROR; else v &= ~MVFP_MIRROR; msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; + return ov7670_write(sd, REG_MVFP, v); } static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) @@ -1439,13 +1451,14 @@ static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) int ret; ret = ov7670_read(sd, REG_MVFP, &v); + if (ret) + return ret; if (value) v |= MVFP_FLIP; else v &= ~MVFP_FLIP; msleep(10); /* FIXME */ - ret += ov7670_write(sd, REG_MVFP, v); - return ret; + return ov7670_write(sd, REG_MVFP, v); } /* @@ -1460,8 +1473,10 @@ static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) unsigned char gain; ret = ov7670_read(sd, REG_GAIN, &gain); + if (ret) + return ret; *value = gain; - return ret; + return 0; } static int ov7670_s_gain(struct v4l2_subdev *sd, int value) @@ -1470,12 +1485,13 @@ static int ov7670_s_gain(struct v4l2_subdev *sd, int value) unsigned char com8; ret = ov7670_write(sd, REG_GAIN, value & 0xff); + if (ret) + return ret; /* Have to turn off AGC as well */ - if (ret == 0) { - ret = ov7670_read(sd, REG_COM8, &com8); - ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); - } - return ret; + ret = ov7670_read(sd, REG_COM8, &com8); + if (ret) + return ret; + return ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); } /* @@ -1680,13 +1696,13 @@ static int ov7670_s_power(struct v4l2_subdev *sd, int on) return 0; if (on) { - ov7670_power_on (sd); + ov7670_power_on(sd); ov7670_init(sd, 0); ov7670_apply_fmt(sd); ov7675_apply_framerate(sd); v4l2_ctrl_handler_setup(&info->hdl); } else { - ov7670_power_off (sd); + ov7670_power_off(sd); } return 0; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 2cc6a678069a..d94cf2d39c2a 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -31,6 +31,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-image-sizes.h> #include <media/v4l2-subdev.h> @@ -226,7 +227,7 @@ /* COM3 */ #define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) -#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) +#define IMG_MASK (VFLIP_IMG | HFLIP_IMG | SCOLOR_TEST) #define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ #define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ @@ -424,6 +425,7 @@ struct ov772x_priv { const struct ov772x_win_size *win; struct v4l2_ctrl *vflip_ctrl; struct v4l2_ctrl *hflip_ctrl; + unsigned int test_pattern; /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ struct v4l2_ctrl *band_filter_ctrl; unsigned int fps; @@ -434,6 +436,7 @@ struct ov772x_priv { #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; #endif + enum v4l2_mbus_type bus_type; }; /* @@ -538,6 +541,11 @@ static const struct ov772x_win_size ov772x_win_sizes[] = { }, }; +static const char * const ov772x_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", +}; + /* * frame rate settings lists */ @@ -581,6 +589,14 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) if (priv->streaming == enable) goto done; + if (priv->bus_type == V4L2_MBUS_BT656) { + ret = regmap_update_bits(priv->regmap, COM7, ITU656_ON_OFF, + enable ? + ITU656_ON_OFF : ~ITU656_ON_OFF); + if (ret) + goto done; + } + ret = regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE, enable ? 0 : SOFT_SLEEP_MODE); if (ret) @@ -800,6 +816,9 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) } return ret; + case V4L2_CID_TEST_PATTERN: + priv->test_pattern = ctrl->val; + return 0; } return -EINVAL; @@ -1098,6 +1117,8 @@ static int ov772x_set_params(struct ov772x_priv *priv, val ^= VFLIP_IMG; if (priv->hflip_ctrl->val) val ^= HFLIP_IMG; + if (priv->test_pattern) + val |= SCOLOR_TEST; ret = regmap_update_bits(priv->regmap, COM3, SWAP_MASK | IMG_MASK, val); if (ret < 0) @@ -1348,6 +1369,46 @@ static const struct v4l2_subdev_ops ov772x_subdev_ops = { .pad = &ov772x_subdev_pad_ops, }; +static int ov772x_parse_dt(struct i2c_client *client, + struct ov772x_priv *priv) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_PARALLEL + }; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!ep) { + dev_err(&client->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + /* + * For backward compatibility with older DTS where the + * bus-type property was not mandatory, assume + * V4L2_MBUS_PARALLEL as it was the only supported bus at the + * time. v4l2_fwnode_endpoint_alloc_parse() will not fail if + * 'bus-type' is not specified. + */ + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) { + bus_cfg = (struct v4l2_fwnode_endpoint) + { .bus_type = V4L2_MBUS_BT656 }; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) + goto error_fwnode_put; + } + + priv->bus_type = bus_cfg.bus_type; + v4l2_fwnode_endpoint_free(&bus_cfg); + +error_fwnode_put: + fwnode_handle_put(ep); + + return ret; +} + /* * i2c_driver function */ @@ -1394,6 +1455,10 @@ static int ov772x_probe(struct i2c_client *client) priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); + v4l2_ctrl_new_std_menu_items(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov772x_test_pattern_menu) - 1, + 0, 0, ov772x_test_pattern_menu); priv->subdev.ctrl_handler = &priv->hdl; if (priv->hdl.error) { ret = priv->hdl.error; @@ -1415,6 +1480,10 @@ static int ov772x_probe(struct i2c_client *client) goto error_clk_put; } + ret = ov772x_parse_dt(client, priv); + if (ret) + goto error_clk_put; + ret = ov772x_video_probe(priv); if (ret < 0) goto error_gpio_put; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 5832461c032d..47a9003d29d6 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1176,8 +1176,7 @@ static int ov7740_remove(struct i2c_client *client) static int __maybe_unused ov7740_runtime_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); ov7740_set_power(ov7740, 0); @@ -1187,8 +1186,7 @@ static int __maybe_unused ov7740_runtime_suspend(struct device *dev) static int __maybe_unused ov7740_runtime_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); return ov7740_set_power(ov7740, 1); diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index 2f4ceaa80593..d8cefd3d4045 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1417,8 +1417,7 @@ static void __ov8856_power_off(struct ov8856 *ov8856) static int __maybe_unused ov8856_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov8856 *ov8856 = to_ov8856(sd); mutex_lock(&ov8856->mutex); @@ -1433,8 +1432,7 @@ static int __maybe_unused ov8856_suspend(struct device *dev) static int __maybe_unused ov8856_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov8856 *ov8856 = to_ov8856(sd); int ret; diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c new file mode 100644 index 000000000000..e212465489e8 --- /dev/null +++ b/drivers/media/i2c/ov9734.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Intel Corporation. + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define OV9734_LINK_FREQ_180MHZ 180000000ULL +#define OV9734_SCLK 36000000LL +#define OV9734_MCLK 19200000 +/* ov9734 only support 1-lane mipi output */ +#define OV9734_DATA_LANES 1 +#define OV9734_RGB_DEPTH 10 + +#define OV9734_REG_CHIP_ID 0x300a +#define OV9734_CHIP_ID 0x9734 + +#define OV9734_REG_MODE_SELECT 0x0100 +#define OV9734_MODE_STANDBY 0x00 +#define OV9734_MODE_STREAMING 0x01 + +/* vertical-timings from sensor */ +#define OV9734_REG_VTS 0x380e +#define OV9734_VTS_30FPS 0x0322 +#define OV9734_VTS_30FPS_MIN 0x0322 +#define OV9734_VTS_MAX 0x7fff + +/* horizontal-timings from sensor */ +#define OV9734_REG_HTS 0x380c + +/* Exposure controls from sensor */ +#define OV9734_REG_EXPOSURE 0x3500 +#define OV9734_EXPOSURE_MIN 4 +#define OV9734_EXPOSURE_MAX_MARGIN 4 +#define OV9734_EXPOSURE_STEP 1 + +/* Analog gain controls from sensor */ +#define OV9734_REG_ANALOG_GAIN 0x350a +#define OV9734_ANAL_GAIN_MIN 16 +#define OV9734_ANAL_GAIN_MAX 248 +#define OV9734_ANAL_GAIN_STEP 1 + +/* Digital gain controls from sensor */ +#define OV9734_REG_MWB_R_GAIN 0x5180 +#define OV9734_REG_MWB_G_GAIN 0x5182 +#define OV9734_REG_MWB_B_GAIN 0x5184 +#define OV9734_DGTL_GAIN_MIN 256 +#define OV9734_DGTL_GAIN_MAX 1023 +#define OV9734_DGTL_GAIN_STEP 1 +#define OV9734_DGTL_GAIN_DEFAULT 256 + +/* Test Pattern Control */ +#define OV9734_REG_TEST_PATTERN 0x5080 +#define OV9734_TEST_PATTERN_ENABLE BIT(7) +#define OV9734_TEST_PATTERN_BAR_SHIFT 2 + +enum { + OV9734_LINK_FREQ_180MHZ_INDEX, +}; + +struct ov9734_reg { + u16 address; + u8 val; +}; + +struct ov9734_reg_list { + u32 num_of_regs; + const struct ov9734_reg *regs; +}; + +struct ov9734_link_freq_config { + const struct ov9734_reg_list reg_list; +}; + +struct ov9734_mode { + /* Frame width in pixels */ + u32 width; + + /* Frame height in pixels */ + u32 height; + + /* Horizontal timining size */ + u32 hts; + + /* Default vertical timining size */ + u32 vts_def; + + /* Min vertical timining size */ + u32 vts_min; + + /* Link frequency needed for this resolution */ + u32 link_freq_index; + + /* Sensor register settings for this resolution */ + const struct ov9734_reg_list reg_list; +}; + +static const struct ov9734_reg mipi_data_rate_360mbps[] = { + {0x3030, 0x19}, + {0x3080, 0x02}, + {0x3081, 0x4b}, + {0x3082, 0x04}, + {0x3083, 0x00}, + {0x3084, 0x02}, + {0x3085, 0x01}, + {0x3086, 0x01}, + {0x3089, 0x01}, + {0x308a, 0x00}, + {0x301e, 0x15}, + {0x3103, 0x01}, +}; + +static const struct ov9734_reg mode_1296x734_regs[] = { + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3007, 0x00}, + {0x3010, 0x00}, + {0x3011, 0x08}, + {0x3014, 0x22}, + {0x3600, 0x55}, + {0x3601, 0x02}, + {0x3605, 0x22}, + {0x3611, 0xe7}, + {0x3654, 0x10}, + {0x3655, 0x77}, + {0x3656, 0x77}, + {0x3657, 0x07}, + {0x3658, 0x22}, + {0x3659, 0x22}, + {0x365a, 0x02}, + {0x3784, 0x05}, + {0x3785, 0x55}, + {0x37c0, 0x07}, + {0x3800, 0x00}, + {0x3801, 0x04}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x05}, + {0x3805, 0x0b}, + {0x3806, 0x02}, + {0x3807, 0xdb}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x02}, + {0x380b, 0xd0}, + {0x380c, 0x05}, + {0x380d, 0xc6}, + {0x380e, 0x03}, + {0x380f, 0x22}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x04}, + {0x3820, 0x18}, + {0x3821, 0x00}, + {0x382c, 0x06}, + {0x3500, 0x00}, + {0x3501, 0x31}, + {0x3502, 0x00}, + {0x3503, 0x03}, + {0x3504, 0x00}, + {0x3505, 0x00}, + {0x3509, 0x10}, + {0x350a, 0x00}, + {0x350b, 0x40}, + {0x3d00, 0x00}, + {0x3d01, 0x00}, + {0x3d02, 0x00}, + {0x3d03, 0x00}, + {0x3d04, 0x00}, + {0x3d05, 0x00}, + {0x3d06, 0x00}, + {0x3d07, 0x00}, + {0x3d08, 0x00}, + {0x3d09, 0x00}, + {0x3d0a, 0x00}, + {0x3d0b, 0x00}, + {0x3d0c, 0x00}, + {0x3d0d, 0x00}, + {0x3d0e, 0x00}, + {0x3d0f, 0x00}, + {0x3d80, 0x00}, + {0x3d81, 0x00}, + {0x3d82, 0x38}, + {0x3d83, 0xa4}, + {0x3d84, 0x00}, + {0x3d85, 0x00}, + {0x3d86, 0x1f}, + {0x3d87, 0x03}, + {0x3d8b, 0x00}, + {0x3d8f, 0x00}, + {0x4001, 0xe0}, + {0x4009, 0x0b}, + {0x4300, 0x03}, + {0x4301, 0xff}, + {0x4304, 0x00}, + {0x4305, 0x00}, + {0x4309, 0x00}, + {0x4600, 0x00}, + {0x4601, 0x80}, + {0x4800, 0x00}, + {0x4805, 0x00}, + {0x4821, 0x50}, + {0x4823, 0x50}, + {0x4837, 0x2d}, + {0x4a00, 0x00}, + {0x4f00, 0x80}, + {0x4f01, 0x10}, + {0x4f02, 0x00}, + {0x4f03, 0x00}, + {0x4f04, 0x00}, + {0x4f05, 0x00}, + {0x4f06, 0x00}, + {0x4f07, 0x00}, + {0x4f08, 0x00}, + {0x4f09, 0x00}, + {0x5000, 0x2f}, + {0x500c, 0x00}, + {0x500d, 0x00}, + {0x500e, 0x00}, + {0x500f, 0x00}, + {0x5010, 0x00}, + {0x5011, 0x00}, + {0x5012, 0x00}, + {0x5013, 0x00}, + {0x5014, 0x00}, + {0x5015, 0x00}, + {0x5016, 0x00}, + {0x5017, 0x00}, + {0x5080, 0x00}, + {0x5180, 0x01}, + {0x5181, 0x00}, + {0x5182, 0x01}, + {0x5183, 0x00}, + {0x5184, 0x01}, + {0x5185, 0x00}, + {0x5708, 0x06}, + {0x380f, 0x2a}, + {0x5780, 0x3e}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5000, 0x3f}, + {0x3801, 0x00}, + {0x3803, 0x00}, + {0x3805, 0x0f}, + {0x3807, 0xdf}, + {0x3809, 0x10}, + {0x380b, 0xde}, + {0x3811, 0x00}, + {0x3813, 0x01}, +}; + +static const char * const ov9734_test_pattern_menu[] = { + "Disabled", + "Standard Color Bar", + "Top-Bottom Darker Color Bar", + "Right-Left Darker Color Bar", + "Bottom-Top Darker Color Bar", +}; + +static const s64 link_freq_menu_items[] = { + OV9734_LINK_FREQ_180MHZ, +}; + +static const struct ov9734_link_freq_config link_freq_configs[] = { + [OV9734_LINK_FREQ_180MHZ_INDEX] = { + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps), + .regs = mipi_data_rate_360mbps, + } + }, +}; + +static const struct ov9734_mode supported_modes[] = { + { + .width = 1296, + .height = 734, + .hts = 0x5c6, + .vts_def = OV9734_VTS_30FPS, + .vts_min = OV9734_VTS_30FPS_MIN, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x734_regs), + .regs = mode_1296x734_regs, + }, + .link_freq_index = OV9734_LINK_FREQ_180MHZ_INDEX, + }, +}; + +struct ov9734 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct ov9734_mode *cur_mode; + + /* To serialize asynchronus callbacks */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct ov9734 *to_ov9734(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ov9734, sd); +} + +static u64 to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = link_freq_menu_items[f_index] * 2 * OV9734_DATA_LANES; + + do_div(pixel_rate, OV9734_RGB_DEPTH); + + return pixel_rate; +} + +static u64 to_pixels_per_line(u32 hts, u32 f_index) +{ + u64 ppl = hts * to_pixel_rate(f_index); + + do_div(ppl, OV9734_SCLK); + + return ppl; +} + +static int ov9734_read_reg(struct ov9734 *ov9734, u16 reg, u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = {0}; + int ret; + + if (len > sizeof(data_buf)) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(addr_buf); + msgs[0].buf = addr_buf; + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[sizeof(data_buf) - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return ret < 0 ? ret : -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +static int ov9734_write_reg(struct ov9734 *ov9734, u16 reg, u16 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + u8 buf[6]; + int ret = 0; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << 8 * (4 - len), buf + 2); + + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int ov9734_write_reg_list(struct ov9734 *ov9734, + const struct ov9734_reg_list *r_list) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + unsigned int i; + int ret; + + for (i = 0; i < r_list->num_of_regs; i++) { + ret = ov9734_write_reg(ov9734, r_list->regs[i].address, 1, + r_list->regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err = %d", + r_list->regs[i].address, ret); + return ret; + } + } + + return 0; +} + +static int ov9734_update_digital_gain(struct ov9734 *ov9734, u32 d_gain) +{ + int ret; + + ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_R_GAIN, 2, d_gain); + if (ret) + return ret; + + ret = ov9734_write_reg(ov9734, OV9734_REG_MWB_G_GAIN, 2, d_gain); + if (ret) + return ret; + + return ov9734_write_reg(ov9734, OV9734_REG_MWB_B_GAIN, 2, d_gain); +} + +static int ov9734_test_pattern(struct ov9734 *ov9734, u32 pattern) +{ + if (pattern) + pattern = (pattern - 1) << OV9734_TEST_PATTERN_BAR_SHIFT | + OV9734_TEST_PATTERN_ENABLE; + + return ov9734_write_reg(ov9734, OV9734_REG_TEST_PATTERN, 1, pattern); +} + +static int ov9734_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9734 *ov9734 = container_of(ctrl->handler, + struct ov9734, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + s64 exposure_max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = ov9734->cur_mode->height + ctrl->val - + OV9734_EXPOSURE_MAX_MARGIN; + __v4l2_ctrl_modify_range(ov9734->exposure, + ov9734->exposure->minimum, + exposure_max, ov9734->exposure->step, + exposure_max); + } + + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = ov9734_write_reg(ov9734, OV9734_REG_ANALOG_GAIN, + 2, ctrl->val); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = ov9734_update_digital_gain(ov9734, ctrl->val); + break; + + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = ov9734_write_reg(ov9734, OV9734_REG_EXPOSURE, + 3, ctrl->val << 4); + break; + + case V4L2_CID_VBLANK: + ret = ov9734_write_reg(ov9734, OV9734_REG_VTS, 2, + ov9734->cur_mode->height + ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov9734_test_pattern(ov9734, ctrl->val); + break; + + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov9734_ctrl_ops = { + .s_ctrl = ov9734_set_ctrl, +}; + +static int ov9734_init_controls(struct ov9734 *ov9734) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct ov9734_mode *cur_mode; + s64 exposure_max, h_blank, pixel_rate; + u32 vblank_min, vblank_max, vblank_default; + int ret, size; + + ctrl_hdlr = &ov9734->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ctrl_hdlr->lock = &ov9734->mutex; + cur_mode = ov9734->cur_mode; + size = ARRAY_SIZE(link_freq_menu_items); + ov9734->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_LINK_FREQ, + size - 1, 0, + link_freq_menu_items); + if (ov9734->link_freq) + ov9734->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = to_pixel_rate(OV9734_LINK_FREQ_180MHZ_INDEX); + ov9734->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + pixel_rate, 1, pixel_rate); + vblank_min = cur_mode->vts_min - cur_mode->height; + vblank_max = OV9734_VTS_MAX - cur_mode->height; + vblank_default = cur_mode->vts_def - cur_mode->height; + ov9734->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_default); + h_blank = to_pixels_per_line(cur_mode->hts, cur_mode->link_freq_index); + h_blank -= cur_mode->width; + ov9734->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (ov9734->hblank) + ov9734->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + OV9734_ANAL_GAIN_MIN, OV9734_ANAL_GAIN_MAX, + OV9734_ANAL_GAIN_STEP, OV9734_ANAL_GAIN_MIN); + v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + OV9734_DGTL_GAIN_MIN, OV9734_DGTL_GAIN_MAX, + OV9734_DGTL_GAIN_STEP, OV9734_DGTL_GAIN_DEFAULT); + exposure_max = ov9734->cur_mode->vts_def - OV9734_EXPOSURE_MAX_MARGIN; + ov9734->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_EXPOSURE, + OV9734_EXPOSURE_MIN, exposure_max, + OV9734_EXPOSURE_STEP, + exposure_max); + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov9734_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov9734_test_pattern_menu) - 1, + 0, 0, ov9734_test_pattern_menu); + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ov9734->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static void ov9734_update_pad_format(const struct ov9734_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov9734_start_streaming(struct ov9734 *ov9734) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + const struct ov9734_reg_list *reg_list; + int link_freq_index, ret; + + link_freq_index = ov9734->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = ov9734_write_reg_list(ov9734, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set plls"); + return ret; + } + + reg_list = &ov9734->cur_mode->reg_list; + ret = ov9734_write_reg_list(ov9734, reg_list); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + ret = __v4l2_ctrl_handler_setup(ov9734->sd.ctrl_handler); + if (ret) + return ret; + + ret = ov9734_write_reg(ov9734, OV9734_REG_MODE_SELECT, + 1, OV9734_MODE_STREAMING); + if (ret) + dev_err(&client->dev, "failed to start stream"); + + return ret; +} + +static void ov9734_stop_streaming(struct ov9734 *ov9734) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + + if (ov9734_write_reg(ov9734, OV9734_REG_MODE_SELECT, + 1, OV9734_MODE_STANDBY)) + dev_err(&client->dev, "failed to stop stream"); +} + +static int ov9734_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov9734 *ov9734 = to_ov9734(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&ov9734->mutex); + if (ov9734->streaming == enable) { + mutex_unlock(&ov9734->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + mutex_unlock(&ov9734->mutex); + return ret; + } + + ret = ov9734_start_streaming(ov9734); + if (ret) { + enable = 0; + ov9734_stop_streaming(ov9734); + pm_runtime_put(&client->dev); + } + } else { + ov9734_stop_streaming(ov9734); + pm_runtime_put(&client->dev); + } + + ov9734->streaming = enable; + mutex_unlock(&ov9734->mutex); + + return ret; +} + +static int __maybe_unused ov9734_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9734 *ov9734 = to_ov9734(sd); + + mutex_lock(&ov9734->mutex); + if (ov9734->streaming) + ov9734_stop_streaming(ov9734); + + mutex_unlock(&ov9734->mutex); + + return 0; +} + +static int __maybe_unused ov9734_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9734 *ov9734 = to_ov9734(sd); + int ret = 0; + + mutex_lock(&ov9734->mutex); + if (!ov9734->streaming) + goto exit; + + ret = ov9734_start_streaming(ov9734); + if (ret) { + ov9734->streaming = false; + ov9734_stop_streaming(ov9734); + } + +exit: + mutex_unlock(&ov9734->mutex); + return ret; +} + +static int ov9734_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9734 *ov9734 = to_ov9734(sd); + const struct ov9734_mode *mode; + s32 vblank_def, h_blank; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, + height, fmt->format.width, + fmt->format.height); + + mutex_lock(&ov9734->mutex); + ov9734_update_pad_format(mode, &fmt->format); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov9734->cur_mode = mode; + __v4l2_ctrl_s_ctrl(ov9734->link_freq, mode->link_freq_index); + __v4l2_ctrl_s_ctrl_int64(ov9734->pixel_rate, + to_pixel_rate(mode->link_freq_index)); + + /* Update limits and set FPS to default */ + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov9734->vblank, + mode->vts_min - mode->height, + OV9734_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov9734->vblank, vblank_def); + h_blank = to_pixels_per_line(mode->hts, mode->link_freq_index) - + mode->width; + __v4l2_ctrl_modify_range(ov9734->hblank, h_blank, h_blank, 1, + h_blank); + } + + mutex_unlock(&ov9734->mutex); + + return 0; +} + +static int ov9734_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov9734 *ov9734 = to_ov9734(sd); + + mutex_lock(&ov9734->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&ov9734->sd, cfg, + fmt->pad); + else + ov9734_update_pad_format(ov9734->cur_mode, &fmt->format); + + mutex_unlock(&ov9734->mutex); + + return 0; +} + +static int ov9734_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int ov9734_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov9734_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov9734 *ov9734 = to_ov9734(sd); + + mutex_lock(&ov9734->mutex); + ov9734_update_pad_format(&supported_modes[0], + v4l2_subdev_get_try_format(sd, fh->pad, 0)); + mutex_unlock(&ov9734->mutex); + + return 0; +} + +static const struct v4l2_subdev_video_ops ov9734_video_ops = { + .s_stream = ov9734_set_stream, +}; + +static const struct v4l2_subdev_pad_ops ov9734_pad_ops = { + .set_fmt = ov9734_set_format, + .get_fmt = ov9734_get_format, + .enum_mbus_code = ov9734_enum_mbus_code, + .enum_frame_size = ov9734_enum_frame_size, +}; + +static const struct v4l2_subdev_ops ov9734_subdev_ops = { + .video = &ov9734_video_ops, + .pad = &ov9734_pad_ops, +}; + +static const struct media_entity_operations ov9734_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops ov9734_internal_ops = { + .open = ov9734_open, +}; + +static int ov9734_identify_module(struct ov9734 *ov9734) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov9734->sd); + int ret; + u32 val; + + ret = ov9734_read_reg(ov9734, OV9734_REG_CHIP_ID, 2, &val); + if (ret) + return ret; + + if (val != OV9734_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + OV9734_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int ov9734_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + u32 mclk; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + if (ret) + return ret; + + if (mclk != OV9734_MCLK) { + dev_err(dev, "external clock %d is not supported", mclk); + return -EINVAL; + } + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENXIO; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto check_hwcfg_error; + } + + for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + link_freq_menu_items[i]); + ret = -EINVAL; + goto check_hwcfg_error; + } + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov9734_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov9734 *ov9734 = to_ov9734(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(&client->dev); + mutex_destroy(&ov9734->mutex); + + return 0; +} + +static int ov9734_probe(struct i2c_client *client) +{ + struct ov9734 *ov9734; + int ret; + + ret = ov9734_check_hwcfg(&client->dev); + if (ret) { + dev_err(&client->dev, "failed to check HW configuration: %d", + ret); + return ret; + } + + ov9734 = devm_kzalloc(&client->dev, sizeof(*ov9734), GFP_KERNEL); + if (!ov9734) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ov9734->sd, client, &ov9734_subdev_ops); + ret = ov9734_identify_module(ov9734); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + return ret; + } + + mutex_init(&ov9734->mutex); + ov9734->cur_mode = &supported_modes[0]; + ret = ov9734_init_controls(ov9734); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov9734->sd.internal_ops = &ov9734_internal_ops; + ov9734->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov9734->sd.entity.ops = &ov9734_subdev_entity_ops; + ov9734->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov9734->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&ov9734->sd.entity, 1, &ov9734->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&ov9734->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov9734->sd.entity); + +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov9734->sd.ctrl_handler); + mutex_destroy(&ov9734->mutex); + + return ret; +} + +static const struct dev_pm_ops ov9734_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov9734_suspend, ov9734_resume) +}; + +static const struct acpi_device_id ov9734_acpi_ids[] = { + { "OVTI9734", }, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov9734_acpi_ids); + +static struct i2c_driver ov9734_i2c_driver = { + .driver = { + .name = "ov9734", + .pm = &ov9734_pm_ops, + .acpi_match_table = ov9734_acpi_ids, + }, + .probe_new = ov9734_probe, + .remove = ov9734_remove, +}; + +module_i2c_driver(ov9734_i2c_driver); + +MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); +MODULE_DESCRIPTION("OmniVision OV9734 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c index 1ed928c4ca70..16bcb764b0e0 100644 --- a/drivers/media/i2c/rdacm20.c +++ b/drivers/media/i2c/rdacm20.c @@ -487,9 +487,18 @@ static int rdacm20_initialize(struct rdacm20_device *dev) * Reset the sensor by cycling the OV10635 reset signal connected to the * MAX9271 GPIO1 and verify communication with the OV10635. */ - max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); + ret = max9271_enable_gpios(dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; + + ret = max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; usleep_range(10000, 15000); - max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); + + ret = max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT); + if (ret) + return ret; usleep_range(10000, 15000); again: diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c deleted file mode 100644 index 690abe8cbdb2..000000000000 --- a/drivers/media/i2c/smiapp-pll.c +++ /dev/null @@ -1,482 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/media/i2c/smiapp-pll.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#include <linux/device.h> -#include <linux/gcd.h> -#include <linux/lcm.h> -#include <linux/module.h> - -#include "smiapp-pll.h" - -/* Return an even number or one. */ -static inline uint32_t clk_div_even(uint32_t a) -{ - return max_t(uint32_t, 1, a & ~1); -} - -/* Return an even number or one. */ -static inline uint32_t clk_div_even_up(uint32_t a) -{ - if (a == 1) - return 1; - return (a + 1) & ~1; -} - -static inline uint32_t is_one_or_even(uint32_t a) -{ - if (a == 1) - return 1; - if (a & 1) - return 0; - - return 1; -} - -static int bounds_check(struct device *dev, uint32_t val, - uint32_t min, uint32_t max, char *str) -{ - if (val >= min && val <= max) - return 0; - - dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); - - return -EINVAL; -} - -static void print_pll(struct device *dev, struct smiapp_pll *pll) -{ - dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div); - dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier); - if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { - dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div); - dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div); - } - dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div); - dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div); - - dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz); - dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz); - dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz); - if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) { - dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n", - pll->op.sys_clk_freq_hz); - dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n", - pll->op.pix_clk_freq_hz); - } - dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz); - dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz); -} - -static int check_all_bounds(struct device *dev, - const struct smiapp_pll_limits *limits, - const struct smiapp_pll_branch_limits *op_limits, - struct smiapp_pll *pll, - struct smiapp_pll_branch *op_pll) -{ - int rval; - - rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, - limits->min_pll_ip_freq_hz, - limits->max_pll_ip_freq_hz, - "pll_ip_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->pll_multiplier, - limits->min_pll_multiplier, limits->max_pll_multiplier, - "pll_multiplier"); - if (!rval) - rval = bounds_check( - dev, pll->pll_op_clk_freq_hz, - limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, - "pll_op_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, op_pll->sys_clk_div, - op_limits->min_sys_clk_div, op_limits->max_sys_clk_div, - "op_sys_clk_div"); - if (!rval) - rval = bounds_check( - dev, op_pll->sys_clk_freq_hz, - op_limits->min_sys_clk_freq_hz, - op_limits->max_sys_clk_freq_hz, - "op_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, op_pll->pix_clk_freq_hz, - op_limits->min_pix_clk_freq_hz, - op_limits->max_pix_clk_freq_hz, - "op_pix_clk_freq_hz"); - - /* - * If there are no OP clocks, the VT clocks are contained in - * the OP clock struct. - */ - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) - return rval; - - if (!rval) - rval = bounds_check( - dev, pll->vt.sys_clk_freq_hz, - limits->vt.min_sys_clk_freq_hz, - limits->vt.max_sys_clk_freq_hz, - "vt_sys_clk_freq_hz"); - if (!rval) - rval = bounds_check( - dev, pll->vt.pix_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz, - limits->vt.max_pix_clk_freq_hz, - "vt_pix_clk_freq_hz"); - - return rval; -} - -/* - * Heuristically guess the PLL tree for a given common multiplier and - * divisor. Begin with the operational timing and continue to video - * timing once operational timing has been verified. - * - * @mul is the PLL multiplier and @div is the common divisor - * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL - * multiplier will be a multiple of @mul. - * - * @return Zero on success, error code on error. - */ -static int __smiapp_pll_calculate( - struct device *dev, const struct smiapp_pll_limits *limits, - const struct smiapp_pll_branch_limits *op_limits, - struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul, - uint32_t div, uint32_t lane_op_clock_ratio) -{ - uint32_t sys_div; - uint32_t best_pix_div = INT_MAX >> 1; - uint32_t vt_op_binning_div; - /* - * Higher multipliers (and divisors) are often required than - * necessitated by the external clock and the output clocks. - * There are limits for all values in the clock tree. These - * are the minimum and maximum multiplier for mul. - */ - uint32_t more_mul_min, more_mul_max; - uint32_t more_mul_factor; - uint32_t min_vt_div, max_vt_div, vt_div; - uint32_t min_sys_div, max_sys_div; - unsigned int i; - - /* - * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be - * too high. - */ - dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div); - - /* Don't go above max pll multiplier. */ - more_mul_max = limits->max_pll_multiplier / mul; - dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n", - more_mul_max); - /* Don't go above max pll op frequency. */ - more_mul_max = - min_t(uint32_t, - more_mul_max, - limits->max_pll_op_freq_hz - / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); - dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n", - more_mul_max); - /* Don't go above the division capability of op sys clock divider. */ - more_mul_max = min(more_mul_max, - op_limits->max_sys_clk_div * pll->pre_pll_clk_div - / div); - dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n", - more_mul_max); - /* Ensure we won't go above min_pll_multiplier. */ - more_mul_max = min(more_mul_max, - DIV_ROUND_UP(limits->max_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n", - more_mul_max); - - /* Ensure we won't go below min_pll_op_freq_hz. */ - more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, - pll->ext_clk_freq_hz / pll->pre_pll_clk_div - * mul); - dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n", - more_mul_min); - /* Ensure we won't go below min_pll_multiplier. */ - more_mul_min = max(more_mul_min, - DIV_ROUND_UP(limits->min_pll_multiplier, mul)); - dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n", - more_mul_min); - - if (more_mul_min > more_mul_max) { - dev_dbg(dev, - "unable to compute more_mul_min and more_mul_max\n"); - return -EINVAL; - } - - more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; - dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor); - more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div); - dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", - more_mul_factor); - i = roundup(more_mul_min, more_mul_factor); - if (!is_one_or_even(i)) - i <<= 1; - - dev_dbg(dev, "final more_mul: %u\n", i); - if (i > more_mul_max) { - dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max); - return -EINVAL; - } - - pll->pll_multiplier = mul * i; - op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div; - dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div); - - pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz - / pll->pre_pll_clk_div; - - pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz - * pll->pll_multiplier; - - /* Derive pll_op_clk_freq_hz. */ - op_pll->sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / op_pll->sys_clk_div; - - op_pll->pix_clk_div = pll->bits_per_pixel; - dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div); - - op_pll->pix_clk_freq_hz = - op_pll->sys_clk_freq_hz / op_pll->pix_clk_div; - - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - /* No OP clocks --- VT clocks are used instead. */ - goto out_skip_vt_calc; - } - - /* - * Some sensors perform analogue binning and some do this - * digitally. The ones doing this digitally can be roughly be - * found out using this formula. The ones doing this digitally - * should run at higher clock rate, so smaller divisor is used - * on video timing side. - */ - if (limits->min_line_length_pck_bin > limits->min_line_length_pck - / pll->binning_horizontal) - vt_op_binning_div = pll->binning_horizontal; - else - vt_op_binning_div = 1; - dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div); - - /* - * Profile 2 supports vt_pix_clk_div E [4, 10] - * - * Horizontal binning can be used as a base for difference in - * divisors. One must make sure that horizontal blanking is - * enough to accommodate the CSI-2 sync codes. - * - * Take scaling factor into account as well. - * - * Find absolute limits for the factor of vt divider. - */ - dev_dbg(dev, "scale_m: %u\n", pll->scale_m); - min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div - * pll->scale_n, - lane_op_clock_ratio * vt_op_binning_div - * pll->scale_m); - - /* Find smallest and biggest allowed vt divisor. */ - dev_dbg(dev, "min_vt_div: %u\n", min_vt_div); - min_vt_div = max(min_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.max_pix_clk_freq_hz)); - dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n", - min_vt_div); - min_vt_div = max_t(uint32_t, min_vt_div, - limits->vt.min_pix_clk_div - * limits->vt.min_sys_clk_div); - dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div); - - max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; - dev_dbg(dev, "max_vt_div: %u\n", max_vt_div); - max_vt_div = min(max_vt_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n", - max_vt_div); - - /* - * Find limitsits for sys_clk_div. Not all values are possible - * with all values of pix_clk_div. - */ - min_sys_div = limits->vt.min_sys_clk_div; - dev_dbg(dev, "min_sys_div: %u\n", min_sys_div); - min_sys_div = max(min_sys_div, - DIV_ROUND_UP(min_vt_div, - limits->vt.max_pix_clk_div)); - dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div); - min_sys_div = max(min_sys_div, - pll->pll_op_clk_freq_hz - / limits->vt.max_sys_clk_freq_hz); - dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div); - min_sys_div = clk_div_even_up(min_sys_div); - dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div); - - max_sys_div = limits->vt.max_sys_clk_div; - dev_dbg(dev, "max_sys_div: %u\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(max_vt_div, - limits->vt.min_pix_clk_div)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div); - max_sys_div = min(max_sys_div, - DIV_ROUND_UP(pll->pll_op_clk_freq_hz, - limits->vt.min_pix_clk_freq_hz)); - dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div); - - /* - * Find pix_div such that a legal pix_div * sys_div results - * into a value which is not smaller than div, the desired - * divisor. - */ - for (vt_div = min_vt_div; vt_div <= max_vt_div; - vt_div += 2 - (vt_div & 1)) { - for (sys_div = min_sys_div; - sys_div <= max_sys_div; - sys_div += 2 - (sys_div & 1)) { - uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div); - - if (pix_div < limits->vt.min_pix_clk_div - || pix_div > limits->vt.max_pix_clk_div) { - dev_dbg(dev, - "pix_div %u too small or too big (%u--%u)\n", - pix_div, - limits->vt.min_pix_clk_div, - limits->vt.max_pix_clk_div); - continue; - } - - /* Check if this one is better. */ - if (pix_div * sys_div - <= roundup(min_vt_div, best_pix_div)) - best_pix_div = pix_div; - } - if (best_pix_div < INT_MAX >> 1) - break; - } - - pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); - pll->vt.pix_clk_div = best_pix_div; - - pll->vt.sys_clk_freq_hz = - pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div; - pll->vt.pix_clk_freq_hz = - pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div; - -out_skip_vt_calc: - pll->pixel_rate_csi = - op_pll->pix_clk_freq_hz * lane_op_clock_ratio; - pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz; - - return check_all_bounds(dev, limits, op_limits, pll, op_pll); -} - -int smiapp_pll_calculate(struct device *dev, - const struct smiapp_pll_limits *limits, - struct smiapp_pll *pll) -{ - const struct smiapp_pll_branch_limits *op_limits = &limits->op; - struct smiapp_pll_branch *op_pll = &pll->op; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t lane_op_clock_ratio; - uint32_t mul, div; - unsigned int i; - int rval = -EINVAL; - - if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { - /* - * If there's no OP PLL at all, use the VT values - * instead. The OP values are ignored for the rest of - * the PLL calculation. - */ - op_limits = &limits->vt; - op_pll = &pll->vt; - } - - if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) - lane_op_clock_ratio = pll->csi2.lanes; - else - lane_op_clock_ratio = 1; - dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio); - - dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal, - pll->binning_vertical); - - switch (pll->bus_type) { - case SMIAPP_PLL_BUS_TYPE_CSI2: - /* CSI transfers 2 bits per clock per lane; thus times 2 */ - pll->pll_op_clk_freq_hz = pll->link_freq * 2 - * (pll->csi2.lanes / lane_op_clock_ratio); - break; - case SMIAPP_PLL_BUS_TYPE_PARALLEL: - pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel - / DIV_ROUND_UP(pll->bits_per_pixel, - pll->parallel.bus_width); - break; - default: - return -EINVAL; - } - - /* Figure out limits for pre-pll divider based on extclk */ - dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n", - limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); - max_pre_pll_clk_div = - min_t(uint16_t, limits->max_pre_pll_clk_div, - clk_div_even(pll->ext_clk_freq_hz / - limits->min_pll_ip_freq_hz)); - min_pre_pll_clk_div = - max_t(uint16_t, limits->min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(pll->ext_clk_freq_hz, - limits->max_pll_ip_freq_hz))); - dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n", - min_pre_pll_clk_div, max_pre_pll_clk_div); - - i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); - mul = div_u64(pll->pll_op_clk_freq_hz, i); - div = pll->ext_clk_freq_hz / i; - dev_dbg(dev, "mul %u / div %u\n", mul, div); - - min_pre_pll_clk_div = - max_t(uint16_t, min_pre_pll_clk_div, - clk_div_even_up( - DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, - limits->max_pll_op_freq_hz))); - dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n", - min_pre_pll_clk_div, max_pre_pll_clk_div); - - for (pll->pre_pll_clk_div = min_pre_pll_clk_div; - pll->pre_pll_clk_div <= max_pre_pll_clk_div; - pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { - rval = __smiapp_pll_calculate(dev, limits, op_limits, pll, - op_pll, mul, div, - lane_op_clock_ratio); - if (rval) - continue; - - print_pll(dev, pll); - return 0; - } - - dev_dbg(dev, "unable to compute pre_pll divisor\n"); - - return rval; -} -EXPORT_SYMBOL_GPL(smiapp_pll_calculate); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>"); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h deleted file mode 100644 index bd6902f54539..000000000000 --- a/drivers/media/i2c/smiapp-pll.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * drivers/media/i2c/smiapp-pll.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#ifndef SMIAPP_PLL_H -#define SMIAPP_PLL_H - -/* CSI-2 or CCP-2 */ -#define SMIAPP_PLL_BUS_TYPE_CSI2 0x00 -#define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01 - -/* op pix clock is for all lanes in total normally */ -#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) -#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) - -struct smiapp_pll_branch { - uint16_t sys_clk_div; - uint16_t pix_clk_div; - uint32_t sys_clk_freq_hz; - uint32_t pix_clk_freq_hz; -}; - -struct smiapp_pll { - /* input values */ - uint8_t bus_type; - union { - struct { - uint8_t lanes; - } csi2; - struct { - uint8_t bus_width; - } parallel; - }; - unsigned long flags; - uint8_t binning_horizontal; - uint8_t binning_vertical; - uint8_t scale_m; - uint8_t scale_n; - uint8_t bits_per_pixel; - uint32_t link_freq; - uint32_t ext_clk_freq_hz; - - /* output values */ - uint16_t pre_pll_clk_div; - uint16_t pll_multiplier; - uint32_t pll_ip_clk_freq_hz; - uint32_t pll_op_clk_freq_hz; - struct smiapp_pll_branch vt; - struct smiapp_pll_branch op; - - uint32_t pixel_rate_csi; - uint32_t pixel_rate_pixel_array; -}; - -struct smiapp_pll_branch_limits { - uint16_t min_sys_clk_div; - uint16_t max_sys_clk_div; - uint32_t min_sys_clk_freq_hz; - uint32_t max_sys_clk_freq_hz; - uint16_t min_pix_clk_div; - uint16_t max_pix_clk_div; - uint32_t min_pix_clk_freq_hz; - uint32_t max_pix_clk_freq_hz; -}; - -struct smiapp_pll_limits { - /* Strict PLL limits */ - uint32_t min_ext_clk_freq_hz; - uint32_t max_ext_clk_freq_hz; - uint16_t min_pre_pll_clk_div; - uint16_t max_pre_pll_clk_div; - uint32_t min_pll_ip_freq_hz; - uint32_t max_pll_ip_freq_hz; - uint16_t min_pll_multiplier; - uint16_t max_pll_multiplier; - uint32_t min_pll_op_freq_hz; - uint32_t max_pll_op_freq_hz; - - struct smiapp_pll_branch_limits vt; - struct smiapp_pll_branch_limits op; - - /* Other relevant limits */ - uint32_t min_line_length_pck_bin; - uint32_t min_line_length_pck; -}; - -struct device; - -int smiapp_pll_calculate(struct device *dev, - const struct smiapp_pll_limits *limits, - struct smiapp_pll *pll); - -#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig deleted file mode 100644 index 6893b532824f..000000000000 --- a/drivers/media/i2c/smiapp/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_SMIAPP - tristate "SMIA++/SMIA sensor support" - depends on I2C && VIDEO_V4L2 && HAVE_CLK - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEO_SMIAPP_PLL - select V4L2_FWNODE - help - This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/i2c/smiapp/Makefile b/drivers/media/i2c/smiapp/Makefile deleted file mode 100644 index 86f57a43f8e8..000000000000 --- a/drivers/media/i2c/smiapp/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -smiapp-objs += smiapp-core.o smiapp-regs.o \ - smiapp-quirk.o smiapp-limits.o -obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o - -ccflags-y += -I $(srctree)/drivers/media/i2c diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c deleted file mode 100644 index 6fc0680a93d0..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ /dev/null @@ -1,3175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/media/i2c/smiapp/smiapp-core.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2010--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - * - * Based on smiapp driver by Vimarsh Zutshi - * Based on jt8ev1.c by Vimarsh Zutshi - * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com> - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/property.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/smiapp.h> -#include <linux/v4l2-mediabus.h> -#include <media/v4l2-fwnode.h> -#include <media/v4l2-device.h> - -#include "smiapp.h" - -#define SMIAPP_ALIGN_DIM(dim, flags) \ - ((flags) & V4L2_SEL_FLAG_GE \ - ? ALIGN((dim), 2) \ - : (dim) & ~1) - -/* - * smiapp_module_idents - supported camera modules - */ -static const struct smiapp_module_ident smiapp_module_idents[] = { - SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), - SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), - SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), - SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), - SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), - SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), - SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), - SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), - SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), - SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), -}; - -/* - * - * Dynamic Capability Identification - * - */ - -static u32 smiapp_get_limit(struct smiapp_sensor *sensor, - unsigned int limit) -{ - if (WARN_ON(limit >= SMIAPP_LIMIT_LAST)) - return 1; - - return sensor->limits[limit]; -} - -#define SMIA_LIM(sensor, limit) \ - smiapp_get_limit(sensor, SMIAPP_LIMIT_##limit) - -static int smiapp_read_all_smia_limits(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int i; - int rval; - - for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { - u32 val; - - rval = smiapp_read( - sensor, smiapp_reg_limits[i].addr, &val); - if (rval) - return rval; - - sensor->limits[i] = val; - - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", - smiapp_reg_limits[i].addr, - smiapp_reg_limits[i].what, val, val); - } - - if (SMIA_LIM(sensor, SCALER_N_MIN) == 0) - smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); - - return 0; -} - -static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; - unsigned int i; - int pixel_count = 0; - int line_count = 0; - int rval; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, - &fmt_model_type); - if (rval) - return rval; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, - &fmt_model_subtype); - if (rval) - return rval; - - ncol_desc = (fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) - >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; - nrow_desc = fmt_model_subtype - & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; - - dev_dbg(&client->dev, "format_model_type %s\n", - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE - ? "2 byte" : - fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE - ? "4 byte" : "is simply bad"); - - for (i = 0; i < ncol_desc + nrow_desc; i++) { - u32 desc; - u32 pixelcode; - u32 pixels; - char *which; - char *what; - u32 reg; - - if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { - reg = SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i); - rval = smiapp_read(sensor, reg, &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; - } else if (fmt_model_type - == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { - reg = SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i); - rval = smiapp_read(sensor, reg, &desc); - if (rval) - return rval; - - pixelcode = - (desc - & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) - >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; - pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; - } else { - dev_dbg(&client->dev, - "invalid frame format model type %d\n", - fmt_model_type); - return -EINVAL; - } - - if (i < ncol_desc) - which = "columns"; - else - which = "rows"; - - switch (pixelcode) { - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: - what = "embedded"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: - what = "dummy"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: - what = "black"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: - what = "dark"; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: - what = "visible"; - break; - default: - what = "invalid"; - break; - } - - dev_dbg(&client->dev, - "0x%8.8x %s pixels: %d %s (pixelcode %u)\n", reg, - what, pixels, which, pixelcode); - - if (i < ncol_desc) { - if (pixelcode == - SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE) - sensor->visible_pixel_start = pixel_count; - pixel_count += pixels; - continue; - } - - /* Handle row descriptors */ - switch (pixelcode) { - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: - if (sensor->embedded_end) - break; - sensor->embedded_start = line_count; - sensor->embedded_end = line_count + pixels; - break; - case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: - sensor->image_start = line_count; - break; - } - line_count += pixels; - } - - if (sensor->embedded_end > sensor->image_start) { - dev_dbg(&client->dev, - "adjusting image start line to %u (was %u)\n", - sensor->embedded_end, sensor->image_start); - sensor->image_start = sensor->embedded_end; - } - - dev_dbg(&client->dev, "embedded data from lines %d to %d\n", - sensor->embedded_start, sensor->embedded_end); - dev_dbg(&client->dev, "image data starts at line %d\n", - sensor->image_start); - - return 0; -} - -static int smiapp_pll_configure(struct smiapp_sensor *sensor) -{ - struct smiapp_pll *pll = &sensor->pll; - int rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt.pix_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt.sys_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); - if (rval < 0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); - if (rval < 0) - return rval; - - /* Lane op clock ratio does not apply here. */ - rval = smiapp_write( - sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, - DIV_ROUND_UP(pll->op.sys_clk_freq_hz, 1000000 / 256 / 256)); - if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) - return rval; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op.pix_clk_div); - if (rval < 0) - return rval; - - return smiapp_write( - sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op.sys_clk_div); -} - -static int smiapp_pll_try(struct smiapp_sensor *sensor, - struct smiapp_pll *pll) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll_limits lim = { - .min_pre_pll_clk_div = SMIA_LIM(sensor, MIN_PRE_PLL_CLK_DIV), - .max_pre_pll_clk_div = SMIA_LIM(sensor, MAX_PRE_PLL_CLK_DIV), - .min_pll_ip_freq_hz = SMIA_LIM(sensor, MIN_PLL_IP_FREQ_HZ), - .max_pll_ip_freq_hz = SMIA_LIM(sensor, MAX_PLL_IP_FREQ_HZ), - .min_pll_multiplier = SMIA_LIM(sensor, MIN_PLL_MULTIPLIER), - .max_pll_multiplier = SMIA_LIM(sensor, MAX_PLL_MULTIPLIER), - .min_pll_op_freq_hz = SMIA_LIM(sensor, MIN_PLL_OP_FREQ_HZ), - .max_pll_op_freq_hz = SMIA_LIM(sensor, MAX_PLL_OP_FREQ_HZ), - - .op.min_sys_clk_div = SMIA_LIM(sensor, MIN_OP_SYS_CLK_DIV), - .op.max_sys_clk_div = SMIA_LIM(sensor, MAX_OP_SYS_CLK_DIV), - .op.min_pix_clk_div = SMIA_LIM(sensor, MIN_OP_PIX_CLK_DIV), - .op.max_pix_clk_div = SMIA_LIM(sensor, MAX_OP_PIX_CLK_DIV), - .op.min_sys_clk_freq_hz = SMIA_LIM(sensor, MIN_OP_SYS_CLK_FREQ_HZ), - .op.max_sys_clk_freq_hz = SMIA_LIM(sensor, MAX_OP_SYS_CLK_FREQ_HZ), - .op.min_pix_clk_freq_hz = SMIA_LIM(sensor, MIN_OP_PIX_CLK_FREQ_HZ), - .op.max_pix_clk_freq_hz = SMIA_LIM(sensor, MAX_OP_PIX_CLK_FREQ_HZ), - - .vt.min_sys_clk_div = SMIA_LIM(sensor, MIN_VT_SYS_CLK_DIV), - .vt.max_sys_clk_div = SMIA_LIM(sensor, MAX_VT_SYS_CLK_DIV), - .vt.min_pix_clk_div = SMIA_LIM(sensor, MIN_VT_PIX_CLK_DIV), - .vt.max_pix_clk_div = SMIA_LIM(sensor, MAX_VT_PIX_CLK_DIV), - .vt.min_sys_clk_freq_hz = SMIA_LIM(sensor, MIN_VT_SYS_CLK_FREQ_HZ), - .vt.max_sys_clk_freq_hz = SMIA_LIM(sensor, MAX_VT_SYS_CLK_FREQ_HZ), - .vt.min_pix_clk_freq_hz = SMIA_LIM(sensor, MIN_VT_PIX_CLK_FREQ_HZ), - .vt.max_pix_clk_freq_hz = SMIA_LIM(sensor, MAX_VT_PIX_CLK_FREQ_HZ), - - .min_line_length_pck_bin = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN), - .min_line_length_pck = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK), - }; - - return smiapp_pll_calculate(&client->dev, &lim, pll); -} - -static int smiapp_pll_update(struct smiapp_sensor *sensor) -{ - struct smiapp_pll *pll = &sensor->pll; - int rval; - - pll->binning_horizontal = sensor->binning_horizontal; - pll->binning_vertical = sensor->binning_vertical; - pll->link_freq = - sensor->link_freq->qmenu_int[sensor->link_freq->val]; - pll->scale_m = sensor->scale_m; - pll->bits_per_pixel = sensor->csi_format->compressed; - - rval = smiapp_pll_try(sensor, pll); - if (rval < 0) - return rval; - - __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray, - pll->pixel_rate_pixel_array); - __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi); - - return 0; -} - - -/* - * - * V4L2 Controls handling - * - */ - -static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *ctrl = sensor->exposure; - int max; - - max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val - - SMIA_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); - - __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); -} - -/* - * Order matters. - * - * 1. Bits-per-pixel, descending. - * 2. Bits-per-pixel compressed, descending. - * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel - * orders must be defined. - */ -static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { - { MEDIA_BUS_FMT_SGRBG16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG16_1X16, 16, 16, SMIAPP_PIXEL_ORDER_GBRG, }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14, 14, SMIAPP_PIXEL_ORDER_GBRG, }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, - { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, -}; - -static const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; - -#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ - - (unsigned long)smiapp_csi_data_formats) \ - / sizeof(*smiapp_csi_data_formats)) - -static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int flip = 0; - - if (sensor->hflip) { - if (sensor->hflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - } - - flip ^= sensor->hvflip_inv_mask; - - dev_dbg(&client->dev, "flip %d\n", flip); - return sensor->default_pixel_order ^ flip; -} - -static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int csi_format_idx = - to_csi_format_idx(sensor->csi_format) & ~3; - unsigned int internal_csi_format_idx = - to_csi_format_idx(sensor->internal_csi_format) & ~3; - unsigned int pixel_order = smiapp_pixel_order(sensor); - - sensor->mbus_frame_fmts = - sensor->default_mbus_frame_fmts << pixel_order; - sensor->csi_format = - &smiapp_csi_data_formats[csi_format_idx + pixel_order]; - sensor->internal_csi_format = - &smiapp_csi_data_formats[internal_csi_format_idx - + pixel_order]; - - BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order - >= ARRAY_SIZE(smiapp_csi_data_formats)); - - dev_dbg(&client->dev, "new pixel order %s\n", - pixel_order_str[pixel_order]); -} - -static const char * const smiapp_test_patterns[] = { - "Disabled", - "Solid Colour", - "Eight Vertical Colour Bars", - "Colour Bars With Fade to Grey", - "Pseudorandom Sequence (PN9)", -}; - -static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct smiapp_sensor *sensor = - container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) - ->sensor; - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int pm_status; - u32 orient = 0; - unsigned int i; - int exposure; - int rval; - - switch (ctrl->id) { - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - if (sensor->streaming) - return -EBUSY; - - if (sensor->hflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; - - if (sensor->vflip->val) - orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; - - orient ^= sensor->hvflip_inv_mask; - - smiapp_update_mbus_formats(sensor); - - break; - case V4L2_CID_VBLANK: - exposure = sensor->exposure->val; - - __smiapp_update_exposure_limits(sensor); - - if (exposure > sensor->exposure->maximum) { - sensor->exposure->val = sensor->exposure->maximum; - rval = smiapp_set_ctrl(sensor->exposure); - if (rval < 0) - return rval; - } - - break; - case V4L2_CID_LINK_FREQ: - if (sensor->streaming) - return -EBUSY; - - rval = smiapp_pll_update(sensor); - if (rval) - return rval; - - return 0; - case V4L2_CID_TEST_PATTERN: - for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) - v4l2_ctrl_activate( - sensor->test_data[i], - ctrl->val == - V4L2_SMIAPP_TEST_PATTERN_MODE_SOLID_COLOUR); - - break; - } - - pm_status = pm_runtime_get_if_active(&client->dev, true); - if (!pm_status) - return 0; - - switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - rval = smiapp_write( - sensor, - SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); - - break; - case V4L2_CID_EXPOSURE: - rval = smiapp_write( - sensor, - SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); - - break; - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - rval = smiapp_write(sensor, SMIAPP_REG_U8_IMAGE_ORIENTATION, - orient); - - break; - case V4L2_CID_VBLANK: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + ctrl->val); - - break; - case V4L2_CID_HBLANK: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + ctrl->val); - - break; - case V4L2_CID_TEST_PATTERN: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TEST_PATTERN_MODE, ctrl->val); - - break; - case V4L2_CID_TEST_PATTERN_RED: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TEST_DATA_RED, ctrl->val); - - break; - case V4L2_CID_TEST_PATTERN_GREENR: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TEST_DATA_GREENR, ctrl->val); - - break; - case V4L2_CID_TEST_PATTERN_BLUE: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TEST_DATA_BLUE, ctrl->val); - - break; - case V4L2_CID_TEST_PATTERN_GREENB: - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TEST_DATA_GREENB, ctrl->val); - - break; - case V4L2_CID_PIXEL_RATE: - /* For v4l2_ctrl_s_ctrl_int64() used internally. */ - rval = 0; - - break; - default: - rval = -EINVAL; - } - - if (pm_status > 0) { - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - } - - return rval; -} - -static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { - .s_ctrl = smiapp_set_ctrl, -}; - -static int smiapp_init_controls(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); - if (rval) - return rval; - - sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; - - sensor->analog_gain = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_ANALOGUE_GAIN, - SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MIN), - SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MAX), - max(SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_STEP), 1U), - SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MIN)); - - /* Exposure limits will be updated soon, use just something here. */ - sensor->exposure = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_EXPOSURE, 0, 0, 1, 0); - - sensor->hflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - sensor->vblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_VBLANK, 0, 1, 1, 0); - - if (sensor->vblank) - sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->hblank = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_HBLANK, 0, 1, 1, 0); - - if (sensor->hblank) - sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; - - sensor->pixel_rate_parray = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); - - v4l2_ctrl_new_std_menu_items(&sensor->pixel_array->ctrl_handler, - &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(smiapp_test_patterns) - 1, - 0, 0, smiapp_test_patterns); - - if (sensor->pixel_array->ctrl_handler.error) { - dev_err(&client->dev, - "pixel array controls initialization failed (%d)\n", - sensor->pixel_array->ctrl_handler.error); - return sensor->pixel_array->ctrl_handler.error; - } - - sensor->pixel_array->sd.ctrl_handler = - &sensor->pixel_array->ctrl_handler; - - v4l2_ctrl_cluster(2, &sensor->hflip); - - rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); - if (rval) - return rval; - - sensor->src->ctrl_handler.lock = &sensor->mutex; - - sensor->pixel_rate_csi = v4l2_ctrl_new_std( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); - - if (sensor->src->ctrl_handler.error) { - dev_err(&client->dev, - "src controls initialization failed (%d)\n", - sensor->src->ctrl_handler.error); - return sensor->src->ctrl_handler.error; - } - - sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler; - - return 0; -} - -/* - * For controls that require information on available media bus codes - * and linke frequencies. - */ -static int smiapp_init_late_controls(struct smiapp_sensor *sensor) -{ - unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ - sensor->csi_format->compressed - sensor->compressed_min_bpp]; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { - int max_value = (1 << sensor->csi_format->width) - 1; - - sensor->test_data[i] = v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, - &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, - 0, max_value, 1, max_value); - } - - sensor->link_freq = v4l2_ctrl_new_int_menu( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), - __ffs(*valid_link_freqs), sensor->hwcfg->op_sys_clock); - - return sensor->src->ctrl_handler.error; -} - -static void smiapp_free_controls(struct smiapp_sensor *sensor) -{ - unsigned int i; - - for (i = 0; i < sensor->ssds_used; i++) - v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); -} - -static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_pll *pll = &sensor->pll; - u8 compressed_max_bpp = 0; - unsigned int type, n; - unsigned int i, pixel_order; - int rval; - - rval = smiapp_read( - sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); - if (rval) - return rval; - - dev_dbg(&client->dev, "data_format_model_type %d\n", type); - - rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, - &pixel_order); - if (rval) - return rval; - - if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { - dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); - return -EINVAL; - } - - dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, - pixel_order_str[pixel_order]); - - switch (type) { - case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; - break; - case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: - n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; - break; - default: - return -EINVAL; - } - - sensor->default_pixel_order = pixel_order; - sensor->mbus_frame_fmts = 0; - - for (i = 0; i < n; i++) { - unsigned int fmt, j; - - rval = smiapp_read( - sensor, - SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); - if (rval) - return rval; - - dev_dbg(&client->dev, "%u: bpp %u, compressed %u\n", - i, fmt >> 8, (u8)fmt); - - for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { - const struct smiapp_csi_data_format *f = - &smiapp_csi_data_formats[j]; - - if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) - continue; - - if (f->width != fmt >> 8 || f->compressed != (u8)fmt) - continue; - - dev_dbg(&client->dev, "jolly good! %d\n", j); - - sensor->default_mbus_frame_fmts |= 1 << j; - } - } - - /* Figure out which BPP values can be used with which formats. */ - pll->binning_horizontal = 1; - pll->binning_vertical = 1; - pll->scale_m = sensor->scale_m; - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - sensor->compressed_min_bpp = - min(smiapp_csi_data_formats[i].compressed, - sensor->compressed_min_bpp); - compressed_max_bpp = - max(smiapp_csi_data_formats[i].compressed, - compressed_max_bpp); - } - - sensor->valid_link_freqs = devm_kcalloc( - &client->dev, - compressed_max_bpp - sensor->compressed_min_bpp + 1, - sizeof(*sensor->valid_link_freqs), GFP_KERNEL); - if (!sensor->valid_link_freqs) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - const struct smiapp_csi_data_format *f = - &smiapp_csi_data_formats[i]; - unsigned long *valid_link_freqs = - &sensor->valid_link_freqs[ - f->compressed - sensor->compressed_min_bpp]; - unsigned int j; - - if (!(sensor->default_mbus_frame_fmts & 1 << i)) - continue; - - pll->bits_per_pixel = f->compressed; - - for (j = 0; sensor->hwcfg->op_sys_clock[j]; j++) { - pll->link_freq = sensor->hwcfg->op_sys_clock[j]; - - rval = smiapp_pll_try(sensor, pll); - dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n", - pll->link_freq, pll->bits_per_pixel, - rval ? "not ok" : "ok"); - if (rval) - continue; - - set_bit(j, valid_link_freqs); - } - - if (!*valid_link_freqs) { - dev_info(&client->dev, - "no valid link frequencies for %u bpp\n", - f->compressed); - sensor->default_mbus_frame_fmts &= ~BIT(i); - continue; - } - - if (!sensor->csi_format - || f->width > sensor->csi_format->width - || (f->width == sensor->csi_format->width - && f->compressed > sensor->csi_format->compressed)) { - sensor->csi_format = f; - sensor->internal_csi_format = f; - } - } - - if (!sensor->csi_format) { - dev_err(&client->dev, "no supported mbus code found\n"); - return -EINVAL; - } - - smiapp_update_mbus_formats(sensor); - - return 0; -} - -static void smiapp_update_blanking(struct smiapp_sensor *sensor) -{ - struct v4l2_ctrl *vblank = sensor->vblank; - struct v4l2_ctrl *hblank = sensor->hblank; - uint16_t min_fll, max_fll, min_llp, max_llp, min_lbp; - int min, max; - - if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { - min_fll = SMIA_LIM(sensor, MIN_FRAME_LENGTH_LINES_BIN); - max_fll = SMIA_LIM(sensor, MAX_FRAME_LENGTH_LINES_BIN); - min_llp = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN); - max_llp = SMIA_LIM(sensor, MAX_LINE_LENGTH_PCK_BIN); - min_lbp = SMIA_LIM(sensor, MIN_LINE_BLANKING_PCK_BIN); - } else { - min_fll = SMIA_LIM(sensor, MIN_FRAME_LENGTH_LINES); - max_fll = SMIA_LIM(sensor, MAX_FRAME_LENGTH_LINES); - min_llp = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK); - max_llp = SMIA_LIM(sensor, MAX_LINE_LENGTH_PCK); - min_lbp = SMIA_LIM(sensor, MIN_LINE_BLANKING_PCK); - } - - min = max_t(int, - SMIA_LIM(sensor, MIN_FRAME_BLANKING_LINES), - min_fll - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); - max = max_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; - - __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); - - min = max_t(int, - min_llp - - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, - min_lbp); - max = max_llp - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; - - __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); - - __smiapp_update_exposure_limits(sensor); -} - -static int smiapp_pll_blanking_update(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - rval = smiapp_pll_update(sensor); - if (rval < 0) - return rval; - - /* Output from pixel array, including blanking */ - smiapp_update_blanking(sensor); - - dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); - dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); - - dev_dbg(&client->dev, "real timeperframe\t100/%d\n", - sensor->pll.pixel_rate_pixel_array / - ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - + sensor->hblank->val) * - (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - + sensor->vblank->val) / 100)); - - return 0; -} - -/* - * - * SMIA++ NVM handling - * - */ - -static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm, - u8 *status) -{ - unsigned int i; - int rval; - u32 s; - - *status = 0; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); - if (rval) - return rval; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, - SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN); - if (rval) - return rval; - - rval = smiapp_read(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, - &s); - if (rval) - return rval; - - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) { - *status = s; - return -ENODATA; - } - - if (SMIA_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & - SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL) { - for (i = 1000; i > 0; i--) { - if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) - break; - - rval = smiapp_read( - sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, - &s); - - if (rval) - return rval; - } - - if (!i) - return -ETIMEDOUT; - } - - for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { - u32 v; - - rval = smiapp_read(sensor, - SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, - &v); - if (rval) - return rval; - - *nvm++ = v; - } - - return 0; -} - -static int smiapp_read_nvm(struct smiapp_sensor *sensor, unsigned char *nvm, - size_t nvm_size) -{ - u8 status = 0; - u32 p; - int rval = 0, rval2; - - for (p = 0; p < nvm_size / SMIAPP_NVM_PAGE_SIZE && !rval; p++) { - rval = smiapp_read_nvm_page(sensor, p, nvm, &status); - nvm += SMIAPP_NVM_PAGE_SIZE; - } - - if (rval == -ENODATA && - status & SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE) - rval = 0; - - rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); - if (rval < 0) - return rval; - else - return rval2 ?: p * SMIAPP_NVM_PAGE_SIZE; -} - -/* - * - * SMIA++ CCI address control - * - */ -static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - u32 val; - - client->addr = sensor->hwcfg->i2c_addr_dfl; - - rval = smiapp_write(sensor, - SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, - sensor->hwcfg->i2c_addr_alt << 1); - if (rval) - return rval; - - client->addr = sensor->hwcfg->i2c_addr_alt; - - /* verify addr change went ok */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); - if (rval) - return rval; - - if (val != sensor->hwcfg->i2c_addr_alt << 1) - return -ENODEV; - - return 0; -} - -/* - * - * SMIA++ Mode Control - * - */ -static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) -{ - struct smiapp_flash_strobe_parms *strobe_setup; - unsigned int ext_freq = sensor->hwcfg->ext_clk; - u32 tmp; - u32 strobe_adjustment; - u32 strobe_width_high_rs; - int rval; - - strobe_setup = sensor->hwcfg->strobe_setup; - - /* - * How to calculate registers related to strobe length. Please - * do not change, or if you do at least know what you're - * doing. :-) - * - * Sakari Ailus <sakari.ailus@iki.fi> 2010-10-25 - * - * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl - * / EXTCLK freq [Hz]) * flash_strobe_adjustment - * - * tFlash_strobe_width_ctrl E N, [1 - 0xffff] - * flash_strobe_adjustment E N, [1 - 0xff] - * - * The formula above is written as below to keep it on one - * line: - * - * l / 10^6 = w / e * a - * - * Let's mark w * a by x: - * - * x = w * a - * - * Thus, we get: - * - * x = l * e / 10^6 - * - * The strobe width must be at least as long as requested, - * thus rounding upwards is needed. - * - * x = (l * e + 10^6 - 1) / 10^6 - * ----------------------------- - * - * Maximum possible accuracy is wanted at all times. Thus keep - * a as small as possible. - * - * Calculate a, assuming maximum w, with rounding upwards: - * - * a = (x + (2^16 - 1) - 1) / (2^16 - 1) - * ------------------------------------- - * - * Thus, we also get w, with that a, with rounding upwards: - * - * w = (x + a - 1) / a - * ------------------- - * - * To get limits: - * - * x E [1, (2^16 - 1) * (2^8 - 1)] - * - * Substituting maximum x to the original formula (with rounding), - * the maximum l is thus - * - * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 - * - * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e - * -------------------------------------------------- - * - * flash_strobe_length must be clamped between 1 and - * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. - * - * Then, - * - * flash_strobe_adjustment = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) - * - * tFlash_strobe_width_ctrl = ((flash_strobe_length * - * EXTCLK freq + 10^6 - 1) / 10^6 + - * flash_strobe_adjustment - 1) / flash_strobe_adjustment - */ - tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - - 1000000 + 1, ext_freq); - strobe_setup->strobe_width_high_us = - clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); - - tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + - 1000000 - 1), 1000000ULL); - strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); - strobe_width_high_rs = (tmp + strobe_adjustment - 1) / - strobe_adjustment; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, - strobe_setup->mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, - strobe_adjustment); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, - strobe_width_high_rs); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, - strobe_setup->strobe_delay); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, - strobe_setup->stobe_start_point); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, - strobe_setup->trigger); - -out: - sensor->hwcfg->strobe_setup->trigger = 0; - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Power management - */ - -static int smiapp_power_on(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - /* - * The sub-device related to the I2C device is always the - * source one, i.e. ssds[0]. - */ - struct smiapp_sensor *sensor = - container_of(ssd, struct smiapp_sensor, ssds[0]); - unsigned int sleep; - int rval; - - rval = regulator_enable(sensor->vana); - if (rval) { - dev_err(&client->dev, "failed to enable vana regulator\n"); - return rval; - } - usleep_range(1000, 1000); - - rval = clk_prepare_enable(sensor->ext_clk); - if (rval < 0) { - dev_dbg(&client->dev, "failed to enable xclk\n"); - goto out_xclk_fail; - } - usleep_range(1000, 1000); - - gpiod_set_value(sensor->xshutdown, 1); - - sleep = SMIAPP_RESET_DELAY(sensor->hwcfg->ext_clk); - usleep_range(sleep, sleep); - - /* - * Failures to respond to the address change command have been noticed. - * Those failures seem to be caused by the sensor requiring a longer - * boot time than advertised. An additional 10ms delay seems to work - * around the issue, but the SMIA++ I2C write retry hack makes the delay - * unnecessary. The failures need to be investigated to find a proper - * fix, and a delay will likely need to be added here if the I2C write - * retry hack is reverted before the root cause of the boot time issue - * is found. - */ - - if (sensor->hwcfg->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - if (rval < 0) { - dev_err(&client->dev, "software reset failed\n"); - goto out_cci_addr_fail; - } - - if (sensor->hwcfg->i2c_addr_alt) { - rval = smiapp_change_cci_addr(sensor); - if (rval) { - dev_err(&client->dev, "cci address change error\n"); - goto out_cci_addr_fail; - } - } - - rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, - SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); - if (rval) { - dev_err(&client->dev, "compression mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, - sensor->hwcfg->ext_clk / (1000000 / (1 << 8))); - if (rval) { - dev_err(&client->dev, "extclk frequency set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, - sensor->hwcfg->lanes - 1); - if (rval) { - dev_err(&client->dev, "csi lane mode set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, - SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); - if (rval) { - dev_err(&client->dev, "fast standby set failed\n"); - goto out_cci_addr_fail; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, - sensor->hwcfg->csi_signalling_mode); - if (rval) { - dev_err(&client->dev, "csi signalling mode set failed\n"); - goto out_cci_addr_fail; - } - - /* DPHY control done by sensor based on requested link rate */ - rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, - SMIAPP_DPHY_CTRL_UI); - if (rval < 0) - goto out_cci_addr_fail; - - rval = smiapp_call_quirk(sensor, post_poweron); - if (rval) { - dev_err(&client->dev, "post_poweron quirks failed\n"); - goto out_cci_addr_fail; - } - - return 0; - -out_cci_addr_fail: - gpiod_set_value(sensor->xshutdown, 0); - clk_disable_unprepare(sensor->ext_clk); - -out_xclk_fail: - regulator_disable(sensor->vana); - - return rval; -} - -static int smiapp_power_off(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct smiapp_sensor *sensor = - container_of(ssd, struct smiapp_sensor, ssds[0]); - - /* - * Currently power/clock to lens are enable/disabled separately - * but they are essentially the same signals. So if the sensor is - * powered off while the lens is powered on the sensor does not - * really see a power off and next time the cci address change - * will fail. So do a soft reset explicitly here. - */ - if (sensor->hwcfg->i2c_addr_alt) - smiapp_write(sensor, - SMIAPP_REG_U8_SOFTWARE_RESET, - SMIAPP_SOFTWARE_RESET); - - gpiod_set_value(sensor->xshutdown, 0); - clk_disable_unprepare(sensor->ext_clk); - usleep_range(5000, 5000); - regulator_disable(sensor->vana); - sensor->streaming = false; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * Video stream management - */ - -static int smiapp_start_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; - int rval; - - mutex_lock(&sensor->mutex); - - rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, - (sensor->csi_format->width << 8) | - sensor->csi_format->compressed); - if (rval) - goto out; - - /* Binning configuration */ - if (sensor->binning_horizontal == 1 && - sensor->binning_vertical == 1) { - binning_mode = 0; - } else { - u8 binning_type = - (sensor->binning_horizontal << 4) - | sensor->binning_vertical; - - rval = smiapp_write( - sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); - if (rval < 0) - goto out; - - binning_mode = 1; - } - rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); - if (rval < 0) - goto out; - - /* Set up PLL */ - rval = smiapp_pll_configure(sensor); - if (rval) - goto out; - - /* Analog crop start coordinates */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); - if (rval < 0) - goto out; - - /* Analog crop end coordinates */ - rval = smiapp_write( - sensor, SMIAPP_REG_U16_X_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_Y_ADDR_END, - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); - if (rval < 0) - goto out; - - /* - * Output from pixel array, including blanking, is set using - * controls below. No need to set here. - */ - - /* Digital crop */ - if (SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].left); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, - sensor->scaler->crop[SMIAPP_PAD_SINK].top); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler->crop[SMIAPP_PAD_SINK].width); - if (rval < 0) - goto out; - - rval = smiapp_write( - sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler->crop[SMIAPP_PAD_SINK].height); - if (rval < 0) - goto out; - } - - /* Scaling */ - if (SMIA_LIM(sensor, SCALING_CAPABILITY) - != SMIAPP_SCALING_CAPABILITY_NONE) { - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, - sensor->scaling_mode); - if (rval < 0) - goto out; - - rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, - sensor->scale_m); - if (rval < 0) - goto out; - } - - /* Output size from sensor */ - rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].width); - if (rval < 0) - goto out; - rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, - sensor->src->crop[SMIAPP_PAD_SRC].height); - if (rval < 0) - goto out; - - if ((SMIA_LIM(sensor, FLASH_MODE_CAPABILITY) & - (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | - SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && - sensor->hwcfg->strobe_setup != NULL && - sensor->hwcfg->strobe_setup->trigger != 0) { - rval = smiapp_setup_flash_strobe(sensor); - if (rval) - goto out; - } - - rval = smiapp_call_quirk(sensor, pre_streamon); - if (rval) { - dev_err(&client->dev, "pre_streamon quirks failed\n"); - goto out; - } - - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_STREAMING); - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static int smiapp_stop_streaming(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - mutex_lock(&sensor->mutex); - rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, - SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); - if (rval) - goto out; - - rval = smiapp_call_quirk(sensor, post_streamoff); - if (rval) - dev_err(&client->dev, "post_streamoff quirks failed\n"); - -out: - mutex_unlock(&sensor->mutex); - return rval; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static int smiapp_pm_get_init(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) { - if (rval != -EBUSY && rval != -EAGAIN) - pm_runtime_set_active(&client->dev); - pm_runtime_put_noidle(&client->dev); - - return rval; - } else if (!rval) { - rval = v4l2_ctrl_handler_setup(&sensor->pixel_array-> - ctrl_handler); - if (rval) - return rval; - - return v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); - } - - return 0; -} - -static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - if (sensor->streaming == enable) - return 0; - - if (!enable) { - smiapp_stop_streaming(sensor); - sensor->streaming = false; - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - - return 0; - } - - rval = smiapp_pm_get_init(sensor); - if (rval) - return rval; - - sensor->streaming = true; - - rval = smiapp_start_streaming(sensor); - if (rval < 0) { - sensor->streaming = false; - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - } - - return rval; -} - -static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - int idx = -1; - int rval = -EINVAL; - - mutex_lock(&sensor->mutex); - - dev_err(&client->dev, "subdev %s, pad %d, index %d\n", - subdev->name, code->pad, code->index); - - if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { - if (code->index) - goto out; - - code->code = sensor->internal_csi_format->code; - rval = 0; - goto out; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i)) - idx++; - - if (idx == code->index) { - code->code = smiapp_csi_data_formats[i].code; - dev_err(&client->dev, "found index %d, i %d, code %x\n", - code->index, i, code->code); - rval = 0; - break; - } - } - -out: - mutex_unlock(&sensor->mutex); - - return rval; -} - -static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, - unsigned int pad) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) - return sensor->csi_format->code; - else - return sensor->internal_csi_format->code; -} - -static int __smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(subdev, cfg, - fmt->pad); - } else { - struct v4l2_rect *r; - - if (fmt->pad == ssd->source_pad) - r = &ssd->crop[ssd->source_pad]; - else - r = &ssd->sink_fmt; - - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width = r->width; - fmt->format.height = r->height; - fmt->format.field = V4L2_FIELD_NONE; - } - - return 0; -} - -static int smiapp_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_format(subdev, cfg, fmt); - mutex_unlock(&sensor->mutex); - - return rval; -} - -static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_rect **crops, - struct v4l2_rect **comps, int which) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - unsigned int i; - - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = &ssd->crop[i]; - if (comps) - *comps = &ssd->compose; - } else { - if (crops) { - for (i = 0; i < subdev->entity.num_pads; i++) { - crops[i] = v4l2_subdev_get_try_crop(subdev, cfg, i); - BUG_ON(!crops[i]); - } - } - if (comps) { - *comps = v4l2_subdev_get_try_compose(subdev, cfg, - SMIAPP_PAD_SINK); - BUG_ON(!*comps); - } - } -} - -/* Changes require propagation only on sink pad. */ -static void smiapp_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, int which, - int target) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, cfg, crops, &comp, which); - - switch (target) { - case V4L2_SEL_TGT_CROP: - comp->width = crops[SMIAPP_PAD_SINK]->width; - comp->height = crops[SMIAPP_PAD_SINK]->height; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (ssd == sensor->scaler) { - sensor->scale_m = - SMIA_LIM(sensor, SCALER_N_MIN); - sensor->scaling_mode = - SMIAPP_SCALING_MODE_NONE; - } else if (ssd == sensor->binner) { - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - } - } - fallthrough; - case V4L2_SEL_TGT_COMPOSE: - *crops[SMIAPP_PAD_SRC] = *comp; - break; - default: - BUG(); - } -} - -static const struct smiapp_csi_data_format -*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i) - && smiapp_csi_data_formats[i].code == code) - return &smiapp_csi_data_formats[i]; - } - - return sensor->csi_format; -} - -static int smiapp_set_format_source(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - const struct smiapp_csi_data_format *csi_format, - *old_csi_format = sensor->csi_format; - unsigned long *valid_link_freqs; - u32 code = fmt->format.code; - unsigned int i; - int rval; - - rval = __smiapp_get_format(subdev, cfg, fmt); - if (rval) - return rval; - - /* - * Media bus code is changeable on src subdev's source pad. On - * other source pads we just get format here. - */ - if (subdev != &sensor->src->sd) - return 0; - - csi_format = smiapp_validate_csi_data_format(sensor, code); - - fmt->format.code = csi_format->code; - - if (fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return 0; - - sensor->csi_format = csi_format; - - if (csi_format->width != old_csi_format->width) - for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) - __v4l2_ctrl_modify_range( - sensor->test_data[i], 0, - (1 << csi_format->width) - 1, 1, 0); - - if (csi_format->compressed == old_csi_format->compressed) - return 0; - - valid_link_freqs = - &sensor->valid_link_freqs[sensor->csi_format->compressed - - sensor->compressed_min_bpp]; - - __v4l2_ctrl_modify_range( - sensor->link_freq, 0, - __fls(*valid_link_freqs), ~*valid_link_freqs, - __ffs(*valid_link_freqs)); - - return smiapp_pll_update(sensor); -} - -static int smiapp_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *crops[SMIAPP_PADS]; - - mutex_lock(&sensor->mutex); - - if (fmt->pad == ssd->source_pad) { - int rval; - - rval = smiapp_set_format_source(subdev, cfg, fmt); - - mutex_unlock(&sensor->mutex); - - return rval; - } - - /* Sink pad. Width and height are changeable here. */ - fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); - fmt->format.width &= ~1; - fmt->format.height &= ~1; - fmt->format.field = V4L2_FIELD_NONE; - - fmt->format.width = - clamp(fmt->format.width, - SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE), - SMIA_LIM(sensor, MAX_X_OUTPUT_SIZE)); - fmt->format.height = - clamp(fmt->format.height, - SMIA_LIM(sensor, MIN_Y_OUTPUT_SIZE), - SMIA_LIM(sensor, MAX_Y_OUTPUT_SIZE)); - - smiapp_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); - - crops[ssd->sink_pad]->left = 0; - crops[ssd->sink_pad]->top = 0; - crops[ssd->sink_pad]->width = fmt->format.width; - crops[ssd->sink_pad]->height = fmt->format.height; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ssd->sink_fmt = *crops[ssd->sink_pad]; - smiapp_propagate(subdev, cfg, fmt->which, - V4L2_SEL_TGT_CROP); - - mutex_unlock(&sensor->mutex); - - return 0; -} - -/* - * Calculate goodness of scaled image size compared to expected image - * size and flags provided. - */ -#define SCALING_GOODNESS 100000 -#define SCALING_GOODNESS_EXTREME 100000000 -static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, - int h, int ask_h, u32 flags) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - int val = 0; - - w &= ~1; - ask_w &= ~1; - h &= ~1; - ask_h &= ~1; - - if (flags & V4L2_SEL_FLAG_GE) { - if (w < ask_w) - val -= SCALING_GOODNESS; - if (h < ask_h) - val -= SCALING_GOODNESS; - } - - if (flags & V4L2_SEL_FLAG_LE) { - if (w > ask_w) - val -= SCALING_GOODNESS; - if (h > ask_h) - val -= SCALING_GOODNESS; - } - - val -= abs(w - ask_w); - val -= abs(h - ask_h); - - if (w < SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE)) - val -= SCALING_GOODNESS_EXTREME; - - dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", - w, ask_w, h, ask_h, val); - - return val; -} - -static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - unsigned int binh = 1, binv = 1; - int best = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width, sel->r.width, - crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / sensor->binning_subtypes[i].horizontal, - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / sensor->binning_subtypes[i].vertical, - sel->r.height, sel->flags); - - if (this > best) { - binh = sensor->binning_subtypes[i].horizontal; - binv = sensor->binning_subtypes[i].vertical; - best = this; - } - } - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->binning_vertical = binv; - sensor->binning_horizontal = binh; - } - - sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; - sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; -} - -/* - * Calculate best scaling ratio and mode for given output resolution. - * - * Try all of these: horizontal ratio, vertical ratio and smallest - * size possible (horizontally). - * - * Also try whether horizontal scaler or full scaler gives a better - * result. - */ -static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - u32 min, max, a, b, max_m; - u32 scale_m = SMIA_LIM(sensor, SCALER_N_MIN); - int mode = SMIAPP_SCALING_MODE_HORIZONTAL; - u32 try[4]; - u32 ntry = 0; - unsigned int i; - int best = INT_MIN; - - sel->r.width = min_t(unsigned int, sel->r.width, - crops[SMIAPP_PAD_SINK]->width); - sel->r.height = min_t(unsigned int, sel->r.height, - crops[SMIAPP_PAD_SINK]->height); - - a = crops[SMIAPP_PAD_SINK]->width - * SMIA_LIM(sensor, SCALER_N_MIN) / sel->r.width; - b = crops[SMIAPP_PAD_SINK]->height - * SMIA_LIM(sensor, SCALER_N_MIN) / sel->r.height; - max_m = crops[SMIAPP_PAD_SINK]->width - * SMIA_LIM(sensor, SCALER_N_MIN) - / SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE); - - a = clamp(a, SMIA_LIM(sensor, SCALER_M_MIN), - SMIA_LIM(sensor, SCALER_M_MAX)); - b = clamp(b, SMIA_LIM(sensor, SCALER_M_MIN), - SMIA_LIM(sensor, SCALER_M_MAX)); - max_m = clamp(max_m, SMIA_LIM(sensor, SCALER_M_MIN), - SMIA_LIM(sensor, SCALER_M_MAX)); - - dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); - - min = min(max_m, min(a, b)); - max = min(max_m, max(a, b)); - - try[ntry] = min; - ntry++; - if (min != max) { - try[ntry] = max; - ntry++; - } - if (max != max_m) { - try[ntry] = min + 1; - ntry++; - if (min != max) { - try[ntry] = max + 1; - ntry++; - } - } - - for (i = 0; i < ntry; i++) { - int this = scaling_goodness( - subdev, - crops[SMIAPP_PAD_SINK]->width - / try[i] - * SMIA_LIM(sensor, SCALER_N_MIN), - sel->r.width, - crops[SMIAPP_PAD_SINK]->height, - sel->r.height, - sel->flags); - - dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_HORIZONTAL; - best = this; - } - - if (SMIA_LIM(sensor, SCALING_CAPABILITY) - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - continue; - - this = scaling_goodness( - subdev, crops[SMIAPP_PAD_SINK]->width - / try[i] - * SMIA_LIM(sensor, SCALER_N_MIN), - sel->r.width, - crops[SMIAPP_PAD_SINK]->height - / try[i] - * SMIA_LIM(sensor, SCALER_N_MIN), - sel->r.height, - sel->flags); - - if (this > best) { - scale_m = try[i]; - mode = SMIAPP_SCALING_MODE_BOTH; - best = this; - } - } - - sel->r.width = - (crops[SMIAPP_PAD_SINK]->width - / scale_m - * SMIA_LIM(sensor, SCALER_N_MIN)) & ~1; - if (mode == SMIAPP_SCALING_MODE_BOTH) - sel->r.height = - (crops[SMIAPP_PAD_SINK]->height - / scale_m - * SMIA_LIM(sensor, SCALER_N_MIN)) - & ~1; - else - sel->r.height = crops[SMIAPP_PAD_SINK]->height; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sensor->scale_m = scale_m; - sensor->scaling_mode = mode; - } -} -/* We're only called on source pads. This function sets scaling. */ -static int smiapp_set_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - - smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); - - sel->r.top = 0; - sel->r.left = 0; - - if (ssd == sensor->binner) - smiapp_set_compose_binner(subdev, cfg, sel, crops, comp); - else - smiapp_set_compose_scaler(subdev, cfg, sel, crops, comp); - - *comp = sel->r; - smiapp_propagate(subdev, cfg, sel->which, V4L2_SEL_TGT_COMPOSE); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return smiapp_pll_blanking_update(sensor); - - return 0; -} - -static int __smiapp_sel_supported(struct v4l2_subdev *subdev, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - - /* We only implement crop in three places. */ - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_CROP_BOUNDS: - if (ssd == sensor->pixel_array - && sel->pad == SMIAPP_PA_PAD_SRC) - return 0; - if (ssd == sensor->src - && sel->pad == SMIAPP_PAD_SRC) - return 0; - if (ssd == sensor->scaler - && sel->pad == SMIAPP_PAD_SINK - && SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) - return 0; - return -EINVAL; - case V4L2_SEL_TGT_NATIVE_SIZE: - if (ssd == sensor->pixel_array - && sel->pad == SMIAPP_PA_PAD_SRC) - return 0; - return -EINVAL; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - if (sel->pad == ssd->source_pad) - return -EINVAL; - if (ssd == sensor->binner) - return 0; - if (ssd == sensor->scaler - && SMIA_LIM(sensor, SCALING_CAPABILITY) - != SMIAPP_SCALING_CAPABILITY_NONE) - return 0; - fallthrough; - default: - return -EINVAL; - } -} - -static int smiapp_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; - struct v4l2_rect _r; - - smiapp_get_crop_compose(subdev, cfg, crops, NULL, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - if (sel->pad == ssd->sink_pad) - src_size = &ssd->sink_fmt; - else - src_size = &ssd->compose; - } else { - if (sel->pad == ssd->sink_pad) { - _r.left = 0; - _r.top = 0; - _r.width = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) - ->width; - _r.height = v4l2_subdev_get_try_format(subdev, cfg, sel->pad) - ->height; - src_size = &_r; - } else { - src_size = v4l2_subdev_get_try_compose( - subdev, cfg, ssd->sink_pad); - } - } - - if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { - sel->r.left = 0; - sel->r.top = 0; - } - - sel->r.width = min(sel->r.width, src_size->width); - sel->r.height = min(sel->r.height, src_size->height); - - sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width); - sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height); - - *crops[sel->pad] = sel->r; - - if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) - smiapp_propagate(subdev, cfg, sel->which, - V4L2_SEL_TGT_CROP); - - return 0; -} - -static void smiapp_get_native_size(struct smiapp_subdev *ssd, - struct v4l2_rect *r) -{ - r->top = 0; - r->left = 0; - r->width = SMIA_LIM(ssd->sensor, X_ADDR_MAX) + 1; - r->height = SMIA_LIM(ssd->sensor, Y_ADDR_MAX) + 1; -} - -static int __smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); - struct v4l2_rect *comp, *crops[SMIAPP_PADS]; - struct v4l2_rect sink_fmt; - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - smiapp_get_crop_compose(subdev, cfg, crops, &comp, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - sink_fmt = ssd->sink_fmt; - } else { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(subdev, cfg, ssd->sink_pad); - - sink_fmt.left = 0; - sink_fmt.top = 0; - sink_fmt.width = fmt->width; - sink_fmt.height = fmt->height; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_NATIVE_SIZE: - if (ssd == sensor->pixel_array) - smiapp_get_native_size(ssd, &sel->r); - else if (sel->pad == ssd->sink_pad) - sel->r = sink_fmt; - else - sel->r = *comp; - break; - case V4L2_SEL_TGT_CROP: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - sel->r = *crops[sel->pad]; - break; - case V4L2_SEL_TGT_COMPOSE: - sel->r = *comp; - break; - } - - return 0; -} - -static int smiapp_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __smiapp_get_selection(subdev, cfg, sel); - mutex_unlock(&sensor->mutex); - - return rval; -} -static int smiapp_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int ret; - - ret = __smiapp_sel_supported(subdev, sel); - if (ret) - return ret; - - mutex_lock(&sensor->mutex); - - sel->r.left = max(0, sel->r.left & ~1); - sel->r.top = max(0, sel->r.top & ~1); - sel->r.width = SMIAPP_ALIGN_DIM(sel->r.width, sel->flags); - sel->r.height = SMIAPP_ALIGN_DIM(sel->r.height, sel->flags); - - sel->r.width = max_t(unsigned int, - SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE), - sel->r.width); - sel->r.height = max_t(unsigned int, - SMIA_LIM(sensor, MIN_Y_OUTPUT_SIZE), - sel->r.height); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - ret = smiapp_set_crop(subdev, cfg, sel); - break; - case V4L2_SEL_TGT_COMPOSE: - ret = smiapp_set_compose(subdev, cfg, sel); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&sensor->mutex); - return ret; -} - -static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - *frames = sensor->frame_skip; - return 0; -} - -static int smiapp_get_skip_top_lines(struct v4l2_subdev *subdev, u32 *lines) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - - *lines = sensor->image_start; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * sysfs attributes - */ - -static ssize_t -smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (!sensor->dev_init_done) - return -EBUSY; - - rval = smiapp_pm_get_init(sensor); - if (rval < 0) - return -ENODEV; - - rval = smiapp_read_nvm(sensor, buf, PAGE_SIZE); - if (rval < 0) { - pm_runtime_put(&client->dev); - dev_err(&client->dev, "nvm read failed\n"); - return -ENODEV; - } - - pm_runtime_mark_last_busy(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - - /* - * NVM is still way below a PAGE_SIZE, so we can safely - * assume this for now. - */ - return rval; -} -static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); - -static ssize_t -smiapp_sysfs_ident_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct smiapp_module_info *minfo = &sensor->minfo; - - return snprintf(buf, PAGE_SIZE, "%2.2x%4.4x%2.2x\n", - minfo->manufacturer_id, minfo->model_id, - minfo->revision_number_major) + 1; -} - -static DEVICE_ATTR(ident, S_IRUGO, smiapp_sysfs_ident_read, NULL); - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int smiapp_identify_module(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct smiapp_module_info *minfo = &sensor->minfo; - unsigned int i; - int rval = 0; - - minfo->name = SMIAPP_NAME; - - /* Module info */ - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, - &minfo->manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, - &minfo->model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, - &minfo->revision_number_major); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_REVISION_NUMBER_MINOR, - &minfo->revision_number_minor); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_YEAR, - &minfo->module_year); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_MODULE_DATE_MONTH, - &minfo->module_month); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, - &minfo->module_day); - - /* Sensor info */ - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, - &minfo->sensor_manufacturer_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U16_SENSOR_MODEL_ID, - &minfo->sensor_model_id); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, - &minfo->sensor_revision_number); - if (!rval) - rval = smiapp_read_8only(sensor, - SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, - &minfo->sensor_firmware_version); - - /* SMIA */ - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, - &minfo->smia_version); - if (!rval) - rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, - &minfo->smiapp_version); - - if (rval) { - dev_err(&client->dev, "sensor detection failed\n"); - return -ENODEV; - } - - dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", - minfo->manufacturer_id, minfo->model_id); - - dev_dbg(&client->dev, - "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", - minfo->revision_number_major, minfo->revision_number_minor, - minfo->module_year, minfo->module_month, minfo->module_day); - - dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", - minfo->sensor_manufacturer_id, minfo->sensor_model_id); - - dev_dbg(&client->dev, - "sensor revision 0x%2.2x firmware version 0x%2.2x\n", - minfo->sensor_revision_number, minfo->sensor_firmware_version); - - dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", - minfo->smia_version, minfo->smiapp_version); - - /* - * Some modules have bad data in the lvalues below. Hope the - * rvalues have better stuff. The lvalues are module - * parameters whereas the rvalues are sensor parameters. - */ - if (!minfo->manufacturer_id && !minfo->model_id) { - minfo->manufacturer_id = minfo->sensor_manufacturer_id; - minfo->model_id = minfo->sensor_model_id; - minfo->revision_number_major = minfo->sensor_revision_number; - } - - for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { - if (smiapp_module_idents[i].manufacturer_id - != minfo->manufacturer_id) - continue; - if (smiapp_module_idents[i].model_id != minfo->model_id) - continue; - if (smiapp_module_idents[i].flags - & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { - if (smiapp_module_idents[i].revision_number_major - < minfo->revision_number_major) - continue; - } else { - if (smiapp_module_idents[i].revision_number_major - != minfo->revision_number_major) - continue; - } - - minfo->name = smiapp_module_idents[i].name; - minfo->quirk = smiapp_module_idents[i].quirk; - break; - } - - if (i >= ARRAY_SIZE(smiapp_module_idents)) - dev_warn(&client->dev, - "no quirks for this module; let's hope it's fully compliant\n"); - - dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", - minfo->name, minfo->manufacturer_id, minfo->model_id, - minfo->revision_number_major); - - return 0; -} - -static const struct v4l2_subdev_ops smiapp_ops; -static const struct v4l2_subdev_internal_ops smiapp_internal_ops; -static const struct media_entity_operations smiapp_entity_ops; - -static int smiapp_register_subdev(struct smiapp_sensor *sensor, - struct smiapp_subdev *ssd, - struct smiapp_subdev *sink_ssd, - u16 source_pad, u16 sink_pad, u32 link_flags) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - int rval; - - if (!sink_ssd) - return 0; - - rval = media_entity_pads_init(&ssd->sd.entity, - ssd->npads, ssd->pads); - if (rval) { - dev_err(&client->dev, - "media_entity_pads_init failed\n"); - return rval; - } - - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &ssd->sd); - if (rval) { - dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); - return rval; - } - - rval = media_create_pad_link(&ssd->sd.entity, source_pad, - &sink_ssd->sd.entity, sink_pad, - link_flags); - if (rval) { - dev_err(&client->dev, - "media_create_pad_link failed\n"); - v4l2_device_unregister_subdev(&ssd->sd); - return rval; - } - - return 0; -} - -static void smiapp_unregistered(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - - for (i = 1; i < sensor->ssds_used; i++) - v4l2_device_unregister_subdev(&sensor->ssds[i].sd); -} - -static int smiapp_registered(struct v4l2_subdev *subdev) -{ - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval; - - if (sensor->scaler) { - rval = smiapp_register_subdev( - sensor, sensor->binner, sensor->scaler, - SMIAPP_PAD_SRC, SMIAPP_PAD_SINK, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (rval < 0) - return rval; - } - - rval = smiapp_register_subdev( - sensor, sensor->pixel_array, sensor->binner, - SMIAPP_PA_PAD_SRC, SMIAPP_PAD_SINK, - MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); - if (rval) - goto out_err; - - return 0; - -out_err: - smiapp_unregistered(subdev); - - return rval; -} - -static void smiapp_cleanup(struct smiapp_sensor *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - device_remove_file(&client->dev, &dev_attr_nvm); - device_remove_file(&client->dev, &dev_attr_ident); - - smiapp_free_controls(sensor); -} - -static void smiapp_create_subdev(struct smiapp_sensor *sensor, - struct smiapp_subdev *ssd, const char *name, - unsigned short num_pads) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - - if (!ssd) - return; - - if (ssd != sensor->src) - v4l2_subdev_init(&ssd->sd, &smiapp_ops); - - ssd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ssd->sensor = sensor; - - ssd->npads = num_pads; - ssd->source_pad = num_pads - 1; - - v4l2_i2c_subdev_set_name(&ssd->sd, client, sensor->minfo.name, name); - - smiapp_get_native_size(ssd, &ssd->sink_fmt); - - ssd->compose.width = ssd->sink_fmt.width; - ssd->compose.height = ssd->sink_fmt.height; - ssd->crop[ssd->source_pad] = ssd->compose; - ssd->pads[ssd->source_pad].flags = MEDIA_PAD_FL_SOURCE; - if (ssd != sensor->pixel_array) { - ssd->crop[ssd->sink_pad] = ssd->compose; - ssd->pads[ssd->sink_pad].flags = MEDIA_PAD_FL_SINK; - } - - ssd->sd.entity.ops = &smiapp_entity_ops; - - if (ssd == sensor->src) - return; - - ssd->sd.internal_ops = &smiapp_internal_ops; - ssd->sd.owner = THIS_MODULE; - ssd->sd.dev = &client->dev; - v4l2_set_subdevdata(&ssd->sd, client); -} - -static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct smiapp_subdev *ssd = to_smiapp_subdev(sd); - struct smiapp_sensor *sensor = ssd->sensor; - unsigned int i; - - mutex_lock(&sensor->mutex); - - for (i = 0; i < ssd->npads; i++) { - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_get_try_format(sd, fh->pad, i); - struct v4l2_rect *try_crop = - v4l2_subdev_get_try_crop(sd, fh->pad, i); - struct v4l2_rect *try_comp; - - smiapp_get_native_size(ssd, try_crop); - - try_fmt->width = try_crop->width; - try_fmt->height = try_crop->height; - try_fmt->code = sensor->internal_csi_format->code; - try_fmt->field = V4L2_FIELD_NONE; - - if (ssd != sensor->pixel_array) - continue; - - try_comp = v4l2_subdev_get_try_compose(sd, fh->pad, i); - *try_comp = *try_crop; - } - - mutex_unlock(&sensor->mutex); - - return 0; -} - -static const struct v4l2_subdev_video_ops smiapp_video_ops = { - .s_stream = smiapp_set_stream, -}; - -static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { - .enum_mbus_code = smiapp_enum_mbus_code, - .get_fmt = smiapp_get_format, - .set_fmt = smiapp_set_format, - .get_selection = smiapp_get_selection, - .set_selection = smiapp_set_selection, -}; - -static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { - .g_skip_frames = smiapp_get_skip_frames, - .g_skip_top_lines = smiapp_get_skip_top_lines, -}; - -static const struct v4l2_subdev_ops smiapp_ops = { - .video = &smiapp_video_ops, - .pad = &smiapp_pad_ops, - .sensor = &smiapp_sensor_ops, -}; - -static const struct media_entity_operations smiapp_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { - .registered = smiapp_registered, - .unregistered = smiapp_unregistered, - .open = smiapp_open, -}; - -static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { - .open = smiapp_open, -}; - -/* ----------------------------------------------------------------------------- - * I2C Driver - */ - -static int __maybe_unused smiapp_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - bool streaming = sensor->streaming; - int rval; - - rval = pm_runtime_get_sync(dev); - if (rval < 0) { - if (rval != -EBUSY && rval != -EAGAIN) - pm_runtime_set_active(&client->dev); - pm_runtime_put(dev); - return -EAGAIN; - } - - if (sensor->streaming) - smiapp_stop_streaming(sensor); - - /* save state for resume */ - sensor->streaming = streaming; - - return 0; -} - -static int __maybe_unused smiapp_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - int rval = 0; - - pm_runtime_put(dev); - - if (sensor->streaming) - rval = smiapp_start_streaming(sensor); - - return rval; -} - -static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) -{ - struct smiapp_hwconfig *hwcfg; - struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; - struct fwnode_handle *ep; - struct fwnode_handle *fwnode = dev_fwnode(dev); - u32 rotation; - int i; - int rval; - - if (!fwnode) - return dev->platform_data; - - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return NULL; - - bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY; - rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - if (rval == -ENXIO) { - bus_cfg = (struct v4l2_fwnode_endpoint) - { .bus_type = V4L2_MBUS_CCP2 }; - rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - } - if (rval) - goto out_err; - - hwcfg = devm_kzalloc(dev, sizeof(*hwcfg), GFP_KERNEL); - if (!hwcfg) - goto out_err; - - switch (bus_cfg.bus_type) { - case V4L2_MBUS_CSI2_DPHY: - hwcfg->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; - hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; - break; - case V4L2_MBUS_CCP2: - hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ? - SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE : - SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK; - hwcfg->lanes = 1; - break; - default: - dev_err(dev, "unsupported bus %u\n", bus_cfg.bus_type); - goto out_err; - } - - dev_dbg(dev, "lanes %u\n", hwcfg->lanes); - - rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); - if (!rval) { - switch (rotation) { - case 180: - hwcfg->module_board_orient = - SMIAPP_MODULE_BOARD_ORIENT_180; - fallthrough; - case 0: - break; - default: - dev_err(dev, "invalid rotation %u\n", rotation); - goto out_err; - } - } - - rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &hwcfg->ext_clk); - if (rval) - dev_info(dev, "can't get clock-frequency\n"); - - dev_dbg(dev, "clk %d, mode %d\n", hwcfg->ext_clk, - hwcfg->csi_signalling_mode); - - if (!bus_cfg.nr_of_link_frequencies) { - dev_warn(dev, "no link frequencies defined\n"); - goto out_err; - } - - hwcfg->op_sys_clock = devm_kcalloc( - dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */, - sizeof(*hwcfg->op_sys_clock), GFP_KERNEL); - if (!hwcfg->op_sys_clock) - goto out_err; - - for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { - hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i]; - dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]); - } - - v4l2_fwnode_endpoint_free(&bus_cfg); - fwnode_handle_put(ep); - return hwcfg; - -out_err: - v4l2_fwnode_endpoint_free(&bus_cfg); - fwnode_handle_put(ep); - return NULL; -} - -static int smiapp_probe(struct i2c_client *client) -{ - struct smiapp_sensor *sensor; - struct smiapp_hwconfig *hwcfg = smiapp_get_hwconfig(&client->dev); - unsigned int i; - int rval; - - if (hwcfg == NULL) - return -ENODEV; - - sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - sensor->hwcfg = hwcfg; - sensor->src = &sensor->ssds[sensor->ssds_used]; - - v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); - sensor->src->sd.internal_ops = &smiapp_internal_src_ops; - - sensor->vana = devm_regulator_get(&client->dev, "vana"); - if (IS_ERR(sensor->vana)) { - dev_err(&client->dev, "could not get regulator for vana\n"); - return PTR_ERR(sensor->vana); - } - - sensor->ext_clk = devm_clk_get(&client->dev, NULL); - if (PTR_ERR(sensor->ext_clk) == -ENOENT) { - dev_info(&client->dev, "no clock defined, continuing...\n"); - sensor->ext_clk = NULL; - } else if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock (%ld)\n", - PTR_ERR(sensor->ext_clk)); - return -EPROBE_DEFER; - } - - if (sensor->ext_clk) { - if (sensor->hwcfg->ext_clk) { - unsigned long rate; - - rval = clk_set_rate(sensor->ext_clk, - sensor->hwcfg->ext_clk); - if (rval < 0) { - dev_err(&client->dev, - "unable to set clock freq to %u\n", - sensor->hwcfg->ext_clk); - return rval; - } - - rate = clk_get_rate(sensor->ext_clk); - if (rate != sensor->hwcfg->ext_clk) { - dev_err(&client->dev, - "can't set clock freq, asked for %u but got %lu\n", - sensor->hwcfg->ext_clk, rate); - return rval; - } - } else { - sensor->hwcfg->ext_clk = clk_get_rate(sensor->ext_clk); - dev_dbg(&client->dev, "obtained clock freq %u\n", - sensor->hwcfg->ext_clk); - } - } else if (sensor->hwcfg->ext_clk) { - dev_dbg(&client->dev, "assuming clock freq %u\n", - sensor->hwcfg->ext_clk); - } else { - dev_err(&client->dev, "unable to obtain clock freq\n"); - return -EINVAL; - } - - sensor->xshutdown = devm_gpiod_get_optional(&client->dev, "xshutdown", - GPIOD_OUT_LOW); - if (IS_ERR(sensor->xshutdown)) - return PTR_ERR(sensor->xshutdown); - - rval = smiapp_power_on(&client->dev); - if (rval < 0) - return rval; - - mutex_init(&sensor->mutex); - - rval = smiapp_identify_module(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - rval = smiapp_read_all_smia_limits(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - rval = smiapp_read_frame_fmt(sensor); - if (rval) { - rval = -ENODEV; - goto out_power_off; - } - - /* - * Handle Sensor Module orientation on the board. - * - * The application of H-FLIP and V-FLIP on the sensor is modified by - * the sensor orientation on the board. - * - * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set - * both H-FLIP and V-FLIP for normal operation which also implies - * that a set/unset operation for user space HFLIP and VFLIP v4l2 - * controls will need to be internally inverted. - * - * Rotation also changes the bayer pattern. - */ - if (sensor->hwcfg->module_board_orient == - SMIAPP_MODULE_BOARD_ORIENT_180) - sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | - SMIAPP_IMAGE_ORIENTATION_VFLIP; - - rval = smiapp_call_quirk(sensor, limits); - if (rval) { - dev_err(&client->dev, "limits quirks failed\n"); - goto out_power_off; - } - - if (SMIA_LIM(sensor, BINNING_CAPABILITY)) { - u32 val; - - rval = smiapp_read(sensor, - SMIAPP_REG_U8_BINNING_SUBTYPES, &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->nbinning_subtypes = min_t(u8, val, - SMIAPP_BINNING_SUBTYPES); - - for (i = 0; i < sensor->nbinning_subtypes; i++) { - rval = smiapp_read( - sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } - sensor->binning_subtypes[i] = - *(struct smiapp_binning_subtype *)&val; - - dev_dbg(&client->dev, "binning %xx%x\n", - sensor->binning_subtypes[i].horizontal, - sensor->binning_subtypes[i].vertical); - } - } - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - - if (device_create_file(&client->dev, &dev_attr_ident) != 0) { - dev_err(&client->dev, "sysfs ident entry creation failed\n"); - rval = -ENOENT; - goto out_power_off; - } - - if (sensor->minfo.smiapp_version && - SMIA_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & - SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) { - if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { - dev_err(&client->dev, "sysfs nvm entry failed\n"); - rval = -EBUSY; - goto out_cleanup; - } - } - - /* We consider this as profile 0 sensor if any of these are zero. */ - if (!SMIA_LIM(sensor, MIN_OP_SYS_CLK_DIV) || - !SMIA_LIM(sensor, MAX_OP_SYS_CLK_DIV) || - !SMIA_LIM(sensor, MIN_OP_PIX_CLK_DIV) || - !SMIA_LIM(sensor, MAX_OP_PIX_CLK_DIV)) { - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; - } else if (SMIA_LIM(sensor, SCALING_CAPABILITY) - != SMIAPP_SCALING_CAPABILITY_NONE) { - if (SMIA_LIM(sensor, SCALING_CAPABILITY) - == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; - else - sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } else if (SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) - == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { - sensor->scaler = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - } - sensor->binner = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; - sensor->ssds_used++; - - sensor->scale_m = SMIA_LIM(sensor, SCALER_N_MIN); - - /* prepare PLL configuration input values */ - sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; - sensor->pll.csi2.lanes = sensor->hwcfg->lanes; - sensor->pll.ext_clk_freq_hz = sensor->hwcfg->ext_clk; - sensor->pll.scale_n = SMIA_LIM(sensor, SCALER_N_MIN); - /* Profile 0 sensors have no separate OP clock branch. */ - if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) - sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - - smiapp_create_subdev(sensor, sensor->scaler, " scaler", 2); - smiapp_create_subdev(sensor, sensor->binner, " binner", 2); - smiapp_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1); - - dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); - - sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - rval = smiapp_init_controls(sensor); - if (rval < 0) - goto out_cleanup; - - rval = smiapp_call_quirk(sensor, init); - if (rval) - goto out_cleanup; - - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_cleanup; - } - - rval = smiapp_init_late_controls(sensor); - if (rval) { - rval = -ENODEV; - goto out_cleanup; - } - - mutex_lock(&sensor->mutex); - rval = smiapp_pll_blanking_update(sensor); - mutex_unlock(&sensor->mutex); - if (rval) { - dev_err(&client->dev, "update mode failed\n"); - goto out_cleanup; - } - - sensor->streaming = false; - sensor->dev_init_done = true; - - rval = media_entity_pads_init(&sensor->src->sd.entity, 2, - sensor->src->pads); - if (rval < 0) - goto out_media_entity_cleanup; - - pm_runtime_set_active(&client->dev); - pm_runtime_get_noresume(&client->dev); - pm_runtime_enable(&client->dev); - - rval = v4l2_async_register_subdev_sensor_common(&sensor->src->sd); - if (rval < 0) - goto out_disable_runtime_pm; - - pm_runtime_set_autosuspend_delay(&client->dev, 1000); - pm_runtime_use_autosuspend(&client->dev); - pm_runtime_put_autosuspend(&client->dev); - - return 0; - -out_disable_runtime_pm: - pm_runtime_put_noidle(&client->dev); - pm_runtime_disable(&client->dev); - -out_media_entity_cleanup: - media_entity_cleanup(&sensor->src->sd.entity); - -out_cleanup: - smiapp_cleanup(sensor); - -out_power_off: - smiapp_power_off(&client->dev); - mutex_destroy(&sensor->mutex); - - return rval; -} - -static int smiapp_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - unsigned int i; - - v4l2_async_unregister_subdev(subdev); - - pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) - smiapp_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); - - for (i = 0; i < sensor->ssds_used; i++) { - v4l2_device_unregister_subdev(&sensor->ssds[i].sd); - media_entity_cleanup(&sensor->ssds[i].sd.entity); - } - smiapp_cleanup(sensor); - mutex_destroy(&sensor->mutex); - - return 0; -} - -static const struct of_device_id smiapp_of_table[] = { - { .compatible = "nokia,smia" }, - { }, -}; -MODULE_DEVICE_TABLE(of, smiapp_of_table); - -static const struct i2c_device_id smiapp_id_table[] = { - { SMIAPP_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, smiapp_id_table); - -static const struct dev_pm_ops smiapp_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(smiapp_suspend, smiapp_resume) - SET_RUNTIME_PM_OPS(smiapp_power_off, smiapp_power_on, NULL) -}; - -static struct i2c_driver smiapp_i2c_driver = { - .driver = { - .of_match_table = smiapp_of_table, - .name = SMIAPP_NAME, - .pm = &smiapp_pm_ops, - }, - .probe_new = smiapp_probe, - .remove = smiapp_remove, - .id_table = smiapp_id_table, -}; - -module_i2c_driver(smiapp_i2c_driver); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>"); -MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c deleted file mode 100644 index de5ee5296713..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-limits.c +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/media/i2c/smiapp/smiapp-limits.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#include "smiapp.h" - -struct smiapp_reg_limits smiapp_reg_limits[] = { - { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, - { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, - { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, - { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, - { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ - { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, - { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, - { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ - { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, - { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, - { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, - { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ - { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, - { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ - { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, - { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ - { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, - { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, - { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ - { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, - { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, - { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, - { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, - { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ - { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, - { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, - { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, - { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, - { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ - { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, - { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, - { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, - { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, - { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ - { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, - { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, - { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, - { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, - { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ - { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, - { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, - { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, - { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, - { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ - { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, - { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, - { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, - { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, - { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ - { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, - { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ - { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, - { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, - { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, - { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ - { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, - { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, - { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, - { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ - { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, - { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, - { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, - { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, - { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, - { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, - { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, - { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, - { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ - { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, - { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, - { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, - { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ - { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, - { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, - { 0, NULL }, -}; diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h deleted file mode 100644 index dbac0b4975f9..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-limits.h +++ /dev/null @@ -1,114 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * drivers/media/i2c/smiapp/smiapp-limits.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 -#define SMIAPP_LIMIT_THS_ZERO_MIN 3 -#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 -#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 -#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 -#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 -#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 -#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 -#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 -#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 -#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 -#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 -#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 -#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 -#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 -#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 -#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 -#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 -#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 -#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 -#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 -#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 -#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 -#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 -#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 -#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 -#define SMIAPP_LIMIT_X_ADDR_MIN 46 -#define SMIAPP_LIMIT_Y_ADDR_MIN 47 -#define SMIAPP_LIMIT_X_ADDR_MAX 48 -#define SMIAPP_LIMIT_Y_ADDR_MAX 49 -#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 -#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 -#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 -#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 -#define SMIAPP_LIMIT_MIN_EVEN_INC 54 -#define SMIAPP_LIMIT_MAX_EVEN_INC 55 -#define SMIAPP_LIMIT_MIN_ODD_INC 56 -#define SMIAPP_LIMIT_MAX_ODD_INC 57 -#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 -#define SMIAPP_LIMIT_SCALER_M_MIN 59 -#define SMIAPP_LIMIT_SCALER_M_MAX 60 -#define SMIAPP_LIMIT_SCALER_N_MIN 61 -#define SMIAPP_LIMIT_SCALER_N_MAX 62 -#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 -#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 -#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 -#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 -#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 -#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 -#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 -#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 -#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 -#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 -#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 -#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 -#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 -#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 -#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 -#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 -#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 -#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 -#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 -#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 -#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 -#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 -#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 -#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 -#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 -#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 -#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 -#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 -#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 -#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 -#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 -#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 -#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 -#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 -#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h deleted file mode 100644 index 865488befc09..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h +++ /dev/null @@ -1,489 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * drivers/media/i2c/smiapp/smiapp-reg-defs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ -#define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r)) -#define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r)) -#define SMIAPP_REG_MK_U32(r) ((SMIAPP_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_MK_F32(r) (SMIAPP_REG_FLAG_FLOAT | (SMIAPP_REG_32BIT << 16) | (r)) - -#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) -#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) -#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) -#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) -#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) -#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) -#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) -#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) -#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) -#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) -#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) -#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) -#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) -#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) -#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) -#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) -#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) -#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) -#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) -#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) -#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ -#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) -#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) -#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) -#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) -#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) -#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) -#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) -#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) -#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) -#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) -#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) -#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) -#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) -#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) -#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) -#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) -#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) -#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) -#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) -#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) -#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) -#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) -#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) -#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) -#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) -#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) -#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) -#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) -#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) -#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) -#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) -#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) -#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) -#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) -#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) -#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) -#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) -#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) -#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) -#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) -#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) -#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) -#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) -#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) -#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) -#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) -#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) -#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) -#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) -#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) -#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) -#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) -#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) -#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) -#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) -#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) -#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) -#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) -#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) -#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) -#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) -#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) -#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) -#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) -#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) -#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) -#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) -#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) -#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) -#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) -#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) -#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) -#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) -#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) -#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) -#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) -#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) -#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) -#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) -#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) -#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) -#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) -#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) -#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) -#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) -#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) -#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) -#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) -#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) -#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) -#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) -#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) -#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) -#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) -#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) -#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) -#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) -#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) -#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) -#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) -#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) -#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) -#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) -#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) -#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) -#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) -#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) -#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) -#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) -#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) -#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) -#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) -#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) -#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) -#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) -#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) -#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) -#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) -#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) -#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) -#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) -#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) -#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) -#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) -#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) -#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) -#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) -#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) -#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) -#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) -#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) -#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) -#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) -#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) -#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) -#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) -#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) -#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) -#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) -#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) -#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) -#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) -#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) -#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) -#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) -#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) -#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) -#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) -#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) -#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) -#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) -#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) -#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) -#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) -#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) -#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) -#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) -#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) -#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) -#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) -#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) -#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) -#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) -#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) -#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) -#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) -#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) -#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) -#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) -#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) -#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) -#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) -#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) -#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) -#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) -#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) -#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) -#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) -#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) -#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) -#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) -#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) -#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) -#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) -#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) -#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) -#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) -#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) -#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) -#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) -#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) -#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) -#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) -#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) -#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) -#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) -#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) -#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) -#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) -#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) -#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) -#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) -#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) -#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) -#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) -#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) -#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) -#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) -#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) -#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) -#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) -#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) -#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) -#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) -#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) -#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) -#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) -#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) -#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) -#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) -#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ -#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) -#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) -#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) -#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) -#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) -#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) -#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) -#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) -#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) -#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) -#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) -#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) -#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) -#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) -#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) -#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) -#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) -#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) -#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) -#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) -#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) -#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) -#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) -#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) -#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) -#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) -#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) -#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h deleted file mode 100644 index e6f96309786f..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ /dev/null @@ -1,116 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * drivers/media/i2c/smiapp/smiapp-reg.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#ifndef __SMIAPP_REG_H_ -#define __SMIAPP_REG_H_ - -#include <linux/bits.h> - -#include "smiapp-reg-defs.h" - -/* Bits for above register */ -#define SMIAPP_IMAGE_ORIENTATION_HFLIP BIT(0) -#define SMIAPP_IMAGE_ORIENTATION_VFLIP BIT(1) - -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN BIT(0) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN BIT(1) -#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR BIT(2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY BIT(0) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY BIT(1) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA BIT(2) -#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE BIT(3) - -#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED BIT(0) -#define SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL BIT(2) - -#define SMIAPP_SOFTWARE_RESET BIT(0) - -#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) -#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) - -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 -#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 - -#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 -/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ -#define SMIAPP_DPHY_CTRL_UI 1 -#define SMIAPP_DPHY_CTRL_REGISTER 2 - -#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 -#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 - -#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 -#define SMIAPP_MODE_SELECT_STREAMING 1 - -#define SMIAPP_SCALING_MODE_NONE 0 -#define SMIAPP_SCALING_MODE_HORIZONTAL 1 -#define SMIAPP_SCALING_MODE_BOTH 2 - -#define SMIAPP_SCALING_CAPABILITY_NONE 0 -#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 -#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ - -/* digital crop right before scaler */ -#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 -#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 - -#define SMIAPP_BINNING_CAPABILITY_NO 0 -#define SMIAPP_BINNING_CAPABILITY_YES 1 - -/* Maximum number of binning subtypes */ -#define SMIAPP_BINNING_SUBTYPES 253 - -#define SMIAPP_PIXEL_ORDER_GRBG 0 -#define SMIAPP_PIXEL_ORDER_RGGB 1 -#define SMIAPP_PIXEL_ORDER_BGGR 2 -#define SMIAPP_PIXEL_ORDER_GBRG 3 - -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 -#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 - -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 -#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 -#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 - -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 -#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff - -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 -#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff - -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 -#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 - -#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 -#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 - -/* Scaling N factor */ -#define SMIAPP_SCALE_N 16 - -/* Image statistics registers */ -/* Registers 0x2000 to 0x2fff are reserved for future - * use for statistics features. - */ - -/* Manufacturer Specific Registers: 0x3000 to 0x3fff - * The manufacturer specifies these as a black box. - */ - -#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c deleted file mode 100644 index 1b58b7c6c839..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/media/i2c/smiapp/smiapp-regs.c - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#include <asm/unaligned.h> - -#include <linux/delay.h> -#include <linux/i2c.h> - -#include "smiapp.h" -#include "smiapp-regs.h" - -static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, - uint32_t phloat) -{ - int32_t exp; - uint64_t man; - - if (phloat >= 0x80000000) { - dev_err(&client->dev, "this is a negative number\n"); - return 0; - } - - if (phloat == 0x7f800000) - return ~0; /* Inf. */ - - if ((phloat & 0x7f800000) == 0x7f800000) { - dev_err(&client->dev, "NaN or other special number\n"); - return 0; - } - - /* Valid cases begin here */ - if (phloat == 0) - return 0; /* Valid zero */ - - if (phloat > 0x4f800000) - return ~0; /* larger than 4294967295 */ - - /* - * Unbias exponent (note how phloat is now guaranteed to - * have 0 in the high bit) - */ - exp = ((int32_t)phloat >> 23) - 127; - - /* Extract mantissa, add missing '1' bit and it's in MHz */ - man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; - - if (exp < 0) - man >>= -exp; - else - man <<= exp; - - man >>= 23; /* Remove mantissa bias */ - - return man & 0xffffffff; -} - - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data_buf[sizeof(u32)] = { 0 }; - unsigned char offset_buf[sizeof(u16)]; - int r; - - if (len > sizeof(data_buf)) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; - msg.len = sizeof(offset_buf); - msg.buf = offset_buf; - put_unaligned_be16(reg, offset_buf); - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - msg.len = len; - msg.flags = I2C_M_RD; - msg.buf = &data_buf[sizeof(data_buf) - len]; - - r = i2c_transfer(client->adapter, &msg, 1); - if (r != 1) { - if (r >= 0) - r = -EBUSY; - goto err; - } - - *val = get_unaligned_be32(data_buf); - - return 0; - -err: - dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); - - return r; -} - -/* Read a register using 8-bit access only. */ -static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, - u16 len, u32 *val) -{ - unsigned int i; - int rval; - - *val = 0; - - for (i = 0; i < len; i++) { - u32 val8; - - rval = ____smiapp_read(sensor, reg + i, 1, &val8); - if (rval < 0) - return rval; - *val |= val8 << ((len - i - 1) << 3); - } - - return 0; -} - -/* - * Read a 8/16/32-bit i2c register. The value is returned in 'val'. - * Returns zero if successful, or non-zero otherwise. - */ -static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, - bool only8) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - u8 len = SMIAPP_REG_WIDTH(reg); - int rval; - - if (len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT - && len != SMIAPP_REG_32BIT) - return -EINVAL; - - if (!only8) - rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val); - else - rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len, - val); - if (rval < 0) - return rval; - - if (reg & SMIAPP_REG_FLAG_FLOAT) - *val = float_to_u32_mul_1000000(client, *val); - - return 0; -} - -int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return __smiapp_read( - sensor, reg, val, - smiapp_needs_quirk(sensor, - SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); -} - -static int smiapp_read_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val, - bool force8) -{ - int rval; - - *val = 0; - rval = smiapp_call_quirk(sensor, reg_access, false, ®, val); - if (rval == -ENOIOCTLCMD) - return 0; - if (rval < 0) - return rval; - - if (force8) - return __smiapp_read(sensor, reg, val, true); - - return smiapp_read_no_quirk(sensor, reg, val); -} - -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return smiapp_read_quirk(sensor, reg, val, false); -} - -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) -{ - return smiapp_read_quirk(sensor, reg, val, true); -} - -int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - struct i2c_msg msg; - unsigned char data[6]; - unsigned int retries; - u8 len = SMIAPP_REG_WIDTH(reg); - int r; - - if (len > sizeof(data) - 2) - return -EINVAL; - - msg.addr = client->addr; - msg.flags = 0; /* Write */ - msg.len = 2 + len; - msg.buf = data; - - put_unaligned_be16(SMIAPP_REG_ADDR(reg), data); - put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2); - - for (retries = 0; retries < 5; retries++) { - /* - * Due to unknown reason sensor stops responding. This - * loop is a temporaty solution until the root cause - * is found. - */ - r = i2c_transfer(client->adapter, &msg, 1); - if (r == 1) { - if (retries) - dev_err(&client->dev, - "sensor i2c stall encountered. retries: %d\n", - retries); - return 0; - } - - usleep_range(2000, 2000); - } - - dev_err(&client->dev, - "wrote 0x%x to offset 0x%x error %d\n", val, - SMIAPP_REG_ADDR(reg), r); - - return r; -} - -/* - * Write to a 8/16-bit register. - * Returns zero if successful, or non-zero otherwise. - */ -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) -{ - int rval; - - rval = smiapp_call_quirk(sensor, reg_access, true, ®, &val); - if (rval == -ENOIOCTLCMD) - return 0; - if (rval < 0) - return rval; - - return smiapp_write_no_quirk(sensor, reg, val); -} diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h deleted file mode 100644 index 8fda6ed5668c..000000000000 --- a/drivers/media/i2c/smiapp/smiapp-regs.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/smiapp/smiapp-regs.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#ifndef SMIAPP_REGS_H -#define SMIAPP_REGS_H - -#include <linux/i2c.h> -#include <linux/types.h> - -#define SMIAPP_REG_ADDR(reg) ((u16)reg) -#define SMIAPP_REG_WIDTH(reg) ((u8)(reg >> 16)) -#define SMIAPP_REG_FLAGS(reg) ((u8)(reg >> 24)) - -/* Use upper 8 bits of the type field for flags */ -#define SMIAPP_REG_FLAG_FLOAT (1 << 24) - -#define SMIAPP_REG_8BIT 1 -#define SMIAPP_REG_16BIT 2 -#define SMIAPP_REG_32BIT 4 - -struct smiapp_sensor; - -int smiapp_read_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); -int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val); -int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); - -#endif diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 7d9401219a3a..e26e3f544054 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1413,8 +1413,7 @@ static const struct media_entity_operations tvp5150_sd_media_ops = { ****************************************************************************/ static int __maybe_unused tvp5150_runtime_suspend(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct tvp5150 *decoder = to_tvp5150(sd); if (decoder->irq) @@ -1427,8 +1426,7 @@ static int __maybe_unused tvp5150_runtime_suspend(struct device *dev) static int __maybe_unused tvp5150_runtime_resume(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct tvp5150 *decoder = to_tvp5150(sd); if (decoder->irq) @@ -2082,6 +2080,7 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0); if (!ep_np) { + ret = -EINVAL; dev_err(dev, "Error no output endpoint available\n"); goto err_free; } diff --git a/drivers/media/pci/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c index ba45b378d739..ff8058568240 100644 --- a/drivers/media/pci/b2c2/flexcop-dma.c +++ b/drivers/media/pci/b2c2/flexcop-dma.c @@ -17,7 +17,7 @@ int flexcop_dma_allocate(struct pci_dev *pdev, return -EINVAL; } - tcpu = pci_alloc_consistent(pdev, size, &tdma); + tcpu = dma_alloc_coherent(&pdev->dev, size, &tdma, GFP_KERNEL); if (tcpu != NULL) { dma->pdev = pdev; dma->cpu_addr0 = tcpu; @@ -33,8 +33,8 @@ EXPORT_SYMBOL(flexcop_dma_allocate); void flexcop_dma_free(struct flexcop_dma *dma) { - pci_free_consistent(dma->pdev, dma->size*2, - dma->cpu_addr0, dma->dma_addr0); + dma_free_coherent(&dma->pdev->dev, dma->size * 2, dma->cpu_addr0, + dma->dma_addr0); memset(dma, 0, sizeof(struct flexcop_dma)); } EXPORT_SYMBOL(flexcop_dma_free); diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 79ba15a9385a..78dd35c9b65d 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -67,14 +67,14 @@ EXPORT_SYMBOL(bt878); static void bt878_mem_free(struct bt878 *bt) { if (bt->buf_cpu) { - pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu, - bt->buf_dma); + dma_free_coherent(&bt->dev->dev, bt->buf_size, bt->buf_cpu, + bt->buf_dma); bt->buf_cpu = NULL; } if (bt->risc_cpu) { - pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu, - bt->risc_dma); + dma_free_coherent(&bt->dev->dev, bt->risc_size, bt->risc_cpu, + bt->risc_dma); bt->risc_cpu = NULL; } } @@ -84,16 +84,16 @@ static int bt878_mem_alloc(struct bt878 *bt) if (!bt->buf_cpu) { bt->buf_size = 128 * 1024; - bt->buf_cpu = pci_zalloc_consistent(bt->dev, bt->buf_size, - &bt->buf_dma); + bt->buf_cpu = dma_alloc_coherent(&bt->dev->dev, bt->buf_size, + &bt->buf_dma, GFP_KERNEL); if (!bt->buf_cpu) return -ENOMEM; } if (!bt->risc_cpu) { bt->risc_size = PAGE_SIZE; - bt->risc_cpu = pci_zalloc_consistent(bt->dev, bt->risc_size, - &bt->risc_dma); + bt->risc_cpu = dma_alloc_coherent(&bt->dev->dev, bt->risc_size, + &bt->risc_dma, GFP_KERNEL); if (!bt->risc_cpu) { bt878_mem_free(bt); return -ENOMEM; diff --git a/drivers/media/pci/bt8xx/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c index 51257980f539..b3179038b900 100644 --- a/drivers/media/pci/bt8xx/btcx-risc.c +++ b/drivers/media/pci/bt8xx/btcx-risc.c @@ -48,7 +48,7 @@ void btcx_riscmem_free(struct pci_dev *pci, dprintk("btcx: riscmem free [%d] dma=%lx\n", memcnt, (unsigned long)risc->dma); - pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + dma_free_coherent(&pci->dev, risc->size, risc->cpu, risc->dma); memset(risc,0,sizeof(*risc)); } @@ -62,7 +62,7 @@ int btcx_riscmem_alloc(struct pci_dev *pci, if (NULL != risc->cpu && risc->size < size) btcx_riscmem_free(pci,risc); if (NULL == risc->cpu) { - cpu = pci_alloc_consistent(pci, size, &dma); + cpu = dma_alloc_coherent(&pci->dev, size, &dma, GFP_KERNEL); if (NULL == cpu) return -ENOMEM; risc->cpu = cpu; @@ -73,7 +73,6 @@ int btcx_riscmem_alloc(struct pci_dev *pci, dprintk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", memcnt, (unsigned long)dma, cpu, size); } - memset(risc->cpu,0,risc->size); return 0; } diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index 16148802dabb..ca20b806e82d 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -3934,8 +3934,10 @@ static void osprey_eeprom(struct bttv *btv, const u8 ee[256]) if (checksum != ee[21]) return; cardid = BTTV_BOARD_OSPREY1x0_848; - for (i = 12; i < 21; i++) - serial *= 10, serial += ee[i] - '0'; + for (i = 12; i < 21; i++) { + serial *= 10; + serial += ee[i] - '0'; + } } } else { unsigned short type; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 8824dd0fb331..1f62a9d8ea1d 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2058,7 +2058,6 @@ verify_window_lock(struct bttv_fh *fh, struct v4l2_window *win, { enum v4l2_field field; unsigned int width_mask; - int rc; if (win->w.width < 48) win->w.width = 48; @@ -2111,13 +2110,10 @@ verify_window_lock(struct bttv_fh *fh, struct v4l2_window *win, win->w.width -= win->w.left & ~width_mask; win->w.left = (win->w.left - width_mask - 1) & width_mask; - rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height, - field, width_mask, - /* width_bias: round down */ 0, - adjust_size, adjust_crop); - if (0 != rc) - return rc; - return 0; + return limit_scaled_size_lock(fh, &win->w.width, &win->w.height, + field, width_mask, + /* width_bias: round down */ 0, + adjust_size, adjust_crop); } static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv, @@ -2143,12 +2139,8 @@ static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv, clips = kmalloc(size,GFP_KERNEL); if (NULL == clips) return -ENOMEM; - if (n > 0) { - if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { - kfree(clips); - return -EFAULT; - } - } + if (n > 0) + memcpy(clips, win->clips, sizeof(struct v4l2_clip) * n); /* clip against screen */ if (NULL != btv->fbuf.base) @@ -4016,7 +4008,7 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) result = -EIO; goto free_mem; } - if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { + if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) { pr_warn("%d: No suitable DMA available\n", btv->c.nr); result = -EIO; goto free_mem; @@ -4270,15 +4262,14 @@ static void bttv_remove(struct pci_dev *pci_dev) return; } -#ifdef CONFIG_PM -static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) +static int __maybe_unused bttv_suspend(struct device *dev) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct bttv *btv = to_bttv(v4l2_dev); struct bttv_buffer_set idle; unsigned long flags; - dprintk("%d: suspend %d\n", btv->c.nr, state.event); + dprintk("%d: suspend\n", btv->c.nr); /* stop dma + irqs */ spin_lock_irqsave(&btv->s_lock,flags); @@ -4298,42 +4289,19 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN); btv->state.gpio_data = gpio_read(); - /* save pci state */ - pci_save_state(pci_dev); - if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { - pci_disable_device(pci_dev); - btv->state.disabled = 1; - } + btv->state.disabled = 1; return 0; } -static int bttv_resume(struct pci_dev *pci_dev) +static int __maybe_unused bttv_resume(struct device *dev) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct bttv *btv = to_bttv(v4l2_dev); unsigned long flags; - int err; dprintk("%d: resume\n", btv->c.nr); - /* restore pci state */ - if (btv->state.disabled) { - err=pci_enable_device(pci_dev); - if (err) { - pr_warn("%d: Can't enable device\n", btv->c.nr); - return err; - } - btv->state.disabled = 0; - } - err=pci_set_power_state(pci_dev, PCI_D0); - if (err) { - pci_disable_device(pci_dev); - pr_warn("%d: Can't enable device\n", btv->c.nr); - btv->state.disabled = 1; - return err; - } - - pci_restore_state(pci_dev); + btv->state.disabled = 0; /* restore bt878 state */ bttv_reinit_bt848(btv); @@ -4351,7 +4319,6 @@ static int bttv_resume(struct pci_dev *pci_dev) spin_unlock_irqrestore(&btv->s_lock,flags); return 0; } -#endif static const struct pci_device_id bttv_pci_tbl[] = { {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0}, @@ -4364,15 +4331,16 @@ static const struct pci_device_id bttv_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); +static SIMPLE_DEV_PM_OPS(bttv_pm_ops, + bttv_suspend, + bttv_resume); + static struct pci_driver bttv_pci_driver = { - .name = "bttv", - .id_table = bttv_pci_tbl, - .probe = bttv_probe, - .remove = bttv_remove, -#ifdef CONFIG_PM - .suspend = bttv_suspend, - .resume = bttv_resume, -#endif + .name = "bttv", + .id_table = bttv_pci_tbl, + .probe = bttv_probe, + .remove = bttv_remove, + .driver.pm = &bttv_pm_ops, }; static int __init bttv_init_module(void) diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 4af72826b006..32fa4a7fe76f 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -572,7 +572,6 @@ bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf { struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); - BUG_ON(in_interrupt()); videobuf_waiton(q, &buf->vb, 0, 0); videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 4b0c53f61fb7..22f55a7840a6 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -1322,7 +1322,6 @@ void cx23885_free_buffer(struct cx23885_dev *dev, struct cx23885_buffer *buf) { struct cx23885_riscmem *risc = &buf->risc; - BUG_ON(in_interrupt()); pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma); } @@ -2074,6 +2073,10 @@ static struct { * 0x1451 is PCI ID for the IOMMU found on Ryzen */ { PCI_VENDOR_ID_AMD, 0x1451 }, + /* According to sudo lspci -nn, + * 0x1423 is the PCI ID for the IOMMU found on Kaveri + */ + { PCI_VENDOR_ID_AMD, 0x1423 }, }; static bool cx23885_does_need_dma_reset(void) diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 55018d9e439f..6f8ffab8840f 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1198,7 +1198,6 @@ EXPORT_SYMBOL(cx25821_risc_databuffer_audio); void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf) { - BUG_ON(in_interrupt()); if (WARN_ON(buf->risc.size == 0)) return; pci_free_consistent(dev->pci, diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index a57c991b165b..a3edb548afde 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -524,8 +524,7 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) core->last_analog_input = core->input; core->input = 0; for (i = 0; - i < (sizeof(core->board.input) / - sizeof(struct cx88_input)); + i < ARRAY_SIZE(core->board.input); i++) { if (core->board.input[i].type == CX88_VMUX_DVB) { core->input = i; diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 9dce31d2b525..4ac645a56c14 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -604,19 +604,17 @@ static void dm1105_set_dma_addr(struct dm1105_dev *dev) static int dm1105_dma_map(struct dm1105_dev *dev) { - dev->ts_buf = pci_alloc_consistent(dev->pdev, - 6 * DM1105_DMA_BYTES, - &dev->dma_addr); + dev->ts_buf = dma_alloc_coherent(&dev->pdev->dev, + 6 * DM1105_DMA_BYTES, &dev->dma_addr, + GFP_KERNEL); return !dev->ts_buf; } static void dm1105_dma_unmap(struct dm1105_dev *dev) { - pci_free_consistent(dev->pdev, - 6 * DM1105_DMA_BYTES, - dev->ts_buf, - dev->dma_addr); + dma_free_coherent(&dev->pdev->dev, 6 * DM1105_DMA_BYTES, dev->ts_buf, + dev->dma_addr); } static void dm1105_enable_irqs(struct dm1105_dev *dev) @@ -1010,7 +1008,7 @@ static int dm1105_probe(struct pci_dev *pdev, if (ret < 0) goto err_kfree; - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret < 0) goto err_pci_disable_device; diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 4e598e937dfe..36e354ecf71e 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/iopoll.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/pfn.h> @@ -33,6 +34,7 @@ struct ipu3_cio2_fmt { u32 mbus_code; u32 fourcc; u8 mipicode; + u8 bpp; }; /* @@ -46,18 +48,22 @@ static const struct ipu3_cio2_fmt formats[] = { .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SGRBG10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SGBRG10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SBGGR10, .mipicode = 0x2b, + .bpp = 10, }, { .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, .fourcc = V4L2_PIX_FMT_IPU3_SRGGB10, .mipicode = 0x2b, + .bpp = 10, }, }; @@ -189,9 +195,8 @@ static void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, * 4095 (PAGE_SIZE - 1) means every single byte in the last page * is available for DMA transfer. */ - entry[1].second_entry.last_page_available_bytes = - (remaining & ~PAGE_MASK) ? - (remaining & ~PAGE_MASK) - 1 : PAGE_SIZE - 1; + remaining = offset_in_page(remaining) ?: PAGE_SIZE; + entry[1].second_entry.last_page_available_bytes = remaining - 1; /* Fill FBPT */ remaining = length; i = 0; @@ -286,37 +291,22 @@ static s32 cio2_rx_timing(s32 a, s32 b, s64 freq, int def) return r; }; -/* Calculate the the delay value for termination enable of clock lane HS Rx */ +/* Calculate the delay value for termination enable of clock lane HS Rx */ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, - struct cio2_csi2_timing *timing) + struct cio2_csi2_timing *timing, + unsigned int bpp, unsigned int lanes) { struct device *dev = &cio2->pci_dev->dev; - struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; - struct v4l2_ctrl *link_freq; s64 freq; - int r; if (!q->sensor) return -ENODEV; - link_freq = v4l2_ctrl_find(q->sensor->ctrl_handler, V4L2_CID_LINK_FREQ); - if (!link_freq) { - dev_err(dev, "failed to find LINK_FREQ\n"); - return -EPIPE; - } - - qm.index = v4l2_ctrl_g_ctrl(link_freq); - r = v4l2_querymenu(q->sensor->ctrl_handler, &qm); - if (r) { - dev_err(dev, "failed to get menu item\n"); - return r; - } - - if (!qm.value) { - dev_err(dev, "error invalid link_freq\n"); - return -EINVAL; + freq = v4l2_get_link_rate(q->sensor->ctrl_handler, bpp, lanes); + if (freq < 0) { + dev_err(dev, "error %lld, invalid link_freq\n", freq); + return freq; } - freq = qm.value; timing->clk_termen = cio2_rx_timing(CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_A, CIO2_CSIRX_DLY_CNT_TERMEN_CLANE_B, @@ -364,7 +354,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) lanes = q->csi2.lanes; - r = cio2_csi2_calc_timing(cio2, q, &timing); + r = cio2_csi2_calc_timing(cio2, q, &timing, fmt->bpp, lanes); if (r) return r; @@ -561,7 +551,9 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) b = q->bufs[q->bufs_first]; if (b) { - unsigned int bytes = entry[1].second_entry.num_of_bytes; + unsigned int received = entry[1].second_entry.num_of_bytes; + unsigned long payload = + vb2_get_plane_payload(&b->vbb.vb2_buf, 0); q->bufs[q->bufs_first] = NULL; atomic_dec(&q->bufs_queued); @@ -571,10 +563,10 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) b->vbb.vb2_buf.timestamp = ns; b->vbb.field = V4L2_FIELD_NONE; b->vbb.sequence = atomic_read(&q->frame_sequence); - if (b->vbb.vb2_buf.planes[0].length != bytes) - dev_warn(dev, "buffer length is %d received %d\n", - b->vbb.vb2_buf.planes[0].length, - bytes); + if (payload != received) + dev_warn(dev, + "payload length is %lu, received %u\n", + payload, received); vb2_buffer_done(&b->vbb.vb2_buf, VB2_BUF_STATE_DONE); } atomic_inc(&q->frame_sequence); @@ -791,6 +783,7 @@ static void cio2_vb2_return_all_buffers(struct cio2_queue *q, atomic_dec(&q->bufs_queued); vb2_buffer_done(&q->bufs[i]->vbb.vb2_buf, state); + q->bufs[i] = NULL; } } } @@ -1094,8 +1087,8 @@ static int cio2_v4l2_try_fmt(struct file *file, void *fh, struct v4l2_format *f) /* Only supports up to 4224x3136 */ if (mpix->width > CIO2_IMAGE_MAX_WIDTH) mpix->width = CIO2_IMAGE_MAX_WIDTH; - if (mpix->height > CIO2_IMAGE_MAX_LENGTH) - mpix->height = CIO2_IMAGE_MAX_LENGTH; + if (mpix->height > CIO2_IMAGE_MAX_HEIGHT) + mpix->height = CIO2_IMAGE_MAX_HEIGHT; mpix->num_planes = 1; mpix->pixelformat = fmt->fourcc; @@ -1232,29 +1225,15 @@ static int cio2_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); - struct v4l2_subdev_format format; - int ret; - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); - return 0; - } - - if (fmt->pad == CIO2_PAD_SINK) { - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, - &format); + mutex_lock(&q->subdev_lock); - if (ret) - return ret; - /* update colorspace etc */ - q->subdev_fmt.colorspace = format.format.colorspace; - q->subdev_fmt.ycbcr_enc = format.format.ycbcr_enc; - q->subdev_fmt.quantization = format.format.quantization; - q->subdev_fmt.xfer_func = format.format.xfer_func; - } + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + else + fmt->format = q->subdev_fmt; - fmt->format = q->subdev_fmt; + mutex_unlock(&q->subdev_lock); return 0; } @@ -1271,6 +1250,9 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev); + struct v4l2_mbus_framefmt *mbus; + u32 mbus_code = fmt->format.code; + unsigned int i; /* * Only allow setting sink pad format; @@ -1279,16 +1261,28 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd, if (fmt->pad == CIO2_PAD_SOURCE) return cio2_subdev_get_fmt(sd, cfg, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; - } else { - /* It's the sink, allow changing frame size */ - q->subdev_fmt.width = fmt->format.width; - q->subdev_fmt.height = fmt->format.height; - q->subdev_fmt.code = fmt->format.code; - fmt->format = q->subdev_fmt; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + mbus = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + else + mbus = &q->subdev_fmt; + + fmt->format.code = formats[0].mbus_code; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].mbus_code == fmt->format.code) { + fmt->format.code = mbus_code; + break; + } } + fmt->format.width = min(fmt->format.width, CIO2_IMAGE_MAX_WIDTH); + fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT); + fmt->format.field = V4L2_FIELD_NONE; + + mutex_lock(&q->subdev_lock); + *mbus = fmt->format; + mutex_unlock(&q->subdev_lock); + return 0; } @@ -1547,6 +1541,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) /* Initialize miscellaneous variables */ mutex_init(&q->lock); + mutex_init(&q->subdev_lock); /* Initialize formats to default values */ fmt = &q->subdev_fmt; @@ -1663,6 +1658,7 @@ fail_vdev_media_entity: fail_subdev_media_entity: cio2_fbpt_exit(q, &cio2->pci_dev->dev); fail_fbpt: + mutex_destroy(&q->subdev_lock); mutex_destroy(&q->lock); return r; @@ -1675,6 +1671,7 @@ static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q) v4l2_device_unregister_subdev(&q->subdev); media_entity_cleanup(&q->subdev.entity); cio2_fbpt_exit(q, &cio2->pci_dev->dev); + mutex_destroy(&q->subdev_lock); mutex_destroy(&q->lock); } diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index 549b08f88f0c..ccf0b85ae36f 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -13,20 +13,20 @@ #define CIO2_PCI_BAR 0 #define CIO2_DMA_MASK DMA_BIT_MASK(39) -#define CIO2_IMAGE_MAX_WIDTH 4224 -#define CIO2_IMAGE_MAX_LENGTH 3136 +#define CIO2_IMAGE_MAX_WIDTH 4224U +#define CIO2_IMAGE_MAX_HEIGHT 3136U /* 32MB = 8xFBPT_entry */ #define CIO2_MAX_LOPS 8 #define CIO2_MAX_BUFFERS (PAGE_SIZE / 16 / CIO2_MAX_LOPS) #define CIO2_LOP_ENTRIES (PAGE_SIZE / sizeof(u32)) -#define CIO2_PAD_SINK 0 -#define CIO2_PAD_SOURCE 1 -#define CIO2_PADS 2 +#define CIO2_PAD_SINK 0U +#define CIO2_PAD_SOURCE 1U +#define CIO2_PADS 2U -#define CIO2_NUM_DMA_CHAN 20 -#define CIO2_NUM_PORTS 4 /* DPHYs */ +#define CIO2_NUM_DMA_CHAN 20U +#define CIO2_NUM_PORTS 4U /* DPHYs */ /* 1 for each sensor */ #define CIO2_QUEUES CIO2_NUM_PORTS @@ -66,12 +66,12 @@ #define CIO2_REG_MIPIBE_FORCE_RAW8 (CIO2_REG_MIPIBE_BASE + 0x20) #define CIO2_REG_MIPIBE_FORCE_RAW8_ENABLE BIT(0) #define CIO2_REG_MIPIBE_FORCE_RAW8_USE_TYPEID BIT(1) -#define CIO2_REG_MIPIBE_FORCE_RAW8_TYPEID_SHIFT 2 +#define CIO2_REG_MIPIBE_FORCE_RAW8_TYPEID_SHIFT 2U #define CIO2_REG_MIPIBE_IRQ_STATUS (CIO2_REG_MIPIBE_BASE + 0x24) #define CIO2_REG_MIPIBE_IRQ_CLEAR (CIO2_REG_MIPIBE_BASE + 0x28) #define CIO2_REG_MIPIBE_GLOBAL_LUT_DISREGARD (CIO2_REG_MIPIBE_BASE + 0x68) -#define CIO2_MIPIBE_GLOBAL_LUT_DISREGARD 1 +#define CIO2_MIPIBE_GLOBAL_LUT_DISREGARD 1U #define CIO2_REG_MIPIBE_PKT_STALL_STATUS (CIO2_REG_MIPIBE_BASE + 0x6c) #define CIO2_REG_MIPIBE_PARSE_GSP_THROUGH_LP_LUT_REG_IDX \ (CIO2_REG_MIPIBE_BASE + 0x70) @@ -79,10 +79,10 @@ (CIO2_REG_MIPIBE_BASE + 0x74 + 4 * (vc)) #define CIO2_REG_MIPIBE_LP_LUT_ENTRY(m) /* m = 0..15 */ \ (CIO2_REG_MIPIBE_BASE + 0x84 + 4 * (m)) -#define CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD 1 -#define CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT 1 -#define CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT 5 -#define CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT 7 +#define CIO2_MIPIBE_LP_LUT_ENTRY_DISREGARD 1U +#define CIO2_MIPIBE_LP_LUT_ENTRY_SID_SHIFT 1U +#define CIO2_MIPIBE_LP_LUT_ENTRY_VC_SHIFT 5U +#define CIO2_MIPIBE_LP_LUT_ENTRY_FORMAT_TYPE_SHIFT 7U /* base register: CIO2_REG_PIPE_BASE(pipe) * CIO2_REG_IRQCTRL_BASE */ /* IRQ registers are 18-bit wide, see cio2_irq_error for bit definitions */ @@ -113,31 +113,31 @@ #define CIO2_CGC_ROSC_DCGE BIT(12) #define CIO2_CGC_XOSC_DCGE BIT(13) #define CIO2_CGC_FLIS_DCGE BIT(14) -#define CIO2_CGC_CLKGATE_HOLDOFF_SHIFT 20 -#define CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT 24 +#define CIO2_CGC_CLKGATE_HOLDOFF_SHIFT 20U +#define CIO2_CGC_CSI_CLKGATE_HOLDOFF_SHIFT 24U #define CIO2_REG_D0I3C 0x1408 #define CIO2_D0I3C_I3 BIT(2) /* Set D0I3 */ #define CIO2_D0I3C_RR BIT(3) /* Restore? */ #define CIO2_REG_SWRESET 0x140c -#define CIO2_SWRESET_SWRESET 1 +#define CIO2_SWRESET_SWRESET 1U #define CIO2_REG_SENSOR_ACTIVE 0x1410 #define CIO2_REG_INT_STS 0x1414 #define CIO2_REG_INT_STS_EXT_OE 0x1418 -#define CIO2_INT_EXT_OE_DMAOE_SHIFT 0 +#define CIO2_INT_EXT_OE_DMAOE_SHIFT 0U #define CIO2_INT_EXT_OE_DMAOE_MASK 0x7ffff -#define CIO2_INT_EXT_OE_OES_SHIFT 24 +#define CIO2_INT_EXT_OE_OES_SHIFT 24U #define CIO2_INT_EXT_OE_OES_MASK (0xf << CIO2_INT_EXT_OE_OES_SHIFT) #define CIO2_REG_INT_EN 0x1420 #define CIO2_REG_INT_EN_IRQ (1 << 24) -#define CIO2_REG_INT_EN_IOS(dma) (1 << (((dma) >> 1) + 12)) +#define CIO2_REG_INT_EN_IOS(dma) (1U << (((dma) >> 1U) + 12U)) /* * Interrupt on completion bit, Eg. DMA 0-3 maps to bit 0-3, * DMA4 & DMA5 map to bit 4 ... DMA18 & DMA19 map to bit 11 Et cetera */ -#define CIO2_INT_IOC(dma) (1 << ((dma) < 4 ? (dma) : ((dma) >> 1) + 2)) +#define CIO2_INT_IOC(dma) (1U << ((dma) < 4U ? (dma) : ((dma) >> 1U) + 2U)) #define CIO2_INT_IOC_SHIFT 0 #define CIO2_INT_IOC_MASK (0x7ff << CIO2_INT_IOC_SHIFT) -#define CIO2_INT_IOS_IOLN(dma) (1 << (((dma) >> 1) + 12)) +#define CIO2_INT_IOS_IOLN(dma) (1U << (((dma) >> 1U) + 12U)) #define CIO2_INT_IOS_IOLN_SHIFT 12 #define CIO2_INT_IOS_IOLN_MASK (0x3ff << CIO2_INT_IOS_IOLN_SHIFT) #define CIO2_INT_IOIE BIT(22) @@ -145,32 +145,32 @@ #define CIO2_INT_IOIRQ BIT(24) #define CIO2_REG_INT_EN_EXT_OE 0x1424 #define CIO2_REG_DMA_DBG 0x1448 -#define CIO2_REG_DMA_DBG_DMA_INDEX_SHIFT 0 +#define CIO2_REG_DMA_DBG_DMA_INDEX_SHIFT 0U #define CIO2_REG_PBM_ARB_CTRL 0x1460 -#define CIO2_PBM_ARB_CTRL_LANES_DIV 0 /* 4-4-2-2 lanes */ -#define CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT 0 +#define CIO2_PBM_ARB_CTRL_LANES_DIV 0U /* 4-4-2-2 lanes */ +#define CIO2_PBM_ARB_CTRL_LANES_DIV_SHIFT 0U #define CIO2_PBM_ARB_CTRL_LE_EN BIT(7) -#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN 2 -#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT 8 -#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP 480 -#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT 16 +#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN 2U +#define CIO2_PBM_ARB_CTRL_PLL_POST_SHTDN_SHIFT 8U +#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP 480U +#define CIO2_PBM_ARB_CTRL_PLL_AHD_WK_UP_SHIFT 16U #define CIO2_REG_PBM_WMCTRL1 0x1464 -#define CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT 0 -#define CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT 8 -#define CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT 16 +#define CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT 0U +#define CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT 8U +#define CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT 16U #define CIO2_PBM_WMCTRL1_TS_COUNT_DISABLE BIT(31) #define CIO2_PBM_WMCTRL1_MIN_2CK (4 << CIO2_PBM_WMCTRL1_MIN_2CK_SHIFT) #define CIO2_PBM_WMCTRL1_MID1_2CK (16 << CIO2_PBM_WMCTRL1_MID1_2CK_SHIFT) #define CIO2_PBM_WMCTRL1_MID2_2CK (21 << CIO2_PBM_WMCTRL1_MID2_2CK_SHIFT) #define CIO2_REG_PBM_WMCTRL2 0x1468 -#define CIO2_PBM_WMCTRL2_HWM_2CK 40 -#define CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT 0 -#define CIO2_PBM_WMCTRL2_LWM_2CK 22 -#define CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT 8 -#define CIO2_PBM_WMCTRL2_OBFFWM_2CK 2 -#define CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT 16 -#define CIO2_PBM_WMCTRL2_TRANSDYN 1 -#define CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT 24 +#define CIO2_PBM_WMCTRL2_HWM_2CK 40U +#define CIO2_PBM_WMCTRL2_HWM_2CK_SHIFT 0U +#define CIO2_PBM_WMCTRL2_LWM_2CK 22U +#define CIO2_PBM_WMCTRL2_LWM_2CK_SHIFT 8U +#define CIO2_PBM_WMCTRL2_OBFFWM_2CK 2U +#define CIO2_PBM_WMCTRL2_OBFFWM_2CK_SHIFT 16U +#define CIO2_PBM_WMCTRL2_TRANSDYN 1U +#define CIO2_PBM_WMCTRL2_TRANSDYN_SHIFT 24U #define CIO2_PBM_WMCTRL2_DYNWMEN BIT(28) #define CIO2_PBM_WMCTRL2_OBFF_MEM_EN BIT(29) #define CIO2_PBM_WMCTRL2_OBFF_CPU_EN BIT(30) @@ -178,12 +178,12 @@ #define CIO2_REG_PBM_TS_COUNT 0x146c #define CIO2_REG_PBM_FOPN_ABORT 0x1474 /* below n = 0..3 */ -#define CIO2_PBM_FOPN_ABORT(n) (0x1 << 8 * (n)) -#define CIO2_PBM_FOPN_FORCE_ABORT(n) (0x2 << 8 * (n)) -#define CIO2_PBM_FOPN_FRAMEOPEN(n) (0x8 << 8 * (n)) +#define CIO2_PBM_FOPN_ABORT(n) (0x1 << 8U * (n)) +#define CIO2_PBM_FOPN_FORCE_ABORT(n) (0x2 << 8U * (n)) +#define CIO2_PBM_FOPN_FRAMEOPEN(n) (0x8 << 8U * (n)) #define CIO2_REG_LTRCTRL 0x1480 #define CIO2_LTRCTRL_LTRDYNEN BIT(16) -#define CIO2_LTRCTRL_LTRSTABLETIME_SHIFT 8 +#define CIO2_LTRCTRL_LTRSTABLETIME_SHIFT 8U #define CIO2_LTRCTRL_LTRSTABLETIME_MASK 0xff #define CIO2_LTRCTRL_LTRSEL1S3 BIT(7) #define CIO2_LTRCTRL_LTRSEL1S2 BIT(6) @@ -195,28 +195,28 @@ #define CIO2_LTRCTRL_LTRSEL2S0 BIT(0) #define CIO2_REG_LTRVAL23 0x1484 #define CIO2_REG_LTRVAL01 0x1488 -#define CIO2_LTRVAL02_VAL_SHIFT 0 -#define CIO2_LTRVAL02_SCALE_SHIFT 10 -#define CIO2_LTRVAL13_VAL_SHIFT 16 -#define CIO2_LTRVAL13_SCALE_SHIFT 26 +#define CIO2_LTRVAL02_VAL_SHIFT 0U +#define CIO2_LTRVAL02_SCALE_SHIFT 10U +#define CIO2_LTRVAL13_VAL_SHIFT 16U +#define CIO2_LTRVAL13_SCALE_SHIFT 26U -#define CIO2_LTRVAL0_VAL 175 +#define CIO2_LTRVAL0_VAL 175U /* Value times 1024 ns */ -#define CIO2_LTRVAL0_SCALE 2 -#define CIO2_LTRVAL1_VAL 90 -#define CIO2_LTRVAL1_SCALE 2 -#define CIO2_LTRVAL2_VAL 90 -#define CIO2_LTRVAL2_SCALE 2 -#define CIO2_LTRVAL3_VAL 90 -#define CIO2_LTRVAL3_SCALE 2 +#define CIO2_LTRVAL0_SCALE 2U +#define CIO2_LTRVAL1_VAL 90U +#define CIO2_LTRVAL1_SCALE 2U +#define CIO2_LTRVAL2_VAL 90U +#define CIO2_LTRVAL2_SCALE 2U +#define CIO2_LTRVAL3_VAL 90U +#define CIO2_LTRVAL3_SCALE 2U #define CIO2_REG_CDMABA(n) (0x1500 + 0x10 * (n)) /* n = 0..19 */ #define CIO2_REG_CDMARI(n) (0x1504 + 0x10 * (n)) -#define CIO2_CDMARI_FBPT_RP_SHIFT 0 +#define CIO2_CDMARI_FBPT_RP_SHIFT 0U #define CIO2_CDMARI_FBPT_RP_MASK 0xff #define CIO2_REG_CDMAC0(n) (0x1508 + 0x10 * (n)) -#define CIO2_CDMAC0_FBPT_LEN_SHIFT 0 -#define CIO2_CDMAC0_FBPT_WIDTH_SHIFT 8 +#define CIO2_CDMAC0_FBPT_LEN_SHIFT 0U +#define CIO2_CDMAC0_FBPT_WIDTH_SHIFT 8U #define CIO2_CDMAC0_FBPT_NS BIT(25) #define CIO2_CDMAC0_DMA_INTR_ON_FS BIT(26) #define CIO2_CDMAC0_DMA_INTR_ON_FE BIT(27) @@ -225,12 +225,12 @@ #define CIO2_CDMAC0_DMA_EN BIT(30) #define CIO2_CDMAC0_DMA_HALTED BIT(31) #define CIO2_REG_CDMAC1(n) (0x150c + 0x10 * (n)) -#define CIO2_CDMAC1_LINENUMINT_SHIFT 0 -#define CIO2_CDMAC1_LINENUMUPDATE_SHIFT 16 +#define CIO2_CDMAC1_LINENUMINT_SHIFT 0U +#define CIO2_CDMAC1_LINENUMUPDATE_SHIFT 16U /* n = 0..3 */ #define CIO2_REG_PXM_PXF_FMT_CFG0(n) (0x1700 + 0x30 * (n)) -#define CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT 0 -#define CIO2_PXM_PXF_FMT_CFG_SID1_SHIFT 16 +#define CIO2_PXM_PXF_FMT_CFG_SID0_SHIFT 0U +#define CIO2_PXM_PXF_FMT_CFG_SID1_SHIFT 16U #define CIO2_PXM_PXF_FMT_CFG_PCK_64B (0 << 0) #define CIO2_PXM_PXF_FMT_CFG_PCK_32B (1 << 0) #define CIO2_PXM_PXF_FMT_CFG_BPP_08 (0 << 2) @@ -249,27 +249,27 @@ #define CIO2_PXM_PXF_FMT_CFG_PSWAP4_2ND_BD (1 << 10) #define CIO2_REG_INT_STS_EXT_IE 0x17e4 #define CIO2_REG_INT_EN_EXT_IE 0x17e8 -#define CIO2_INT_EXT_IE_ECC_RE(n) (0x01 << (8 * (n))) -#define CIO2_INT_EXT_IE_DPHY_NR(n) (0x02 << (8 * (n))) -#define CIO2_INT_EXT_IE_ECC_NR(n) (0x04 << (8 * (n))) -#define CIO2_INT_EXT_IE_CRCERR(n) (0x08 << (8 * (n))) -#define CIO2_INT_EXT_IE_INTERFRAMEDATA(n) (0x10 << (8 * (n))) -#define CIO2_INT_EXT_IE_PKT2SHORT(n) (0x20 << (8 * (n))) -#define CIO2_INT_EXT_IE_PKT2LONG(n) (0x40 << (8 * (n))) -#define CIO2_INT_EXT_IE_IRQ(n) (0x80 << (8 * (n))) +#define CIO2_INT_EXT_IE_ECC_RE(n) (0x01 << (8U * (n))) +#define CIO2_INT_EXT_IE_DPHY_NR(n) (0x02 << (8U * (n))) +#define CIO2_INT_EXT_IE_ECC_NR(n) (0x04 << (8U * (n))) +#define CIO2_INT_EXT_IE_CRCERR(n) (0x08 << (8U * (n))) +#define CIO2_INT_EXT_IE_INTERFRAMEDATA(n) (0x10 << (8U * (n))) +#define CIO2_INT_EXT_IE_PKT2SHORT(n) (0x20 << (8U * (n))) +#define CIO2_INT_EXT_IE_PKT2LONG(n) (0x40 << (8U * (n))) +#define CIO2_INT_EXT_IE_IRQ(n) (0x80 << (8U * (n))) #define CIO2_REG_PXM_FRF_CFG(n) (0x1720 + 0x30 * (n)) #define CIO2_PXM_FRF_CFG_FNSEL BIT(0) #define CIO2_PXM_FRF_CFG_FN_RST BIT(1) #define CIO2_PXM_FRF_CFG_ABORT BIT(2) -#define CIO2_PXM_FRF_CFG_CRC_TH_SHIFT 3 +#define CIO2_PXM_FRF_CFG_CRC_TH_SHIFT 3U #define CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NR BIT(8) #define CIO2_PXM_FRF_CFG_MSK_ECC_RE BIT(9) #define CIO2_PXM_FRF_CFG_MSK_ECC_DPHY_NE BIT(10) -#define CIO2_PXM_FRF_CFG_EVEN_ODD_MODE_SHIFT 11 +#define CIO2_PXM_FRF_CFG_EVEN_ODD_MODE_SHIFT 11U #define CIO2_PXM_FRF_CFG_MASK_CRC_THRES BIT(13) #define CIO2_PXM_FRF_CFG_MASK_CSI_ACCEPT BIT(14) #define CIO2_PXM_FRF_CFG_CIOHC_FS_MODE BIT(15) -#define CIO2_PXM_FRF_CFG_CIOHC_FRST_FRM_SHIFT 16 +#define CIO2_PXM_FRF_CFG_CIOHC_FRST_FRM_SHIFT 16U #define CIO2_REG_PXM_SID2BID0(n) (0x1724 + 0x30 * (n)) #define CIO2_FB_HPLL_FREQ 0x2 #define CIO2_ISCLK_RATIO 0xc @@ -278,14 +278,14 @@ #define CIO2_INT_EN_EXT_OE_MASK 0x8f0fffff -#define CIO2_CGC_CLKGATE_HOLDOFF 3 -#define CIO2_CGC_CSI_CLKGATE_HOLDOFF 5 +#define CIO2_CGC_CLKGATE_HOLDOFF 3U +#define CIO2_CGC_CSI_CLKGATE_HOLDOFF 5U #define CIO2_PXM_FRF_CFG_CRC_TH 16 #define CIO2_INT_EN_EXT_IE_MASK 0xffffffff -#define CIO2_DMA_CHAN 0 +#define CIO2_DMA_CHAN 0U #define CIO2_CSIRX_DLY_CNT_CLANE_IDX -1 @@ -302,8 +302,8 @@ #define CIO2_CSIRX_DLY_CNT_TERMEN_DEFAULT 0x4 #define CIO2_CSIRX_DLY_CNT_SETTLE_DEFAULT 0x570 -#define CIO2_PMCSR_OFFSET 4 -#define CIO2_PMCSR_D0D3_SHIFT 2 +#define CIO2_PMCSR_OFFSET 4U +#define CIO2_PMCSR_D0D3_SHIFT 2U #define CIO2_PMCSR_D3 0x3 struct cio2_csi2_timing { @@ -335,6 +335,7 @@ struct cio2_queue { /* Subdev, /dev/v4l-subdevX */ struct v4l2_subdev subdev; + struct mutex subdev_lock; /* Serialise acces to subdev_fmt field */ struct media_pad subdev_pads[CIO2_PADS]; struct v4l2_mbus_framefmt subdev_fmt; atomic_t frame_sequence; diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c index 37bd386f3ed8..ce1e8737b14b 100644 --- a/drivers/media/pci/mantis/hopper_vp3028.c +++ b/drivers/media/pci/mantis/hopper_vp3028.c @@ -33,7 +33,7 @@ static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend * { struct i2c_adapter *adapter = &mantis->adapter; struct mantis_hwconfig *config = mantis->hwconfig; - int err = 0; + int err; mantis_gpio_set_bits(mantis, config->reset, 0); msleep(100); diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 9a6a6b68f8e3..0f9d6b9edb90 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -868,8 +868,11 @@ static int buffer_activate(struct saa7134_dev *dev, lines_uv = dev->height >> dev->fmt->vshift; base2 = base + bpl * dev->height; base3 = base2 + bpl_uv * lines_uv; - if (dev->fmt->uvswap) - tmp = base2, base2 = base3, base3 = tmp; + if (dev->fmt->uvswap) { + tmp = base2; + base2 = base3; + base3 = tmp; + } video_dbg("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", bpl_uv,lines_uv,base2,base3); if (V4L2_FIELD_HAS_BOTH(dev->field)) { @@ -1265,9 +1268,7 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = video_drvdata(file); - struct v4l2_clip __user *clips = f->fmt.win.clips; u32 clipcount = f->fmt.win.clipcount; - int err = 0; int i; if (saa7134_no_overlay > 0) { @@ -1275,20 +1276,20 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, return -EINVAL; } f->fmt.win = dev->win; - f->fmt.win.clips = clips; - if (clips == NULL) - clipcount = 0; + if (!f->fmt.win.clips) { + f->fmt.win.clipcount = 0; + return 0; + } if (dev->nclips < clipcount) clipcount = dev->nclips; f->fmt.win.clipcount = clipcount; - for (i = 0; !err && i < clipcount; i++) { - if (copy_to_user(&f->fmt.win.clips[i].c, &dev->clips[i].c, - sizeof(struct v4l2_rect))) - err = -EFAULT; + for (i = 0; i < clipcount; i++) { + memcpy(&f->fmt.win.clips[i].c, &dev->clips[i].c, + sizeof(struct v4l2_rect)); } - return err; + return 0; } static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, @@ -1396,9 +1397,8 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, dev->win = f->fmt.win; dev->nclips = f->fmt.win.clipcount; - if (copy_from_user(dev->clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * dev->nclips)) - return -EFAULT; + memcpy(dev->clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * dev->nclips); if (priv == dev->overlay_owner) { spin_lock_irqsave(&dev->slock, flags); diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index 129a1f8ebe1a..73fc901ecf3d 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -641,16 +641,17 @@ static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio * struct mxb *mxb = (struct mxb *)dev->ext_priv; DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) { - if (mxb->cur_audinput != a->index) { - mxb->cur_audinput = a->index; - tea6420_route(mxb, a->index); - if (mxb->cur_audinput == 0) - mxb_update_audmode(mxb); - } - return 0; + if (a->index >= 32 || + !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) + return -EINVAL; + + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); } - return -EINVAL; + return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 6c08b77bfd47..f3a4e575a782 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1139,32 +1139,21 @@ static int saa7164_seq_show(struct seq_file *m, void *v) return 0; } -static const struct seq_operations saa7164_seq_ops = { +static const struct seq_operations saa7164_sops = { .start = saa7164_seq_start, .next = saa7164_seq_next, .stop = saa7164_seq_stop, .show = saa7164_seq_show, }; -static int saa7164_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &saa7164_seq_ops); -} - -static const struct file_operations saa7164_operations = { - .owner = THIS_MODULE, - .open = saa7164_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; +DEFINE_SEQ_ATTRIBUTE(saa7164); static struct dentry *saa7614_dentry; static void __init saa7164_debugfs_create(void) { saa7614_dentry = debugfs_create_file("saa7164", 0444, NULL, NULL, - &saa7164_operations); + &saa7164_fops); } static void __exit saa7164_debugfs_remove(void) diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 906ce86437ae..6cebad665565 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -124,9 +124,10 @@ static int snd_solo_pcm_open(struct snd_pcm_substream *ss) if (solo_pcm == NULL) goto oom; - solo_pcm->g723_buf = pci_alloc_consistent(solo_dev->pdev, - G723_PERIOD_BYTES, - &solo_pcm->g723_dma); + solo_pcm->g723_buf = dma_alloc_coherent(&solo_dev->pdev->dev, + G723_PERIOD_BYTES, + &solo_pcm->g723_dma, + GFP_KERNEL); if (solo_pcm->g723_buf == NULL) goto oom; @@ -148,8 +149,8 @@ static int snd_solo_pcm_close(struct snd_pcm_substream *ss) struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; - pci_free_consistent(solo_pcm->solo_dev->pdev, G723_PERIOD_BYTES, - solo_pcm->g723_buf, solo_pcm->g723_dma); + dma_free_coherent(&solo_pcm->solo_dev->pdev->dev, G723_PERIOD_BYTES, + solo_pcm->g723_buf, solo_pcm->g723_dma); kfree(solo_pcm); return 0; @@ -385,7 +386,7 @@ int solo_g723_init(struct solo_dev *solo_dev) ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); if (ret < 0) - return ret; + goto snd_error; ret = solo_snd_pcm_init(solo_dev); if (ret < 0) diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index db2afc6a5fcb..ca70a864a3ef 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -37,16 +37,16 @@ int solo_p2m_dma(struct solo_dev *solo_dev, int wr, if (WARN_ON_ONCE(!size)) return -EINVAL; - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(solo_dev->pdev, dma_addr)) + dma_addr = dma_map_single(&solo_dev->pdev->dev, sys_addr, size, + wr ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_mapping_error(&solo_dev->pdev->dev, dma_addr)) return -ENOMEM; ret = solo_p2m_dma_t(solo_dev, wr, dma_addr, ext_addr, size, repeat, ext_size); - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + dma_unmap_single(&solo_dev->pdev->dev, dma_addr, size, + wr ? DMA_TO_DEVICE : DMA_FROM_DEVICE); return ret; } diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 3cf7bd6186aa..0abcad4e84fa 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1286,10 +1286,11 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, memcpy(solo_enc->jpeg_header, jpeg_header, solo_enc->jpeg_len); solo_enc->desc_nelts = 32; - solo_enc->desc_items = pci_alloc_consistent(solo_dev->pdev, - sizeof(struct solo_p2m_desc) * - solo_enc->desc_nelts, - &solo_enc->desc_dma); + solo_enc->desc_items = dma_alloc_coherent(&solo_dev->pdev->dev, + sizeof(struct solo_p2m_desc) * + solo_enc->desc_nelts, + &solo_enc->desc_dma, + GFP_KERNEL); ret = -ENOMEM; if (solo_enc->desc_items == NULL) goto hdl_free; @@ -1317,9 +1318,9 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, vdev_release: video_device_release(solo_enc->vfd); pci_free: - pci_free_consistent(solo_enc->solo_dev->pdev, - sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, - solo_enc->desc_items, solo_enc->desc_dma); + dma_free_coherent(&solo_enc->solo_dev->pdev->dev, + sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, + solo_enc->desc_items, solo_enc->desc_dma); hdl_free: v4l2_ctrl_handler_free(hdl); kfree(solo_enc); @@ -1331,9 +1332,9 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc) if (solo_enc == NULL) return; - pci_free_consistent(solo_enc->solo_dev->pdev, - sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, - solo_enc->desc_items, solo_enc->desc_dma); + dma_free_coherent(&solo_enc->solo_dev->pdev->dev, + sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts, + solo_enc->desc_items, solo_enc->desc_dma); video_unregister_device(solo_enc->vfd); v4l2_ctrl_handler_free(&solo_enc->hdl); kfree(solo_enc); @@ -1346,9 +1347,9 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) init_waitqueue_head(&solo_dev->ring_thread_wait); solo_dev->vh_size = sizeof(vop_header); - solo_dev->vh_buf = pci_alloc_consistent(solo_dev->pdev, - solo_dev->vh_size, - &solo_dev->vh_dma); + solo_dev->vh_buf = dma_alloc_coherent(&solo_dev->pdev->dev, + solo_dev->vh_size, + &solo_dev->vh_dma, GFP_KERNEL); if (solo_dev->vh_buf == NULL) return -ENOMEM; @@ -1363,8 +1364,8 @@ int solo_enc_v4l2_init(struct solo_dev *solo_dev, unsigned nr) while (i--) solo_enc_free(solo_dev->v4l2_enc[i]); - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); + dma_free_coherent(&solo_dev->pdev->dev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); solo_dev->vh_buf = NULL; return ret; } @@ -1391,6 +1392,6 @@ void solo_enc_v4l2_exit(struct solo_dev *solo_dev) solo_enc_free(solo_dev->v4l2_enc[i]); if (solo_dev->vh_buf) - pci_free_consistent(solo_dev->pdev, solo_dev->vh_size, - solo_dev->vh_buf, solo_dev->vh_dma); + dma_free_coherent(&solo_dev->pdev->dev, solo_dev->vh_size, + solo_dev->vh_buf, solo_dev->vh_dma); } diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 2f7069e19b78..d74ee0ecfb36 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -1250,7 +1250,8 @@ static void vpeirq(struct tasklet_struct *t) return; /* Ensure streamed PCI data is synced to CPU */ - pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); + dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, + budget->pt.nents, DMA_FROM_DEVICE); #if 0 /* track rps1 activity */ @@ -2637,7 +2638,8 @@ static int av7110_attach(struct saa7146_dev* dev, av7110->arm_thread = NULL; /* allocate and init buffers */ - av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus); + av7110->debi_virt = dma_alloc_coherent(&pdev->dev, 8192, + &av7110->debi_bus, GFP_KERNEL); if (!av7110->debi_virt) goto err_saa71466_vfree_4; @@ -2726,7 +2728,8 @@ err_av7110_av_exit_7: err_iobuf_vfree_6: vfree(av7110->iobuf); err_pci_free_5: - pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus); + dma_free_coherent(&pdev->dev, 8192, av7110->debi_virt, + av7110->debi_bus); err_saa71466_vfree_4: if (av7110->grabbing) saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); @@ -2779,8 +2782,8 @@ static int av7110_detach(struct saa7146_dev* saa) av7110_av_exit(av7110); vfree(av7110->iobuf); - pci_free_consistent(saa->pci, 8192, av7110->debi_virt, - av7110->debi_bus); + dma_free_coherent(&saa->pci->dev, 8192, av7110->debi_virt, + av7110->debi_bus); i2c_del_adapter(&av7110->i2c_adap); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 7e152bbb4fa6..35a18d388f3f 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -147,6 +147,24 @@ config VIDEO_RENESAS_CEU help This is a v4l2 driver for the Renesas CEU Interface +config VIDEO_ROCKCHIP_ISP1 + tristate "Rockchip Image Signal Processing v1 Unit driver" + depends on VIDEO_V4L2 && OF + depends on ARCH_ROCKCHIP || COMPILE_TEST + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_FWNODE + select GENERIC_PHY_MIPI_DPHY + default n + help + Enable this to support the Image Signal Processing (ISP) module + present in RK3399 SoCs. + + To compile this driver as a module, choose M here: the module + will be called rockchip-isp1. + source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" @@ -183,7 +201,7 @@ if V4L_MEM2MEM_DRIVERS config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" - depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST) + depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST) select SRAM select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC @@ -288,6 +306,19 @@ config VIDEO_MEM2MEM_DEINTERLACE help Generic deinterlacing V4L2 driver. +config VIDEO_MESON_GE2D + tristate "Amlogic 2D Graphic Acceleration Unit" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MESON || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a v4l2 driver for Amlogic GE2D 2D graphics accelerator. + GE2D is a standalone 2D graphic acceleration unit, with color converter, + image scaling, BitBLT & alpha blending operations. + + To compile this driver as a module choose m here. + config VIDEO_SAMSUNG_S5P_G2D tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" depends on VIDEO_DEV && VIDEO_V4L2 diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 62b6cdc8c730..1d63aa956bcd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ +obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip/rkisp1/ obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/ obj-y += omap/ @@ -80,3 +81,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += sunxi/ + +obj-$(CONFIG_VIDEO_MESON_GE2D) += meson/ge2d/ diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index bf75927bac4e..2f42808c43a4 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/log2.h> #include <linux/platform_device.h> +#include <linux/ratelimit.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -293,12 +294,11 @@ static bool coda_bitstream_try_queue(struct coda_ctx *ctx, coda_dbg(1, ctx, "could not parse header, sequence initialization might fail\n"); } - } - /* Add padding before the first buffer, if it is too small */ - if (ctx->qsequence == 0 && payload < 512 && - ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) - coda_h264_bitstream_pad(ctx, 512 - payload); + /* Add padding before the first buffer, if it is too small */ + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264) + coda_h264_bitstream_pad(ctx, 512 - payload); + } ret = coda_bitstream_queue(ctx, vaddr, payload); if (ret < 0) { @@ -1837,6 +1837,29 @@ static bool coda_reorder_enable(struct coda_ctx *ctx) return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; } +static void coda_decoder_drop_used_metas(struct coda_ctx *ctx) +{ + struct coda_buffer_meta *meta, *tmp; + + /* + * All metas that end at or before the RD pointer (fifo out), + * are now consumed by the VPU and should be released. + */ + spin_lock(&ctx->buffer_meta_lock); + list_for_each_entry_safe(meta, tmp, &ctx->buffer_meta_list, list) { + if (ctx->bitstream_fifo.kfifo.out >= meta->end) { + coda_dbg(2, ctx, "releasing meta: seq=%d start=%d end=%d\n", + meta->sequence, meta->start, meta->end); + + list_del(&meta->list); + ctx->num_metas--; + ctx->first_frame_sequence++; + kfree(meta); + } + } + spin_unlock(&ctx->buffer_meta_lock); +} + static int __coda_decoder_seq_init(struct coda_ctx *ctx) { struct coda_q_data *q_data_src, *q_data_dst; @@ -1922,10 +1945,17 @@ static int __coda_decoder_seq_init(struct coda_ctx *ctx) } ctx->sequence_offset = ~0U; ctx->initialized = 1; + ctx->first_frame_sequence = 0; /* Update kfifo out pointer from coda bitstream read pointer */ coda_kfifo_sync_from_device(ctx); + /* + * After updating the read pointer, we need to check if + * any metas are consumed and should be released. + */ + coda_decoder_drop_used_metas(ctx); + if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) { v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT failed, error code = 0x%x\n", @@ -2005,21 +2035,13 @@ static void coda_dec_seq_init_work(struct work_struct *work) struct coda_ctx *ctx = container_of(work, struct coda_ctx, seq_init_work); struct coda_dev *dev = ctx->dev; - int ret; mutex_lock(&ctx->buffer_mutex); mutex_lock(&dev->coda_mutex); - if (ctx->initialized == 1) - goto out; - - ret = __coda_decoder_seq_init(ctx); - if (ret < 0) - goto out; - - ctx->initialized = 1; + if (!ctx->initialized) + __coda_decoder_seq_init(ctx); -out: mutex_unlock(&dev->coda_mutex); mutex_unlock(&ctx->buffer_mutex); } @@ -2348,9 +2370,12 @@ static void coda_finish_decode(struct coda_ctx *ctx) } err_mb = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB); - if (err_mb > 0) - v4l2_err(&dev->v4l2_dev, - "errors in %d macroblocks\n", err_mb); + if (err_mb > 0) { + if (__ratelimit(&dev->mb_err_rs)) + coda_dbg(1, ctx, "errors in %d macroblocks\n", err_mb); + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, + v4l2_ctrl_g_ctrl(ctx->mb_err_cnt_ctrl) + err_mb); + } if (dev->devtype->product == CODA_HX4 || dev->devtype->product == CODA_7541) { @@ -2404,12 +2429,16 @@ static void coda_finish_decode(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "decoded frame index out of range: %d\n", decoded_idx); } else { + int sequence; + decoded_frame = &ctx->internal_frames[decoded_idx]; val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM); if (ctx->sequence_offset == -1) ctx->sequence_offset = val; - val -= ctx->sequence_offset; + + sequence = val + ctx->first_frame_sequence + - ctx->sequence_offset; spin_lock(&ctx->buffer_meta_lock); if (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, @@ -2424,10 +2453,10 @@ static void coda_finish_decode(struct coda_ctx *ctx) * should be enough to detect most errors and saves us * from doing different things based on the format. */ - if ((val & 0xffff) != (meta->sequence & 0xffff)) { + if ((sequence & 0xffff) != (meta->sequence & 0xffff)) { v4l2_err(&dev->v4l2_dev, "sequence number mismatch (%d(%d) != %d)\n", - val, ctx->sequence_offset, + sequence, ctx->sequence_offset, meta->sequence); } decoded_frame->meta = *meta; @@ -2437,7 +2466,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n"); memset(&decoded_frame->meta, 0, sizeof(struct coda_buffer_meta)); - decoded_frame->meta.sequence = val; + decoded_frame->meta.sequence = sequence; decoded_frame->meta.last = false; ctx->sequence_offset++; } diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 87a2c706f747..995e95272e51 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <linux/of.h> -#include <linux/platform_data/media/coda.h> +#include <linux/ratelimit.h> #include <linux/reset.h> #include <media/v4l2-ctrls.h> @@ -172,7 +172,7 @@ struct coda_video_device { }; static const struct coda_video_device coda_bit_encoder = { - .name = "coda-encoder", + .name = "coda-video-encoder", .type = CODA_INST_ENCODER, .ops = &coda_bit_encode_ops, .src_formats = { @@ -202,7 +202,7 @@ static const struct coda_video_device coda_bit_jpeg_encoder = { }; static const struct coda_video_device coda_bit_decoder = { - .name = "coda-decoder", + .name = "coda-video-decoder", .type = CODA_INST_DECODER, .ops = &coda_bit_decode_ops, .src_formats = { @@ -2062,6 +2062,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG) ctx->params.gop_size = 1; ctx->gopcounter = ctx->params.gop_size - 1; + v4l2_ctrl_s_ctrl(ctx->mb_err_cnt_ctrl, 0); ret = ctx->ops->start_streaming(ctx); if (ctx->inst_type == CODA_INST_DECODER) { @@ -2462,6 +2463,15 @@ static void coda_decode_ctrls(struct coda_ctx *ctx) ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; } +static const struct v4l2_ctrl_config coda_mb_err_cnt_ctrl_config = { + .id = V4L2_CID_CODA_MB_ERR_CNT, + .name = "Macroblocks Error Count", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x7fffffff, + .step = 1, +}; + static int coda_ctrls_setup(struct coda_ctx *ctx) { v4l2_ctrl_handler_init(&ctx->ctrls, 2); @@ -2484,6 +2494,12 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) 1, 1, 1, 1); if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) coda_decode_ctrls(ctx); + + ctx->mb_err_cnt_ctrl = v4l2_ctrl_new_custom(&ctx->ctrls, + &coda_mb_err_cnt_ctrl_config, + NULL); + if (ctx->mb_err_cnt_ctrl) + ctx->mb_err_cnt_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; } if (ctx->ctrls.error) { @@ -2851,17 +2867,17 @@ err_clk_per: static int coda_register_device(struct coda_dev *dev, int i) { struct video_device *vfd = &dev->vfd[i]; - enum coda_inst_type type; + const char *name; int ret; if (i >= dev->devtype->num_vdevs) return -EINVAL; - type = dev->devtype->vdevs[i]->type; + name = dev->devtype->vdevs[i]->name; strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); vfd->fops = &coda_fops; vfd->ioctl_ops = &coda_ioctl_ops; - vfd->release = video_device_release_empty, + vfd->release = video_device_release_empty; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; @@ -2876,8 +2892,7 @@ static int coda_register_device(struct coda_dev *dev, int i) ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (!ret) v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", - type == CODA_INST_ENCODER ? "encoder" : "decoder", - video_device_node_name(vfd)); + name, video_device_node_name(vfd)); return ret; } @@ -3086,13 +3101,6 @@ static const struct coda_devtype coda_devdata[] = { }, }; -static const struct platform_device_id coda_platform_ids[] = { - { .name = "coda-imx27", .driver_data = CODA_IMX27 }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, coda_platform_ids); - -#ifdef CONFIG_OF static const struct of_device_id coda_dt_ids[] = { { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, { .compatible = "fsl,imx51-vpu", .data = &coda_devdata[CODA_IMX51] }, @@ -3102,14 +3110,9 @@ static const struct of_device_id coda_dt_ids[] = { { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, coda_dt_ids); -#endif static int coda_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev); - const struct platform_device_id *pdev_id; - struct coda_platform_data *pdata = pdev->dev.platform_data; struct device_node *np = pdev->dev.of_node; struct gen_pool *pool; struct coda_dev *dev; @@ -3119,14 +3122,7 @@ static int coda_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); - - if (of_id) - dev->devtype = of_id->data; - else if (pdev_id) - dev->devtype = &coda_devdata[pdev_id->driver_data]; - else - return -EINVAL; + dev->devtype = of_device_get_match_data(&pdev->dev); dev->dev = &pdev->dev; dev->clk_per = devm_clk_get(&pdev->dev, "per"); @@ -3154,7 +3150,7 @@ static int coda_probe(struct platform_device *pdev) return irq; ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, - dev_name(&pdev->dev), dev); + CODA_NAME "-video", dev); if (ret < 0) { dev_err(&pdev->dev, "failed to request irq: %d\n", ret); return ret; @@ -3168,7 +3164,7 @@ static int coda_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda9_jpeg_irq_handler, - IRQF_ONESHOT, CODA_NAME " jpeg", + IRQF_ONESHOT, CODA_NAME "-jpeg", dev); if (ret < 0) { dev_err(&pdev->dev, "failed to request jpeg irq\n"); @@ -3184,10 +3180,8 @@ static int coda_probe(struct platform_device *pdev) return ret; } - /* Get IRAM pool from device tree or platform data */ + /* Get IRAM pool from device tree */ pool = of_gen_pool_get(np, "iram", 0); - if (!pool && pdata) - pool = gen_pool_get(pdata->iram_dev, NULL); if (!pool) { dev_err(&pdev->dev, "iram pool not available\n"); return -ENOMEM; @@ -3203,6 +3197,7 @@ static int coda_probe(struct platform_device *pdev) if (ret) return ret; + ratelimit_default_init(&dev->mb_err_rs); mutex_init(&dev->dev_mutex); mutex_init(&dev->coda_mutex); ida_init(&dev->ida); @@ -3325,7 +3320,6 @@ static struct platform_driver coda_driver = { .of_match_table = of_match_ptr(coda_dt_ids), .pm = &coda_pm_ops, }, - .id_table = coda_platform_ids, }; module_platform_driver(coda_driver); diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index b81f3aca9209..dcf35641c603 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -17,6 +17,7 @@ #include <linux/mutex.h> #include <linux/kfifo.h> #include <linux/videodev2.h> +#include <linux/ratelimit.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -28,6 +29,13 @@ #define CODA_MAX_FRAMEBUFFERS 19 #define FMO_SLICE_SAVE_BUF_SIZE (32) +/* + * This control allows applications to read the per-stream + * (i.e. per-context) Macroblocks Error Count. This value + * is CODA specific. + */ +#define V4L2_CID_CODA_MB_ERR_CNT (V4L2_CID_USER_CODA_BASE + 0) + enum { V4L2_M2M_SRC = 0, V4L2_M2M_DST = 1, @@ -92,6 +100,7 @@ struct coda_dev { struct v4l2_m2m_dev *m2m_dev; struct ida ida; struct dentry *debugfs_root; + struct ratelimit_state mb_err_rs; }; struct coda_codec { @@ -242,6 +251,7 @@ struct coda_ctx { struct v4l2_ctrl *mpeg2_level_ctrl; struct v4l2_ctrl *mpeg4_profile_ctrl; struct v4l2_ctrl *mpeg4_level_ctrl; + struct v4l2_ctrl *mb_err_cnt_ctrl; struct v4l2_fh fh; int gopcounter; int runcounter; @@ -259,6 +269,7 @@ struct coda_ctx { struct list_head buffer_meta_list; spinlock_t buffer_meta_lock; int num_metas; + unsigned int first_frame_sequence; struct coda_aux_buf workbuf; int num_internal_frames; int idx; diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index c98edb67cfb2..c53cecd072b1 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -1075,10 +1075,14 @@ fail_base_iomap: release_mem_region(res->start, resource_size(res)); i--; fail_nobase_res: - if (isif_cfg.base_addr) + if (isif_cfg.base_addr) { iounmap(isif_cfg.base_addr); - if (isif_cfg.linear_tbl0_addr) + isif_cfg.base_addr = NULL; + } + if (isif_cfg.linear_tbl0_addr) { iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; + } while (i >= 0) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); @@ -1096,8 +1100,11 @@ static int isif_remove(struct platform_device *pdev) int i = 0; iounmap(isif_cfg.base_addr); + isif_cfg.base_addr = NULL; iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; iounmap(isif_cfg.linear_tbl1_addr); + isif_cfg.linear_tbl1_addr = NULL; while (i < 3) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (res) diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 6000a4e789ad..13c838d3f947 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -201,7 +201,7 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) if (!list_empty(&cap->pending_buf_q)) { v_buf = fimc_pending_queue_pop(cap); - fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); + fimc_hw_set_output_addr(fimc, &v_buf->addr, cap->buf_index); v_buf->index = cap->buf_index; /* Move the buffer to the capture active queue */ @@ -410,7 +410,7 @@ static void buffer_queue(struct vb2_buffer *vb) int min_bufs; spin_lock_irqsave(&fimc->slock, flags); - fimc_prepare_addr(ctx, &buf->vb.vb2_buf, &ctx->d_frame, &buf->paddr); + fimc_prepare_addr(ctx, &buf->vb.vb2_buf, &ctx->d_frame, &buf->addr); if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) && !test_bit(ST_CAPT_STREAM, &fimc->state) && @@ -419,7 +419,7 @@ static void buffer_queue(struct vb2_buffer *vb) int buf_id = (vid_cap->reqbufs_count == 1) ? -1 : vid_cap->buf_index; - fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id); + fimc_hw_set_output_addr(fimc, &buf->addr, buf_id); buf->index = vid_cap->buf_index; fimc_active_queue_add(vid_cap, buf); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 08d1f39a914c..bfdee771cef9 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -214,11 +214,13 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) while (sh--) { u32 tmp = 1 << sh; if (src >= tar * tmp) { - *shift = sh, *ratio = tmp; + *shift = sh; + *ratio = tmp; return 0; } } - *shift = 0, *ratio = 1; + *shift = 0; + *ratio = 1; return 0; } @@ -325,7 +327,7 @@ out: /* The color format (colplanes, memplanes) must be already configured. */ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, - struct fimc_frame *frame, struct fimc_addr *paddr) + struct fimc_frame *frame, struct fimc_addr *addr) { int ret = 0; u32 pix_size; @@ -338,42 +340,40 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, dbg("memplanes= %d, colplanes= %d, pix_size= %d", frame->fmt->memplanes, frame->fmt->colplanes, pix_size); - paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); + addr->y = vb2_dma_contig_plane_dma_addr(vb, 0); if (frame->fmt->memplanes == 1) { switch (frame->fmt->colplanes) { case 1: - paddr->cb = 0; - paddr->cr = 0; + addr->cb = 0; + addr->cr = 0; break; case 2: /* decompose Y into Y/Cb */ - paddr->cb = (u32)(paddr->y + pix_size); - paddr->cr = 0; + addr->cb = (u32)(addr->y + pix_size); + addr->cr = 0; break; case 3: - paddr->cb = (u32)(paddr->y + pix_size); + addr->cb = (u32)(addr->y + pix_size); /* decompose Y into Y/Cb/Cr */ if (FIMC_FMT_YCBCR420 == frame->fmt->color) - paddr->cr = (u32)(paddr->cb - + (pix_size >> 2)); + addr->cr = (u32)(addr->cb + (pix_size >> 2)); else /* 422 */ - paddr->cr = (u32)(paddr->cb - + (pix_size >> 1)); + addr->cr = (u32)(addr->cb + (pix_size >> 1)); break; default: return -EINVAL; } } else if (!frame->fmt->mdataplanes) { if (frame->fmt->memplanes >= 2) - paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); + addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); if (frame->fmt->memplanes == 3) - paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); + addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); } - dbg("PHYS_ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", - paddr->y, paddr->cb, paddr->cr, ret); + dbg("DMA ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", + addr->y, addr->cb, addr->cr, ret); return ret; } diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index e4a56232907a..58b72a052cef 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -202,10 +202,10 @@ struct fimc_scaler { }; /** - * struct fimc_addr - the FIMC physical address set for DMA - * @y: luminance plane physical address - * @cb: Cb plane physical address - * @cr: Cr plane physical address + * struct fimc_addr - the FIMC address set for DMA + * @y: luminance plane address + * @cb: Cb plane address + * @cr: Cr plane address */ struct fimc_addr { u32 y; @@ -217,13 +217,13 @@ struct fimc_addr { * struct fimc_vid_buffer - the driver's video buffer * @vb: v4l videobuf buffer * @list: linked list structure for buffer queue - * @paddr: precalculated physical address set + * @addr: precalculated DMA address set * @index: buffer index for the output DMA engine */ struct fimc_vid_buffer { struct vb2_v4l2_buffer vb; struct list_head list; - struct fimc_addr paddr; + struct fimc_addr addr; int index; }; @@ -239,7 +239,7 @@ struct fimc_vid_buffer { * @height: image pixel weight * @payload: image size in bytes (w x h x bpp) * @bytesperline: bytesperline value for each plane - * @paddr: image frame buffer physical addresses + * @addr: image frame buffer DMA addresses * @dma_offset: DMA offset in bytes * @fmt: fimc color format pointer */ @@ -254,7 +254,7 @@ struct fimc_frame { u32 height; unsigned int payload[VIDEO_MAX_PLANES]; unsigned int bytesperline[VIDEO_MAX_PLANES]; - struct fimc_addr paddr; + struct fimc_addr addr; struct fimc_dma_offset dma_offset; struct fimc_fmt *fmt; u8 alpha; @@ -626,7 +626,7 @@ int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int fimc_set_scaler_info(struct fimc_ctx *ctx); int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags); int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, - struct fimc_frame *frame, struct fimc_addr *paddr); + struct fimc_frame *frame, struct fimc_addr *addr); void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); void fimc_set_yuv_order(struct fimc_ctx *ctx); void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 32ab01e89196..972d9601d236 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -268,7 +268,7 @@ int fimc_is_cpu_set_power(struct fimc_is *is, int on) mcuctl_write(0, is, REG_WDT_ISP); /* Cortex-A5 start address setting */ - mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR); + mcuctl_write(is->memory.addr, is, MCUCTL_REG_BBOAR); /* Enable and start Cortex-A5 */ pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION); @@ -335,26 +335,26 @@ static int fimc_is_alloc_cpu_memory(struct fimc_is *is) struct device *dev = &is->pdev->dev; is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE, - &is->memory.paddr, GFP_KERNEL); + &is->memory.addr, GFP_KERNEL); if (is->memory.vaddr == NULL) return -ENOMEM; is->memory.size = FIMC_IS_CPU_MEM_SIZE; - dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr); + dev_info(dev, "FIMC-IS CPU memory base: %pad\n", &is->memory.addr); - if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) { + if (((u32)is->memory.addr) & FIMC_IS_FW_ADDR_MASK) { dev_err(dev, "invalid firmware memory alignment: %#x\n", - (u32)is->memory.paddr); + (u32)is->memory.addr); dma_free_coherent(dev, is->memory.size, is->memory.vaddr, - is->memory.paddr); + is->memory.addr); return -EIO; } is->is_p_region = (struct is_region *)(is->memory.vaddr + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE); - is->is_dma_p_region = is->memory.paddr + + is->is_dma_p_region = is->memory.addr + FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE; is->is_shared_region = (struct is_share_region *)(is->memory.vaddr + @@ -370,7 +370,7 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is) return; dma_free_coherent(dev, is->memory.size, is->memory.vaddr, - is->memory.paddr); + is->memory.addr); } static void fimc_is_load_firmware(const struct firmware *fw, void *context) @@ -415,7 +415,7 @@ static void fimc_is_load_firmware(const struct firmware *fw, void *context) dev_info(dev, "loaded firmware: %s, rev. %s\n", is->fw.info, is->fw.version); - dev_dbg(dev, "FW size: %zu, paddr: %pad\n", fw->size, &is->memory.paddr); + dev_dbg(dev, "FW size: %zu, DMA addr: %pad\n", fw->size, &is->memory.addr); is->is_shared_region->chip_id = 0xe4412; is->is_shared_region->chip_rev_no = 1; @@ -698,7 +698,7 @@ int fimc_is_hw_initialize(struct fimc_is *is) } pr_debug("shared region: %pad, parameter region: %pad\n", - &is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET, + &is->memory.addr + FIMC_IS_SHARED_REGION_OFFSET, &is->is_dma_p_region); is->setfile.sub_index = 0; diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 7ee96a058d40..ce30b007bc55 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -174,7 +174,7 @@ struct is_af_info { struct fimc_is_firmware { const struct firmware *f_w; - dma_addr_t paddr; + dma_addr_t addr; void *vaddr; unsigned int size; @@ -185,8 +185,8 @@ struct fimc_is_firmware { }; struct fimc_is_memory { - /* physical base address */ - dma_addr_t paddr; + /* DMA base address */ + dma_addr_t addr; /* virtual base address */ void *vaddr; /* total length */ diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index 85f765e0f4e1..57996b4104b4 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -272,9 +272,9 @@ void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf) index = buf->index; if (index == 0) - writel(buf->paddr, dev->regs + FLITE_REG_CIOSA); + writel(buf->addr, dev->regs + FLITE_REG_CIOSA); else - writel(buf->paddr, dev->regs + FLITE_REG_CIOSAN(index - 1)); + writel(buf->addr, dev->regs + FLITE_REG_CIOSAN(index - 1)); cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ); cfg |= BIT(index); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index fdd0d369b192..fe20af3a7178 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -409,7 +409,7 @@ static void buffer_queue(struct vb2_buffer *vb) unsigned long flags; spin_lock_irqsave(&fimc->slock, flags); - buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + buf->addr = vb2_dma_contig_plane_dma_addr(vb, 0); buf->index = fimc->buf_index++; if (fimc->buf_index >= fimc->reqbufs_count) diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index e6846c5fc9ac..e2d4d628b5aa 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -93,13 +93,13 @@ struct flite_frame { * struct flite_buffer - video buffer structure * @vb: vb2 buffer * @list: list head for the buffers queue - * @paddr: DMA buffer start address + * @addr: DMA buffer start address * @index: DMA start address register's index */ struct flite_buffer { struct vb2_v4l2_buffer vb; struct list_head list; - dma_addr_t paddr; + dma_addr_t addr; unsigned short index; }; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 4acb179556c4..c9704a147e5c 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -115,12 +115,12 @@ static void fimc_device_run(void *priv) } src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->paddr); + ret = fimc_prepare_addr(ctx, &src_vb->vb2_buf, sf, &sf->addr); if (ret) goto dma_unlock; dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->paddr); + ret = fimc_prepare_addr(ctx, &dst_vb->vb2_buf, df, &df->addr); if (ret) goto dma_unlock; @@ -152,8 +152,8 @@ static void fimc_device_run(void *priv) fimc_hw_set_rgb_alpha(ctx); fimc_hw_set_output_path(ctx); } - fimc_hw_set_input_addr(fimc, &sf->paddr); - fimc_hw_set_output_addr(fimc, &df->paddr, -1); + fimc_hw_set_input_addr(fimc, &sf->addr); + fimc_hw_set_output_addr(fimc, &df->addr, -1); fimc_activate_capture(ctx); ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 8764999a5fd7..95165a2cc7d1 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -526,30 +526,30 @@ void fimc_hw_set_output_path(struct fimc_ctx *ctx) writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } -void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) +void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *addr) { u32 cfg = readl(dev->regs + FIMC_REG_CIREAL_ISIZE); cfg |= FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); - writel(paddr->y, dev->regs + FIMC_REG_CIIYSA(0)); - writel(paddr->cb, dev->regs + FIMC_REG_CIICBSA(0)); - writel(paddr->cr, dev->regs + FIMC_REG_CIICRSA(0)); + writel(addr->y, dev->regs + FIMC_REG_CIIYSA(0)); + writel(addr->cb, dev->regs + FIMC_REG_CIICBSA(0)); + writel(addr->cr, dev->regs + FIMC_REG_CIICRSA(0)); cfg &= ~FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); } void fimc_hw_set_output_addr(struct fimc_dev *dev, - struct fimc_addr *paddr, int index) + struct fimc_addr *addr, int index) { int i = (index == -1) ? 0 : index; do { - writel(paddr->y, dev->regs + FIMC_REG_CIOYSA(i)); - writel(paddr->cb, dev->regs + FIMC_REG_CIOCBSA(i)); - writel(paddr->cr, dev->regs + FIMC_REG_CIOCRSA(i)); + writel(addr->y, dev->regs + FIMC_REG_CIOYSA(i)); + writel(addr->cb, dev->regs + FIMC_REG_CIOCBSA(i)); + writel(addr->cr, dev->regs + FIMC_REG_CIOCRSA(i)); dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", - i, paddr->y, paddr->cb, paddr->cr); + i, addr->y, addr->cb, addr->cr); } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); } diff --git a/drivers/media/platform/exynos4-is/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h index b81826d04936..d7a62465c14e 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-reg.h @@ -302,8 +302,8 @@ void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); void fimc_hw_set_input_path(struct fimc_ctx *ctx); void fimc_hw_set_output_path(struct fimc_ctx *ctx); -void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); -void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, +void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *addr); +void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *addr, int index); int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct fimc_source_info *cam); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 4f2a0f992905..1f1042d5c865 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -31,12 +31,6 @@ #define DRV_NAME "fsl_viu" #define VIU_VERSION "0.5.1" -/* Allow building this driver with COMPILE_TEST */ -#if !defined(CONFIG_PPC) && !defined(CONFIG_MICROBLAZE) && !defined(CONFIG_M68K) -#define out_be32(v, a) iowrite32be(a, (void __iomem *)v) -#define in_be32(a) ioread32be((void __iomem *)a) -#endif - #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ #define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ @@ -250,8 +244,8 @@ static void viu_start_dma(struct viu_dev *dev) dev->field = 0; /* Enable DMA operation */ - out_be32(&vr->status_cfg, SOFT_RST); - out_be32(&vr->status_cfg, INT_FIELD_EN); + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(INT_FIELD_EN, &vr->status_cfg); } static void viu_stop_dma(struct viu_dev *dev) @@ -260,27 +254,27 @@ static void viu_stop_dma(struct viu_dev *dev) int cnt = 100; u32 status_cfg; - out_be32(&vr->status_cfg, 0); + iowrite32be(0, &vr->status_cfg); /* Clear pending interrupts */ - status_cfg = in_be32(&vr->status_cfg); + status_cfg = ioread32be(&vr->status_cfg); if (status_cfg & 0x3f0000) - out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); if (status_cfg & DMA_ACT) { do { - status_cfg = in_be32(&vr->status_cfg); + status_cfg = ioread32be(&vr->status_cfg); if (status_cfg & INT_DMA_END_STATUS) break; } while (cnt--); if (cnt < 0) { /* timed out, issue soft reset */ - out_be32(&vr->status_cfg, SOFT_RST); - out_be32(&vr->status_cfg, 0); + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(0, &vr->status_cfg); } else { /* clear DMA_END and other pending irqs */ - out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); } } @@ -381,8 +375,6 @@ static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) struct videobuf_buffer *vb = &buf->vb; void *vaddr = NULL; - BUG_ON(in_interrupt()); - videobuf_waiton(vq, &buf->vb, 0, 0); if (vq->int_ops && vq->int_ops->vaddr) @@ -436,9 +428,9 @@ inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) reg_val.dma_inc = 0; - out_be32(&vr->dma_inc, reg_val.dma_inc); - out_be32(&vr->picture_count, reg_val.picture_count); - out_be32(&vr->field_base_addr, reg_val.field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); return 0; } @@ -698,9 +690,9 @@ static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) inline void viu_activate_overlay(struct viu_reg __iomem *vr) { - out_be32(&vr->field_base_addr, reg_val.field_base_addr); - out_be32(&vr->dma_inc, reg_val.dma_inc); - out_be32(&vr->picture_count, reg_val.picture_count); + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); } static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) @@ -978,14 +970,14 @@ inline void viu_activate_next_buf(struct viu_dev *dev, inline void viu_default_settings(struct viu_reg __iomem *vr) { - out_be32(&vr->luminance, 0x9512A254); - out_be32(&vr->chroma_r, 0x03310000); - out_be32(&vr->chroma_g, 0x06600F38); - out_be32(&vr->chroma_b, 0x00000409); - out_be32(&vr->alpha, 0x000000ff); - out_be32(&vr->req_alarm, 0x00000090); + iowrite32be(0x9512A254, &vr->luminance); + iowrite32be(0x03310000, &vr->chroma_r); + iowrite32be(0x06600F38, &vr->chroma_g); + iowrite32be(0x00000409, &vr->chroma_b); + iowrite32be(0x000000ff, &vr->alpha); + iowrite32be(0x00000090, &vr->req_alarm); dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", - in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr)); + ioread32be(&vr->status_cfg), ioread32be(&vr->field_base_addr)); } static void viu_overlay_intr(struct viu_dev *dev, u32 status) @@ -1003,17 +995,15 @@ static void viu_overlay_intr(struct viu_dev *dev, u32 status) if (status & FIELD_NO) addr += reg_val.dma_inc; - out_be32(&vr->field_base_addr, addr); - out_be32(&vr->dma_inc, reg_val.dma_inc); - out_be32(&vr->status_cfg, - (status & 0xffc0ffff) | + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS) | - reg_val.status_cfg); + reg_val.status_cfg, &vr->status_cfg); } else if (status & INT_VSYNC_STATUS) { - out_be32(&vr->status_cfg, - (status & 0xffc0ffff) | + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS) | - reg_val.status_cfg); + reg_val.status_cfg, &vr->status_cfg); } } } @@ -1059,12 +1049,11 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) dprintk(1, "field 1, 0x%lx, dev field %d\n", (unsigned long)addr, dev->field); } - out_be32(&vr->field_base_addr, addr); - out_be32(&vr->dma_inc, reg_val.dma_inc); - out_be32(&vr->status_cfg, - (status & 0xffc0ffff) | + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS) | - reg_val.status_cfg); + reg_val.status_cfg, &vr->status_cfg); return; } } @@ -1076,7 +1065,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", buf, buf->vb.i, (unsigned long)videobuf_to_dma_contig(&buf->vb), - (unsigned long)in_be32(&vr->field_base_addr)); + (unsigned long)ioread32be(&vr->field_base_addr)); if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); @@ -1097,7 +1086,7 @@ static irqreturn_t viu_intr(int irq, void *dev_id) u32 status; u32 error; - status = in_be32(&vr->status_cfg); + status = ioread32be(&vr->status_cfg); if (status & INT_ERROR_STATUS) { dev->irqs.error_irq++; @@ -1106,8 +1095,8 @@ static irqreturn_t viu_intr(int irq, void *dev_id) dprintk(1, "Err: error(%d), times:%d!\n", error >> 4, dev->irqs.error_irq); /* Clear interrupt error bit and error flags */ - out_be32(&vr->status_cfg, - (status & 0xffc0ffff) | INT_ERROR_STATUS); + iowrite32be((status & 0xffc0ffff) | INT_ERROR_STATUS, + &vr->status_cfg); } if (status & INT_DMA_END_STATUS) { @@ -1136,9 +1125,9 @@ static irqreturn_t viu_intr(int irq, void *dev_id) } /* clear all pending irqs */ - status = in_be32(&vr->status_cfg); - out_be32(&vr->status_cfg, - (status & 0xffc0ffff) | (status & INT_ALL_STATUS)); + status = ioread32be(&vr->status_cfg); + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS), + &vr->status_cfg); if (dev->ovenable) { viu_overlay_intr(dev, status); @@ -1207,14 +1196,14 @@ static int viu_open(struct file *file) viu_default_settings(vr); - status_cfg = in_be32(&vr->status_cfg); - out_be32(&vr->status_cfg, - status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | INT_FIELD_EN | INT_VSTART_EN | - INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN)); + INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN), + &vr->status_cfg); - status_cfg = in_be32(&vr->status_cfg); - out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS); + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg | INT_ALL_STATUS, &vr->status_cfg); spin_lock_init(&fh->vbq_lock); videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, @@ -1294,16 +1283,16 @@ static int viu_release(struct file *file) static void viu_reset(struct viu_reg __iomem *reg) { - out_be32(®->status_cfg, 0); - out_be32(®->luminance, 0x9512a254); - out_be32(®->chroma_r, 0x03310000); - out_be32(®->chroma_g, 0x06600f38); - out_be32(®->chroma_b, 0x00000409); - out_be32(®->field_base_addr, 0); - out_be32(®->dma_inc, 0); - out_be32(®->picture_count, 0x01e002d0); - out_be32(®->req_alarm, 0x00000090); - out_be32(®->alpha, 0x000000ff); + iowrite32be(0, ®->status_cfg); + iowrite32be(0x9512a254, ®->luminance); + iowrite32be(0x03310000, ®->chroma_r); + iowrite32be(0x06600f38, ®->chroma_g); + iowrite32be(0x00000409, ®->chroma_b); + iowrite32be(0, ®->field_base_addr); + iowrite32be(0, ®->dma_inc); + iowrite32be(0x01e002d0, ®->picture_count); + iowrite32be(0x00000090, ®->req_alarm); + iowrite32be(0x000000ff, ®->alpha); } static int viu_mmap(struct file *file, struct vm_area_struct *vma) diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 63fce1b85d26..032fdddbbecc 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -307,8 +307,7 @@ static int mmpcam_platform_remove(struct platform_device *pdev) * Suspend/resume support. */ -#ifdef CONFIG_PM -static int mmpcam_runtime_resume(struct device *dev) +static int __maybe_unused mmpcam_runtime_resume(struct device *dev) { struct mmp_camera *cam = dev_get_drvdata(dev); struct mcam_camera *mcam = &cam->mcam; @@ -322,7 +321,7 @@ static int mmpcam_runtime_resume(struct device *dev) return 0; } -static int mmpcam_runtime_suspend(struct device *dev) +static int __maybe_unused mmpcam_runtime_suspend(struct device *dev) { struct mmp_camera *cam = dev_get_drvdata(dev); struct mcam_camera *mcam = &cam->mcam; @@ -353,7 +352,6 @@ static int __maybe_unused mmpcam_resume(struct device *dev) return mccic_resume(&cam->mcam); return 0; } -#endif static const struct dev_pm_ops mmpcam_pm_ops = { SET_RUNTIME_PM_OPS(mmpcam_runtime_suspend, mmpcam_runtime_resume, NULL) diff --git a/drivers/media/platform/meson/ge2d/Makefile b/drivers/media/platform/meson/ge2d/Makefile new file mode 100644 index 000000000000..450586df27d7 --- /dev/null +++ b/drivers/media/platform/meson/ge2d/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_VIDEO_MESON_GE2D) += ge2d.o diff --git a/drivers/media/platform/meson/ge2d/ge2d-regs.h b/drivers/media/platform/meson/ge2d/ge2d-regs.h new file mode 100644 index 000000000000..2a76dd4c0ccb --- /dev/null +++ b/drivers/media/platform/meson/ge2d/ge2d-regs.h @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + */ + +#ifndef __GE2D_REGS__ +#define __GE2D_REGS__ + +/* Registers starts at (GE2D_REG(0x8a0 * 4) */ +#define GE2D_REG(x) ((0x8a0 + (x)) * 4) + +#define GE2D_GEN_CTRL0 GE2D_REG(0x00) + +#define GE2D_DST_BYTEMASK_ONLY BIT(31) +#define GE2D_DST_BITMASK_EN BIT(30) +#define GE2D_SRC2_KEY_EN BIT(29) +#define GE2D_SRC2_KEY_MODE BIT(28) +#define GE2D_SRC1_KEY_EN BIT(27) +#define GE2D_SRC1_KEY_MODE BIT(26) +#define GE2D_DST1_8B_MODE_SEL GENMASK(25, 24) +#define GE2D_DST_CLIP_MODE BIT(23) +#define GE2D_SRC2_8B_MODE_SEL GENMASK(16, 15) +#define GE2D_SRC2_FILL_MODE BIT(14) +#define GE2D_SRC2_PIC_STRUCT GENMASK(13, 12) +#define GE2D_SRC2_X_YC_RATIO BIT(11) +#define GE2D_SRC1_8B_MODE_SEL GENMASK(6, 5) +#define GE2D_SRC1_FILL_MODE BIT(4) +#define GE2D_SRC1_LUT_EN BIT(3) +#define GE2D_SRC1_PIC_STRUCT GENMASK(2, 1) + +#define GE2D_GEN_CTRL1 GE2D_REG(0x01) + +#define GE2D_SOFT_RST BIT(31) +#define GE2D_DST_WRITE_RESP_CNT_RST BIT(30) +#define GE2D_DST_WRITE_RESP_CNT_ADD_DIS BIT(29) +#define GE2D_COLOR_CONVERSION_MODE1 BIT(26) +#define GE2D_INTERRUPT_CTRL GENMASK(25, 24) +#define GE2D_SRC2_BURST_SIZE_CTRL GENMASK(23, 22) +#define GE2D_SRC1_BURST_SIZE_CTRL GENMASK(21, 16) +#define GE2D_DST1_PIC_STRUCT GENMASK(15, 14) +#define GE2D_SRC_RD_CTRL GENMASK(13, 12) +#define GE2D_DST2_URGENT_EN BIT(11) +#define GE2D_SRC1_URGENT_EN BIT(10) +#define GE2D_SRC2_URGENT_EN BIT(9) +#define GE2D_DST1_URGENT_EN BIT(8) +#define GE2D_SRC1_GB_ALPHA GENMASK(7, 0) + +#define GE2D_GEN_CTRL2 GE2D_REG(0x02) + +#define GE2D_ALPHA_CONVERSION_MODE0 BIT(31) +#define GE2D_COLOR_CONVERSION_MODE0 BIT(30) +#define GE2D_SRC1_GB_ALPHA_EN BIT(29) +#define GE2D_DST1_COLOR_ROUND_MODE BIT(28) +#define GE2D_SRC2_COLOR_EXPAND_MODE BIT(27) +#define GE2D_SRC2_ALPHA_EXPAND_MODE BIT(26) +#define GE2D_SRC1_COLOR_EXPAND_MODE BIT(25) +#define GE2D_SRC1_ALPHA_EXPAND_MODE BIT(24) +#define GE2D_DST_LITTLE_ENDIAN BIT(23) +#define GE2D_DST1_COLOR_MAP GENMASK(22, 19) +#define GE2D_ALU_MULT_MODE BIT(18) +#define GE2D_DST1_FORMAT GENMASK(17, 16) +#define GE2D_SRC2_LITTLE_ENDIAN BIT(15) +#define GE2D_SRC2_COLOR_MAP GENMASK(14, 11) +#define GE2D_ALPHA_CONVERSION_MODE1 BIT(10) +#define GE2D_SRC2_FORMAT GENMASK(9, 8) +#define GE2D_SRC1_LITTLE_ENDIAN BIT(7) +#define GE2D_SRC1_COLOR_MAP GENMASK(6, 3) +#define GE2D_SRC1_DEEPCOLOR BIT(2) +#define GE2D_SRC1_FORMAT GENMASK(1, 0) + +#define GE2D_FORMAT_8BIT 0 +#define GE2D_FORMAT_16BIT 1 +#define GE2D_FORMAT_24BIT 2 +#define GE2D_FORMAT_32BIT 3 + +/* 16 bit */ +#define GE2D_COLOR_MAP_YUV422 0 +#define GE2D_COLOR_MAP_RGB655 1 +#define GE2D_COLOR_MAP_YUV655 1 +#define GE2D_COLOR_MAP_RGB844 2 +#define GE2D_COLOR_MAP_YUV844 2 +#define GE2D_COLOR_MAP_RGBA6442 3 +#define GE2D_COLOR_MAP_YUVA6442 3 +#define GE2D_COLOR_MAP_RGBA4444 4 +#define GE2D_COLOR_MAP_YUVA4444 4 +#define GE2D_COLOR_MAP_RGB565 5 +#define GE2D_COLOR_MAP_YUV565 5 +#define GE2D_COLOR_MAP_ARGB4444 6 +#define GE2D_COLOR_MAP_AYUV4444 6 +#define GE2D_COLOR_MAP_ARGB1555 7 +#define GE2D_COLOR_MAP_AYUV1555 7 +#define GE2D_COLOR_MAP_RGBA4642 8 +#define GE2D_COLOR_MAP_YUVA4642 8 + +/* 24 bit */ +#define GE2D_COLOR_MAP_RGB888 0 +#define GE2D_COLOR_MAP_YUV444 0 +#define GE2D_COLOR_MAP_RGBA5658 1 +#define GE2D_COLOR_MAP_YUVA5658 1 +#define GE2D_COLOR_MAP_ARGB8565 2 +#define GE2D_COLOR_MAP_AYUV8565 2 +#define GE2D_COLOR_MAP_RGBA6666 3 +#define GE2D_COLOR_MAP_YUVA6666 3 +#define GE2D_COLOR_MAP_ARGB6666 4 +#define GE2D_COLOR_MAP_AYUV6666 4 +#define GE2D_COLOR_MAP_BGR888 5 +#define GE2D_COLOR_MAP_VUY888 5 + +/* 32 bit */ +#define GE2D_COLOR_MAP_RGBA8888 0 +#define GE2D_COLOR_MAP_YUVA8888 0 +#define GE2D_COLOR_MAP_ARGB8888 1 +#define GE2D_COLOR_MAP_AYUV8888 1 +#define GE2D_COLOR_MAP_ABGR8888 2 +#define GE2D_COLOR_MAP_AVUY8888 2 +#define GE2D_COLOR_MAP_BGRA8888 3 +#define GE2D_COLOR_MAP_VUYA8888 3 + +#define GE2D_CMD_CTRL GE2D_REG(0x03) + +#define GE2D_SRC2_FILL_COLOR_EN BIT(9) +#define GE2D_SRC1_FILL_COLOR_EN BIT(8) +#define GE2D_DST_XY_SWAP BIT(7) +#define GE2D_DST_X_REV BIT(6) +#define GE2D_DST_Y_REV BIT(5) +#define GE2D_SRC2_X_REV BIT(4) +#define GE2D_SRC2_Y_REV BIT(3) +#define GE2D_SRC1_X_REV BIT(2) +#define GE2D_SRC1_Y_REV BIT(1) +#define GE2D_CBUS_CMD_WR BIT(0) + +#define GE2D_STATUS0 GE2D_REG(0x04) + +#define GE2D_DST_WRITE_RSP_CNT GENMASK(28, 17) +#define GE2D_DP_STATUS GENMASK(16, 7) +#define GE2D_R1CMD_RDY BIT(6) +#define GE2D_R2CMD_RDY BIT(5) +#define GE2D_PDPCMD_VALID BIT(4) +#define GE2D_DPCMD_RDY BIT(3) +#define GE2D_BUF_CMD_VALID BIT(2) +#define GE2D_CURR_CMD_VALID BIT(1) +#define GE2D_GE2D_BUSY BIT(0) + +#define GE2D_STATUS1 GE2D_REG(0x05) + +#define GE2D_WR_DST1_STATUS GENMASK(29, 16) +#define GE2D_RD_SRC2_FIFO_EMPTY BIT(15) +#define GE2D_RD_SRC2_FIFO_OVERFLOW BIT(14) +#define GE2D_RD_SRC2_STATE_Y GENMASK(13, 12) +#define GE2D_RD_SRC2_WIN_ERR BIT(11) +#define GE2D_RD_SRC2_CMD_BUSY BIT(10) +#define GE2D_RD_SRC1_FIFO_EMPTY BIT(9) +#define GE2D_RD_SRC1_FIFO_OVERFLOW BIT(8) +#define GE2D_RD_SRC1_STATE_CR GENMASK(7, 6) +#define GE2D_RD_SRC1_STATE_CB GENMASK(5, 4) +#define GE2D_RD_SRC1_STATE_Y GENMASK(3, 2) +#define GE2D_RD_SRC1_WIN_ERR BIT(1) +#define GE2D_RD_SRC1_CMD_BUSY BIT(0) + +#define GE2D_SRC1_DEF_COLOR GE2D_REG(0x06) + +#define GE2D_COLOR_R_Y GENMASK(31, 24) +#define GE2D_COLOR_B_CB GENMASK(23, 16) +#define GE2D_COLOR_B_CR GENMASK(15, 8) +#define GE2D_COLOR_ALPHA GENMASK(7, 0) + +#define GE2D_SRC1_CLIPX_START_END GE2D_REG(0x07) + +#define GE2D_START_EXTRA BIT(31) /* For GE2D_SRC1_CLIPX/Y_START_END */ +#define GE2D_START_EXTRA0 BIT(30) /* For GE2D_SRC1_X/Y_START_END */ +#define GE2D_START GENMASK(28, 16) +#define GE2D_END_EXTRA BIT(15) /* For GE2D_SRC1_CLIPX/Y_START_END */ +#define GE2D_END_EXTRA0 BIT(14) /* For GE2D_SRC1_X/Y_START_END */ +#define GE2D_END GENMASK(12, 0) + +#define GE2D_SRC1_CLIPY_START_END GE2D_REG(0x08) +#define GE2D_SRC1_CANVAS GE2D_REG(0x09) + +#define GE2D_SRC1_CANVAS_ADDR GENMASK(31, 24) + +#define GE2D_SRC1_X_START_END GE2D_REG(0x0a) +#define GE2D_SRC1_Y_START_END GE2D_REG(0x0b) +#define GE2D_SRC1_LUT_ADDR GE2D_REG(0x0c) + +#define GE2D_LUT_READ BIT(8) +#define GE2D_LUT_ADDR GENMASK(7, 0) + +#define GE2D_SRC1_LUT_DAT GE2D_REG(0x0d) +#define GE2D_SRC1_FMT_CTRL GE2D_REG(0x0e) +#define GE2D_SRC2_DEF_COLOR GE2D_REG(0x0f) +#define GE2D_SRC2_CLIPX_START_END GE2D_REG(0x10) +#define GE2D_SRC2_CLIPY_START_END GE2D_REG(0x11) +#define GE2D_SRC2_X_START_END GE2D_REG(0x12) +#define GE2D_SRC2_Y_START_END GE2D_REG(0x13) +#define GE2D_DST_CLIPX_START_END GE2D_REG(0x14) +#define GE2D_DST_CLIPY_START_END GE2D_REG(0x15) +#define GE2D_DST_X_START_END GE2D_REG(0x16) +#define GE2D_DST_Y_START_END GE2D_REG(0x17) +#define GE2D_SRC2_DST_CANVAS GE2D_REG(0x18) + +#define GE2D_DST2_CANVAS_ADDR GENMASK(23, 16) +#define GE2D_SRC2_CANVAS_ADDR GENMASK(15, 8) +#define GE2D_DST1_CANVAS_ADDR GENMASK(7, 0) + +#define GE2D_VSC_START_PHASE_STEP GE2D_REG(0x19) +#define GE2D_VSC_PHASE_SLOPE GE2D_REG(0x1a) +#define GE2D_VSC_INI_CTRL GE2D_REG(0x1b) +#define GE2D_HSC_START_PHASE_STEP GE2D_REG(0x1c) +#define GE2D_HSC_PHASE_SLOPE GE2D_REG(0x1d) +#define GE2D_HSC_INI_CTRL GE2D_REG(0x1e) +#define GE2D_HSC_ADV_CTRL GE2D_REG(0x1f) +#define GE2D_SC_MISC_CTRL GE2D_REG(0x20) +#define GE2D_VSC_NRND_POINT GE2D_REG(0x21) +#define GE2D_VSC_NRND_PHASE GE2D_REG(0x22) +#define GE2D_HSC_NRND_POINT GE2D_REG(0x23) +#define GE2D_HSC_NRND_PHASE GE2D_REG(0x24) +#define GE2D_MATRIX_PRE_OFFSET GE2D_REG(0x25) +#define GE2D_MATRIX_COEF00_01 GE2D_REG(0x26) +#define GE2D_MATRIX_COEF02_10 GE2D_REG(0x27) +#define GE2D_MATRIX_COEF11_12 GE2D_REG(0x28) +#define GE2D_MATRIX_COEF20_21 GE2D_REG(0x29) +#define GE2D_MATRIX_COEF22_CTRL GE2D_REG(0x2a) +#define GE2D_MATRIX_OFFSET GE2D_REG(0x2b) +#define GE2D_ALU_OP_CTRL GE2D_REG(0x2c) + +#define GE2D_SRC1_COLOR_MULT_ALPHA_SEL GENMASK(26, 25) +#define GE2D_SRC2_COLOR_MULT_ALPHA_SEL BIT(24) +#define GE2D_ALU_BLEND_MODE GENMASK(22, 20) + +#define OPERATION_ADD 0 /* Cd = Cs*Fs+Cd*Fd */ +#define OPERATION_SUB 1 /* Cd = Cs*Fs-Cd*Fd */ +#define OPERATION_REVERSE_SUB 2 /* Cd = Cd*Fd-Cs*Fs */ +#define OPERATION_MIN 3 /* Cd = Min(Cd*Fd,Cs*Fs) */ +#define OPERATION_MAX 4 /* Cd = Max(Cd*Fd,Cs*Fs) */ +#define OPERATION_LOGIC 5 + +#define GE2D_ALU_SRC_COLOR_BLEND_FACTOR GENMASK(19, 16) +#define GE2D_ALU_DST_COLOR_BLEND_FACTOR GENMASK(15, 12) + +#define COLOR_FACTOR_ZERO 0 +#define COLOR_FACTOR_ONE 1 +#define COLOR_FACTOR_SRC_COLOR 2 +#define COLOR_FACTOR_ONE_MINUS_SRC_COLOR 3 +#define COLOR_FACTOR_DST_COLOR 4 +#define COLOR_FACTOR_ONE_MINUS_DST_COLOR 5 +#define COLOR_FACTOR_SRC_ALPHA 6 +#define COLOR_FACTOR_ONE_MINUS_SRC_ALPHA 7 +#define COLOR_FACTOR_DST_ALPHA 8 +#define COLOR_FACTOR_ONE_MINUS_DST_ALPHA 9 +#define COLOR_FACTOR_CONST_COLOR 10 +#define COLOR_FACTOR_ONE_MINUS_CONST_COLOR 11 +#define COLOR_FACTOR_CONST_ALPHA 12 +#define COLOR_FACTOR_ONE_MINUS_CONST_ALPHA 13 +#define COLOR_FACTOR_SRC_ALPHA_SATURATE 14 + +#define GE2D_ALU_OPERATION_LOGIC GENMASK(15, 12) + +#define LOGIC_OPERATION_CLEAR 0 +#define LOGIC_OPERATION_COPY 1 +#define LOGIC_OPERATION_NOOP 2 +#define LOGIC_OPERATION_SET 3 +#define LOGIC_OPERATION_COPY_INVERT 4 +#define LOGIC_OPERATION_INVERT 5 +#define LOGIC_OPERATION_AND_REVERSE 6 +#define LOGIC_OPERATION_OR_REVERSE 7 +#define LOGIC_OPERATION_AND 8 +#define LOGIC_OPERATION_OR 9 +#define LOGIC_OPERATION_NAND 10 +#define LOGIC_OPERATION_NOR 11 +#define LOGIC_OPERATION_XOR 12 +#define LOGIC_OPERATION_EQUIV 13 +#define LOGIC_OPERATION_AND_INVERT 14 +#define LOGIC_OPERATION_OR_INVERT 15 + +#define GE2D_ALU_ALPHA_BLEND_MODE GENMASK(10, 8) +#define GE2D_ALU_SRC_ALPHA_BLEND_FACTOR GENMASK(7, 4) +#define GE2D_ALU_DST_ALPHA_BLEND_FACTOR GENMASK(3, 0) + +#define ALPHA_FACTOR_ZERO 0 +#define ALPHA_FACTOR_ONE 1 +#define ALPHA_FACTOR_SRC_ALPHA 2 +#define ALPHA_FACTOR_ONE_MINUS_SRC_ALPHA 3 +#define ALPHA_FACTOR_DST_ALPHA 4 +#define ALPHA_FACTOR_ONE_MINUS_DST_ALPHA 5 +#define ALPHA_FACTOR_CONST_ALPHA 6 +#define ALPHA_FACTOR_ONE_MINUS_CONST_ALPHA 7 + +#define GE2D_ALU_ALPHA_OPERATION_LOGIC GENMASK(3, 0) + +#define GE2D_ALU_COLOR_OP(__op, __src_factor, __dst_factor) \ + (FIELD_PREP(GE2D_ALU_BLEND_MODE, __op) | \ + FIELD_PREP(GE2D_ALU_SRC_COLOR_BLEND_FACTOR, __src_factor) | \ + FIELD_PREP(GE2D_ALU_DST_COLOR_BLEND_FACTOR, __dst_factor)) + +#define GE2D_ALU_DO_COLOR_OPERATION_LOGIC(__op, __src_factor) \ + GE2D_ALU_COLOR_OP(OPERATION_LOGIC, __src_factor, __op) + +#define GE2D_ALU_ALPHA_OP(__op, __src_factor, __dst_factor) \ + (FIELD_PREP(GE2D_ALU_ALPHA_BLEND_MODE, __op) | \ + FIELD_PREP(GE2D_ALU_SRC_ALPHA_BLEND_FACTOR, __src_factor) | \ + FIELD_PREP(GE2D_ALU_DST_ALPHA_BLEND_FACTOR, __dst_factor)) + +#define GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(__op, __src_factor) \ + GE2D_ALU_ALPHA_OP(OPERATION_LOGIC, __src_factor, __op) + +#define GE2D_ALU_CONST_COLOR GE2D_REG(0x2d) +#define GE2D_SRC1_KEY GE2D_REG(0x2e) +#define GE2D_SRC1_KEY_MASK GE2D_REG(0x2f) +#define GE2D_SRC2_KEY GE2D_REG(0x30) +#define GE2D_SRC2_KEY_MASK GE2D_REG(0x31) +#define GE2D_DST_BITMASK GE2D_REG(0x32) +#define GE2D_DP_ONOFF_CTRL GE2D_REG(0x33) +#define GE2D_SCALE_COEF_IDX GE2D_REG(0x34) +#define GE2D_SCALE_COEF GE2D_REG(0x35) +#define GE2D_SRC_OUTSIDE_ALPHA GE2D_REG(0x36) +#define GE2D_ANTIFLICK_CTRL0 GE2D_REG(0x38) +#define GE2D_ANTIFLICK_CTRL1 GE2D_REG(0x39) +#define GE2D_ANTIFLICK_COLOR_FILT0 GE2D_REG(0x3a) +#define GE2D_ANTIFLICK_COLOR_FILT1 GE2D_REG(0x3b) +#define GE2D_ANTIFLICK_COLOR_FILT2 GE2D_REG(0x3c) +#define GE2D_ANTIFLICK_COLOR_FILT3 GE2D_REG(0x3d) +#define GE2D_ANTIFLICK_ALPHA_FILT0 GE2D_REG(0x3e) +#define GE2D_ANTIFLICK_ALPHA_FILT1 GE2D_REG(0x3f) +#define GE2D_ANTIFLICK_ALPHA_FILT2 GE2D_REG(0x40) +#define GE2D_ANTIFLICK_ALPHA_FILT3 GE2D_REG(0x41) +#define GE2D_SRC1_RANGE_MAP_Y_CTRL GE2D_REG(0x43) +#define GE2D_SRC1_RANGE_MAP_CB_CTRL GE2D_REG(0x44) +#define GE2D_SRC1_RANGE_MAP_CR_CTRL GE2D_REG(0x45) +#define GE2D_ARB_BURST_NUM GE2D_REG(0x46) +#define GE2D_TID_TOKEN GE2D_REG(0x47) +#define GE2D_GEN_CTRL3 GE2D_REG(0x48) + +#define GE2D_DST2_BYTEMASK_VAL GENMASK(31, 28) +#define GE2D_DST2_PIC_STRUCT GENMASK(27, 26) +#define GE2D_DST2_8B_MODE_SEL GENMASK(25, 24) +#define GE2D_DST2_COLOR_MAP GENMASK(22, 19) +#define GE2D_DST2_FORMAT GENMASK(17, 16) +#define GE2D_DST2_COLOR_ROUND_MODE BIT(14) +#define GE2D_DST2_X_DISCARD_MODE GENMASK(13, 12) +#define GE2D_DST2_Y_DISCARD_MODE GENMASK(11, 10) +#define GE2D_DST2_ENABLE BIT(8) +#define GE2D_DST1_X_DISCARD_MODE GENMASK(5, 4) +#define GE2D_DST1_Y_DISCARD_MODE GENMASK(3, 2) +#define GE2D_DST1_ENABLE BIT(0) + +#define GE2D_STATUS2 GE2D_REG(0x49) +#define GE2D_GEN_CTRL4 GE2D_REG(0x4a) +#define GE2D_DST1_BADDR_CTRL GE2D_REG(0x51) +#define GE2D_DST1_STRIDE_CTRL GE2D_REG(0x52) + +#define GE2D_STRIDE_SIZE GENMASK(19, 0) + +#define GE2D_SRC1_BADDR_CTRL GE2D_REG(0x53) +#define GE2D_SRC1_STRIDE_CTRL GE2D_REG(0x54) +#define GE2D_SRC2_BADDR_CTRL GE2D_REG(0x55) +#define GE2D_SRC2_STRIDE_CTRL GE2D_REG(0x56) + +#endif /* __GE2D_REGS__ */ diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/meson/ge2d/ge2d.c new file mode 100644 index 000000000000..f526501bd473 --- /dev/null +++ b/drivers/media/platform/meson/ge2d/ge2d.c @@ -0,0 +1,1067 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/bitfield.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/reset.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/regmap.h> + +#include <linux/platform_device.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-ctrls.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> + +#include "ge2d-regs.h" + +#define GE2D_NAME "meson-ge2d" + +#define DEFAULT_WIDTH 128 +#define DEFAULT_HEIGHT 128 +#define DEFAULT_STRIDE 512 + +#define MAX_WIDTH 8191 +#define MAX_HEIGHT 8191 + +/* + * Missing features: + * - Scaling + * - Simple 1/2 vertical scaling + * - YUV input support + * - Source global alpha + * - Colorspace conversion + */ + +struct ge2d_fmt { + u32 fourcc; + bool alpha; + bool le; + unsigned int depth; + unsigned int hw_fmt; + unsigned int hw_map; +}; + +struct ge2d_frame { + struct vb2_v4l2_buffer *buf; + + /* Image Format */ + struct v4l2_pix_format pix_fmt; + + /* Crop */ + struct v4l2_rect crop; + + /* Image format */ + const struct ge2d_fmt *fmt; +}; + +struct ge2d_ctx { + struct v4l2_fh fh; + struct meson_ge2d *ge2d; + struct ge2d_frame in; + struct ge2d_frame out; + struct v4l2_ctrl_handler ctrl_handler; + + unsigned long sequence_out, sequence_cap; + + /* Control values */ + u32 hflip; + u32 vflip; + u32 xy_swap; +}; + +struct meson_ge2d { + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct video_device *vfd; + + struct device *dev; + struct regmap *map; + struct clk *clk; + + /* vb2 queue lock */ + struct mutex mutex; + + struct ge2d_ctx *curr; +}; + +#define FMT(_fourcc, _alpha, _depth, _map) \ +{ \ + .fourcc = _fourcc, \ + .alpha = (_alpha), \ + .depth = (_depth), \ + .hw_fmt = GE2D_FORMAT_ ## _depth ## BIT, \ + .hw_map = GE2D_COLOR_MAP_ ## _map, \ +} + +/* TOFIX Handle the YUV input formats */ +static const struct ge2d_fmt formats[] = { + /* FOURCC Alpha HW FMT HW MAP */ + FMT(V4L2_PIX_FMT_XRGB32, false, 32, BGRA8888), + FMT(V4L2_PIX_FMT_RGB32, true, 32, BGRA8888), + FMT(V4L2_PIX_FMT_ARGB32, true, 32, BGRA8888), + FMT(V4L2_PIX_FMT_RGBX32, false, 32, ABGR8888), + FMT(V4L2_PIX_FMT_RGBA32, true, 32, ABGR8888), + FMT(V4L2_PIX_FMT_BGRX32, false, 32, RGBA8888), + FMT(V4L2_PIX_FMT_BGRA32, true, 32, RGBA8888), + FMT(V4L2_PIX_FMT_BGR32, true, 32, ARGB8888), + FMT(V4L2_PIX_FMT_ABGR32, true, 32, ARGB8888), + FMT(V4L2_PIX_FMT_XBGR32, false, 32, ARGB8888), + + FMT(V4L2_PIX_FMT_RGB24, false, 24, BGR888), + FMT(V4L2_PIX_FMT_BGR24, false, 24, RGB888), + + FMT(V4L2_PIX_FMT_XRGB555X, false, 16, ARGB1555), + FMT(V4L2_PIX_FMT_ARGB555X, true, 16, ARGB1555), + FMT(V4L2_PIX_FMT_RGB565, false, 16, RGB565), + FMT(V4L2_PIX_FMT_RGBX444, false, 16, RGBA4444), + FMT(V4L2_PIX_FMT_RGBA444, true, 16, RGBA4444), + FMT(V4L2_PIX_FMT_XRGB444, false, 16, ARGB4444), + FMT(V4L2_PIX_FMT_ARGB444, true, 16, ARGB4444), +}; + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static const struct ge2d_fmt *find_fmt(struct v4l2_format *f) +{ + unsigned int i; + + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].fourcc == f->fmt.pix.pixelformat) + return &formats[i]; + } + + /* + * TRY_FMT/S_FMT should never return an error when the requested format + * is not supported. Drivers should always return a valid format, + * preferably a format that is as widely supported by applications as + * possible. + */ + return &formats[0]; +} + +static struct ge2d_frame *get_frame(struct ge2d_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &ctx->in; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &ctx->out; + default: + /* This should never happen, warn and return OUTPUT frame */ + dev_warn(ctx->ge2d->dev, "%s: invalid buffer type\n", __func__); + return &ctx->in; + } +} + +static void ge2d_hw_start(struct meson_ge2d *ge2d) +{ + struct ge2d_ctx *ctx = ge2d->curr; + u32 reg; + + /* Reset */ + regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1, + GE2D_SOFT_RST, GE2D_SOFT_RST); + regmap_update_bits(ge2d->map, GE2D_GEN_CTRL1, + GE2D_SOFT_RST, 0); + + usleep_range(100, 200); + + /* Implement CANVAS for non-AXG */ + regmap_write(ge2d->map, GE2D_SRC1_BADDR_CTRL, + (vb2_dma_contig_plane_dma_addr(&ctx->in.buf->vb2_buf, 0) + 7) >> 3); + regmap_write(ge2d->map, GE2D_SRC1_STRIDE_CTRL, + (ctx->in.pix_fmt.bytesperline + 7) >> 3); + regmap_write(ge2d->map, GE2D_SRC2_BADDR_CTRL, + (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3); + regmap_write(ge2d->map, GE2D_SRC2_STRIDE_CTRL, + (ctx->out.pix_fmt.bytesperline + 7) >> 3); + regmap_write(ge2d->map, GE2D_DST1_BADDR_CTRL, + (vb2_dma_contig_plane_dma_addr(&ctx->out.buf->vb2_buf, 0) + 7) >> 3); + regmap_write(ge2d->map, GE2D_DST1_STRIDE_CTRL, + (ctx->out.pix_fmt.bytesperline + 7) >> 3); + + regmap_write(ge2d->map, GE2D_GEN_CTRL0, 0); + regmap_write(ge2d->map, GE2D_GEN_CTRL1, + FIELD_PREP(GE2D_INTERRUPT_CTRL, 2) | + FIELD_PREP(GE2D_SRC2_BURST_SIZE_CTRL, 3) | + FIELD_PREP(GE2D_SRC1_BURST_SIZE_CTRL, 0x3f)); + + regmap_write(ge2d->map, GE2D_GEN_CTRL2, + GE2D_SRC1_LITTLE_ENDIAN | + GE2D_SRC2_LITTLE_ENDIAN | + GE2D_DST_LITTLE_ENDIAN | + FIELD_PREP(GE2D_DST1_COLOR_MAP, ctx->out.fmt->hw_map) | + FIELD_PREP(GE2D_DST1_FORMAT, ctx->out.fmt->hw_fmt) | + FIELD_PREP(GE2D_SRC2_COLOR_MAP, ctx->out.fmt->hw_map) | + FIELD_PREP(GE2D_SRC2_FORMAT, ctx->out.fmt->hw_fmt) | + FIELD_PREP(GE2D_SRC1_COLOR_MAP, ctx->in.fmt->hw_map) | + FIELD_PREP(GE2D_SRC1_FORMAT, ctx->in.fmt->hw_fmt)); + regmap_write(ge2d->map, GE2D_GEN_CTRL3, + GE2D_DST1_ENABLE); + + regmap_write(ge2d->map, GE2D_SRC1_CLIPY_START_END, + FIELD_PREP(GE2D_START, ctx->in.crop.top) | + FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height)); + regmap_write(ge2d->map, GE2D_SRC1_CLIPX_START_END, + FIELD_PREP(GE2D_START, ctx->in.crop.left) | + FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width)); + regmap_write(ge2d->map, GE2D_SRC2_CLIPY_START_END, + FIELD_PREP(GE2D_START, ctx->out.crop.top) | + FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height)); + regmap_write(ge2d->map, GE2D_SRC2_CLIPX_START_END, + FIELD_PREP(GE2D_START, ctx->out.crop.left) | + FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width)); + regmap_write(ge2d->map, GE2D_DST_CLIPY_START_END, + FIELD_PREP(GE2D_START, ctx->out.crop.top) | + FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height)); + regmap_write(ge2d->map, GE2D_DST_CLIPX_START_END, + FIELD_PREP(GE2D_START, ctx->out.crop.left) | + FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width)); + + regmap_write(ge2d->map, GE2D_SRC1_Y_START_END, + FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height)); + regmap_write(ge2d->map, GE2D_SRC1_X_START_END, + FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width)); + regmap_write(ge2d->map, GE2D_SRC2_Y_START_END, + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height)); + regmap_write(ge2d->map, GE2D_SRC2_X_START_END, + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width)); + regmap_write(ge2d->map, GE2D_DST_Y_START_END, + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height)); + regmap_write(ge2d->map, GE2D_DST_X_START_END, + FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width)); + + /* Color, no blend, use source color */ + reg = GE2D_ALU_DO_COLOR_OPERATION_LOGIC(LOGIC_OPERATION_COPY, + COLOR_FACTOR_SRC_COLOR); + + if (ctx->in.fmt->alpha && ctx->out.fmt->alpha) + /* Take source alpha */ + reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY, + COLOR_FACTOR_SRC_ALPHA); + else if (!ctx->out.fmt->alpha) + /* Set alpha to 0 */ + reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_SET, + COLOR_FACTOR_ZERO); + else + /* Keep original alpha */ + reg |= GE2D_ALU_DO_ALPHA_OPERATION_LOGIC(LOGIC_OPERATION_COPY, + COLOR_FACTOR_DST_ALPHA); + + regmap_write(ge2d->map, GE2D_ALU_OP_CTRL, reg); + + /* Start */ + regmap_write(ge2d->map, GE2D_CMD_CTRL, + (ctx->xy_swap ? GE2D_DST_XY_SWAP : 0) | + (ctx->hflip ? GE2D_SRC1_Y_REV : 0) | + (ctx->vflip ? GE2D_SRC1_X_REV : 0) | + GE2D_CBUS_CMD_WR); +} + +static void device_run(void *priv) +{ + struct ge2d_ctx *ctx = priv; + struct meson_ge2d *ge2d = ctx->ge2d; + + ge2d->curr = ctx; + + ctx->in.buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + ctx->out.buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + ge2d_hw_start(ge2d); +} + +static irqreturn_t ge2d_isr(int irq, void *priv) +{ + struct meson_ge2d *ge2d = priv; + u32 intr; + + regmap_read(ge2d->map, GE2D_STATUS0, &intr); + + if (!(intr & GE2D_GE2D_BUSY)) { + struct vb2_v4l2_buffer *src, *dst; + struct ge2d_ctx *ctx = ge2d->curr; + + ge2d->curr = NULL; + + src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + src->sequence = ctx->sequence_out++; + dst->sequence = ctx->sequence_cap++; + + dst->timecode = src->timecode; + dst->vb2_buf.timestamp = src->vb2_buf.timestamp; + dst->flags = src->flags; + + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + v4l2_m2m_job_finish(ge2d->m2m_dev, ctx->fh.m2m_ctx); + } + + return IRQ_HANDLED; +} + +static const struct v4l2_m2m_ops ge2d_m2m_ops = { + .device_run = device_run, +}; + +static int ge2d_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct ge2d_ctx *ctx = vb2_get_drv_priv(vq); + struct ge2d_frame *f = get_frame(ctx, vq->type); + + if (*nplanes) + return sizes[0] < f->pix_fmt.sizeimage ? -EINVAL : 0; + + sizes[0] = f->pix_fmt.sizeimage; + *nplanes = 1; + + return 0; +} + +static int ge2d_buf_prepare(struct vb2_buffer *vb) +{ + struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct ge2d_frame *f = get_frame(ctx, vb->vb2_queue->type); + + vbuf->field = V4L2_FIELD_NONE; + + vb2_set_plane_payload(vb, 0, f->pix_fmt.sizeimage); + + return 0; +} + +static void ge2d_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct ge2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int ge2d_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct ge2d_ctx *ctx = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + ctx->sequence_out = 0; + else + ctx->sequence_cap = 0; + + return 0; +} + +static void ge2d_stop_streaming(struct vb2_queue *vq) +{ + struct ge2d_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + break; + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops ge2d_qops = { + .queue_setup = ge2d_queue_setup, + .buf_prepare = ge2d_buf_prepare, + .buf_queue = ge2d_buf_queue, + .start_streaming = ge2d_start_streaming, + .stop_streaming = ge2d_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int +queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct ge2d_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->ops = &ge2d_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->ge2d->mutex; + src_vq->dev = ctx->ge2d->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->ops = &ge2d_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->ge2d->mutex; + dst_vq->dev = ctx->ge2d->v4l2_dev.dev; + + return vb2_queue_init(dst_vq); +} + +static int +vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, GE2D_NAME, sizeof(cap->driver)); + strscpy(cap->card, GE2D_NAME, sizeof(cap->card)); + strscpy(cap->bus_info, "platform:" GE2D_NAME, sizeof(cap->bus_info)); + + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + const struct ge2d_fmt *fmt; + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + fmt = &formats[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int vidioc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct ge2d_ctx *ctx = priv; + struct ge2d_frame *f; + bool use_frame = false; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + f = get_frame(ctx, s->type); + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + use_frame = true; + break; + case V4L2_SEL_TGT_CROP: + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + use_frame = true; + break; + default: + return -EINVAL; + } + + if (use_frame) { + s->r = f->crop; + } else { + s->r.left = 0; + s->r.top = 0; + s->r.width = f->pix_fmt.width; + s->r.height = f->pix_fmt.height; + } + + return 0; +} + +static int vidioc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct ge2d_ctx *ctx = priv; + struct meson_ge2d *ge2d = ctx->ge2d; + struct ge2d_frame *f; + int ret = 0; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + f = get_frame(ctx, s->type); + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + /* + * COMPOSE target is only valid for capture buffer type, return + * error for output buffer type + */ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + case V4L2_SEL_TGT_CROP: + /* + * CROP target is only valid for output buffer type, return + * error for capture buffer type + */ + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + /* + * bound and default crop/compose targets are invalid targets to + * try/set + */ + default: + return -EINVAL; + } + + if (s->r.top < 0 || s->r.left < 0) { + v4l2_err(&ge2d->v4l2_dev, + "doesn't support negative values for top & left.\n"); + return -EINVAL; + } + + if (s->r.left + s->r.width > f->pix_fmt.width || + s->r.top + s->r.height > f->pix_fmt.height) { + v4l2_err(&ge2d->v4l2_dev, "unsupported rectangle value.\n"); + return -EINVAL; + } + + f->crop = s->r; + + return ret; +} + +static void vidioc_setup_cap_fmt(struct ge2d_ctx *ctx, struct v4l2_pix_format *f) +{ + struct ge2d_frame *frm_out = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + *f = frm_out->pix_fmt; + + if (ctx->xy_swap) { + f->width = frm_out->pix_fmt.height; + f->height = frm_out->pix_fmt.width; + } +} + +static int vidioc_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + const struct ge2d_fmt *fmt = find_fmt(f); + struct ge2d_ctx *ctx = priv; + struct v4l2_pix_format fmt_cap; + + vidioc_setup_cap_fmt(ctx, &fmt_cap); + + fmt_cap.pixelformat = fmt->fourcc; + + fmt_cap.bytesperline = max(f->fmt.pix.bytesperline, + ALIGN((fmt_cap.width * fmt->depth) >> 3, 8)); + + fmt_cap.sizeimage = max(f->fmt.pix.sizeimage, + fmt_cap.height * fmt_cap.bytesperline); + + f->fmt.pix = fmt_cap; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct ge2d_ctx *ctx = priv; + struct meson_ge2d *ge2d = ctx->ge2d; + struct vb2_queue *vq; + struct ge2d_frame *frm; + int ret = 0; + + /* Adjust all values accordingly to the hardware capabilities + * and chosen format. + */ + ret = vidioc_try_fmt_cap(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) { + v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type); + return -EBUSY; + } + + frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + frm->pix_fmt = f->fmt.pix; + frm->fmt = find_fmt(f); + f->fmt.pix.pixelformat = frm->fmt->fourcc; + + /* Reset crop settings */ + frm->crop.left = 0; + frm->crop.top = 0; + frm->crop.width = frm->pix_fmt.width; + frm->crop.height = frm->pix_fmt.height; + + return 0; +} + +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct ge2d_ctx *ctx = priv; + struct vb2_queue *vq; + struct ge2d_frame *frm; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + frm = get_frame(ctx, f->type); + + f->fmt.pix = frm->pix_fmt; + f->fmt.pix.pixelformat = frm->fmt->fourcc; + + return 0; +} + +static int vidioc_try_fmt_out(struct file *file, void *priv, struct v4l2_format *f) +{ + const struct ge2d_fmt *fmt = find_fmt(f); + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = fmt->fourcc; + + if (f->fmt.pix.width > MAX_WIDTH) + f->fmt.pix.width = MAX_WIDTH; + if (f->fmt.pix.height > MAX_HEIGHT) + f->fmt.pix.height = MAX_HEIGHT; + + f->fmt.pix.bytesperline = max(f->fmt.pix.bytesperline, + ALIGN((f->fmt.pix.width * fmt->depth) >> 3, 8)); + + f->fmt.pix.sizeimage = max(f->fmt.pix.sizeimage, + f->fmt.pix.height * f->fmt.pix.bytesperline); + + return 0; +} + +static int vidioc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *f) +{ + struct ge2d_ctx *ctx = priv; + struct meson_ge2d *ge2d = ctx->ge2d; + struct vb2_queue *vq; + struct ge2d_frame *frm, *frm_cap; + int ret = 0; + + /* Adjust all values accordingly to the hardware capabilities + * and chosen format. + */ + ret = vidioc_try_fmt_out(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) { + v4l2_err(&ge2d->v4l2_dev, "queue (%d) bust\n", f->type); + return -EBUSY; + } + + frm = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + frm_cap = get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + frm->pix_fmt = f->fmt.pix; + frm->fmt = find_fmt(f); + f->fmt.pix.pixelformat = frm->fmt->fourcc; + + /* Reset crop settings */ + frm->crop.left = 0; + frm->crop.top = 0; + frm->crop.width = frm->pix_fmt.width; + frm->crop.height = frm->pix_fmt.height; + + /* Propagate settings to capture */ + vidioc_setup_cap_fmt(ctx, &frm_cap->pix_fmt); + + return 0; +} + +static const struct v4l2_ioctl_ops ge2d_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, + .vidioc_g_fmt_vid_out = vidioc_g_fmt, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, +}; + +static int ge2d_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ge2d_ctx *ctx = container_of(ctrl->handler, struct ge2d_ctx, + ctrl_handler); + struct v4l2_pix_format fmt; + struct vb2_queue *vq; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctx->hflip = ctrl->val; + break; + case V4L2_CID_VFLIP: + ctx->vflip = ctrl->val; + break; + case V4L2_CID_ROTATE: + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (vb2_is_busy(vq)) + return -EBUSY; + + if (ctrl->val == 90) { + ctx->hflip = 0; + ctx->vflip = 0; + ctx->xy_swap = 1; + } else if (ctrl->val == 180) { + ctx->hflip = 1; + ctx->vflip = 1; + ctx->xy_swap = 0; + } else if (ctrl->val == 270) { + ctx->hflip = 1; + ctx->vflip = 1; + ctx->xy_swap = 1; + } else { + ctx->hflip = 0; + ctx->vflip = 0; + ctx->xy_swap = 0; + } + + vidioc_setup_cap_fmt(ctx, &fmt); + + /* + * If the rotation parameter changes the OUTPUT frames + * parameters, take them in account + */ + if (fmt.width != ctx->out.pix_fmt.width || + fmt.height != ctx->out.pix_fmt.width || + fmt.bytesperline > ctx->out.pix_fmt.bytesperline || + fmt.sizeimage > ctx->out.pix_fmt.sizeimage) + ctx->out.pix_fmt = fmt; + + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops ge2d_ctrl_ops = { + .s_ctrl = ge2d_s_ctrl, +}; + +static int ge2d_setup_ctrls(struct ge2d_ctx *ctx) +{ + struct meson_ge2d *ge2d = ctx->ge2d; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &ge2d_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + v4l2_err(&ge2d->v4l2_dev, "%s failed\n", __func__); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; + } + + return 0; +} + +static const struct ge2d_frame def_frame = { + .pix_fmt = { + .width = DEFAULT_WIDTH, + .height = DEFAULT_HEIGHT, + .bytesperline = DEFAULT_STRIDE, + .sizeimage = DEFAULT_STRIDE * DEFAULT_HEIGHT, + .field = V4L2_FIELD_NONE, + }, + .crop.width = DEFAULT_WIDTH, + .crop.height = DEFAULT_HEIGHT, + .fmt = &formats[0], +}; + +static int ge2d_open(struct file *file) +{ + struct meson_ge2d *ge2d = video_drvdata(file); + struct ge2d_ctx *ctx = NULL; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + ctx->ge2d = ge2d; + + /* Set default formats */ + ctx->in = def_frame; + ctx->out = def_frame; + + if (mutex_lock_interruptible(&ge2d->mutex)) { + kfree(ctx); + return -ERESTARTSYS; + } + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ge2d->m2m_dev, ctx, &queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + mutex_unlock(&ge2d->mutex); + kfree(ctx); + return ret; + } + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ge2d_setup_ctrls(ctx); + + /* Write the default values to the ctx struct */ + v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + mutex_unlock(&ge2d->mutex); + + return 0; +} + +static int ge2d_release(struct file *file) +{ + struct ge2d_ctx *ctx = + container_of(file->private_data, struct ge2d_ctx, fh); + struct meson_ge2d *ge2d = ctx->ge2d; + + mutex_lock(&ge2d->mutex); + + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + + mutex_unlock(&ge2d->mutex); + + return 0; +} + +static const struct v4l2_file_operations ge2d_fops = { + .owner = THIS_MODULE, + .open = ge2d_open, + .release = ge2d_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device ge2d_videodev = { + .name = "meson-ge2d", + .fops = &ge2d_fops, + .ioctl_ops = &ge2d_ioctl_ops, + .minor = -1, + .release = video_device_release, + .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct regmap_config meson_ge2d_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = GE2D_SRC2_STRIDE_CTRL, +}; + +static int ge2d_probe(struct platform_device *pdev) +{ + struct reset_control *rst; + struct video_device *vfd; + struct meson_ge2d *ge2d; + struct resource *res; + void __iomem *regs; + int ret = 0; + int irq; + + if (!pdev->dev.of_node) + return -ENODEV; + + ge2d = devm_kzalloc(&pdev->dev, sizeof(*ge2d), GFP_KERNEL); + if (!ge2d) + return -ENOMEM; + + ge2d->dev = &pdev->dev; + mutex_init(&ge2d->mutex); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(ge2d->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + ge2d->map = devm_regmap_init_mmio(ge2d->dev, regs, + &meson_ge2d_regmap_conf); + if (IS_ERR(ge2d->map)) + return PTR_ERR(ge2d->map); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(ge2d->dev, irq, ge2d_isr, 0, + dev_name(ge2d->dev), ge2d); + if (ret < 0) { + dev_err(ge2d->dev, "failed to request irq\n"); + return ret; + } + + rst = devm_reset_control_get(ge2d->dev, NULL); + if (IS_ERR(rst)) { + dev_err(ge2d->dev, "failed to get core reset controller\n"); + return PTR_ERR(rst); + } + + ge2d->clk = devm_clk_get(ge2d->dev, NULL); + if (IS_ERR(ge2d->clk)) { + dev_err(ge2d->dev, "failed to get clock\n"); + return PTR_ERR(ge2d->clk); + } + + reset_control_assert(rst); + udelay(1); + reset_control_deassert(rst); + + ret = clk_prepare_enable(ge2d->clk); + if (ret) { + dev_err(ge2d->dev, "Cannot enable ge2d sclk: %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &ge2d->v4l2_dev); + if (ret) + goto disable_clks; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(&ge2d->v4l2_dev, "Failed to allocate video device\n"); + goto unreg_v4l2_dev; + } + + *vfd = ge2d_videodev; + vfd->lock = &ge2d->mutex; + vfd->v4l2_dev = &ge2d->v4l2_dev; + + video_set_drvdata(vfd, ge2d); + ge2d->vfd = vfd; + + platform_set_drvdata(pdev, ge2d); + ge2d->m2m_dev = v4l2_m2m_init(&ge2d_m2m_ops); + if (IS_ERR(ge2d->m2m_dev)) { + v4l2_err(&ge2d->v4l2_dev, "Failed to init mem2mem device\n"); + ret = PTR_ERR(ge2d->m2m_dev); + goto rel_vdev; + } + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(&ge2d->v4l2_dev, "Failed to register video device\n"); + goto rel_m2m; + } + + v4l2_info(&ge2d->v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + + return 0; + +rel_m2m: + v4l2_m2m_release(ge2d->m2m_dev); +rel_vdev: + video_device_release(ge2d->vfd); +unreg_v4l2_dev: + v4l2_device_unregister(&ge2d->v4l2_dev); +disable_clks: + clk_disable_unprepare(ge2d->clk); + + return ret; +} + +static int ge2d_remove(struct platform_device *pdev) +{ + struct meson_ge2d *ge2d = platform_get_drvdata(pdev); + + video_unregister_device(ge2d->vfd); + v4l2_m2m_release(ge2d->m2m_dev); + video_device_release(ge2d->vfd); + v4l2_device_unregister(&ge2d->v4l2_dev); + clk_disable_unprepare(ge2d->clk); + + return 0; +} + +static const struct of_device_id meson_ge2d_match[] = { + { + .compatible = "amlogic,axg-ge2d", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, meson_ge2d_match); + +static struct platform_driver ge2d_drv = { + .probe = ge2d_probe, + .remove = ge2d_remove, + .driver = { + .name = "meson-ge2d", + .of_match_table = meson_ge2d_match, + }, +}; + +module_platform_driver(ge2d_drv); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION("Amlogic 2D Graphic Acceleration Unit"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 227245ccaedc..88a23bce569d 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -1306,6 +1306,7 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg) jpeg->variant->clks); if (ret) { dev_err(&pdev->dev, "failed to get jpeg clock:%d\n", ret); + put_device(&pdev->dev); return ret; } @@ -1331,6 +1332,12 @@ static void mtk_jpeg_job_timeout_work(struct work_struct *work) v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } + +static inline void mtk_jpeg_clk_release(struct mtk_jpeg_dev *jpeg) +{ + put_device(jpeg->larb); +} + static int mtk_jpeg_probe(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg; @@ -1435,6 +1442,7 @@ err_m2m_init: v4l2_device_unregister(&jpeg->v4l2_dev); err_dev_register: + mtk_jpeg_clk_release(jpeg); err_clk_init: @@ -1452,6 +1460,7 @@ static int mtk_jpeg_remove(struct platform_device *pdev) video_device_release(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); + mtk_jpeg_clk_release(jpeg); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 145686d2c219..147dfef1638d 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -232,14 +232,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mtk_v4l2_err("Could not get vdec IPI device"); return -ENODEV; } - if (!pdev->dev.dma_parms) { - pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, - sizeof(*pdev->dev.dma_parms), - GFP_KERNEL); - if (!pdev->dev.dma_parms) - return -ENOMEM; - } - dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(&pdev->dev, UINT_MAX); dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER); if (IS_ERR(dev->fw_handler)) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 36dfe3fc056a..ddee7046ce42 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -47,11 +47,14 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) dec_clk->clk_info = devm_kcalloc(&pdev->dev, dec_clk->clk_num, sizeof(*clk_info), GFP_KERNEL); - if (!dec_clk->clk_info) - return -ENOMEM; + if (!dec_clk->clk_info) { + ret = -ENOMEM; + goto put_device; + } } else { mtk_v4l2_err("Failed to get vdec clock count"); - return -EINVAL; + ret = -EINVAL; + goto put_device; } for (i = 0; i < dec_clk->clk_num; i++) { @@ -60,25 +63,29 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) "clock-names", i, &clk_info->clk_name); if (ret) { mtk_v4l2_err("Failed to get clock name id = %d", i); - return ret; + goto put_device; } clk_info->vcodec_clk = devm_clk_get(&pdev->dev, clk_info->clk_name); if (IS_ERR(clk_info->vcodec_clk)) { mtk_v4l2_err("devm_clk_get (%d)%s fail", i, clk_info->clk_name); - return PTR_ERR(clk_info->vcodec_clk); + ret = PTR_ERR(clk_info->vcodec_clk); + goto put_device; } } pm_runtime_enable(&pdev->dev); - + return 0; +put_device: + put_device(pm->larbvdec); return ret; } void mtk_vcodec_release_dec_pm(struct mtk_vcodec_dev *dev) { pm_runtime_disable(dev->pm.dev); + put_device(dev->pm.larbvdec); } void mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index 3be8a04c4c67..dfb42e19bf81 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -284,14 +284,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mtk_v4l2_err("Could not get venc IPI device"); return -ENODEV; } - if (!pdev->dev.dma_parms) { - pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, - sizeof(*pdev->dev.dma_parms), - GFP_KERNEL); - if (!pdev->dev.dma_parms) - return -ENOMEM; - } - dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(&pdev->dev, UINT_MAX); dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER); if (IS_ERR(dev->fw_handler)) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index ee22902aaa71..3b7c54d6aa8f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -47,14 +47,16 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) node = of_parse_phandle(dev->of_node, "mediatek,larb", 1); if (!node) { mtk_v4l2_err("no mediatek,larb found"); - return -ENODEV; + ret = -ENODEV; + goto put_larbvenc; } pdev = of_find_device_by_node(node); of_node_put(node); if (!pdev) { mtk_v4l2_err("no mediatek,larb device found"); - return -ENODEV; + ret = -ENODEV; + goto put_larbvenc; } pm->larbvenclt = &pdev->dev; @@ -67,11 +69,14 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) enc_clk->clk_info = devm_kcalloc(&pdev->dev, enc_clk->clk_num, sizeof(*clk_info), GFP_KERNEL); - if (!enc_clk->clk_info) - return -ENOMEM; + if (!enc_clk->clk_info) { + ret = -ENOMEM; + goto put_larbvenclt; + } } else { mtk_v4l2_err("Failed to get venc clock count"); - return -EINVAL; + ret = -EINVAL; + goto put_larbvenclt; } for (i = 0; i < enc_clk->clk_num; i++) { @@ -80,22 +85,31 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) "clock-names", i, &clk_info->clk_name); if (ret) { mtk_v4l2_err("venc failed to get clk name %d", i); - return ret; + goto put_larbvenclt; } clk_info->vcodec_clk = devm_clk_get(&pdev->dev, clk_info->clk_name); if (IS_ERR(clk_info->vcodec_clk)) { mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, clk_info->clk_name); - return PTR_ERR(clk_info->vcodec_clk); + ret = PTR_ERR(clk_info->vcodec_clk); + goto put_larbvenclt; } } + return 0; + +put_larbvenclt: + put_device(pm->larbvenclt); +put_larbvenc: + put_device(pm->larbvenc); return ret; } void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev) { + put_device(mtkdev->pm.larbvenclt); + put_device(mtkdev->pm.larbvenc); } diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index 36cb9b6131f7..043894f7188c 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -27,6 +27,7 @@ #define INIT_TIMEOUT_MS 2000U #define IPI_TIMEOUT_MS 2000U +#define VPU_IDLE_TIMEOUT_MS 1000U #define VPU_FW_VER_LEN 16 /* maximum program/data TCM (Tightly-Coupled Memory) size */ @@ -57,11 +58,17 @@ #define VPU_DMEM_EXT0_ADDR 0x0014 #define VPU_DMEM_EXT1_ADDR 0x0018 #define HOST_TO_VPU 0x0024 +#define VPU_IDLE_REG 0x002C +#define VPU_INT_STATUS 0x0034 #define VPU_PC_REG 0x0060 +#define VPU_SP_REG 0x0064 +#define VPU_RA_REG 0x0068 #define VPU_WDT_REG 0x0084 /* vpu inter-processor communication interrupt */ #define VPU_IPC_INT BIT(8) +/* vpu idle state */ +#define VPU_IDLE_STATE BIT(23) /** * enum vpu_fw_type - VPU firmware type @@ -263,6 +270,20 @@ static int vpu_clock_enable(struct mtk_vpu *vpu) return ret; } +static void vpu_dump_status(struct mtk_vpu *vpu) +{ + dev_info(vpu->dev, + "vpu: run %x, pc = 0x%x, ra = 0x%x, sp = 0x%x, idle = 0x%x\n" + "vpu: int %x, hv = 0x%x, vh = 0x%x, wdt = 0x%x\n", + vpu_running(vpu), vpu_cfg_readl(vpu, VPU_PC_REG), + vpu_cfg_readl(vpu, VPU_RA_REG), vpu_cfg_readl(vpu, VPU_SP_REG), + vpu_cfg_readl(vpu, VPU_IDLE_REG), + vpu_cfg_readl(vpu, VPU_INT_STATUS), + vpu_cfg_readl(vpu, HOST_TO_VPU), + vpu_cfg_readl(vpu, VPU_TO_HOST), + vpu_cfg_readl(vpu, VPU_WDT_REG)); +} + int vpu_ipi_register(struct platform_device *pdev, enum ipi_id id, ipi_handler_t handler, const char *name, void *priv) @@ -323,6 +344,7 @@ int vpu_ipi_send(struct platform_device *pdev, if (time_after(jiffies, timeout)) { dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n"); ret = -EIO; + vpu_dump_status(vpu); goto mut_unlock; } } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); @@ -342,8 +364,9 @@ int vpu_ipi_send(struct platform_device *pdev, ret = wait_event_timeout(vpu->ack_wq, vpu->ipi_id_ack[id], timeout); vpu->ipi_id_ack[id] = false; if (ret == 0) { - dev_err(vpu->dev, "vpu ipi %d ack time out !", id); + dev_err(vpu->dev, "vpu ipi %d ack time out !\n", id); ret = -EIO; + vpu_dump_status(vpu); goto clock_disable; } vpu_clock_disable(vpu); @@ -628,7 +651,7 @@ static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, { char buf[256]; unsigned int len; - unsigned int running, pc, vpu_to_host, host_to_vpu, wdt; + unsigned int running, pc, vpu_to_host, host_to_vpu, wdt, idle, ra, sp; int ret; struct device *dev = file->private_data; struct mtk_vpu *vpu = dev_get_drvdata(dev); @@ -645,6 +668,10 @@ static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, wdt = vpu_cfg_readl(vpu, VPU_WDT_REG); host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU); vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); + ra = vpu_cfg_readl(vpu, VPU_RA_REG); + sp = vpu_cfg_readl(vpu, VPU_SP_REG); + idle = vpu_cfg_readl(vpu, VPU_IDLE_REG); + vpu_clock_disable(vpu); if (running) { @@ -653,9 +680,12 @@ static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, "PC: 0x%x\n" "WDT: 0x%x\n" "Host to VPU: 0x%x\n" - "VPU to Host: 0x%x\n", + "VPU to Host: 0x%x\n" + "SP: 0x%x\n" + "RA: 0x%x\n" + "idle: 0x%x\n", vpu->run.fw_ver, pc, wdt, - host_to_vpu, vpu_to_host); + host_to_vpu, vpu_to_host, sp, ra, idle); } else { len = snprintf(buf, sizeof(buf), "VPU not running\n"); } @@ -945,11 +975,74 @@ static int mtk_vpu_remove(struct platform_device *pdev) return 0; } +static int mtk_vpu_suspend(struct device *dev) +{ + struct mtk_vpu *vpu = dev_get_drvdata(dev); + unsigned long timeout; + int ret; + + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "failed to enable vpu clock\n"); + return ret; + } + + mutex_lock(&vpu->vpu_mutex); + /* disable vpu timer interrupt */ + vpu_cfg_writel(vpu, vpu_cfg_readl(vpu, VPU_INT_STATUS) | VPU_IDLE_STATE, + VPU_INT_STATUS); + /* check if vpu is idle for system suspend */ + timeout = jiffies + msecs_to_jiffies(VPU_IDLE_TIMEOUT_MS); + do { + if (time_after(jiffies, timeout)) { + dev_err(dev, "vpu idle timeout\n"); + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + return -EIO; + } + } while (!vpu_cfg_readl(vpu, VPU_IDLE_REG)); + + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + clk_unprepare(vpu->clk); + + return 0; +} + +static int mtk_vpu_resume(struct device *dev) +{ + struct mtk_vpu *vpu = dev_get_drvdata(dev); + int ret; + + clk_prepare(vpu->clk); + ret = vpu_clock_enable(vpu); + if (ret) { + dev_err(dev, "failed to enable vpu clock\n"); + return ret; + } + + mutex_lock(&vpu->vpu_mutex); + /* enable vpu timer interrupt */ + vpu_cfg_writel(vpu, + vpu_cfg_readl(vpu, VPU_INT_STATUS) & ~(VPU_IDLE_STATE), + VPU_INT_STATUS); + mutex_unlock(&vpu->vpu_mutex); + vpu_clock_disable(vpu); + + return 0; +} + +static const struct dev_pm_ops mtk_vpu_pm = { + .suspend = mtk_vpu_suspend, + .resume = mtk_vpu_resume, +}; + static struct platform_driver mtk_vpu_driver = { .probe = mtk_vpu_probe, .remove = mtk_vpu_remove, .driver = { .name = "mtk_vpu", + .pm = &mtk_vpu_pm, .of_match_table = mtk_vpu_match, }, }; diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 0fbb2aa6dd2c..4e8905ef362f 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -299,11 +299,10 @@ static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) ISPCCDC_LSC_BUSY; } -/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine +/* + * __ccdc_lsc_configure - Apply a new configuration to the LSC engine * @ccdc: Pointer to ISP CCDC device * @req: New configuration request - * - * context: in_interrupt() */ static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, struct ispccdc_lsc_config_req *req) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index e47520fcb93c..b664ce7558a1 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1256,7 +1256,7 @@ static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev, * transformation. Note that UYVY is the only format that * should be used if pxa framebuffer Overlay2 is used. */ - /* fall through */ + fallthrough; case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: @@ -1667,7 +1667,7 @@ static int pxa_camera_get_formats(struct v4l2_device *v4l2_dev, "Providing format %s using code %d\n", pxa_camera_formats[0].name, code.code); } - /* fall through */ + fallthrough; case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 2ffcda06706b..be3fe76f3dc3 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -383,7 +383,8 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, return 0; return sink_code; - } else if (csid->camss->version == CAMSS_8x96) { + } else if (csid->camss->version == CAMSS_8x96 || + csid->camss->version == CAMSS_660) { switch (sink_code) { case MEDIA_BUS_FMT_SBGGR10_1X10: { @@ -718,7 +719,8 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; - if (csid->camss->version == CAMSS_8x96) { + if (csid->camss->version == CAMSS_8x96 || + csid->camss->version == CAMSS_660) { u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; @@ -1098,7 +1100,8 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->formats = csid_formats_8x16; csid->nformats = ARRAY_SIZE(csid_formats_8x16); - } else if (camss->version == CAMSS_8x96) { + } else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { csid->formats = csid_formats_8x96; csid->nformats = ARRAY_SIZE(csid_formats_8x96); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c index 2e65caf1ecae..97cb9de85031 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -8,6 +8,7 @@ * Copyright (C) 2016-2018 Linaro Ltd. */ +#include "camss.h" #include "camss-csiphy.h" #include <linux/delay.h> @@ -21,6 +22,7 @@ #define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n)) #define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n)) #define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4 +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660 0xa5 #define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n)) #define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02 #define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50 @@ -198,7 +200,10 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l)); - val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; + if (csiphy->camss->version == CAMSS_660) + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS_660; + else + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG4(l)); val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 85b24054f35e..509c9a59c09c 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -113,9 +113,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) for (i = 0; i < csiphy->nclocks; i++) { struct camss_clock *clock = &csiphy->clock[i]; - if (!strcmp(clock->name, "csiphy0_timer") || - !strcmp(clock->name, "csiphy1_timer") || - !strcmp(clock->name, "csiphy2_timer")) { + if (csiphy->rate_set[i]) { u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); @@ -554,7 +552,8 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->ops = &csiphy_ops_2ph_1_0; csiphy->formats = csiphy_formats_8x16; csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); - } else if (camss->version == CAMSS_8x96) { + } else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { csiphy->ops = &csiphy_ops_3ph_1_0; csiphy->formats = csiphy_formats_8x96; csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); @@ -612,6 +611,13 @@ int msm_csiphy_subdev_init(struct camss *camss, if (!csiphy->clock) return -ENOMEM; + csiphy->rate_set = devm_kcalloc(dev, + csiphy->nclocks, + sizeof(*csiphy->rate_set), + GFP_KERNEL); + if (!csiphy->rate_set) + return -ENOMEM; + for (i = 0; i < csiphy->nclocks; i++) { struct camss_clock *clock = &csiphy->clock[i]; @@ -639,6 +645,17 @@ int msm_csiphy_subdev_init(struct camss *camss, for (j = 0; j < clock->nfreqs; j++) clock->freq[j] = res->clock_rate[i][j]; + + if (!strcmp(clock->name, "csiphy0_timer") || + !strcmp(clock->name, "csiphy1_timer") || + !strcmp(clock->name, "csiphy2_timer")) + csiphy->rate_set[i] = true; + + if (camss->version == CAMSS_660 && + (!strcmp(clock->name, "csi0_phy") || + !strcmp(clock->name, "csi1_phy") || + !strcmp(clock->name, "csi2_phy"))) + csiphy->rate_set[i] = true; } return 0; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 376f865ad383..f7967ef836dc 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -66,6 +66,7 @@ struct csiphy_device { u32 irq; char irq_name[30]; struct camss_clock *clock; + bool *rate_set; int nclocks; u32 timer_clk_rate; struct csiphy_config cfg; diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index db94cfd6c508..adeb92808998 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -26,6 +26,7 @@ #define MSM_ISPIF_NAME "msm_ispif" #define ISPIF_RST_CMD_0 0x008 +#define ISPIF_RST_CMD_1 0x00c #define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) #define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) #define ISPIF_RST_CMD_0_SW_REG_RST (1 << 2) @@ -179,7 +180,10 @@ static irqreturn_t ispif_isr_8x96(int irq, void *dev) writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); if ((value0 >> 27) & 0x1) - complete(&ispif->reset_complete); + complete(&ispif->reset_complete[0]); + + if ((value3 >> 27) & 0x1) + complete(&ispif->reset_complete[1]); if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); @@ -237,7 +241,7 @@ static irqreturn_t ispif_isr_8x16(int irq, void *dev) writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); if ((value0 >> 27) & 0x1) - complete(&ispif->reset_complete); + complete(&ispif->reset_complete[0]); if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); @@ -257,33 +261,18 @@ static irqreturn_t ispif_isr_8x16(int irq, void *dev) return IRQ_HANDLED; } -/* - * ispif_reset - Trigger reset on ISPIF module and wait to complete - * @ispif: ISPIF device - * - * Return 0 on success or a negative error code otherwise - */ -static int ispif_reset(struct ispif_device *ispif) +static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) { unsigned long time; u32 val; - int ret; - - ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0); - if (ret < 0) - return ret; - ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1); - if (ret < 0) - return ret; - - ret = camss_enable_clocks(ispif->nclocks_for_reset, - ispif->clock_for_reset, - to_device(ispif)); - if (ret < 0) - return ret; + if (vfe_id > (to_camss(ispif)->vfe_num - 1)) { + dev_err(to_device(ispif), + "Error: asked reset for invalid VFE%d\n", vfe_id); + return -ENOENT; + } - reinit_completion(&ispif->reset_complete); + reinit_completion(&ispif->reset_complete[vfe_id]); val = ISPIF_RST_CMD_0_STROBED_RST_EN | ISPIF_RST_CMD_0_MISC_LOGIC_RST | @@ -303,15 +292,50 @@ static int ispif_reset(struct ispif_device *ispif) ISPIF_RST_CMD_0_RDI_OUTPUT_1_MISR_RST | ISPIF_RST_CMD_0_RDI_OUTPUT_2_MISR_RST; - writel_relaxed(val, ispif->base + ISPIF_RST_CMD_0); + if (vfe_id == 1) + writel_relaxed(val, ispif->base + ISPIF_RST_CMD_1); + else + writel_relaxed(val, ispif->base + ISPIF_RST_CMD_0); - time = wait_for_completion_timeout(&ispif->reset_complete, + time = wait_for_completion_timeout(&ispif->reset_complete[vfe_id], msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device(ispif), "ISPIF reset timeout\n"); - ret = -EIO; + dev_err(to_device(ispif), + "ISPIF for VFE%d reset timeout\n", vfe_id); + return -EIO; } + return 0; +} + +/* + * ispif_reset - Trigger reset on ISPIF module and wait to complete + * @ispif: ISPIF device + * + * Return 0 on success or a negative error code otherwise + */ +static int ispif_reset(struct ispif_device *ispif, u8 vfe_id) +{ + int ret; + + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0); + if (ret < 0) + return ret; + + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1); + if (ret < 0) + return ret; + + ret = camss_enable_clocks(ispif->nclocks_for_reset, + ispif->clock_for_reset, + to_device(ispif)); + if (ret < 0) + return ret; + + ret = ispif_vfe_reset(ispif, vfe_id); + if (ret) + dev_dbg(to_device(ispif), "ISPIF Reset failed\n"); + camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0); @@ -355,7 +379,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } - ret = ispif_reset(ispif); + ret = ispif_reset(ispif, line->vfe_id); if (ret < 0) { pm_runtime_put_sync(dev); camss_disable_clocks(ispif->nclocks, ispif->clock); @@ -801,7 +825,8 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) ispif_select_csid(ispif, intf, csid, vfe, 1); ispif_select_cid(ispif, intf, cid, vfe, 1); ispif_config_irq(ispif, intf, vfe, 1); - if (to_camss(ispif)->version == CAMSS_8x96) + if (to_camss(ispif)->version == CAMSS_8x96 || + to_camss(ispif)->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, intf, cid, vfe, 1); @@ -818,7 +843,8 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) return ret; mutex_lock(&ispif->config_lock); - if (to_camss(ispif)->version == CAMSS_8x96) + if (to_camss(ispif)->version == CAMSS_8x96 || + to_camss(ispif)->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, intf, cid, vfe, 0); @@ -1074,7 +1100,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, /* Number of ISPIF lines - same as number of CSID hardware modules */ if (to_camss(ispif)->version == CAMSS_8x16) ispif->line_num = 2; - else if (to_camss(ispif)->version == CAMSS_8x96) + else if (to_camss(ispif)->version == CAMSS_8x96 || + to_camss(ispif)->version == CAMSS_660) ispif->line_num = 4; else return -EINVAL; @@ -1092,7 +1119,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, ispif->line[i].formats = ispif_formats_8x16; ispif->line[i].nformats = ARRAY_SIZE(ispif_formats_8x16); - } else if (to_camss(ispif)->version == CAMSS_8x96) { + } else if (to_camss(ispif)->version == CAMSS_8x96 || + to_camss(ispif)->version == CAMSS_660) { ispif->line[i].formats = ispif_formats_8x96; ispif->line[i].nformats = ARRAY_SIZE(ispif_formats_8x96); @@ -1132,7 +1160,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, if (to_camss(ispif)->version == CAMSS_8x16) ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); - else if (to_camss(ispif)->version == CAMSS_8x96) + else if (to_camss(ispif)->version == CAMSS_8x96 || + to_camss(ispif)->version == CAMSS_660) ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); else @@ -1192,7 +1221,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, mutex_init(&ispif->config_lock); - init_completion(&ispif->reset_complete); + for (i = 0; i < MSM_ISPIF_VFE_NUM; i++) + init_completion(&ispif->reset_complete[i]); return 0; } diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index 1a5ba2425a42..4132174f7ea1 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -56,7 +56,7 @@ struct ispif_device { int nclocks; struct camss_clock *clock_for_reset; int nclocks_for_reset; - struct completion reset_complete; + struct completion reset_complete[MSM_ISPIF_VFE_NUM]; int power_count; struct mutex power_lock; struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM]; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index 0dca8bf9281e..b5704a2f119b 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -133,6 +133,11 @@ #define VFE_0_BUS_BDG_QOS_CFG_7 0x420 #define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa9 +#define VFE48_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 +#define VFE48_0_BUS_BDG_QOS_CFG_3_CFG 0xaa55aaa5 +#define VFE48_0_BUS_BDG_QOS_CFG_4_CFG 0xaa55aa55 +#define VFE48_0_BUS_BDG_QOS_CFG_7_CFG 0x0005aa55 + #define VFE_0_BUS_BDG_DS_CFG_0 0x424 #define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc0011 #define VFE_0_BUS_BDG_DS_CFG_1 0x428 @@ -153,6 +158,9 @@ #define VFE_0_BUS_BDG_DS_CFG_16 0x464 #define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x40000103 +#define VFE48_0_BUS_BDG_DS_CFG_0_CFG 0xcccc1111 +#define VFE48_0_BUS_BDG_DS_CFG_16_CFG 0x00000110 + #define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x))) #define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 #define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) @@ -231,6 +239,9 @@ #define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3) #define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4) +#define VFE48_0_BUS_IMAGE_MASTER_CMD 0xcec +#define VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(x) (2 * (x)) + #define CAMIF_TIMEOUT_SLEEP_US 1000 #define CAMIF_TIMEOUT_ALL_US 1000000 @@ -246,7 +257,7 @@ static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version); } -static u16 vfe_get_ub_size(u8 vfe_id) +static u16 vfe47_get_ub_size(u8 vfe_id) { if (vfe_id == 0) return MSM_VFE_VFE0_UB_SIZE_RDI; @@ -299,7 +310,7 @@ static void vfe_halt_clear(struct vfe_device *vfe) writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); } -static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +static void vfe47_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) { if (enable) vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), @@ -883,7 +894,7 @@ static void vfe_set_clamp_cfg(struct vfe_device *vfe) writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); } -static void vfe_set_qos(struct vfe_device *vfe) +static void vfe47_set_qos(struct vfe_device *vfe) { u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; @@ -898,7 +909,7 @@ static void vfe_set_qos(struct vfe_device *vfe) writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); } -static void vfe_set_ds(struct vfe_device *vfe) +static void vfe47_set_ds(struct vfe_device *vfe) { u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG; u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG; @@ -1098,11 +1109,115 @@ static irqreturn_t vfe_isr(int irq, void *dev) const struct vfe_hw_ops vfe_ops_4_7 = { .hw_version_read = vfe_hw_version_read, - .get_ub_size = vfe_get_ub_size, + .get_ub_size = vfe47_get_ub_size, + .global_reset = vfe_global_reset, + .halt_request = vfe_halt_request, + .halt_clear = vfe_halt_clear, + .wm_enable = vfe47_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, + .bus_reload_wm = vfe_bus_reload_wm, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .wm_set_subsample = vfe_wm_set_subsample, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .set_xbar_cfg = vfe_set_xbar_cfg, + .set_realign_cfg = vfe_set_realign_cfg, + .set_rdi_cid = vfe_set_rdi_cid, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_common = vfe_enable_irq_common, + .set_demux_cfg = vfe_set_demux_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_qos = vfe47_set_qos, + .set_ds = vfe47_set_ds, + .set_cgc_override = vfe_set_cgc_override, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_module_cfg = vfe_set_module_cfg, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .isr_read = vfe_isr_read, + .violation_read = vfe_violation_read, + .isr = vfe_isr, +}; + +static u16 vfe48_get_ub_size(u8 vfe_id) +{ + /* On VFE4.8 the ub-size is the same on both instances */ + return MSM_VFE_VFE0_UB_SIZE_RDI; +} + +static void vfe48_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + writel_relaxed(2 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm), + vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD); + else + writel_relaxed(1 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm), + vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD); + + /* The WM must be enabled before sending other commands */ + wmb(); +} + +static void vfe48_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE48_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val3 = VFE48_0_BUS_BDG_QOS_CFG_3_CFG; + u32 val4 = VFE48_0_BUS_BDG_QOS_CFG_4_CFG; + u32 val7 = VFE48_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe48_set_ds(struct vfe_device *vfe) +{ + u32 val = VFE48_0_BUS_BDG_DS_CFG_0_CFG; + u32 val16 = VFE48_0_BUS_BDG_DS_CFG_16_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15); + writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16); +} + +const struct vfe_hw_ops vfe_ops_4_8 = { + .hw_version_read = vfe_hw_version_read, + .get_ub_size = vfe48_get_ub_size, .global_reset = vfe_global_reset, .halt_request = vfe_halt_request, .halt_clear = vfe_halt_clear, - .wm_enable = vfe_wm_enable, + .wm_enable = vfe48_wm_enable, .wm_frame_based = vfe_wm_frame_based, .wm_line_based = vfe_wm_line_based, .wm_set_framedrop_period = vfe_wm_set_framedrop_period, @@ -1128,8 +1243,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = { .set_scale_cfg = vfe_set_scale_cfg, .set_crop_cfg = vfe_set_crop_cfg, .set_clamp_cfg = vfe_set_clamp_cfg, - .set_qos = vfe_set_qos, - .set_ds = vfe_set_ds, + .set_qos = vfe48_set_qos, + .set_ds = vfe48_set_ds, .set_cgc_override = vfe_set_cgc_override, .set_camif_cfg = vfe_set_camif_cfg, .set_camif_cmd = vfe_set_camif_cmd, diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index b7d2293a5004..fae2b513b2f9 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -205,7 +205,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, return sink_code; } - else if (vfe->camss->version == CAMSS_8x96) + else if (vfe->camss->version == CAMSS_8x96 || + vfe->camss->version == CAMSS_660) switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_2X8: { @@ -1991,12 +1992,19 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->isr_ops.comp_done = vfe_isr_comp_done; vfe->isr_ops.wm_done = vfe_isr_wm_done; - if (camss->version == CAMSS_8x16) + switch (camss->version) { + case CAMSS_8x16: vfe->ops = &vfe_ops_4_1; - else if (camss->version == CAMSS_8x96) + break; + case CAMSS_8x96: vfe->ops = &vfe_ops_4_7; - else + break; + case CAMSS_660: + vfe->ops = &vfe_ops_4_8; + break; + default: return -EINVAL; + } /* Memory */ @@ -2095,7 +2103,8 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, l->formats = formats_rdi_8x16; l->nformats = ARRAY_SIZE(formats_rdi_8x16); } - } else if (camss->version == CAMSS_8x96) { + } else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { if (i == VFE_LINE_PIX) { l->formats = formats_pix_8x96; l->nformats = ARRAY_SIZE(formats_pix_8x96); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index a90b0d2cc6de..5bce6736e4bb 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -180,5 +180,6 @@ void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; +extern const struct vfe_hw_ops vfe_ops_4_8; #endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 114c3ae4a4ab..bd9334af1c73 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -535,20 +535,38 @@ static int video_querycap(struct file *file, void *fh, return 0; } -/* - * Returns the index in the video->formats[] array of the element which - * has the "ndx"th unique value of pixelformat field. - * If not found (no more unique pixelformat's) returns -EINVAL. - */ -static int video_get_unique_pixelformat_by_index(struct camss_video *video, - int ndx) +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) { + struct camss_video *video = video_drvdata(file); int i, j, k; + u32 mcode = f->mbus_code; - /* find index "i" of "k"th unique pixelformat in formats array */ + if (f->type != video->type) + return -EINVAL; + + if (f->index >= video->nformats) + return -EINVAL; + + /* + * Find index "i" of "k"th unique pixelformat in formats array. + * + * If f->mbus_code passed to video_enum_fmt() is not zero, a device + * with V4L2_CAP_IO_MC capability restricts enumeration to only the + * pixel formats that can be produced from that media bus code. + * This is implemented by skipping video->formats[] entries with + * code != f->mbus_code (if f->mbus_code is not zero). + * If the f->mbus_code passed to video_enum_fmt() is not supported, + * -EINVAL is returned. + * If f->mbus_code is zero, all the pixel formats are enumerated. + */ k = -1; for (i = 0; i < video->nformats; i++) { + if (mcode != 0 && video->formats[i].code != mcode) + continue; + for (j = 0; j < i; j++) { + if (mcode != 0 && video->formats[j].code != mcode) + continue; if (video->formats[i].pixelformat == video->formats[j].pixelformat) break; @@ -557,53 +575,16 @@ static int video_get_unique_pixelformat_by_index(struct camss_video *video, if (j == i) k++; - if (k == ndx) - return i; - } - - return -EINVAL; -} - -/* - * Returns the index in the video->formats[] array of the element which - * has code equal to mcode. - * If not found returns -EINVAL. - */ -static int video_get_pixelformat_by_mbus_code(struct camss_video *video, - u32 mcode) -{ - int i; - - for (i = 0; i < video->nformats; i++) { - if (video->formats[i].code == mcode) - return i; - } - - return -EINVAL; -} - -static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) -{ - struct camss_video *video = video_drvdata(file); - int i; - - if (f->type != video->type) - return -EINVAL; - - if (f->index >= video->nformats) - return -EINVAL; - - if (f->mbus_code) { - /* Each entry in formats[] table has unique mbus_code */ - if (f->index > 0) - return -EINVAL; - - i = video_get_pixelformat_by_mbus_code(video, f->mbus_code); - } else { - i = video_get_unique_pixelformat_by_index(video, f->index); + if (k == f->index) + break; } - if (i < 0) + if (k < f->index) + /* + * All the unique pixel formats matching the arguments + * have been enumerated (k >= 0 and f->index > 0), or + * no pixel formats match the non-zero f->mbus_code (k == -1). + */ return -EINVAL; f->pixelformat = video->formats[i].pixelformat; @@ -970,7 +951,8 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, video->formats = formats_rdi_8x16; video->nformats = ARRAY_SIZE(formats_rdi_8x16); } - } else if (video->camss->version == CAMSS_8x96) { + } else if (video->camss->version == CAMSS_8x96 || + video->camss->version == CAMSS_660) { if (is_pix) { video->formats = formats_pix_8x96; video->nformats = ARRAY_SIZE(formats_pix_8x96); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 9186881afc98..8fefce57bc49 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -283,6 +283,188 @@ static const struct resources vfe_res_8x96[] = { } }; +static const struct resources csiphy_res_660[] = { + /* CSIPHY0 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer", + "csi0_phy", "csiphy_ahb2crif" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 269333333 }, + { 0 } }, + .reg = { "csiphy0", "csiphy0_clk_mux" }, + .interrupt = { "csiphy0" } + }, + + /* CSIPHY1 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer", + "csi1_phy", "csiphy_ahb2crif" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 269333333 }, + { 0 } }, + .reg = { "csiphy1", "csiphy1_clk_mux" }, + .interrupt = { "csiphy1" } + }, + + /* CSIPHY2 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer", + "csi2_phy", "csiphy_ahb2crif" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 269333333 }, + { 0 } }, + .reg = { "csiphy2", "csiphy2_clk_mux" }, + .interrupt = { "csiphy2" } + } +}; + +static const struct resources csid_res_660[] = { + /* CSID0 */ + { + .regulator = { "vdda", "vdd_sec" }, + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi", + "cphy_csid0" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 404000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + + /* CSID1 */ + { + .regulator = { "vdda", "vdd_sec" }, + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi", + "cphy_csid1" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 404000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, + + /* CSID2 */ + { + .regulator = { "vdda", "vdd_sec" }, + .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", + "csi2", "csi2_phy", "csi2_pix", "csi2_rdi", + "cphy_csid2" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 404000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" } + }, + + /* CSID3 */ + { + .regulator = { "vdda", "vdd_sec" }, + .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", + "csi3", "csi3_phy", "csi3_pix", "csi3_rdi", + "cphy_csid3" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 310000000, + 404000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid3" }, + .interrupt = { "csid3" } + } +}; + +static const struct resources_ispif ispif_res_660 = { + /* ISPIF */ + .clock = { "top_ahb", "ahb", "ispif_ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi", + "csi2", "csi2_pix", "csi2_rdi", + "csi3", "csi3_pix", "csi3_rdi" }, + .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = "ispif" +}; + +static const struct resources vfe_res_660[] = { + /* VFE0 */ + { + .regulator = { NULL }, + .clock = { "throttle_axi", "top_ahb", "ahb", "vfe0", + "csi_vfe0", "vfe_ahb", "vfe0_ahb", "vfe_axi", + "vfe0_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 120000000, 200000000, 256000000, + 300000000, 404000000, 480000000, + 540000000, 576000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + }, + + /* VFE1 */ + { + .regulator = { NULL }, + .clock = { "throttle_axi", "top_ahb", "ahb", "vfe1", + "csi_vfe1", "vfe_ahb", "vfe1_ahb", "vfe_axi", + "vfe1_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 120000000, 200000000, 256000000, + 300000000, 404000000, 480000000, + 540000000, 576000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" } + } +}; + /* * camss_add_clock_margin - Add margin to clock frequency rate * @rate: Clock frequency rate @@ -397,7 +579,8 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) int camss_pm_domain_on(struct camss *camss, int id) { - if (camss->version == CAMSS_8x96) { + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); @@ -411,7 +594,8 @@ int camss_pm_domain_on(struct camss *camss, int id) void camss_pm_domain_off(struct camss *camss, int id) { - if (camss->version == CAMSS_8x96) + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) device_link_del(camss->genpd_link[id]); } @@ -533,6 +717,11 @@ static int camss_init_subdevices(struct camss *camss) csid_res = csid_res_8x96; ispif_res = &ispif_res_8x96; vfe_res = vfe_res_8x96; + } else if (camss->version == CAMSS_660) { + csiphy_res = csiphy_res_660; + csid_res = csid_res_660; + ispif_res = &ispif_res_660; + vfe_res = vfe_res_660; } else { return -EINVAL; } @@ -833,6 +1022,12 @@ static int camss_probe(struct platform_device *pdev) camss->csiphy_num = 3; camss->csid_num = 4; camss->vfe_num = 2; + } else if (of_device_is_compatible(dev->of_node, + "qcom,sdm660-camss")) { + camss->version = CAMSS_660; + camss->csiphy_num = 3; + camss->csid_num = 4; + camss->vfe_num = 2; } else { ret = -EINVAL; goto err_free; @@ -919,7 +1114,8 @@ static int camss_probe(struct platform_device *pdev) } } - if (camss->version == CAMSS_8x96) { + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( camss->dev, PM_DOMAIN_VFE0); if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) @@ -958,7 +1154,8 @@ void camss_delete(struct camss *camss) pm_runtime_disable(camss->dev); - if (camss->version == CAMSS_8x96) { + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); } @@ -989,6 +1186,7 @@ static int camss_remove(struct platform_device *pdev) static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss" }, { .compatible = "qcom,msm8996-camss" }, + { .compatible = "qcom,sdm660-camss" }, { } }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 1376b07889bf..3a0484683cd6 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -65,6 +65,7 @@ enum pm_domain { enum camss_version { CAMSS_8x16, CAMSS_8x96, + CAMSS_660, }; struct camss { diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 6103aaf43987..bdd293faaad0 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -345,6 +345,14 @@ static int venus_remove(struct platform_device *pdev) return ret; } +static void venus_core_shutdown(struct platform_device *pdev) +{ + struct venus_core *core = platform_get_drvdata(pdev); + + venus_shutdown(core); + venus_firmware_deinit(core); +} + static __maybe_unused int venus_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); @@ -355,12 +363,26 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) if (ret) return ret; + if (pm_ops->core_power) { + ret = pm_ops->core_power(dev, POWER_OFF); + if (ret) + return ret; + } + ret = icc_set_bw(core->cpucfg_path, 0, 0); if (ret) - return ret; + goto err_cpucfg_path; - if (pm_ops->core_power) - ret = pm_ops->core_power(dev, POWER_OFF); + ret = icc_set_bw(core->video_path, 0, 0); + if (ret) + goto err_video_path; + + return ret; + +err_video_path: + icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); +err_cpucfg_path: + pm_ops->core_power(dev, POWER_ON); return ret; } @@ -371,16 +393,20 @@ static __maybe_unused int venus_runtime_resume(struct device *dev) const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; + ret = icc_set_bw(core->video_path, kbps_to_icc(20000), 0); + if (ret) + return ret; + + ret = icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); + if (ret) + return ret; + if (pm_ops->core_power) { ret = pm_ops->core_power(dev, POWER_ON); if (ret) return ret; } - ret = icc_set_bw(core->cpucfg_path, 0, kbps_to_icc(1000)); - if (ret) - return ret; - return hfi_core_resume(core, false); } @@ -602,6 +628,7 @@ static struct platform_driver qcom_venus_driver = { .of_match_table = venus_dt_match, .pm = &venus_pm_ops, }, + .shutdown = venus_core_shutdown, }; module_platform_driver(qcom_venus_driver); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 05c9fbd51f0c..f03ed427accd 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -372,6 +372,7 @@ struct venus_inst { unsigned int streamon_cap, streamon_out; u32 width; u32 height; + struct v4l2_rect crop; u32 out_width; u32 out_height; u32 colorspace; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 1db64a854b88..d03e2dd5808c 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -171,9 +171,14 @@ static int venus_shutdown_no_tz(struct venus_core *core) iommu = core->fw.iommu_domain; - unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped); - if (unmapped != mapped) - dev_err(dev, "failed to unmap firmware\n"); + if (core->fw.mapped_mem_size && iommu) { + unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped); + + if (unmapped != mapped) + dev_err(dev, "failed to unmap firmware\n"); + else + core->fw.mapped_mem_size = 0; + } return 0; } @@ -305,7 +310,11 @@ void venus_firmware_deinit(struct venus_core *core) iommu = core->fw.iommu_domain; iommu_detach_device(iommu, core->fw.dev); - iommu_domain_free(iommu); + + if (core->fw.iommu_domain) { + iommu_domain_free(iommu); + core->fw.iommu_domain = NULL; + } platform_device_unregister(to_platform_device(core->fw.dev)); } diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index a59022adb14c..638ed5cfe05e 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -198,6 +198,18 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) const struct hfi_ops *ops = core->ops; int ret; + /* + * If core shutdown is in progress or if we are in system + * recovery, return an error as during system error recovery + * session_init() can't pass successfully + */ + mutex_lock(&core->lock); + if (!core->ops || core->sys_error) { + mutex_unlock(&core->lock); + return -EIO; + } + mutex_unlock(&core->lock); + if (inst->state != INST_UNINIT) return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index a9538c2cc3c9..ca99908ca3d3 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -212,6 +212,16 @@ static int load_scale_bw(struct venus_core *core) } mutex_unlock(&core->lock); + /* + * keep minimum bandwidth vote for "video-mem" path, + * so that clks can be disabled during vdec_session_release(). + * Actual bandwidth drop will be done during device supend + * so that device can power down without any warnings. + */ + + if (!total_avg && !total_peak) + total_avg = kbps_to_icc(1000); + dev_dbg(core->dev, VDBGL "total: avg_bw: %u, peak_bw: %u\n", total_avg, total_peak); @@ -928,7 +938,7 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst, u32 fps = (u32)inst->fps; u32 mbs_per_sec; - mbs_per_sec = load_per_instance(inst) / fps; + mbs_per_sec = load_per_instance(inst); vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; /* 21 / 20 is overhead factor */ diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index ea13170a6a2c..8488411204c3 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -325,6 +325,10 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) inst->width = format.fmt.pix_mp.width; inst->height = format.fmt.pix_mp.height; + inst->crop.top = 0; + inst->crop.left = 0; + inst->crop.width = inst->width; + inst->crop.height = inst->height; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) inst->fmt_out = fmt; @@ -343,6 +347,9 @@ vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; + s->r.top = 0; + s->r.left = 0; + switch (s->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: @@ -363,16 +370,12 @@ vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) case V4L2_SEL_TGT_COMPOSE: if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - s->r.width = inst->out_width; - s->r.height = inst->out_height; + s->r = inst->crop; break; default: return -EINVAL; } - s->r.top = 0; - s->r.left = 0; - return 0; } @@ -1309,6 +1312,21 @@ static void vdec_event_change(struct venus_inst *inst, inst->width = format.fmt.pix_mp.width; inst->height = format.fmt.pix_mp.height; + /* + * Some versions of the firmware do not report crop information for + * all codecs. For these cases, set the crop to the coded resolution. + */ + if (ev_data->input_crop.width > 0 && ev_data->input_crop.height > 0) { + inst->crop.left = ev_data->input_crop.left; + inst->crop.top = ev_data->input_crop.top; + inst->crop.width = ev_data->input_crop.width; + inst->crop.height = ev_data->input_crop.height; + } else { + inst->crop.left = 0; + inst->crop.top = 0; + inst->crop.width = ev_data->width; + inst->crop.height = ev_data->height; + } inst->out_width = ev_data->width; inst->out_height = ev_data->height; @@ -1412,6 +1430,10 @@ static void vdec_inst_init(struct venus_inst *inst) inst->fmt_cap = &vdec_formats[0]; inst->width = frame_width_min(inst); inst->height = ALIGN(frame_height_min(inst), 32); + inst->crop.left = 0; + inst->crop.top = 0; + inst->crop.width = inst->width; + inst->crop.height = inst->height; inst->out_width = frame_width_min(inst); inst->out_height = frame_height_min(inst); inst->fps = 30; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 47246528ac7e..1c61602c5de1 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1028,7 +1028,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, static void venc_inst_init(struct venus_inst *inst) { - inst->fmt_cap = &venc_formats[2]; + inst->fmt_cap = &venc_formats[3]; inst->fmt_out = &venc_formats[0]; inst->width = 1280; inst->height = ALIGN(720, 32); diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 34d003e0e9b9..98bff765b02e 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -185,8 +185,8 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, */ sd = media_entity_to_v4l2_subdev(link->source->entity); for (i = 0; i < RCAR_VIN_NUM; i++) { - if (group->vin[i] && group->vin[i]->parallel && - group->vin[i]->parallel->subdev == sd) { + if (group->vin[i] && + group->vin[i]->parallel.subdev == sd) { group->vin[i]->is_csi = false; ret = 0; goto out; @@ -440,20 +440,20 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); if (ret < 0) return ret; - vin->parallel->source_pad = ret; + vin->parallel.source_pad = ret; ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->parallel->sink_pad = ret < 0 ? 0 : ret; + vin->parallel.sink_pad = ret < 0 ? 0 : ret; if (vin->info->use_mc) { - vin->parallel->subdev = subdev; + vin->parallel.subdev = subdev; return 0; } /* Find compatible subdevices mbus format */ vin->mbus_code = 0; code.index = 0; - code.pad = vin->parallel->source_pad; + code.pad = vin->parallel.source_pad; while (!vin->mbus_code && !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { code.index++; @@ -512,7 +512,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, vin->vdev.ctrl_handler = &vin->ctrl_handler; - vin->parallel->subdev = subdev; + vin->parallel.subdev = subdev; return 0; } @@ -520,7 +520,7 @@ static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) { rvin_v4l2_unregister(vin); - vin->parallel->subdev = NULL; + vin->parallel.subdev = NULL; if (!vin->info->use_mc) { v4l2_ctrl_handler_free(&vin->ctrl_handler); @@ -551,11 +551,11 @@ static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) return 0; /* If we're running with media-controller, link the subdevs. */ - source = &vin->parallel->subdev->entity; + source = &vin->parallel.subdev->entity; sink = &vin->vdev.entity; - ret = media_create_pad_link(source, vin->parallel->source_pad, - sink, vin->parallel->sink_pad, 0); + ret = media_create_pad_link(source, vin->parallel.source_pad, + sink, vin->parallel.sink_pad, 0); if (ret) vin_err(vin, "Error adding link from %s to %s: %d\n", source->name, sink->name, ret); @@ -592,8 +592,8 @@ static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->parallel->source_pad, - vin->parallel->sink_pad); + subdev->name, vin->parallel.source_pad, + vin->parallel.sink_pad); return 0; } @@ -604,33 +604,56 @@ static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { .complete = rvin_parallel_notify_complete, }; -static int rvin_parallel_parse_v4l2(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int rvin_parallel_parse_of(struct rvin_dev *vin) { - struct rvin_dev *vin = dev_get_drvdata(dev); - struct rvin_parallel_entity *rvpe = - container_of(asd, struct rvin_parallel_entity, asd); + struct fwnode_handle *ep, *fwnode; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_UNKNOWN, + }; + struct v4l2_async_subdev *asd; + int ret; - if (vep->base.port || vep->base.id) - return -ENOTCONN; + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0); + if (!ep) + return 0; - vin->parallel = rvpe; - vin->parallel->mbus_type = vep->bus_type; + fwnode = fwnode_graph_get_remote_endpoint(ep); + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) { + vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode)); + ret = -EINVAL; + goto out; + } - switch (vin->parallel->mbus_type) { + switch (vep.bus_type) { case V4L2_MBUS_PARALLEL: case V4L2_MBUS_BT656: vin_dbg(vin, "Found %s media bus\n", - vin->parallel->mbus_type == V4L2_MBUS_PARALLEL ? + vep.bus_type == V4L2_MBUS_PARALLEL ? "PARALLEL" : "BT656"); - vin->parallel->bus = vep->bus.parallel; + vin->parallel.mbus_type = vep.bus_type; + vin->parallel.bus = vep.bus.parallel; break; default: vin_err(vin, "Unknown media bus type\n"); - return -EINVAL; + ret = -EINVAL; + goto out; + } + + asd = v4l2_async_notifier_add_fwnode_subdev(&vin->notifier, fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out; } + vin->parallel.asd = asd; + + vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode)); +out: + fwnode_handle_put(fwnode); + return 0; } @@ -640,18 +663,16 @@ static int rvin_parallel_init(struct rvin_dev *vin) v4l2_async_notifier_init(&vin->notifier); - ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( - vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity), - 0, rvin_parallel_parse_v4l2); + ret = rvin_parallel_parse_of(vin); if (ret) return ret; /* If using mc, it's fine not to have any input registered. */ - if (!vin->parallel) + if (!vin->parallel.asd) return vin->info->use_mc ? 0 : -ENODEV; vin_dbg(vin, "Found parallel subdevice %pOF\n", - to_of_node(vin->parallel->asd.match.fwnode)); + to_of_node(vin->parallel.asd->match.fwnode)); vin->notifier.ops = &rvin_parallel_notify_ops; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); @@ -751,7 +772,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, mutex_lock(&vin->group->lock); for (i = 0; i < RVIN_CSI_MAX; i++) { - if (vin->group->csi[i].fwnode != asd->match.fwnode) + if (vin->group->csi[i].asd != asd) continue; vin->group->csi[i].subdev = NULL; vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i); @@ -773,7 +794,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, mutex_lock(&vin->group->lock); for (i = 0; i < RVIN_CSI_MAX; i++) { - if (vin->group->csi[i].fwnode != asd->match.fwnode) + if (vin->group->csi[i].asd != asd) continue; vin->group->csi[i].subdev = subdev; vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i); @@ -791,37 +812,48 @@ static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { .complete = rvin_group_notify_complete, }; -static int rvin_mc_parse_of_endpoint(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int rvin_mc_parse_of(struct rvin_dev *vin, unsigned int id) { - struct rvin_dev *vin = dev_get_drvdata(dev); - int ret = 0; + struct fwnode_handle *ep, *fwnode; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct v4l2_async_subdev *asd; + int ret; - if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX) - return -EINVAL; + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 1, id, 0); + if (!ep) + return 0; - if (!of_device_is_available(to_of_node(asd->match.fwnode))) { - vin_dbg(vin, "OF device %pOF disabled, ignoring\n", - to_of_node(asd->match.fwnode)); - return -ENOTCONN; + fwnode = fwnode_graph_get_remote_endpoint(ep); + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) { + vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode)); + ret = -EINVAL; + goto out; } - mutex_lock(&vin->group->lock); - - if (vin->group->csi[vep->base.id].fwnode) { - vin_dbg(vin, "OF device %pOF already handled\n", - to_of_node(asd->match.fwnode)); + if (!of_device_is_available(to_of_node(fwnode))) { + vin_dbg(vin, "OF device %pOF disabled, ignoring\n", + to_of_node(fwnode)); ret = -ENOTCONN; goto out; } - vin->group->csi[vep->base.id].fwnode = asd->match.fwnode; + asd = v4l2_async_notifier_add_fwnode_subdev(&vin->group->notifier, + fwnode, sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto out; + } + + vin->group->csi[vep.base.id].asd = asd; vin_dbg(vin, "Add group OF device %pOF to slot %u\n", - to_of_node(asd->match.fwnode), vep->base.id); + to_of_node(fwnode), vep.base.id); out: - mutex_unlock(&vin->group->lock); + fwnode_handle_put(fwnode); return ret; } @@ -829,7 +861,7 @@ out: static int rvin_mc_parse_of_graph(struct rvin_dev *vin) { unsigned int count = 0, vin_mask = 0; - unsigned int i; + unsigned int i, id; int ret; mutex_lock(&vin->group->lock); @@ -860,12 +892,14 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) if (!(vin_mask & BIT(i))) continue; - ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( - vin->group->vin[i]->dev, &vin->group->notifier, - sizeof(struct v4l2_async_subdev), 1, - rvin_mc_parse_of_endpoint); - if (ret) - return ret; + for (id = 0; id < RVIN_CSI_MAX; id++) { + if (vin->group->csi[id].asd) + continue; + + ret = rvin_mc_parse_of(vin->group->vin[i], id); + if (ret) + return ret; + } } if (list_empty(&vin->group->notifier.asd_list)) @@ -919,6 +953,54 @@ static int rvin_mc_init(struct rvin_dev *vin) } /* ----------------------------------------------------------------------------- + * Suspend / Resume + */ + +static int __maybe_unused rvin_suspend(struct device *dev) +{ + struct rvin_dev *vin = dev_get_drvdata(dev); + + if (vin->state != RUNNING) + return 0; + + rvin_stop_streaming(vin); + + vin->state = SUSPENDED; + + return 0; +} + +static int __maybe_unused rvin_resume(struct device *dev) +{ + struct rvin_dev *vin = dev_get_drvdata(dev); + + if (vin->state != SUSPENDED) + return 0; + + /* + * Restore group master CHSEL setting. + * + * This needs to be done by every VIN resuming not only the master + * as we don't know if and in which order the master VINs will + * be resumed. + */ + if (vin->info->use_mc) { + unsigned int master_id = rvin_group_id_to_master(vin->id); + struct rvin_dev *master = vin->group->vin[master_id]; + int ret; + + if (WARN_ON(!master)) + return -ENODEV; + + ret = rvin_set_channel_routing(master, master->chsel); + if (ret) + return ret; + } + + return rvin_start_streaming(vin); +} + +/* ----------------------------------------------------------------------------- * Platform Device Driver */ @@ -1268,22 +1350,6 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_h1, }, { - .compatible = "renesas,vin-r8a7790", - .data = &rcar_info_gen2, - }, - { - .compatible = "renesas,vin-r8a7791", - .data = &rcar_info_gen2, - }, - { - .compatible = "renesas,vin-r8a7793", - .data = &rcar_info_gen2, - }, - { - .compatible = "renesas,vin-r8a7794", - .data = &rcar_info_gen2, - }, - { .compatible = "renesas,rcar-gen2-vin", .data = &rcar_info_gen2, }, @@ -1421,9 +1487,12 @@ static int rcar_vin_remove(struct platform_device *pdev) return 0; } +static SIMPLE_DEV_PM_OPS(rvin_pm_ops, rvin_suspend, rvin_resume); + static struct platform_driver rcar_vin_driver = { .driver = { .name = "rcar-vin", + .pm = &rvin_pm_ops, .of_match_table = rvin_of_id_table, }, .probe = rcar_vin_probe, diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 79f229756805..945d2eb87233 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -876,31 +876,33 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) { struct v4l2_async_subdev *asd; struct fwnode_handle *fwnode; - struct device_node *ep; - struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint v4l2_ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; int ret; - ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0); + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(priv->dev), 0, 0, 0); if (!ep) { dev_err(priv->dev, "Not connected to subdevice\n"); return -EINVAL; } - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); if (ret) { dev_err(priv->dev, "Could not parse v4l2 endpoint\n"); - of_node_put(ep); + fwnode_handle_put(ep); return -EINVAL; } ret = rcsi2_parse_v4l2(priv, &v4l2_ep); if (ret) { - of_node_put(ep); + fwnode_handle_put(ep); return ret; } - fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); - of_node_put(ep); + fwnode = fwnode_graph_get_remote_endpoint(ep); + fwnode_handle_put(ep); dev_dbg(priv->dev, "Found '%pOF'\n", to_of_node(fwnode)); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 692dea300b0d..48280ddb15b9 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -133,7 +133,6 @@ #define VNCSI_IFMD_DES1 (1 << 26) #define VNCSI_IFMD_DES0 (1 << 25) #define VNCSI_IFMD_CSI_CHSEL(n) (((n) & 0xf) << 0) -#define VNCSI_IFMD_CSI_CHSEL_MASK 0xf struct rvin_buffer { struct vb2_v4l2_buffer vb; @@ -672,7 +671,7 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ if (!vin->is_csi && - vin->parallel->mbus_type == V4L2_MBUS_BT656) + vin->parallel.mbus_type == V4L2_MBUS_BT656) vnmc |= VNMC_INF_YUV8_BT656; else vnmc |= VNMC_INF_YUV8_BT601; @@ -685,7 +684,7 @@ static int rvin_setup(struct rvin_dev *vin) case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ if (!vin->is_csi && - vin->parallel->mbus_type == V4L2_MBUS_BT656) + vin->parallel.mbus_type == V4L2_MBUS_BT656) vnmc |= VNMC_INF_YUV10_BT656; else vnmc |= VNMC_INF_YUV10_BT601; @@ -710,21 +709,21 @@ static int rvin_setup(struct rvin_dev *vin) if (!vin->is_csi) { /* Hsync Signal Polarity Select */ - if (!(vin->parallel->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + if (!(vin->parallel.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_HPS; /* Vsync Signal Polarity Select */ - if (!(vin->parallel->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + if (!(vin->parallel.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_VPS; /* Data Enable Polarity Select */ - if (vin->parallel->bus.flags & V4L2_MBUS_DATA_ENABLE_LOW) + if (vin->parallel.bus.flags & V4L2_MBUS_DATA_ENABLE_LOW) dmr2 |= VNDMR2_CES; switch (vin->mbus_code) { case MEDIA_BUS_FMT_UYVY8_2X8: - if (vin->parallel->bus.bus_width == 8 && - vin->parallel->bus.data_shift == 8) + if (vin->parallel.bus.bus_width == 8 && + vin->parallel.bus.data_shift == 8) dmr2 |= VNDMR2_YDS; break; default: @@ -905,7 +904,7 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) vin->format.sizeimage / 2; break; } - } else if (list_empty(&vin->buf_list)) { + } else if (vin->state != RUNNING || list_empty(&vin->buf_list)) { vin->buf_hw[slot].buffer = NULL; vin->buf_hw[slot].type = FULL; phys_addr = vin->scratch_phys; @@ -998,12 +997,6 @@ static irqreturn_t rvin_irq(int irq, void *data) goto done; } - /* Nothing to do if capture status is 'STOPPING' */ - if (vin->state == STOPPING) { - vin_dbg(vin, "IRQ while state stopping\n"); - goto done; - } - /* Prepare for capture and update state */ vnms = rvin_read(vin, VNMS_REG); slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; @@ -1056,33 +1049,20 @@ done: return IRQ_RETVAL(handled); } -/* Need to hold qlock before calling */ -static void return_all_buffers(struct rvin_dev *vin, - enum vb2_buffer_state state) +static void return_unused_buffers(struct rvin_dev *vin, + enum vb2_buffer_state state) { struct rvin_buffer *buf, *node; - struct vb2_v4l2_buffer *freed[HW_BUFFER_NUM]; - unsigned int i, n; - - for (i = 0; i < HW_BUFFER_NUM; i++) { - freed[i] = vin->buf_hw[i].buffer; - vin->buf_hw[i].buffer = NULL; - - for (n = 0; n < i; n++) { - if (freed[i] == freed[n]) { - freed[i] = NULL; - break; - } - } + unsigned long flags; - if (freed[i]) - vb2_buffer_done(&freed[i]->vb2_buf, state); - } + spin_lock_irqsave(&vin->qlock, flags); list_for_each_entry_safe(buf, node, &vin->buf_list, list) { vb2_buffer_done(&buf->vb.vb2_buf, state); list_del(&buf->list); } + + spin_unlock_irqrestore(&vin->qlock, flags); } static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, @@ -1222,7 +1202,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) /* No media controller used, simply pass operation to subdevice. */ if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->parallel->subdev, video, s_stream, + ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream, on); return ret == -ENOIOCTLCMD ? 0 : ret; @@ -1266,61 +1246,81 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) return ret; } -static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) +int rvin_start_streaming(struct rvin_dev *vin) { - struct rvin_dev *vin = vb2_get_drv_priv(vq); unsigned long flags; int ret; - /* Allocate scratch buffer. */ - vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage, - &vin->scratch_phys, GFP_KERNEL); - if (!vin->scratch) { - spin_lock_irqsave(&vin->qlock, flags); - return_all_buffers(vin, VB2_BUF_STATE_QUEUED); - spin_unlock_irqrestore(&vin->qlock, flags); - vin_err(vin, "Failed to allocate scratch buffer\n"); - return -ENOMEM; - } - ret = rvin_set_stream(vin, 1); - if (ret) { - spin_lock_irqsave(&vin->qlock, flags); - return_all_buffers(vin, VB2_BUF_STATE_QUEUED); - spin_unlock_irqrestore(&vin->qlock, flags); - goto out; - } + if (ret) + return ret; spin_lock_irqsave(&vin->qlock, flags); vin->sequence = 0; ret = rvin_capture_start(vin); - if (ret) { - return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + if (ret) rvin_set_stream(vin, 0); - } spin_unlock_irqrestore(&vin->qlock, flags); -out: - if (ret) - dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, - vin->scratch_phys); return ret; } -static void rvin_stop_streaming(struct vb2_queue *vq) +static int rvin_start_streaming_vq(struct vb2_queue *vq, unsigned int count) { struct rvin_dev *vin = vb2_get_drv_priv(vq); + int ret = -ENOMEM; + + /* Allocate scratch buffer. */ + vin->scratch = dma_alloc_coherent(vin->dev, vin->format.sizeimage, + &vin->scratch_phys, GFP_KERNEL); + if (!vin->scratch) + goto err_scratch; + + ret = rvin_start_streaming(vin); + if (ret) + goto err_start; + + return 0; +err_start: + dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, + vin->scratch_phys); +err_scratch: + return_unused_buffers(vin, VB2_BUF_STATE_QUEUED); + + return ret; +} + +void rvin_stop_streaming(struct rvin_dev *vin) +{ + unsigned int i, retries; unsigned long flags; - int retries = 0; + bool buffersFreed; spin_lock_irqsave(&vin->qlock, flags); vin->state = STOPPING; + /* Wait until only scratch buffer is used, max 3 interrupts. */ + retries = 0; + while (retries++ < RVIN_RETRIES) { + buffersFreed = true; + for (i = 0; i < HW_BUFFER_NUM; i++) + if (vin->buf_hw[i].buffer) + buffersFreed = false; + + if (buffersFreed) + break; + + spin_unlock_irqrestore(&vin->qlock, flags); + msleep(RVIN_TIMEOUT_MS); + spin_lock_irqsave(&vin->qlock, flags); + } + /* Wait for streaming to stop */ + retries = 0; while (retries++ < RVIN_RETRIES) { rvin_capture_stop(vin); @@ -1336,7 +1336,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq) spin_lock_irqsave(&vin->qlock, flags); } - if (vin->state != STOPPED) { + if (!buffersFreed || vin->state != STOPPED) { /* * If this happens something have gone horribly wrong. * Set state to stopped to prevent the interrupt handler @@ -1346,27 +1346,33 @@ static void rvin_stop_streaming(struct vb2_queue *vq) vin->state = STOPPED; } - /* Release all active buffers */ - return_all_buffers(vin, VB2_BUF_STATE_ERROR); - spin_unlock_irqrestore(&vin->qlock, flags); rvin_set_stream(vin, 0); /* disable interrupts */ rvin_disable_interrupts(vin); +} + +static void rvin_stop_streaming_vq(struct vb2_queue *vq) +{ + struct rvin_dev *vin = vb2_get_drv_priv(vq); + + rvin_stop_streaming(vin); /* Free scratch buffer. */ dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, vin->scratch_phys); + + return_unused_buffers(vin, VB2_BUF_STATE_ERROR); } static const struct vb2_ops rvin_qops = { .queue_setup = rvin_queue_setup, .buf_prepare = rvin_buffer_prepare, .buf_queue = rvin_buffer_queue, - .start_streaming = rvin_start_streaming, - .stop_streaming = rvin_stop_streaming, + .start_streaming = rvin_start_streaming_vq, + .stop_streaming = rvin_stop_streaming_vq, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; @@ -1442,7 +1448,9 @@ error: */ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) { - u32 ifmd, vnmc; + const struct rvin_group_route *route; + u32 ifmd = 0; + u32 vnmc; int ret; ret = pm_runtime_get_sync(vin->dev); @@ -1455,12 +1463,31 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) vnmc = rvin_read(vin, VNMC_REG); rvin_write(vin, vnmc & ~VNMC_VUP, VNMC_REG); - ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0 | VNCSI_IFMD_CSI_CHSEL(chsel); + /* + * Set data expansion mode to "pad with 0s" by inspecting the routes + * table to find out which bit fields are available in the IFMD + * register. IFMD_DES1 controls data expansion mode for CSI20/21, + * IFMD_DES0 controls data expansion mode for CSI40/41. + */ + for (route = vin->info->routes; route->mask; route++) { + if (route->csi == RVIN_CSI20 || route->csi == RVIN_CSI21) + ifmd |= VNCSI_IFMD_DES1; + else + ifmd |= VNCSI_IFMD_DES0; + + if (ifmd == (VNCSI_IFMD_DES0 | VNCSI_IFMD_DES1)) + break; + } - rvin_write(vin, ifmd, VNCSI_IFMD_REG); + if (ifmd) { + ifmd |= VNCSI_IFMD_CSI_CHSEL(chsel); + rvin_write(vin, ifmd, VNCSI_IFMD_REG); + } vin_dbg(vin, "Set IFMD 0x%x\n", ifmd); + vin->chsel = chsel; + /* Restore VNMC. */ rvin_write(vin, vnmc, VNMC_REG); diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 3e7a3ae2a6b9..e6ea2b7991b8 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -205,7 +205,7 @@ static int rvin_reset_format(struct rvin_dev *vin) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = vin->parallel->source_pad, + .pad = vin->parallel.source_pad, }; int ret; @@ -246,7 +246,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, - .pad = vin->parallel->source_pad, + .pad = vin->parallel.source_pad, }; enum v4l2_field field; u32 width, height; @@ -632,7 +632,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, if (timings->pad) return -EINVAL; - timings->pad = vin->parallel->sink_pad; + timings->pad = vin->parallel.sink_pad; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -684,7 +684,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, if (cap->pad) return -EINVAL; - cap->pad = vin->parallel->sink_pad; + cap->pad = vin->parallel.sink_pad; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -702,7 +702,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->parallel->sink_pad; + edid->pad = vin->parallel.sink_pad; ret = v4l2_subdev_call(sd, pad, get_edid, edid); @@ -720,7 +720,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->parallel->sink_pad; + edid->pad = vin->parallel.sink_pad; ret = v4l2_subdev_call(sd, pad, set_edid, edid); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 8396e0e45478..0ee9d402f5ac 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -49,16 +49,18 @@ enum rvin_csi_id { }; /** - * STOPPED - No operation in progress - * STARTING - Capture starting up - * RUNNING - Operation in progress have buffers - * STOPPING - Stopping operation + * STOPPED - No operation in progress + * STARTING - Capture starting up + * RUNNING - Operation in progress have buffers + * STOPPING - Stopping operation + * SUSPENDED - Capture is suspended */ enum rvin_dma_state { STOPPED = 0, STARTING, RUNNING, STOPPING, + SUSPENDED, }; /** @@ -99,7 +101,7 @@ struct rvin_video_format { * */ struct rvin_parallel_entity { - struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asd; struct v4l2_subdev *subdev; enum v4l2_mbus_type mbus_type; @@ -189,6 +191,7 @@ struct rvin_info { * @state: keeps track of operation state * * @is_csi: flag to mark the VIN as using a CSI-2 subdevice + * @chsel Cached value of the current CSI-2 channel selection * * @mbus_code: media bus format code * @format: active V4L2 pixel format @@ -210,7 +213,7 @@ struct rvin_dev { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_parallel_entity *parallel; + struct rvin_parallel_entity parallel; struct rvin_group *group; unsigned int id; @@ -232,6 +235,7 @@ struct rvin_dev { enum rvin_dma_state state; bool is_csi; + unsigned int chsel; u32 mbus_code; struct v4l2_pix_format format; @@ -244,7 +248,7 @@ struct rvin_dev { unsigned int alpha; }; -#define vin_to_source(vin) ((vin)->parallel->subdev) +#define vin_to_source(vin) ((vin)->parallel.subdev) /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) @@ -276,7 +280,7 @@ struct rvin_group { struct rvin_dev *vin[RCAR_VIN_NUM]; struct { - struct fwnode_handle *fwnode; + struct v4l2_async_subdev *asd; struct v4l2_subdev *subdev; } csi[RVIN_CSI_MAX]; }; @@ -297,4 +301,7 @@ void rvin_crop_scale_comp(struct rvin_dev *vin); int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel); void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha); +int rvin_start_streaming(struct rvin_dev *vin); +void rvin_stop_streaming(struct rvin_dev *vin); + #endif diff --git a/drivers/staging/media/rkisp1/Makefile b/drivers/media/platform/rockchip/rkisp1/Makefile index ab32a77db8f7..ab32a77db8f7 100644 --- a/drivers/staging/media/rkisp1/Makefile +++ b/drivers/media/platform/rockchip/rkisp1/Makefile diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index b6f497ce3e95..5f6c9d1623e4 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -46,7 +46,7 @@ enum rkisp1_plane { /* * @fourcc: pixel format * @fmt_type: helper filed for pixel format - * @uv_swap: if cb cr swaped, for yuv + * @uv_swap: if cb cr swapped, for yuv * @write_format: defines how YCbCr self picture data is written to memory * @output_format: defines sp output format * @mbus: the mbus code on the src resizer pad that matches the pixel format @@ -565,7 +565,7 @@ static void rkisp1_sp_set_data_path(struct rkisp1_capture *cap) rkisp1_write(cap->rkisp1, dpcl, RKISP1_CIF_VI_DPCL); } -static struct rkisp1_capture_ops rkisp1_capture_ops_mp = { +static const struct rkisp1_capture_ops rkisp1_capture_ops_mp = { .config = rkisp1_mp_config, .enable = rkisp1_mp_enable, .disable = rkisp1_mp_disable, @@ -574,7 +574,7 @@ static struct rkisp1_capture_ops rkisp1_capture_ops_mp = { .is_stopped = rkisp1_mp_is_stopped, }; -static struct rkisp1_capture_ops rkisp1_capture_ops_sp = { +static const struct rkisp1_capture_ops rkisp1_capture_ops_sp = { .config = rkisp1_sp_config, .enable = rkisp1_sp_enable, .disable = rkisp1_sp_disable, @@ -830,75 +830,47 @@ static void rkisp1_return_all_buffers(struct rkisp1_capture *cap, } /* - * rkisp1_pipeline_sink_walk - Walk through the pipeline and call cb - * @from: entity at which to start pipeline walk - * @until: entity at which to stop pipeline walk - * - * Walk the entities chain starting at the pipeline video node and stop - * all subdevices in the chain. - * - * If the until argument isn't NULL, stop the pipeline walk when reaching the - * until entity. This is used to disable a partially started pipeline due to a - * subdev start error. + * Most of registers inside rockchip ISP1 have shadow register since + * they must be not be changed during processing a frame. + * Usually, each sub-module updates its shadow register after + * processing the last pixel of a frame. */ -static int rkisp1_pipeline_sink_walk(struct media_entity *from, - struct media_entity *until, - int (*cb)(struct media_entity *from, - struct media_entity *curr)) +static void rkisp1_cap_stream_enable(struct rkisp1_capture *cap) { - struct media_entity *entity = from; - struct media_pad *pad; - unsigned int i; - int ret; - - while (1) { - pad = NULL; - /* Find remote source pad */ - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *spad = &entity->pads[i]; - - if (!(spad->flags & MEDIA_PAD_FL_SINK)) - continue; - pad = media_entity_remote_pad(spad); - if (pad && is_media_entity_v4l2_subdev(pad->entity)) - break; - } - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - break; + struct rkisp1_device *rkisp1 = cap->rkisp1; + struct rkisp1_capture *other = &rkisp1->capture_devs[cap->id ^ 1]; - entity = pad->entity; - if (entity == until) - break; + cap->ops->set_data_path(cap); + cap->ops->config(cap); - ret = cb(from, entity); - if (ret) - return ret; + /* Setup a buffer for the next frame */ + spin_lock_irq(&cap->buf.lock); + rkisp1_set_next_buf(cap); + cap->ops->enable(cap); + /* It's safe to config ACTIVE and SHADOW regs for the + * first stream. While when the second is starting, do NOT + * force update because it also update the first one. + * + * The latter case would drop one more buf(that is 2) since + * there's not buf in shadow when the second FE received. This's + * also required because the second FE maybe corrupt especially + * when run at 120fps. + */ + if (!other->is_streaming) { + /* force cfg update */ + rkisp1_write(rkisp1, + RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); + rkisp1_set_next_buf(cap); } - - return 0; -} - -static int rkisp1_pipeline_disable_cb(struct media_entity *from, - struct media_entity *curr) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(curr); - - return v4l2_subdev_call(sd, video, s_stream, false); -} - -static int rkisp1_pipeline_enable_cb(struct media_entity *from, - struct media_entity *curr) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(curr); - - return v4l2_subdev_call(sd, video, s_stream, true); + spin_unlock_irq(&cap->buf.lock); + cap->is_streaming = true; } -static void rkisp1_stream_stop(struct rkisp1_capture *cap) +static void rkisp1_cap_stream_disable(struct rkisp1_capture *cap) { int ret; - /* Stream should stop in interrupt. If it dosn't, stop it by force. */ + /* Stream should stop in interrupt. If it doesn't, stop it by force. */ cap->is_stopping = true; ret = wait_event_timeout(cap->done, !cap->is_streaming, @@ -911,6 +883,82 @@ static void rkisp1_stream_stop(struct rkisp1_capture *cap) } } +/* + * rkisp1_pipeline_stream_disable - disable nodes in the pipeline + * + * Call s_stream(false) in the reverse order from + * rkisp1_pipeline_stream_enable() and disable the DMA engine. + * Should be called before media_pipeline_stop() + */ +static void rkisp1_pipeline_stream_disable(struct rkisp1_capture *cap) + __must_hold(&cap->rkisp1->stream_lock) +{ + struct rkisp1_device *rkisp1 = cap->rkisp1; + + rkisp1_cap_stream_disable(cap); + + /* + * If the other capture is streaming, isp and sensor nodes shouldn't + * be disabled, skip them. + */ + if (rkisp1->pipe.streaming_count < 2) { + v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream, + false); + v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false); + } + + v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream, + false); +} + +/* + * rkisp1_pipeline_stream_enable - enable nodes in the pipeline + * + * Enable the DMA Engine and call s_stream(true) through the pipeline. + * Should be called after media_pipeline_start() + */ +static int rkisp1_pipeline_stream_enable(struct rkisp1_capture *cap) + __must_hold(&cap->rkisp1->stream_lock) +{ + struct rkisp1_device *rkisp1 = cap->rkisp1; + int ret; + + rkisp1_cap_stream_enable(cap); + + ret = v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, + s_stream, true); + if (ret) + goto err_disable_cap; + + /* + * If the other capture is streaming, isp and sensor nodes are already + * enabled, skip them. + */ + if (rkisp1->pipe.streaming_count > 1) + return 0; + + ret = v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, true); + if (ret) + goto err_disable_rsz; + + ret = v4l2_subdev_call(rkisp1->active_sensor->sd, video, s_stream, + true); + if (ret) + goto err_disable_isp; + + return 0; + +err_disable_isp: + v4l2_subdev_call(&rkisp1->isp.sd, video, s_stream, false); +err_disable_rsz: + v4l2_subdev_call(&rkisp1->resizer_devs[cap->id].sd, video, s_stream, + false); +err_disable_cap: + rkisp1_cap_stream_disable(cap); + + return ret; +} + static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) { struct rkisp1_capture *cap = queue->drv_priv; @@ -920,13 +968,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) mutex_lock(&cap->rkisp1->stream_lock); - rkisp1_stream_stop(cap); - media_pipeline_stop(&node->vdev.entity); - ret = rkisp1_pipeline_sink_walk(&node->vdev.entity, NULL, - rkisp1_pipeline_disable_cb); - if (ret) - dev_err(rkisp1->dev, - "pipeline stream-off failed error:%d\n", ret); + rkisp1_pipeline_stream_disable(cap); rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR); @@ -937,44 +979,9 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) rkisp1_dummy_buf_destroy(cap); - mutex_unlock(&cap->rkisp1->stream_lock); -} - -/* - * Most of registers inside rockchip ISP1 have shadow register since - * they must be not be changed during processing a frame. - * Usually, each sub-module updates its shadow register after - * processing the last pixel of a frame. - */ -static void rkisp1_stream_start(struct rkisp1_capture *cap) -{ - struct rkisp1_device *rkisp1 = cap->rkisp1; - struct rkisp1_capture *other = &rkisp1->capture_devs[cap->id ^ 1]; - - cap->ops->set_data_path(cap); - cap->ops->config(cap); + media_pipeline_stop(&node->vdev.entity); - /* Setup a buffer for the next frame */ - spin_lock_irq(&cap->buf.lock); - rkisp1_set_next_buf(cap); - cap->ops->enable(cap); - /* It's safe to config ACTIVE and SHADOW regs for the - * first stream. While when the second is starting, do NOT - * force update because it also update the first one. - * - * The latter case would drop one more buf(that is 2) since - * there's not buf in shadow when the second FE received. This's - * also required because the second FE maybe corrupt especially - * when run at 120fps. - */ - if (!other->is_streaming) { - /* force cfg update */ - rkisp1_write(rkisp1, - RKISP1_CIF_MI_INIT_SOFT_UPD, RKISP1_CIF_MI_INIT); - rkisp1_set_next_buf(cap); - } - spin_unlock_irq(&cap->buf.lock); - cap->is_streaming = true; + mutex_unlock(&cap->rkisp1->stream_lock); } static int @@ -986,12 +993,19 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) mutex_lock(&cap->rkisp1->stream_lock); + ret = media_pipeline_start(entity, &cap->rkisp1->pipe); + if (ret) { + dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret); + goto err_ret_buffers; + } + ret = rkisp1_dummy_buf_create(cap); if (ret) - goto err_ret_buffers; + goto err_pipeline_stop; ret = pm_runtime_get_sync(cap->rkisp1->dev); if (ret < 0) { + pm_runtime_put_noidle(cap->rkisp1->dev); dev_err(cap->rkisp1->dev, "power up failed %d\n", ret); goto err_destroy_dummy; } @@ -1001,33 +1015,22 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) goto err_pipe_pm_put; } - rkisp1_stream_start(cap); - - /* start sub-devices */ - ret = rkisp1_pipeline_sink_walk(entity, NULL, - rkisp1_pipeline_enable_cb); + ret = rkisp1_pipeline_stream_enable(cap); if (ret) - goto err_stop_stream; - - ret = media_pipeline_start(entity, &cap->rkisp1->pipe); - if (ret) { - dev_err(cap->rkisp1->dev, "start pipeline failed %d\n", ret); - goto err_pipe_disable; - } + goto err_v4l2_pm_put; mutex_unlock(&cap->rkisp1->stream_lock); return 0; -err_pipe_disable: - rkisp1_pipeline_sink_walk(entity, NULL, rkisp1_pipeline_disable_cb); -err_stop_stream: - rkisp1_stream_stop(cap); +err_v4l2_pm_put: v4l2_pipeline_pm_put(entity); err_pipe_pm_put: pm_runtime_put(cap->rkisp1->dev); err_destroy_dummy: rkisp1_dummy_buf_destroy(cap); +err_pipeline_stop: + media_pipeline_stop(entity); err_ret_buffers: rkisp1_return_all_buffers(cap, VB2_BUF_STATE_QUEUED); mutex_unlock(&cap->rkisp1->stream_lock); @@ -1035,7 +1038,7 @@ err_ret_buffers: return ret; } -static struct vb2_ops rkisp1_vb2_ops = { +static const struct vb2_ops rkisp1_vb2_ops = { .queue_setup = rkisp1_vb2_queue_setup, .buf_queue = rkisp1_vb2_buf_queue, .buf_prepare = rkisp1_vb2_buf_prepare, @@ -1132,6 +1135,7 @@ static void rkisp1_try_fmt(const struct rkisp1_capture *cap, pixm->field = V4L2_FIELD_NONE; pixm->colorspace = V4L2_COLORSPACE_DEFAULT; pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pixm->quantization = V4L2_QUANTIZATION_DEFAULT; info = rkisp1_fill_pixfmt(pixm, cap->id); diff --git a/drivers/staging/media/rkisp1/rkisp1-common.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c index cf889666e166..cf889666e166 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 45abacdbb664..038c303a8aed 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/mutex.h> +#include <linux/rkisp1-config.h> #include <media/media-device.h> #include <media/media-entity.h> #include <media/v4l2-ctrls.h> @@ -20,7 +21,6 @@ #include <media/videobuf2-v4l2.h> #include "rkisp1-regs.h" -#include "uapi/rkisp1-config.h" /* * flags on the 'direction' field in struct 'rkisp1_isp_mbus_info' that indicate @@ -214,7 +214,7 @@ struct rkisp1_capture { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; enum rkisp1_stream_id id; - struct rkisp1_capture_ops *ops; + const struct rkisp1_capture_ops *ops; const struct rkisp1_capture_config *config; bool is_streaming; bool is_stopping; @@ -240,19 +240,17 @@ struct rkisp1_capture { * * @vnode: video node * @rkisp1: pointer to the rkisp1 device - * @lock: locks the buffer list 'stat' and 'is_streaming' + * @lock: locks the buffer list 'stat' * @stat: queue of rkisp1_buffer * @vdev_fmt: v4l2_format of the metadata format - * @is_streaming: device is streaming */ struct rkisp1_stats { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; - spinlock_t lock; /* locks the buffers list 'stats' and 'is_streaming' */ + spinlock_t lock; /* locks the buffers list 'stats' */ struct list_head stat; struct v4l2_format vdev_fmt; - bool is_streaming; }; /* @@ -260,10 +258,9 @@ struct rkisp1_stats { * * @vnode: video node * @rkisp1: pointer to the rkisp1 device - * @config_lock: locks the buffer list 'params' and 'is_streaming' + * @config_lock: locks the buffer list 'params' * @params: queue of rkisp1_buffer * @vdev_fmt: v4l2_format of the metadata format - * @is_streaming: device is streaming * @quantization: the quantization configured on the isp's src pad * @raw_type: the bayer pattern on the isp video sink pad */ @@ -271,10 +268,9 @@ struct rkisp1_params { struct rkisp1_vdev_node vnode; struct rkisp1_device *rkisp1; - spinlock_t config_lock; /* locks the buffers list 'params' and 'is_streaming' */ + spinlock_t config_lock; /* locks the buffers list 'params' */ struct list_head params; struct v4l2_format vdev_fmt; - bool is_streaming; enum v4l2_quantization quantization; enum rkisp1_fmt_raw_pat_type raw_type; diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 91584695804b..68da1eed753d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -255,7 +255,8 @@ static int rkisp1_subdev_notifier(struct rkisp1_device *rkisp1) struct fwnode_handle *ep; ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(rkisp1->dev), - 0, next_id, FWNODE_GRAPH_ENDPOINT_NEXT); + 0, next_id, + FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) break; @@ -405,11 +406,9 @@ static irqreturn_t rkisp1_isr(int irq, void *ctx) } static const char * const rk3399_isp_clks[] = { - "clk_isp", - "aclk_isp", - "hclk_isp", - "aclk_isp_wrap", - "hclk_isp_wrap", + "isp", + "aclk", + "hclk", }; static const struct rkisp1_match_data rk3399_isp_clk_data = { @@ -431,10 +430,6 @@ static void rkisp1_debug_init(struct rkisp1_device *rkisp1) struct rkisp1_debug *debug = &rkisp1->debug; debug->debugfs_dir = debugfs_create_dir(RKISP1_DRIVER_NAME, NULL); - if (!debug->debugfs_dir) { - dev_dbg(rkisp1->dev, "failed to create debugfs directory\n"); - return; - } debugfs_create_ulong("data_loss", 0444, debug->debugfs_dir, &debug->data_loss); debugfs_create_ulong("outform_size_err", 0444, debug->debugfs_dir, diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index a9715b0b7264..889982d8ca41 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -574,7 +574,7 @@ static int rkisp1_isp_enum_mbus_code(struct v4l2_subdev *sd, } else { if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_FIXED; + code->code = MEDIA_BUS_FMT_METADATA_FIXED; return 0; } @@ -633,10 +633,10 @@ static int rkisp1_isp_init_config(struct v4l2_subdev *sd, RKISP1_ISP_PAD_SINK_PARAMS); src_fmt = v4l2_subdev_get_try_format(sd, cfg, RKISP1_ISP_PAD_SOURCE_STATS); - sink_fmt->width = RKISP1_DEFAULT_WIDTH; - sink_fmt->height = RKISP1_DEFAULT_HEIGHT; + sink_fmt->width = 0; + sink_fmt->height = 0; sink_fmt->field = V4L2_FIELD_NONE; - sink_fmt->code = MEDIA_BUS_FMT_FIXED; + sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED; *src_fmt = *sink_fmt; return 0; @@ -1157,5 +1157,4 @@ void rkisp1_isp_isr(struct rkisp1_device *rkisp1) */ rkisp1_params_isr(rkisp1); } - } diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 986d293201e6..6af4d551ffb5 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -186,7 +186,7 @@ static void rkisp1_bls_config(struct rkisp1_params *params, /* ISP LS correction interface function */ static void rkisp1_lsc_correct_matrix_config(struct rkisp1_params *params, - const struct rkisp1_cif_isp_lsc_config *pconfig) + const struct rkisp1_cif_isp_lsc_config *pconfig) { unsigned int isp_lsc_status, sram_addr, isp_lsc_table_sel, i, j, data; @@ -434,7 +434,7 @@ static void rkisp1_ctk_enable(struct rkisp1_params *params, bool en) /* ISP White Balance Mode */ static void rkisp1_awb_meas_config(struct rkisp1_params *params, - const struct rkisp1_cif_isp_awb_meas_config *arg) + const struct rkisp1_cif_isp_awb_meas_config *arg) { u32 reg_val = 0; /* based on the mode,configure the awb module */ @@ -891,31 +891,31 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params, if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)) { /*update dpc config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC) rkisp1_dpcc_config(params, &new_params->others.dpcc_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_DPCC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_DPCC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_DPCC) rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPCC_MODE, RKISP1_CIF_ISP_DPCC_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_DPCC_MODE, - RKISP1_CIF_ISP_DPCC_ENA); + RKISP1_CIF_ISP_DPCC_MODE, + RKISP1_CIF_ISP_DPCC_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_BLS) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)) { /* update bls config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BLS) rkisp1_bls_config(params, &new_params->others.bls_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_BLS) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_BLS)) + if (module_ens & RKISP1_CIF_ISP_MODULE_BLS) rkisp1_param_set_bits(params, RKISP1_CIF_ISP_BLS_CTRL, RKISP1_CIF_ISP_BLS_ENA); @@ -929,177 +929,177 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params, if ((module_en_update & RKISP1_CIF_ISP_MODULE_SDG) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)) { /* update sdg config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_SDG) rkisp1_sdg_config(params, &new_params->others.sdg_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_SDG) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_SDG)) + if (module_ens & RKISP1_CIF_ISP_MODULE_SDG) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_LSC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)) { /* update lsc config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_LSC) rkisp1_lsc_config(params, &new_params->others.lsc_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_LSC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_LSC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_LSC) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_LSC_CTRL, - RKISP1_CIF_ISP_LSC_CTRL_ENA); + RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_LSC_CTRL, - RKISP1_CIF_ISP_LSC_CTRL_ENA); + RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)) { /* update awb gains */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) rkisp1_awb_gain_config(params, - &new_params->others.awb_gain_config); + &new_params->others.awb_gain_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB_GAIN) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_AWB_GAIN)) + if (module_ens & RKISP1_CIF_ISP_MODULE_AWB_GAIN) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_BDM) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)) { /* update bdm config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_BDM) rkisp1_bdm_config(params, &new_params->others.bdm_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_BDM) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_BDM)) + if (module_ens & RKISP1_CIF_ISP_MODULE_BDM) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_DEMOSAIC, - RKISP1_CIF_ISP_DEMOSAIC_BYPASS); + RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_DEMOSAIC, - RKISP1_CIF_ISP_DEMOSAIC_BYPASS); + RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_FLT) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)) { /* update filter config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_FLT) rkisp1_flt_config(params, &new_params->others.flt_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_FLT) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_FLT)) + if (module_ens & RKISP1_CIF_ISP_MODULE_FLT) rkisp1_param_set_bits(params, RKISP1_CIF_ISP_FILT_MODE, RKISP1_CIF_ISP_FLT_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_FILT_MODE, - RKISP1_CIF_ISP_FLT_ENA); + RKISP1_CIF_ISP_FILT_MODE, + RKISP1_CIF_ISP_FLT_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_CTK) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)) { /* update ctk config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CTK) rkisp1_ctk_config(params, &new_params->others.ctk_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_CTK) rkisp1_ctk_enable(params, - !!(module_ens & RKISP1_CIF_ISP_MODULE_CTK)); + !!(module_ens & RKISP1_CIF_ISP_MODULE_CTK)); } if ((module_en_update & RKISP1_CIF_ISP_MODULE_GOC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)) { /* update goc config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_GOC) rkisp1_goc_config(params, &new_params->others.goc_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_GOC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_GOC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_GOC) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_CTRL, - RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); + RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC)) { /* update cproc config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC)) { + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_CPROC) { rkisp1_cproc_config(params, &new_params->others.cproc_config); } if (module_en_update & RKISP1_CIF_ISP_MODULE_CPROC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_CPROC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_CPROC) rkisp1_param_set_bits(params, - RKISP1_CIF_C_PROC_CTRL, - RKISP1_CIF_C_PROC_CTR_ENABLE); + RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); else rkisp1_param_clear_bits(params, - RKISP1_CIF_C_PROC_CTRL, - RKISP1_CIF_C_PROC_CTR_ENABLE); + RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); } } if ((module_en_update & RKISP1_CIF_ISP_MODULE_IE) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)) { /* update ie config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_IE)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_IE) rkisp1_ie_config(params, &new_params->others.ie_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_IE) rkisp1_ie_enable(params, - !!(module_ens & RKISP1_CIF_ISP_MODULE_IE)); + !!(module_ens & RKISP1_CIF_ISP_MODULE_IE)); } if ((module_en_update & RKISP1_CIF_ISP_MODULE_DPF) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)) { /* update dpf config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF) rkisp1_dpf_config(params, &new_params->others.dpf_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_DPF) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_DPF)) + if (module_ens & RKISP1_CIF_ISP_MODULE_DPF) rkisp1_param_set_bits(params, - RKISP1_CIF_ISP_DPF_MODE, - RKISP1_CIF_ISP_DPF_MODE_EN); + RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); else rkisp1_param_clear_bits(params, - RKISP1_CIF_ISP_DPF_MODE, - RKISP1_CIF_ISP_DPF_MODE_EN); + RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); } } @@ -1107,7 +1107,7 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params, (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPF_STRENGTH)) { /* update dpf strength config */ rkisp1_dpf_strength_config(params, - &new_params->others.dpf_strength_config); + &new_params->others.dpf_strength_config); } } @@ -1123,25 +1123,25 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, if ((module_en_update & RKISP1_CIF_ISP_MODULE_AWB) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)) { /* update awb config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AWB) rkisp1_awb_meas_config(params, - &new_params->meas.awb_meas_config); + &new_params->meas.awb_meas_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_AWB) rkisp1_awb_meas_enable(params, - &new_params->meas.awb_meas_config, - !!(module_ens & RKISP1_CIF_ISP_MODULE_AWB)); + &new_params->meas.awb_meas_config, + !!(module_ens & RKISP1_CIF_ISP_MODULE_AWB)); } if ((module_en_update & RKISP1_CIF_ISP_MODULE_AFC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)) { /* update afc config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AFC) rkisp1_afm_config(params, &new_params->meas.afc_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_AFC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_AFC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_AFC) rkisp1_param_set_bits(params, RKISP1_CIF_ISP_AFM_CTRL, RKISP1_CIF_ISP_AFM_ENA); @@ -1155,25 +1155,25 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, if ((module_en_update & RKISP1_CIF_ISP_MODULE_HST) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)) { /* update hst config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_HST)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_HST) rkisp1_hst_config(params, &new_params->meas.hst_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_HST) rkisp1_hst_enable(params, - &new_params->meas.hst_config, - !!(module_ens & RKISP1_CIF_ISP_MODULE_HST)); + &new_params->meas.hst_config, + !!(module_ens & RKISP1_CIF_ISP_MODULE_HST)); } if ((module_en_update & RKISP1_CIF_ISP_MODULE_AEC) || (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)) { /* update aec config */ - if ((module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC)) + if (module_cfg_update & RKISP1_CIF_ISP_MODULE_AEC) rkisp1_aec_config(params, &new_params->meas.aec_config); if (module_en_update & RKISP1_CIF_ISP_MODULE_AEC) { - if (!!(module_ens & RKISP1_CIF_ISP_MODULE_AEC)) + if (module_ens & RKISP1_CIF_ISP_MODULE_AEC) rkisp1_param_set_bits(params, RKISP1_CIF_ISP_EXP_CTRL, RKISP1_CIF_ISP_EXP_ENA); @@ -1224,10 +1224,6 @@ void rkisp1_params_isr(struct rkisp1_device *rkisp1) struct rkisp1_params *params = &rkisp1->params; spin_lock(¶ms->config_lock); - if (!params->is_streaming) { - spin_unlock(¶ms->config_lock); - return; - } rkisp1_params_apply_params_cfg(params, frame_sequence); spin_unlock(¶ms->config_lock); @@ -1303,8 +1299,7 @@ static void rkisp1_params_config_parameter(struct rkisp1_params *params) spin_lock_irq(¶ms->config_lock); /* apply the first buffer if there is one already */ - if (params->is_streaming) - rkisp1_params_apply_params_cfg(params, 0); + rkisp1_params_apply_params_cfg(params, 0); spin_unlock_irq(¶ms->config_lock); } @@ -1420,8 +1415,6 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { - struct rkisp1_params *params = vq->drv_priv; - *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_PARAMS_REQ_BUFS_MIN, RKISP1_ISP_PARAMS_REQ_BUFS_MAX); @@ -1430,7 +1423,6 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, sizes[0] = sizeof(struct rkisp1_params_cfg); - INIT_LIST_HEAD(¶ms->params); return 0; } @@ -1462,9 +1454,7 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_params *params = vq->drv_priv; struct rkisp1_buffer *buf; - struct list_head tmp_list; - - INIT_LIST_HEAD(&tmp_list); + LIST_HEAD(tmp_list); /* * we first move the buffers into a local list 'tmp_list' @@ -1472,38 +1462,24 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) * without holding the lock */ spin_lock_irq(¶ms->config_lock); - params->is_streaming = false; - list_cut_position(&tmp_list, ¶ms->params, params->params.prev); + list_splice_init(¶ms->params, &tmp_list); spin_unlock_irq(¶ms->config_lock); list_for_each_entry(buf, &tmp_list, queue) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } -static int -rkisp1_params_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) -{ - struct rkisp1_params *params = queue->drv_priv; - - spin_lock_irq(¶ms->config_lock); - params->is_streaming = true; - spin_unlock_irq(¶ms->config_lock); - - return 0; -} - -static struct vb2_ops rkisp1_params_vb2_ops = { +static const struct vb2_ops rkisp1_params_vb2_ops = { .queue_setup = rkisp1_params_vb2_queue_setup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .buf_queue = rkisp1_params_vb2_buf_queue, .buf_prepare = rkisp1_params_vb2_buf_prepare, - .start_streaming = rkisp1_params_vb2_start_streaming, .stop_streaming = rkisp1_params_vb2_stop_streaming, }; -static struct v4l2_file_operations rkisp1_params_fops = { +static const struct v4l2_file_operations rkisp1_params_fops = { .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, .poll = vb2_fop_poll, @@ -1547,6 +1523,7 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) params->rkisp1 = rkisp1; mutex_init(&node->vlock); + INIT_LIST_HEAD(¶ms->params); spin_lock_init(¶ms->config_lock); strscpy(vdev->name, RKISP1_PARAMS_DEV_NAME, sizeof(vdev->name)); diff --git a/drivers/staging/media/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index 049f6c3a11df..8a8d960a679c 100644 --- a/drivers/staging/media/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -106,8 +106,8 @@ #define RKISP1_CIF_MI_SP_Y_FULL_YUV2RGB BIT(8) #define RKISP1_CIF_MI_SP_CBCR_FULL_YUV2RGB BIT(9) #define RKISP1_CIF_MI_SP_422NONCOSITEED BIT(10) -#define RKISP1_CIF_MI_MP_PINGPONG_ENABEL BIT(11) -#define RKISP1_CIF_MI_SP_PINGPONG_ENABEL BIT(12) +#define RKISP1_CIF_MI_MP_PINGPONG_ENABLE BIT(11) +#define RKISP1_CIF_MI_SP_PINGPONG_ENABLE BIT(12) #define RKISP1_CIF_MI_MP_AUTOUPDATE_ENABLE BIT(13) #define RKISP1_CIF_MI_SP_AUTOUPDATE_ENABLE BIT(14) #define RKISP1_CIF_MI_LAST_PIXEL_SIG_ENABLE BIT(15) diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 1687d82e6c68..813670ed9577 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -610,8 +610,8 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, RKISP1_ISP_MIN_WIDTH, RKISP1_ISP_MAX_WIDTH); sink_fmt->height = clamp_t(u32, format->height, - RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + RKISP1_ISP_MIN_HEIGHT, + RKISP1_ISP_MAX_HEIGHT); *format = *sink_fmt; @@ -763,8 +763,10 @@ static void rkisp1_rsz_unregister(struct rkisp1_resizer *rsz) static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) { - const char * const dev_names[] = {RKISP1_RSZ_MP_DEV_NAME, - RKISP1_RSZ_SP_DEV_NAME}; + static const char * const dev_names[] = { + RKISP1_RSZ_MP_DEV_NAME, + RKISP1_RSZ_SP_DEV_NAME + }; struct media_pad *pads = rsz->pads; struct v4l2_subdev *sd = &rsz->sd; int ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c index 51c64f75fe29..3ddab8fa8f2d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c @@ -94,8 +94,6 @@ static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { - struct rkisp1_stats *stats = vq->drv_priv; - *num_planes = 1; *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_STATS_REQ_BUFS_MIN, @@ -103,8 +101,6 @@ static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq, sizes[0] = sizeof(struct rkisp1_stat_buffer); - INIT_LIST_HEAD(&stats->stat); - return 0; } @@ -140,7 +136,6 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) unsigned int i; spin_lock_irq(&stats->lock); - stats->is_streaming = false; for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) { if (list_empty(&stats->stat)) break; @@ -152,18 +147,6 @@ static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq) spin_unlock_irq(&stats->lock); } -static int -rkisp1_stats_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) -{ - struct rkisp1_stats *stats = queue->drv_priv; - - spin_lock_irq(&stats->lock); - stats->is_streaming = true; - spin_unlock_irq(&stats->lock); - - return 0; -} - static const struct vb2_ops rkisp1_stats_vb2_ops = { .queue_setup = rkisp1_stats_vb2_queue_setup, .buf_queue = rkisp1_stats_vb2_buf_queue, @@ -171,7 +154,6 @@ static const struct vb2_ops rkisp1_stats_vb2_ops = { .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .stop_streaming = rkisp1_stats_vb2_stop_streaming, - .start_streaming = rkisp1_stats_vb2_start_streaming, }; static int @@ -359,12 +341,9 @@ void rkisp1_stats_isr(struct rkisp1_stats *stats, u32 isp_ris) if (isp_mis_tmp & RKISP1_STATS_MEAS_MASK) rkisp1->debug.stats_error++; - if (!stats->is_streaming) - goto unlock; if (isp_ris & RKISP1_STATS_MEAS_MASK) rkisp1_stats_send_measurement(stats, isp_ris); -unlock: spin_unlock(&stats->lock); } diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 422fd549e9c8..4c3c00d59c92 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -131,11 +131,13 @@ static int camif_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) while (sh--) { unsigned int tmp = 1 << sh; if (src >= tar * tmp) { - *shift = sh, *ratio = tmp; + *shift = sh; + *ratio = tmp; return 0; } } - *shift = 0, *ratio = 1; + *shift = 0; + *ratio = 1; return 0; } diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 9b22dd8e34f4..026111505f5a 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2862,6 +2862,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev) return -ENOMEM; jpeg->variant = jpeg_get_drv_data(&pdev->dev); + if (!jpeg->variant) + return -ENODEV; mutex_init(&jpeg->lock); spin_lock_init(&jpeg->slock); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 61e144a35201..a71753d459ba 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -1109,7 +1109,7 @@ const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void) return &s5p_mfc_dec_ioctl_ops; } -#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) \ +#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_CODEC) \ && V4L2_CTRL_DRIVER_PRIV(x)) int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index acc2217dd7e9..1fad99edb091 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -2614,7 +2614,7 @@ const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void) return &s5p_mfc_enc_ioctl_ops; } -#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) \ +#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_CODEC) \ && V4L2_CTRL_DRIVER_PRIV(x)) int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index dbe7788083a4..5ceb366648b3 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -765,7 +765,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) if (!fei->channel_data[index]) { ret = -ENOMEM; - goto err_clk_disable; + goto err_node_put; } tsin = fei->channel_data[index]; @@ -775,7 +775,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); if (ret) { dev_err(&pdev->dev, "No tsin_num found\n"); - goto err_clk_disable; + goto err_node_put; } /* sanity check value */ @@ -784,7 +784,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) "tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)", tsin->tsin_id, fei->hw_stats.num_ib); ret = -EINVAL; - goto err_clk_disable; + goto err_node_put; } tsin->invert_ts_clk = of_property_read_bool(child, @@ -800,14 +800,14 @@ static int c8sectpfe_probe(struct platform_device *pdev) &tsin->dvb_card); if (ret) { dev_err(&pdev->dev, "No dvb-card found\n"); - goto err_clk_disable; + goto err_node_put; } i2c_bus = of_parse_phandle(child, "i2c-bus", 0); if (!i2c_bus) { dev_err(&pdev->dev, "No i2c-bus found\n"); ret = -ENODEV; - goto err_clk_disable; + goto err_node_put; } tsin->i2c_adapter = of_find_i2c_adapter_by_node(i2c_bus); @@ -815,7 +815,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No i2c adapter found\n"); of_node_put(i2c_bus); ret = -ENODEV; - goto err_clk_disable; + goto err_node_put; } of_node_put(i2c_bus); @@ -826,7 +826,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) dev_err(dev, "reset gpio for tsin%d not valid (gpio=%d)\n", tsin->tsin_id, tsin->rst_gpio); - goto err_clk_disable; + goto err_node_put; } ret = devm_gpio_request_one(dev, tsin->rst_gpio, @@ -834,7 +834,7 @@ static int c8sectpfe_probe(struct platform_device *pdev) if (ret && ret != -EBUSY) { dev_err(dev, "Can't request tsin%d reset gpio\n" , fei->channel_data[index]->tsin_id); - goto err_clk_disable; + goto err_node_put; } if (!ret) { @@ -877,6 +877,8 @@ static int c8sectpfe_probe(struct platform_device *pdev) return 0; +err_node_put: + of_node_put(child); err_clk_disable: clk_disable_unprepare(fei->c8sectpfeclk); return ret; @@ -1032,9 +1034,8 @@ static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, dev_dbg(fei->dev, "Loading IMEM segment %d 0x%08x\n\t (0x%x bytes) -> 0x%p (0x%x bytes)\n", -seg_num, - phdr->p_paddr, phdr->p_filesz, - dest, phdr->p_memsz + phdr->p_memsz / 3); + seg_num, phdr->p_paddr, phdr->p_filesz, dest, + phdr->p_memsz + phdr->p_memsz / 3); for (i = 0; i < phdr->p_filesz; i++) { diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index fd1c41cba52f..b745f1342c2e 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -157,6 +157,7 @@ struct stm32_dcmi { struct vb2_queue queue; struct v4l2_fwnode_bus_parallel bus; + enum v4l2_mbus_type bus_type; struct completion complete; struct clk *mclk; enum state state; @@ -324,7 +325,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, } /* - * Avoid call of dmaengine_terminate_all() between + * Avoid call of dmaengine_terminate_sync() between * dmaengine_prep_slave_single() and dmaengine_submit() * by locking the whole DMA submission sequence */ @@ -438,7 +439,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) } /* Abort DMA operation */ - dmaengine_terminate_all(dcmi->dma_chan); + dmaengine_terminate_sync(dcmi->dma_chan); /* Restart capture */ if (dcmi_restart_capture(dcmi)) @@ -777,6 +778,23 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (dcmi->bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) val |= CR_PCKPOL; + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMI this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (dcmi->bus_type == V4L2_MBUS_BT656) { + val |= CR_ESS; + + /* Unmask all codes */ + reg_write(dcmi->regs, DCMI_ESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(dcmi->regs, DCMI_ESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + reg_write(dcmi->regs, DCMI_CR, val); /* Set crop */ @@ -882,7 +900,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* Stop all pending DMA operations */ mutex_lock(&dcmi->dma_lock); - dmaengine_terminate_all(dcmi->dma_chan); + dmaengine_terminate_sync(dcmi->dma_chan); mutex_unlock(&dcmi->dma_lock); pm_runtime_put(dcmi->dev); @@ -1067,8 +1085,9 @@ static int dcmi_set_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f) if (ret) return ret; - /* Disable crop if JPEG is requested */ - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) + /* Disable crop if JPEG is requested or BT656 bus is selected */ + if (pix->pixelformat == V4L2_PIX_FMT_JPEG && + dcmi->bus_type != V4L2_MBUS_BT656) dcmi->do_crop = false; /* pix to mbus format */ @@ -1574,6 +1593,22 @@ static const struct dcmi_format dcmi_formats[] = { .fourcc = V4L2_PIX_FMT_JPEG, .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, .bpp = 1, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .bpp = 1, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .bpp = 1, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .bpp = 1, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .bpp = 1, }, }; @@ -1592,6 +1627,11 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) if (dcmi_formats[i].mbus_code != mbus_code.code) continue; + /* Exclude JPEG if BT656 bus is selected */ + if (dcmi_formats[i].fourcc == V4L2_PIX_FMT_JPEG && + dcmi->bus_type == V4L2_MBUS_BT656) + continue; + /* Code supported, have we got this fourcc yet? */ for (j = 0; j < num_fmts; j++) if (sd_fmts[j]->fourcc == @@ -1851,7 +1891,9 @@ static int dcmi_probe(struct platform_device *pdev) dcmi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(dcmi->rstc)) { - dev_err(&pdev->dev, "Could not get reset control\n"); + if (PTR_ERR(dcmi->rstc) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not get reset control\n"); + return PTR_ERR(dcmi->rstc); } @@ -1873,9 +1915,18 @@ static int dcmi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "CSI bus not supported\n"); return -ENODEV; } + + if (ep.bus_type == V4L2_MBUS_BT656 && + ep.bus.parallel.bus_width != 8) { + dev_err(&pdev->dev, "BT656 bus conflicts with %u bits bus width (8 bits required)\n", + ep.bus.parallel.bus_width); + return -ENODEV; + } + dcmi->bus.flags = ep.bus.parallel.flags; dcmi->bus.bus_width = ep.bus.parallel.bus_width; dcmi->bus.data_shift = ep.bus.parallel.data_shift; + dcmi->bus_type = ep.bus_type; irq = platform_get_irq(pdev, 0); if (irq <= 0) diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 8f4e254b6a41..1a2f65d83a6c 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -363,7 +363,7 @@ int sun4i_csi_v4l2_register(struct sun4i_csi *csi) vdev->lock = &csi->lock; /* Set a default format */ - csi->fmt.pixelformat = sun4i_csi_formats[0].fourcc, + csi->fmt.pixelformat = sun4i_csi_formats[0].fourcc; csi->fmt.width = CSI_DEFAULT_WIDTH; csi->fmt.height = CSI_DEFAULT_HEIGHT; _sun4i_csi_try_fmt(csi, &csi->fmt); diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index bd323e640f1a..0388894cfe41 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -215,7 +215,7 @@ static int fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) return -EBUSY; strscpy(fmr2->v4l2_dev.name, "radio-sf16fmr2", - sizeof(fmr2->v4l2_dev.name)), + sizeof(fmr2->v4l2_dev.name)); fmr2->io = io; if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) { diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 6afa7c3464ab..adbf43ff6a21 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -86,7 +86,7 @@ MODULE_VERSION("0.0.1"); #define check_command_failed(status) (!(status & SI4713_CTS) || \ (status & SI4713_ERR)) /* mute definition */ -#define set_mute(p) ((p & 1) | ((p & 1) << 1)); +#define set_mute(p) (((p) & 1) | (((p) & 1) << 1)) #ifdef DEBUG #define DBG_BUFFER(device, message, buffer, size) \ diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index aaa1bf81d00d..b252a1d2ebd6 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-it913x-v2.o \ rc-kaiomy.o \ rc-khadas.o \ + rc-khamsin.o \ rc-kworld-315u.o \ rc-kworld-pc150u.o \ rc-kworld-plus-tv-analog.o \ @@ -79,6 +80,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-npgtech.o \ rc-odroid.o \ rc-pctv-sedna.o \ + rc-pine64.o \ rc-pinnacle-color.o \ rc-pinnacle-grey.o \ rc-pinnacle-pctv-hd.o \ diff --git a/drivers/media/rc/keymaps/rc-khamsin.c b/drivers/media/rc/keymaps/rc-khamsin.c new file mode 100644 index 000000000000..0c98c2faacff --- /dev/null +++ b/drivers/media/rc/keymaps/rc-khamsin.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Christian Hewitt + +#include <media/rc-map.h> +#include <linux/module.h> + +/* + * KHAMSIN is an IR/Bluetooth RCU supplied with the SmartLabs + * SML-5442TW DVB-S/VOD box. The RCU has separate IR (TV) and + * BT (STB) modes. This keymap suppors the IR controls. + */ + +static struct rc_map_table khamsin[] = { + { 0x70702, KEY_POWER}, + + { 0x70701, KEY_VIDEO}, // source + + { 0x7076c, KEY_RED}, + { 0x70714, KEY_GREEN}, + { 0x70715, KEY_YELLOW}, + { 0x70716, KEY_BLUE}, + + { 0x7071a, KEY_MENU}, + { 0x7074f, KEY_EPG}, + + { 0x70760, KEY_UP }, + { 0x70761, KEY_DOWN }, + { 0x70765, KEY_LEFT }, + { 0x70762, KEY_RIGHT }, + { 0x70768, KEY_ENTER }, + + { 0x7072d, KEY_ESC }, // back + + { 0x70707, KEY_VOLUMEUP }, + { 0x7070b, KEY_VOLUMEDOWN }, + { 0x7070f, KEY_MUTE }, + { 0x70712, KEY_CHANNELUP }, + { 0x70710, KEY_CHANNELDOWN }, + + { 0x70704, KEY_1 }, + { 0x70705, KEY_2 }, + { 0x70706, KEY_3 }, + { 0x70708, KEY_4 }, + { 0x70709, KEY_5 }, + { 0x7070a, KEY_6 }, + { 0x7070c, KEY_7 }, + { 0x7070d, KEY_8 }, + { 0x7070e, KEY_9 }, + { 0x70711, KEY_0 }, +}; + +static struct rc_map_list khamsin_map = { + .map = { + .scan = khamsin, + .size = ARRAY_SIZE(khamsin), + .rc_proto = RC_PROTO_NECX, + .name = RC_MAP_KHAMSIN, + } +}; + +static int __init init_rc_map_khamsin(void) +{ + return rc_map_register(&khamsin_map); +} + +static void __exit exit_rc_map_khamsin(void) +{ + rc_map_unregister(&khamsin_map); +} + +module_init(init_rc_map_khamsin) +module_exit(exit_rc_map_khamsin) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hewitt <christianshewitt@gmail.com>"); diff --git a/drivers/media/rc/keymaps/rc-pine64.c b/drivers/media/rc/keymaps/rc-pine64.c new file mode 100644 index 000000000000..9b2bdbbce04e --- /dev/null +++ b/drivers/media/rc/keymaps/rc-pine64.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ + +// Keytable for the Pine64 IR Remote Controller +// Copyright (c) 2017 Jonas Karlman + +#include <media/rc-map.h> +#include <linux/module.h> + +static struct rc_map_table pine64[] = { + { 0x40404d, KEY_POWER }, + { 0x40401f, KEY_WWW }, + { 0x40400a, KEY_MUTE }, + + { 0x404017, KEY_VOLUMEDOWN }, + { 0x404018, KEY_VOLUMEUP }, + + { 0x404010, KEY_LEFT }, + { 0x404011, KEY_RIGHT }, + { 0x40400b, KEY_UP }, + { 0x40400e, KEY_DOWN }, + { 0x40400d, KEY_OK }, + + { 0x40401d, KEY_MENU }, + { 0x40401a, KEY_HOME }, + + { 0x404045, KEY_BACK }, + + { 0x404001, KEY_NUMERIC_1 }, + { 0x404002, KEY_NUMERIC_2 }, + { 0x404003, KEY_NUMERIC_3 }, + { 0x404004, KEY_NUMERIC_4 }, + { 0x404005, KEY_NUMERIC_5 }, + { 0x404006, KEY_NUMERIC_6 }, + { 0x404007, KEY_NUMERIC_7 }, + { 0x404008, KEY_NUMERIC_8 }, + { 0x404009, KEY_NUMERIC_9 }, + { 0x40400c, KEY_BACKSPACE }, + { 0x404000, KEY_NUMERIC_0 }, + { 0x404047, KEY_EPG }, // mouse +}; + +static struct rc_map_list pine64_map = { + .map = { + .scan = pine64, + .size = ARRAY_SIZE(pine64), + .rc_proto = RC_PROTO_NECX, + .name = RC_MAP_PINE64, + } +}; + +static int __init init_rc_map_pine64(void) +{ + return rc_map_register(&pine64_map); +} + +static void __exit exit_rc_map_pine64(void) +{ + rc_map_unregister(&pine64_map); +} + +module_init(init_rc_map_pine64) +module_exit(exit_rc_map_pine64) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonas Karlman"); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 220363b9a868..116daf90c858 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -263,7 +263,8 @@ static ssize_t lirc_transmit(struct file *file, const char __user *buf, goto out_unlock; } - if (scan.flags || scan.keycode || scan.timestamp) { + if (scan.flags || scan.keycode || scan.timestamp || + scan.rc_proto > RC_PROTO_MAX) { ret = -EINVAL; goto out_unlock; } diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index ddee6ee37bab..8555c7798706 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -73,10 +73,6 @@ #define SUNXI_IR_BASE_CLK 8000000 /* Noise threshold in samples */ #define SUNXI_IR_RXNOISE 1 -/* Idle Threshold in samples */ -#define SUNXI_IR_RXIDLE 20 -/* Time after which device stops sending data in ms */ -#define SUNXI_IR_TIMEOUT 120 /** * struct sunxi_ir_quirks - Differences between SoC variants. @@ -137,6 +133,8 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) } else if (status & REG_RXSTA_RPE) { ir_raw_event_set_idle(ir->rc, true); ir_raw_event_handle(ir->rc); + } else { + ir_raw_event_handle(ir->rc); } spin_unlock(&ir->ir_lock); @@ -144,6 +142,41 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) return IRQ_HANDLED; } +/* Convert idle threshold to usec */ +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr) +{ + return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1), + base_clk / (128 * 64)); +} + +/* Convert usec to idle threshold */ +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec) +{ + /* make sure we don't end up with a timeout less than requested */ + return DIV_ROUND_UP((base_clk / (128 * 64)) * usec, USEC_PER_SEC) - 1; +} + +static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout) +{ + struct sunxi_ir *ir = rc_dev->priv; + unsigned int base_clk = clk_get_rate(ir->clk); + unsigned long flags; + + unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout); + + dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr); + + spin_lock_irqsave(&ir->ir_lock, flags); + /* Set noise threshold and idle threshold */ + writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr), + ir->base + SUNXI_IR_CIR_REG); + spin_unlock_irqrestore(&ir->ir_lock, flags); + + rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr); + + return 0; +} + static int sunxi_ir_probe(struct platform_device *pdev) { int ret = 0; @@ -240,9 +273,11 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - /* Frequency after IR internal divider with sample period in ns */ + /* Frequency after IR internal divider with sample period in us */ ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); - ir->rc->timeout = MS_TO_US(SUNXI_IR_TIMEOUT); + ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0); + ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255); + ir->rc->s_timeout = sunxi_ir_set_timeout; ir->rc->driver_name = SUNXI_IR_DEV; ret = rc_register_device(ir->rc); @@ -270,8 +305,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG); /* Set noise threshold and idle threshold */ - writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE), - ir->base + SUNXI_IR_CIR_REG); + sunxi_ir_set_timeout(ir->rc, IR_DEFAULT_TIMEOUT); /* Invert Input Signal */ writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); diff --git a/drivers/media/test-drivers/vicodec/codec-fwht.c b/drivers/media/test-drivers/vicodec/codec-fwht.c index 31faf319e508..1ce682e1b85c 100644 --- a/drivers/media/test-drivers/vicodec/codec-fwht.c +++ b/drivers/media/test-drivers/vicodec/codec-fwht.c @@ -11,6 +11,7 @@ #include <linux/string.h> #include <linux/kernel.h> +#include <linux/videodev2.h> #include "codec-fwht.h" #define OVERFLOW_BIT BIT(14) @@ -920,7 +921,7 @@ bool fwht_decode_frame(struct fwht_cframe *cf, u32 hdr_flags, if (!decode_plane(cf, &rlco, height, width, ref->luma, ref_stride, ref->luma_alpha_step, dst->luma, dst_stride, dst->luma_alpha_step, - hdr_flags & FWHT_FL_LUMA_IS_UNCOMPRESSED, + hdr_flags & V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED, end_of_rlco_buf)) return false; @@ -928,21 +929,21 @@ bool fwht_decode_frame(struct fwht_cframe *cf, u32 hdr_flags, u32 h = height; u32 w = width; - if (!(hdr_flags & FWHT_FL_CHROMA_FULL_HEIGHT)) + if (!(hdr_flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT)) h /= 2; - if (!(hdr_flags & FWHT_FL_CHROMA_FULL_WIDTH)) + if (!(hdr_flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH)) w /= 2; if (!decode_plane(cf, &rlco, h, w, ref->cb, ref_chroma_stride, ref->chroma_step, dst->cb, dst_chroma_stride, dst->chroma_step, - hdr_flags & FWHT_FL_CB_IS_UNCOMPRESSED, + hdr_flags & V4L2_FWHT_FL_CB_IS_UNCOMPRESSED, end_of_rlco_buf)) return false; if (!decode_plane(cf, &rlco, h, w, ref->cr, ref_chroma_stride, ref->chroma_step, dst->cr, dst_chroma_stride, dst->chroma_step, - hdr_flags & FWHT_FL_CR_IS_UNCOMPRESSED, + hdr_flags & V4L2_FWHT_FL_CR_IS_UNCOMPRESSED, end_of_rlco_buf)) return false; } @@ -951,7 +952,7 @@ bool fwht_decode_frame(struct fwht_cframe *cf, u32 hdr_flags, if (!decode_plane(cf, &rlco, height, width, ref->alpha, ref_stride, ref->luma_alpha_step, dst->alpha, dst_stride, dst->luma_alpha_step, - hdr_flags & FWHT_FL_ALPHA_IS_UNCOMPRESSED, + hdr_flags & V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED, end_of_rlco_buf)) return false; return true; diff --git a/drivers/media/test-drivers/vicodec/codec-fwht.h b/drivers/media/test-drivers/vicodec/codec-fwht.h index b6fec2b1cbca..0eab24020e9e 100644 --- a/drivers/media/test-drivers/vicodec/codec-fwht.h +++ b/drivers/media/test-drivers/vicodec/codec-fwht.h @@ -56,38 +56,6 @@ #define FWHT_MAGIC1 0x4f4f4f4f #define FWHT_MAGIC2 0xffffffff -#define FWHT_VERSION 3 - -/* Set if this is an interlaced format */ -#define FWHT_FL_IS_INTERLACED BIT(0) -/* Set if this is a bottom-first (NTSC) interlaced format */ -#define FWHT_FL_IS_BOTTOM_FIRST BIT(1) -/* Set if each 'frame' contains just one field */ -#define FWHT_FL_IS_ALTERNATE BIT(2) -/* - * If FWHT_FL_IS_ALTERNATE was set, then this is set if this - * 'frame' is the bottom field, else it is the top field. - */ -#define FWHT_FL_IS_BOTTOM_FIELD BIT(3) -/* Set if this frame is uncompressed */ -#define FWHT_FL_LUMA_IS_UNCOMPRESSED BIT(4) -#define FWHT_FL_CB_IS_UNCOMPRESSED BIT(5) -#define FWHT_FL_CR_IS_UNCOMPRESSED BIT(6) -#define FWHT_FL_CHROMA_FULL_HEIGHT BIT(7) -#define FWHT_FL_CHROMA_FULL_WIDTH BIT(8) -#define FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) -#define FWHT_FL_I_FRAME BIT(10) - -/* A 4-values flag - the number of components - 1 */ -#define FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) -#define FWHT_FL_COMPONENTS_NUM_OFFSET 16 - -#define FWHT_FL_PIXENC_MSK GENMASK(20, 19) -#define FWHT_FL_PIXENC_OFFSET 19 -#define FWHT_FL_PIXENC_YUV (1 << FWHT_FL_PIXENC_OFFSET) -#define FWHT_FL_PIXENC_RGB (2 << FWHT_FL_PIXENC_OFFSET) -#define FWHT_FL_PIXENC_HSV (3 << FWHT_FL_PIXENC_OFFSET) - /* * A macro to calculate the needed padding in order to make sure * both luma and chroma components resolutions are rounded up to diff --git a/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c index b6e39fbd8ad5..0c83678fcdad 100644 --- a/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c +++ b/drivers/media/test-drivers/vicodec/codec-v4l2-fwht.c @@ -11,34 +11,34 @@ #include "codec-v4l2-fwht.h" static const struct v4l2_fwht_pixfmt_info v4l2_fwht_pixfmts[] = { - { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, FWHT_FL_PIXENC_YUV}, - { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, FWHT_FL_PIXENC_HSV}, - { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_BGRX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_BGRA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_RGBX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_RGBA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_RGB}, - { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 4, 1, FWHT_FL_PIXENC_HSV}, - { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_YUV420, 1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVU420, 1, 3, 2, 1, 1, 2, 2, 3, 3, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUV422P, 1, 2, 1, 1, 1, 2, 1, 3, 3, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV12, 1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV21, 1, 3, 2, 1, 2, 2, 2, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV16, 1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV61, 1, 2, 1, 1, 2, 2, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV24, 1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_NV42, 1, 3, 1, 1, 2, 1, 1, 3, 2, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YUYV, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_YVYU, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_UYVY, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_VYUY, 2, 2, 1, 2, 4, 2, 1, 3, 1, V4L2_FWHT_FL_PIXENC_YUV}, + { V4L2_PIX_FMT_BGR24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV24, 3, 3, 1, 3, 3, 1, 1, 3, 1, V4L2_FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_BGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XBGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_ABGR32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_XRGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_ARGB32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_BGRX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_BGRA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGBX32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_RGBA32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_RGB}, + { V4L2_PIX_FMT_HSV32, 4, 4, 1, 4, 4, 1, 1, 4, 1, V4L2_FWHT_FL_PIXENC_HSV}, + { V4L2_PIX_FMT_GREY, 1, 1, 1, 1, 0, 1, 1, 1, 1, V4L2_FWHT_FL_PIXENC_RGB}, }; bool v4l2_fwht_validate_fmt(const struct v4l2_fwht_pixfmt_info *info, @@ -251,25 +251,25 @@ int v4l2_fwht_encode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) p_hdr = (struct fwht_cframe_hdr *)p_out; p_hdr->magic1 = FWHT_MAGIC1; p_hdr->magic2 = FWHT_MAGIC2; - p_hdr->version = htonl(FWHT_VERSION); + p_hdr->version = htonl(V4L2_FWHT_VERSION); p_hdr->width = htonl(state->visible_width); p_hdr->height = htonl(state->visible_height); - flags |= (info->components_num - 1) << FWHT_FL_COMPONENTS_NUM_OFFSET; + flags |= (info->components_num - 1) << V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET; flags |= info->pixenc; if (encoding & FWHT_LUMA_UNENCODED) - flags |= FWHT_FL_LUMA_IS_UNCOMPRESSED; + flags |= V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED; if (encoding & FWHT_CB_UNENCODED) - flags |= FWHT_FL_CB_IS_UNCOMPRESSED; + flags |= V4L2_FWHT_FL_CB_IS_UNCOMPRESSED; if (encoding & FWHT_CR_UNENCODED) - flags |= FWHT_FL_CR_IS_UNCOMPRESSED; + flags |= V4L2_FWHT_FL_CR_IS_UNCOMPRESSED; if (encoding & FWHT_ALPHA_UNENCODED) - flags |= FWHT_FL_ALPHA_IS_UNCOMPRESSED; + flags |= V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED; if (!(encoding & FWHT_FRAME_PCODED)) - flags |= FWHT_FL_I_FRAME; + flags |= V4L2_FWHT_FL_I_FRAME; if (rf.height_div == 1) - flags |= FWHT_FL_CHROMA_FULL_HEIGHT; + flags |= V4L2_FWHT_FL_CHROMA_FULL_HEIGHT; if (rf.width_div == 1) - flags |= FWHT_FL_CHROMA_FULL_WIDTH; + flags |= V4L2_FWHT_FL_CHROMA_FULL_WIDTH; p_hdr->flags = htonl(flags); p_hdr->colorspace = htonl(state->colorspace); p_hdr->xfer_func = htonl(state->xfer_func); @@ -299,9 +299,9 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) info = state->info; version = ntohl(state->header.version); - if (!version || version > FWHT_VERSION) { + if (!version || version > V4L2_FWHT_VERSION) { pr_err("version %d is not supported, current version is %d\n", - version, FWHT_VERSION); + version, V4L2_FWHT_VERSION); return -EINVAL; } @@ -317,10 +317,10 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) flags = ntohl(state->header.flags); if (version >= 2) { - if ((flags & FWHT_FL_PIXENC_MSK) != info->pixenc) + if ((flags & V4L2_FWHT_FL_PIXENC_MSK) != info->pixenc) return -EINVAL; - components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); + components_num = 1 + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> + V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); } if (components_num != info->components_num) @@ -333,8 +333,8 @@ int v4l2_fwht_decode(struct v4l2_fwht_state *state, u8 *p_in, u8 *p_out) cf.rlc_data = (__be16 *)p_in; cf.size = ntohl(state->header.size); - hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; - hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; if (hdr_width_div != info->width_div || hdr_height_div != info->height_div) return -EINVAL; diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 0e115683f8da..025f3ff77302 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -200,14 +200,14 @@ static void copy_cap_to_ref(const u8 *cap, const struct v4l2_fwht_pixfmt_info *i static bool validate_by_version(unsigned int flags, unsigned int version) { - if (!version || version > FWHT_VERSION) + if (!version || version > V4L2_FWHT_VERSION) return false; if (version >= 2) { unsigned int components_num = 1 + - ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); - unsigned int pixenc = flags & FWHT_FL_PIXENC_MSK; + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> + V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); + unsigned int pixenc = flags & V4L2_FWHT_FL_PIXENC_MSK; if (components_num == 0 || components_num > 4 || !pixenc) return false; @@ -219,18 +219,18 @@ static bool validate_stateless_params_flags(const struct v4l2_ctrl_fwht_params * const struct v4l2_fwht_pixfmt_info *cur_info) { unsigned int width_div = - (params->flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + (params->flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; unsigned int height_div = - (params->flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + (params->flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; unsigned int components_num = 3; unsigned int pixenc = 0; if (params->version < 3) return false; - components_num = 1 + ((params->flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); - pixenc = (params->flags & FWHT_FL_PIXENC_MSK); + components_num = 1 + ((params->flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> + V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); + pixenc = (params->flags & V4L2_FWHT_FL_PIXENC_MSK); if (v4l2_fwht_validate_fmt(cur_info, width_div, height_div, components_num, pixenc)) return true; @@ -278,7 +278,7 @@ static int device_process(struct vicodec_ctx *ctx, * set the reference buffer from the reference timestamp * only if this is a P-frame */ - if (!(ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME)) { + if (!(ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME)) { struct vb2_buffer *ref_vb2_buf; int ref_buf_idx; struct vb2_queue *vq_cap = @@ -331,7 +331,7 @@ static int device_process(struct vicodec_ctx *ctx, copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage); - if (ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME) + if (ntohl(ctx->state.header.flags) & V4L2_FWHT_FL_I_FRAME) dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME; else dst_vb->flags |= V4L2_BUF_FLAG_PFRAME; @@ -480,16 +480,16 @@ static const struct v4l2_fwht_pixfmt_info * info_from_header(const struct fwht_cframe_hdr *p_hdr) { unsigned int flags = ntohl(p_hdr->flags); - unsigned int width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; - unsigned int height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + unsigned int width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; unsigned int components_num = 3; unsigned int pixenc = 0; unsigned int version = ntohl(p_hdr->version); if (version >= 2) { - components_num = 1 + ((flags & FWHT_FL_COMPONENTS_NUM_MSK) >> - FWHT_FL_COMPONENTS_NUM_OFFSET); - pixenc = (flags & FWHT_FL_PIXENC_MSK); + components_num = 1 + ((flags & V4L2_FWHT_FL_COMPONENTS_NUM_MSK) >> + V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET); + pixenc = (flags & V4L2_FWHT_FL_PIXENC_MSK); } return v4l2_fwht_find_nth_fmt(width_div, height_div, components_num, pixenc, 0); @@ -522,8 +522,8 @@ static void update_capture_data_from_header(struct vicodec_ctx *ctx) const struct fwht_cframe_hdr *p_hdr = &ctx->state.header; const struct v4l2_fwht_pixfmt_info *info = info_from_header(p_hdr); unsigned int flags = ntohl(p_hdr->flags); - unsigned int hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; - unsigned int hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + unsigned int hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + unsigned int hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; /* * This function should not be used by a stateless codec since @@ -657,8 +657,8 @@ restart: if (!is_header_valid(&ctx->state.header) && ctx->comp_has_frame) return 1; flags = ntohl(ctx->state.header.flags); - hdr_width_div = (flags & FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; - hdr_height_div = (flags & FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; + hdr_width_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_WIDTH) ? 1 : 2; + hdr_height_div = (flags & V4L2_FWHT_FL_CHROMA_FULL_HEIGHT) ? 1 : 2; if (ntohl(ctx->state.header.width) != q_dst->visible_width || ntohl(ctx->state.header.height) != q_dst->visible_height || @@ -1746,7 +1746,7 @@ static int vicodec_try_ctrl(struct v4l2_ctrl *ctrl) V4L2_BUF_TYPE_VIDEO_CAPTURE); switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: + case V4L2_CID_STATELESS_FWHT_PARAMS: if (!q_dst->info) return -EINVAL; params = ctrl->p_new.p_fwht_params; @@ -1799,7 +1799,7 @@ static int vicodec_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_FWHT_P_FRAME_QP: ctx->state.p_frame_qp = ctrl->val; return 0; - case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: + case V4L2_CID_STATELESS_FWHT_PARAMS: params = ctrl->p_new.p_fwht_params; update_header_from_stateless_params(ctx, params); ctx->state.ref_frame_ts = params->backward_ref_ts; @@ -1815,7 +1815,7 @@ static const struct v4l2_ctrl_ops vicodec_ctrl_ops = { static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { .ops = &vicodec_ctrl_ops, - .id = V4L2_CID_MPEG_VIDEO_FWHT_PARAMS, + .id = V4L2_CID_STATELESS_FWHT_PARAMS, .elem_size = sizeof(struct v4l2_ctrl_fwht_params), }; diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c index a776bb8e0e09..331a9053a0ed 100644 --- a/drivers/media/test-drivers/vim2m.c +++ b/drivers/media/test-drivers/vim2m.c @@ -1325,12 +1325,6 @@ static int vim2m_probe(struct platform_device *pdev) vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto error_v4l2; - } - video_set_drvdata(vfd, dev); v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n", vfd->num); @@ -1345,6 +1339,12 @@ static int vim2m_probe(struct platform_device *pdev) goto error_dev; } + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto error_m2m; + } + #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; strscpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); @@ -1358,7 +1358,7 @@ static int vim2m_probe(struct platform_device *pdev) MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); - goto error_dev; + goto error_v4l2; } ret = media_device_register(&dev->mdev); @@ -1373,11 +1373,13 @@ static int vim2m_probe(struct platform_device *pdev) error_m2m_mc: v4l2_m2m_unregister_media_controller(dev->m2m_dev); #endif -error_dev: +error_v4l2: video_unregister_device(&dev->vfd); /* vim2m_device_release called by video_unregister_device to release various objects */ return ret; -error_v4l2: +error_m2m: + v4l2_m2m_release(dev->m2m_dev); +error_dev: v4l2_device_unregister(&dev->v4l2_dev); error_free: kfree(dev); diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index aa8d350fd682..0dc65ef3aa14 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -547,11 +547,13 @@ static int vivid_s_fmt_cap_mplane(struct file *file, void *priv, return vidioc_s_fmt_vid_cap_mplane(file, priv, f); } -static bool vivid_is_in_use(struct video_device *vdev) +static bool vivid_is_in_use(bool valid, struct video_device *vdev) { unsigned long flags; bool res; + if (!valid) + return false; spin_lock_irqsave(&vdev->fh_lock, flags); res = !list_empty(&vdev->fh_list); spin_unlock_irqrestore(&vdev->fh_lock, flags); @@ -560,20 +562,46 @@ static bool vivid_is_in_use(struct video_device *vdev) static bool vivid_is_last_user(struct vivid_dev *dev) { - unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) + - vivid_is_in_use(&dev->vid_out_dev) + - vivid_is_in_use(&dev->vbi_cap_dev) + - vivid_is_in_use(&dev->vbi_out_dev) + - vivid_is_in_use(&dev->sdr_cap_dev) + - vivid_is_in_use(&dev->radio_rx_dev) + - vivid_is_in_use(&dev->radio_tx_dev) + - vivid_is_in_use(&dev->meta_cap_dev) + - vivid_is_in_use(&dev->meta_out_dev) + - vivid_is_in_use(&dev->touch_cap_dev); + unsigned int uses = + vivid_is_in_use(dev->has_vid_cap, &dev->vid_cap_dev) + + vivid_is_in_use(dev->has_vid_out, &dev->vid_out_dev) + + vivid_is_in_use(dev->has_vbi_cap, &dev->vbi_cap_dev) + + vivid_is_in_use(dev->has_vbi_out, &dev->vbi_out_dev) + + vivid_is_in_use(dev->has_radio_rx, &dev->radio_rx_dev) + + vivid_is_in_use(dev->has_radio_tx, &dev->radio_tx_dev) + + vivid_is_in_use(dev->has_sdr_cap, &dev->sdr_cap_dev) + + vivid_is_in_use(dev->has_meta_cap, &dev->meta_cap_dev) + + vivid_is_in_use(dev->has_meta_out, &dev->meta_out_dev) + + vivid_is_in_use(dev->has_touch_cap, &dev->touch_cap_dev); return uses == 1; } +static void vivid_reconnect(struct vivid_dev *dev) +{ + if (dev->has_vid_cap) + set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); + if (dev->has_vid_out) + set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); + if (dev->has_vbi_cap) + set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); + if (dev->has_vbi_out) + set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); + if (dev->has_radio_rx) + set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); + if (dev->has_radio_tx) + set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + if (dev->has_sdr_cap) + set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + if (dev->has_meta_cap) + set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); + if (dev->has_meta_out) + set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); + if (dev->has_touch_cap) + set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags); + dev->disconnect_error = false; +} + static int vivid_fop_release(struct file *file) { struct vivid_dev *dev = video_drvdata(file); @@ -581,23 +609,15 @@ static int vivid_fop_release(struct file *file) mutex_lock(&dev->mutex); if (!no_error_inj && v4l2_fh_is_singular_file(file) && - !video_is_registered(vdev) && vivid_is_last_user(dev)) { + dev->disconnect_error && !video_is_registered(vdev) && + vivid_is_last_user(dev)) { /* * I am the last user of this driver, and a disconnect * was forced (since this video_device is unregistered), * so re-register all video_device's again. */ v4l2_info(&dev->v4l2_dev, "reconnect\n"); - set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); - set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags); + vivid_reconnect(dev); } mutex_unlock(&dev->mutex); if (file->private_data == dev->overlay_cap_owner) @@ -1968,6 +1988,8 @@ static int vivid_remove(struct platform_device *pdev) if (!dev) continue; + if (dev->disconnect_error) + vivid_reconnect(dev); #ifdef CONFIG_MEDIA_CONTROLLER media_device_unregister(&dev->mdev); #endif diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index 99e69b8f770f..9c2d1470b597 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -303,6 +303,7 @@ struct vivid_dev { struct fb_fix_screeninfo fb_fix; /* Error injection */ + bool disconnect_error; bool queue_setup_error; bool buf_prepare_error; bool start_streaming_error; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 334130568dcb..11e3b5617f52 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -107,14 +107,27 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIVID_CID_DISCONNECT: v4l2_info(&dev->v4l2_dev, "disconnect\n"); - clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); - clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); + dev->disconnect_error = true; + if (dev->has_vid_cap) + clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags); + if (dev->has_vid_out) + clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags); + if (dev->has_vbi_cap) + clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags); + if (dev->has_vbi_out) + clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags); + if (dev->has_radio_rx) + clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); + if (dev->has_radio_tx) + clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); + if (dev->has_sdr_cap) + clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); + if (dev->has_meta_cap) + clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); + if (dev->has_meta_out) + clear_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags); + if (dev->has_touch_cap) + clear_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags); break; case VIVID_CID_BUTTON: dev->button_pressed = 30; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 01a9d671b947..67fb3c00f9ad 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -819,7 +819,7 @@ static int vivid_thread_vid_cap(void *data) break; if (!mutex_trylock(&dev->mutex)) { - schedule_timeout_uninterruptible(1); + schedule(); continue; } @@ -888,7 +888,9 @@ static int vivid_thread_vid_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + while (jiffies - cur_jiffies < wait_jiffies && + !kthread_should_stop()) + schedule(); } dprintk(dev, 1, "Video Capture Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-out.c b/drivers/media/test-drivers/vivid/vivid-kthread-out.c index 6780687978f9..79c57d14ac4e 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-out.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-out.c @@ -167,7 +167,7 @@ static int vivid_thread_vid_out(void *data) break; if (!mutex_trylock(&dev->mutex)) { - schedule_timeout_uninterruptible(1); + schedule(); continue; } @@ -233,7 +233,9 @@ static int vivid_thread_vid_out(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + while (jiffies - cur_jiffies < wait_jiffies && + !kthread_should_stop()) + schedule(); } dprintk(dev, 1, "Video Output Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c index 674507b5ccb5..38fdfee79498 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-touch.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-touch.c @@ -69,7 +69,7 @@ static int vivid_thread_touch_cap(void *data) break; if (!mutex_trylock(&dev->mutex)) { - schedule_timeout_uninterruptible(1); + schedule(); continue; } cur_jiffies = jiffies; @@ -128,7 +128,9 @@ static int vivid_thread_touch_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + while (jiffies - cur_jiffies < wait_jiffies && + !kthread_should_stop()) + schedule(); } dprintk(dev, 1, "Touch Capture Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index 2b7522e16efc..a1e52708b7ca 100644 --- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c @@ -142,7 +142,7 @@ static int vivid_thread_sdr_cap(void *data) break; if (!mutex_trylock(&dev->mutex)) { - schedule_timeout_uninterruptible(1); + schedule(); continue; } @@ -201,7 +201,9 @@ static int vivid_thread_sdr_cap(void *data) next_jiffies_since_start = jiffies_since_start; wait_jiffies = next_jiffies_since_start - jiffies_since_start; - schedule_timeout_interruptible(wait_jiffies ? wait_jiffies : 1); + while (jiffies - cur_jiffies < wait_jiffies && + !kthread_should_stop()) + schedule(); } dprintk(dev, 1, "SDR Capture Thread End\n"); return 0; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index eadf28ab1e39..b9caa4b26209 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -1107,11 +1107,9 @@ int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, ((compose->width + 7) / 8) * compose->height)) return -EFAULT; } - if (clipcount && win->clips) { - if (copy_to_user(win->clips, dev->clips_cap, - clipcount * sizeof(dev->clips_cap[0]))) - return -EFAULT; - } + if (clipcount && win->clips) + memcpy(win->clips, dev->clips_cap, + clipcount * sizeof(dev->clips_cap[0])); return 0; } @@ -1141,9 +1139,8 @@ int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, if (win->clipcount > MAX_CLIPS) win->clipcount = MAX_CLIPS; if (win->clipcount) { - if (copy_from_user(dev->try_clips_cap, win->clips, - win->clipcount * sizeof(dev->clips_cap[0]))) - return -EFAULT; + memcpy(dev->try_clips_cap, win->clips, + win->clipcount * sizeof(dev->clips_cap[0])); for (i = 0; i < win->clipcount; i++) { struct v4l2_rect *r = &dev->try_clips_cap[i].c; @@ -1166,9 +1163,8 @@ int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, return -EINVAL; } } - if (copy_to_user(win->clips, dev->try_clips_cap, - win->clipcount * sizeof(dev->clips_cap[0]))) - return -EFAULT; + memcpy(win->clips, dev->try_clips_cap, + win->clipcount * sizeof(dev->clips_cap[0])); } return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index ee3446e3217c..ac1e981e8342 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -857,11 +857,9 @@ int vidioc_g_fmt_vid_out_overlay(struct file *file, void *priv, ((dev->compose_out.width + 7) / 8) * dev->compose_out.height)) return -EFAULT; } - if (clipcount && win->clips) { - if (copy_to_user(win->clips, dev->clips_out, - clipcount * sizeof(dev->clips_out[0]))) - return -EFAULT; - } + if (clipcount && win->clips) + memcpy(win->clips, dev->clips_out, + clipcount * sizeof(dev->clips_out[0])); return 0; } @@ -891,9 +889,8 @@ int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, if (win->clipcount > MAX_CLIPS) win->clipcount = MAX_CLIPS; if (win->clipcount) { - if (copy_from_user(dev->try_clips_out, win->clips, - win->clipcount * sizeof(dev->clips_out[0]))) - return -EFAULT; + memcpy(dev->try_clips_out, win->clips, + win->clipcount * sizeof(dev->clips_out[0])); for (i = 0; i < win->clipcount; i++) { struct v4l2_rect *r = &dev->try_clips_out[i].c; @@ -916,9 +913,8 @@ int vidioc_try_fmt_vid_out_overlay(struct file *file, void *priv, return -EINVAL; } } - if (copy_to_user(win->clips, dev->try_clips_out, - win->clipcount * sizeof(dev->clips_out[0]))) - return -EFAULT; + memcpy(win->clips, dev->try_clips_out, + win->clipcount * sizeof(dev->clips_out[0])); } return 0; } diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 0e7ac2b49990..204e6186bf71 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -215,7 +215,7 @@ static int mt2060_set_params(struct dvb_frontend *fe) f_lo2 = f_lo1 - freq - IF2; // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise f_lo2 = ((f_lo2 + 25) / 50) * 50; - priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, + priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000; #ifdef MT2060_SPURCHECK // LO-related spurs detection and correction diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 2240d214dfac..d105431a2e2d 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -1849,7 +1849,6 @@ static int mt2063_init(struct dvb_frontend *fe) default: return -ENODEV; - break; } while (status >= 0 && *def) { diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index 1c07e2225fb3..f6e82a8e7d37 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -3926,15 +3926,26 @@ static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth) { struct mxl5005s_state *state = fe->tuner_priv; - - u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; - u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; + u8 *AddrTable; + u8 *ByteTable; int TableLen; dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth); mxl5005s_reset(fe); + AddrTable = kcalloc(MXL5005S_REG_WRITING_TABLE_LEN_MAX, sizeof(u8), + GFP_KERNEL); + if (!AddrTable) + return -ENOMEM; + + ByteTable = kcalloc(MXL5005S_REG_WRITING_TABLE_LEN_MAX, sizeof(u8), + GFP_KERNEL); + if (!ByteTable) { + kfree(AddrTable); + return -ENOMEM; + } + /* Tuner initialization stage 0 */ MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); AddrTable[0] = MASTER_CONTROL_ADDR; @@ -3949,6 +3960,9 @@ static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); + kfree(AddrTable); + kfree(ByteTable); + return 0; } diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index aa5bc6a2ae20..c0f118563c7d 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -231,6 +231,7 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets, for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); if (!urb) { + au0828_isocdbg("cannot allocate URB\n"); au0828_uninit_isoc(dev); return -ENOMEM; } @@ -239,16 +240,14 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets, dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev, sb_size, GFP_KERNEL, &urb->transfer_dma); if (!dev->isoc_ctl.transfer_buffer[i]) { - printk("unable to allocate %i bytes for transfer buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); + au0828_isocdbg("cannot allocate transfer buffer\n"); au0828_uninit_isoc(dev); return -ENOMEM; } memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); pipe = usb_rcvisocpipe(dev->usbdev, - dev->isoc_in_endpointaddr), + dev->isoc_in_endpointaddr); usb_fill_int_urb(urb, dev->usbdev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index de42db6f6ad1..9c71b32552df 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -670,7 +670,7 @@ static int cx231xx_audio_fini(struct cx231xx *dev) } if (dev->adev.sndcard) { - snd_card_free(dev->adev.sndcard); + snd_card_free_when_closed(dev->adev.sndcard); kfree(dev->adev.alt_max_pkt_size); dev->adev.sndcard = NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 05d91caaed0c..727e6268567f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -1061,9 +1061,8 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, &urb->transfer_dma); if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) { dev_err(dev->dev, - "unable to allocate %i bytes for transfer buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); + "unable to allocate %i bytes for transfer buffer %i\n", + sb_size, i); cx231xx_uninit_isoc(dev); return -ENOMEM; } @@ -1197,9 +1196,8 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, &urb->transfer_dma); if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) { dev_err(dev->dev, - "unable to allocate %i bytes for transfer buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); + "unable to allocate %i bytes for transfer buffer %i\n", + sb_size, i); cx231xx_uninit_bulk(dev); return -ENOMEM; } diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index d2f143a096d1..fdc8b7f7b0c1 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -408,9 +408,8 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, kzalloc(sb_size, GFP_KERNEL); if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) { dev_err(dev->dev, - "unable to allocate %i bytes for transfer buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); + "unable to allocate %i bytes for transfer buffer %i\n", + sb_size, i); cx231xx_uninit_vbi_isoc(dev); return -ENOMEM; } diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 0d9657f7f29d..689829f1b52a 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -287,8 +287,8 @@ static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) m88ds3103_pdata.ts_clk = 16000; m88ds3103_pdata.ts_clk_pol = 0; m88ds3103_pdata.agc = 0x99; - m88ds3103_pdata.lnb_hv_pol = 1, - m88ds3103_pdata.lnb_en_pol = 1, + m88ds3103_pdata.lnb_hv_pol = 1; + m88ds3103_pdata.lnb_en_pol = 1; state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, &d->i2c_adap, @@ -383,15 +383,15 @@ static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap) struct sp2_config sp2_config = {}; /* attach demod */ - m88ds3103_pdata.clk = 27000000, - m88ds3103_pdata.i2c_wr_max = 33, - m88ds3103_pdata.clk_out = 0, - m88ds3103_pdata.ts_mode = M88DS3103_TS_CI, - m88ds3103_pdata.ts_clk = 10000, - m88ds3103_pdata.ts_clk_pol = 1, - m88ds3103_pdata.agc = 0x99, - m88ds3103_pdata.lnb_hv_pol = 0, - m88ds3103_pdata.lnb_en_pol = 1, + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.clk_out = 0; + m88ds3103_pdata.ts_mode = M88DS3103_TS_CI; + m88ds3103_pdata.ts_clk = 10000; + m88ds3103_pdata.ts_clk_pol = 1; + m88ds3103_pdata.agc = 0x99; + m88ds3103_pdata.lnb_hv_pol = 0; + m88ds3103_pdata.lnb_en_pol = 1; state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, &d->i2c_adap, diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 91460e4d0c30..3952cc534b4a 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -955,7 +955,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) struct mn88472_config mn88472_config = {}; mn88472_config.fe = &adap->fe[1]; - mn88472_config.i2c_wr_max = 22, + mn88472_config.i2c_wr_max = 22; strscpy(info.type, "mn88472", I2C_NAME_SIZE); mn88472_config.xtal = 20500000; mn88472_config.ts_mode = SERIAL_TS_MODE; @@ -980,7 +980,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) struct mn88473_config mn88473_config = {}; mn88473_config.fe = &adap->fe[1]; - mn88473_config.i2c_wr_max = 22, + mn88473_config.i2c_wr_max = 22; strscpy(info.type, "mn88473", I2C_NAME_SIZE); info.addr = 0x18; info.platform_data = &mn88473_config; diff --git a/drivers/media/usb/dvb-usb-v2/zd1301.c b/drivers/media/usb/dvb-usb-v2/zd1301.c index ba2c1b0d3989..72aa6a9a9ed4 100644 --- a/drivers/media/usb/dvb-usb-v2/zd1301.c +++ b/drivers/media/usb/dvb-usb-v2/zd1301.c @@ -150,7 +150,7 @@ static int zd1301_frontend_attach(struct dvb_usb_adapter *adap) } if (!pdev->dev.driver) { ret = -ENODEV; - goto err; + goto err_platform_device_unregister; } if (!try_module_get(pdev->dev.driver->owner)) { ret = -ENODEV; diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index d3288c107906..710c1afe3e85 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -3074,8 +3074,8 @@ static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = st->dib7000p_ops.get_i2c_tuner(adap->fe_adap[0].fe); - nim7090_dib0090_config.reset = st->dib7000p_ops.tuner_sleep, - nim7090_dib0090_config.sleep = st->dib7000p_ops.tuner_sleep, + nim7090_dib0090_config.reset = st->dib7000p_ops.tuner_sleep; + nim7090_dib0090_config.sleep = st->dib7000p_ops.tuner_sleep; nim7090_dib0090_config.get_adc_power = st->dib7000p_ops.get_adc_power; if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index a27a68440325..f0e686b05dc6 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1770,6 +1770,7 @@ enum dw2102_table_entry { TEVII_S660, PROF_7500, GENIATECH_SU3000, + HAUPPAUGE_MAX_S2, TERRATEC_CINERGY_S2, TEVII_S480_1, TEVII_S480_2, @@ -1802,6 +1803,7 @@ static struct usb_device_id dw2102_table[] = { [TEVII_S660] = {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, [PROF_7500] = {USB_DEVICE(0x3034, 0x7500)}, [GENIATECH_SU3000] = {USB_DEVICE(0x1f4d, 0x3000)}, + [HAUPPAUGE_MAX_S2] = {USB_DEVICE(0x2040, 0xd900)}, [TERRATEC_CINERGY_S2] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R1)}, [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, @@ -2230,12 +2232,16 @@ static struct dvb_usb_device_properties su3000_properties = { }}, } }, - .num_device_descs = 8, + .num_device_descs = 9, .devices = { { "SU3000HD DVB-S USB2.0", { &dw2102_table[GENIATECH_SU3000], NULL }, { NULL }, }, + { "Hauppauge MAX S2 or WinTV NOVA HD USB2.0", + { &dw2102_table[HAUPPAUGE_MAX_S2], NULL }, + { NULL }, + }, { "Terratec Cinergy S2 USB HD", { &dw2102_table[TERRATEC_CINERGY_S2], NULL }, { NULL }, diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index c07f46f5176e..b4f661bb5648 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -182,7 +182,7 @@ out_rel_fw: static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) { - u8 status, buf; + u8 status = 0, buf; int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); if (onoff) { diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index dc968fd5ace9..4d5ab1433b44 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -583,9 +583,9 @@ static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, struct snd_kcontrol_new tmp; memset(&tmp, 0, sizeof(tmp)); - tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - tmp.private_value = id, - tmp.name = ctl_name, + tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tmp.private_value = id; + tmp.name = ctl_name; /* Add Mute Control */ sprintf(ctl_name, "%s Switch", name); @@ -600,16 +600,16 @@ static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev, ctl_name, id); memset(&tmp, 0, sizeof(tmp)); - tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - tmp.private_value = id, - tmp.name = ctl_name, + tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tmp.private_value = id; + tmp.name = ctl_name; /* Add Volume Control */ sprintf(ctl_name, "%s Volume", name); tmp.get = em28xx_vol_get; tmp.put = em28xx_vol_put; tmp.info = em28xx_vol_info; - tmp.tlv.p = em28xx_db_scale, + tmp.tlv.p = em28xx_db_scale; kctl = snd_ctl_new1(&tmp, dev); err = snd_ctl_add(card, kctl); if (err < 0) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index c295f642d352..158c8e28ed2c 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1575,6 +1575,7 @@ out: input_unregister_device(gspca_dev->input_dev); #endif v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + v4l2_device_unregister(&gspca_dev->v4l2_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 9a11158f38da..8b6a57f170d0 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -1220,9 +1220,9 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) int hflip_def; if (sd->sensor == SENSOR_OV767x) { - saturation_min = 0, - saturation_max = 6, - saturation_def = 3, + saturation_min = 0; + saturation_max = 6; + saturation_def = 3; brightness_min = -127; brightness_max = 127; brightness_def = 0; @@ -1233,9 +1233,9 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) exposure_def = 0x13; hflip_def = 1; } else { - saturation_min = 0, - saturation_max = 255, - saturation_def = 64, + saturation_min = 0; + saturation_max = 255; + saturation_def = 64; brightness_min = 0; brightness_max = 255; brightness_def = 0; diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 65be6f140fe8..63882a5248ae 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -867,7 +867,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq) /* according to tests, at least 700us delay is required */ msleep(20); - if (!msi2500_ctrl_msg(dev, CMD_STOP_STREAMING, 0)) { + if (dev->udev && !msi2500_ctrl_msg(dev, CMD_STOP_STREAMING, 0)) { /* sleep USB IF / ADC */ msi2500_ctrl_msg(dev, CMD_WREG, 0x01000003); } @@ -1230,7 +1230,7 @@ static int msi2500_probe(struct usb_interface *intf, } dev->master = master; - master->bus_num = 0; + master->bus_num = -1; master->num_chipselect = 1; master->transfer_one_message = msi2500_transfer_one_message; spi_master_set_devdata(master, dev); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c index 1fcf63218885..d1b984ec757d 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -594,7 +594,7 @@ static int pvr2_lgdt3306a_attach(struct pvr2_dvb_adapter *adap) lgdt3306a_config.mpeg_mode = LGDT3306A_MPEG_PARALLEL; lgdt3306a_config.tpclk_edge = LGDT3306A_TPCLK_FALLING_EDGE; lgdt3306a_config.tpvalid_polarity = LGDT3306A_TP_VALID_LOW; - lgdt3306a_config.xtalMHz = 25, /* demod clock MHz; 24/25 supported */ + lgdt3306a_config.xtalMHz = 25; /* demod clock MHz; 24/25 supported */ adap->i2c_client_demod[0] = dvb_module_probe("lgdt3306a", NULL, &adap->channel.hdw->i2c_adap, diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index bfba06ea60e9..3f650ede0c3d 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -461,11 +461,12 @@ static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) if (dev->urb_buffer) return 0; - dev->urb_buffer = kmalloc_array(num_bufs, sizeof(void *), GFP_KERNEL); + dev->urb_buffer = kmalloc_array(num_bufs, sizeof(*dev->urb_buffer), + GFP_KERNEL); if (!dev->urb_buffer) return -ENOMEM; - dev->urb_dma = kmalloc_array(num_bufs, sizeof(dma_addr_t *), + dev->urb_dma = kmalloc_array(num_bufs, sizeof(*dev->urb_dma), GFP_KERNEL); if (!dev->urb_dma) return -ENOMEM; @@ -692,8 +693,6 @@ static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) struct tm6000_core *dev = fh->dev; unsigned long flags; - BUG_ON(in_interrupt()); - /* We used to wait for the buffer to finish here, but this didn't work because, as we were keeping the state as VIDEOBUF_QUEUED, videobuf_queue_cancel marked it as finished for us. diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index f479d8971dfb..011e69427b7c 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1608,8 +1608,8 @@ int uvc_ctrl_set(struct uvc_fh *handle, if (step == 0) step = 1; - xctrl->value = min + ((u32)(xctrl->value - min) + step / 2) - / step * step; + xctrl->value = min + DIV_ROUND_CLOSEST((u32)(xctrl->value - min), + step) * step; if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) xctrl->value = clamp(xctrl->value, min, max); else diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 8c670934d920..1e1c6b4d1874 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -357,8 +357,6 @@ static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) { _DBG("%s\n", __func__); - BUG_ON(in_interrupt()); - videobuf_vmalloc_free(&buf->vb); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -1327,6 +1325,7 @@ static int zr364xx_board_init(struct zr364xx_camera *cam) { struct zr364xx_pipeinfo *pipe = cam->pipe; unsigned long i; + int err; DBG("board init: %p\n", cam); memset(pipe, 0, sizeof(*pipe)); @@ -1359,9 +1358,8 @@ static int zr364xx_board_init(struct zr364xx_camera *cam) if (i == 0) { printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); - kfree(cam->pipe->transfer_buffer); - cam->pipe->transfer_buffer = NULL; - return -ENOMEM; + err = -ENOMEM; + goto err_free; } else cam->buffer.dwFrames = i; @@ -1376,9 +1374,17 @@ static int zr364xx_board_init(struct zr364xx_camera *cam) /*** end create system buffers ***/ /* start read pipe */ - zr364xx_start_readpipe(cam); + err = zr364xx_start_readpipe(cam); + if (err) + goto err_free; + DBG(": board initialized\n"); return 0; + +err_free: + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return err; } static int zr364xx_probe(struct usb_interface *intf, @@ -1575,10 +1581,19 @@ static int zr364xx_resume(struct usb_interface *intf) if (!cam->was_streaming) return 0; - zr364xx_start_readpipe(cam); + res = zr364xx_start_readpipe(cam); + if (res) + return res; + res = zr364xx_prepare(cam); - if (!res) - zr364xx_start_acquire(cam); + if (res) + goto err_prepare; + + zr364xx_start_acquire(cam); + return 0; + +err_prepare: + zr364xx_stop_readpipe(cam); return res; } #endif diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 3dc17ebe14fa..78007dba4677 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -441,3 +441,36 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, return 0; } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt); + +s64 v4l2_get_link_rate(struct v4l2_ctrl_handler *handler, unsigned int mul, + unsigned int div) +{ + struct v4l2_ctrl *ctrl; + s64 freq; + + ctrl = v4l2_ctrl_find(handler, V4L2_CID_LINK_FREQ); + if (ctrl) { + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; + int ret; + + qm.index = v4l2_ctrl_g_ctrl(ctrl); + + ret = v4l2_querymenu(handler, &qm); + if (ret) + return -ENOENT; + + freq = qm.value; + } else { + if (!mul || !div) + return -ENOENT; + + ctrl = v4l2_ctrl_find(handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) + return -ENOENT; + + freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div); + } + + return freq > 0 ? freq : -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_get_link_rate); diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index a99e82ec9ab6..0ca75f6784c5 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -23,103 +23,6 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-ioctl.h> -/** - * assign_in_user() - Copy from one __user var to another one - * - * @to: __user var where data will be stored - * @from: __user var where data will be retrieved. - * - * As this code very often needs to allocate userspace memory, it is easier - * to have a macro that will do both get_user() and put_user() at once. - * - * This function complements the macros defined at asm-generic/uaccess.h. - * It uses the same argument order as copy_in_user() - */ -#define assign_in_user(to, from) \ -({ \ - typeof(*from) __assign_tmp; \ - \ - get_user(__assign_tmp, from) || put_user(__assign_tmp, to); \ -}) - -/** - * get_user_cast() - Stores at a kernelspace local var the contents from a - * pointer with userspace data that is not tagged with __user. - * - * @__x: var where data will be stored - * @__ptr: var where data will be retrieved. - * - * Sometimes we need to declare a pointer without __user because it - * comes from a pointer struct field that will be retrieved from userspace - * by the 64-bit native ioctl handler. This function ensures that the - * @__ptr will be cast to __user before calling get_user() in order to - * avoid warnings with static code analyzers like smatch. - */ -#define get_user_cast(__x, __ptr) \ -({ \ - get_user(__x, (typeof(*__ptr) __user *)(__ptr)); \ -}) - -/** - * put_user_force() - Stores the contents of a kernelspace local var - * into a userspace pointer, removing any __user cast. - * - * @__x: var where data will be stored - * @__ptr: var where data will be retrieved. - * - * Sometimes we need to remove the __user attribute from some data, - * by passing the __force macro. This function ensures that the - * @__ptr will be cast with __force before calling put_user(), in order to - * avoid warnings with static code analyzers like smatch. - */ -#define put_user_force(__x, __ptr) \ -({ \ - put_user((typeof(*__x) __force *)(__x), __ptr); \ -}) - -/** - * assign_in_user_cast() - Copy from one __user var to another one - * - * @to: __user var where data will be stored - * @from: var where data will be retrieved that needs to be cast to __user. - * - * As this code very often needs to allocate userspace memory, it is easier - * to have a macro that will do both get_user_cast() and put_user() at once. - * - * This function should be used instead of assign_in_user() when the @from - * variable was not declared as __user. See get_user_cast() for more details. - * - * This function complements the macros defined at asm-generic/uaccess.h. - * It uses the same argument order as copy_in_user() - */ -#define assign_in_user_cast(to, from) \ -({ \ - typeof(*from) __assign_tmp; \ - \ - get_user_cast(__assign_tmp, from) || put_user(__assign_tmp, to);\ -}) - -/** - * native_ioctl - Ancillary function that calls the native 64 bits ioctl - * handler. - * - * @file: pointer to &struct file with the file handler - * @cmd: ioctl to be called - * @arg: arguments passed from/to the ioctl handler - * - * This function calls the native ioctl handler at v4l2-dev, e. g. v4l2_ioctl() - */ -static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - long ret = -ENOIOCTLCMD; - - if (file->f_op->unlocked_ioctl) - ret = file->f_op->unlocked_ioctl(file, cmd, arg); - - return ret; -} - - /* * Per-ioctl data copy handlers. * @@ -150,77 +53,54 @@ struct v4l2_window32 { __u8 global_alpha; }; -static int get_v4l2_window32(struct v4l2_window __user *p64, - struct v4l2_window32 __user *p32, - void __user *aux_buf, u32 aux_space) +static int get_v4l2_window32(struct v4l2_window *p64, + struct v4l2_window32 __user *p32) { - struct v4l2_clip32 __user *uclips; - struct v4l2_clip __user *kclips; - compat_caddr_t p; - u32 clipcount; - - if (!access_ok(p32, sizeof(*p32)) || - copy_in_user(&p64->w, &p32->w, sizeof(p32->w)) || - assign_in_user(&p64->field, &p32->field) || - assign_in_user(&p64->chromakey, &p32->chromakey) || - assign_in_user(&p64->global_alpha, &p32->global_alpha) || - get_user(clipcount, &p32->clipcount) || - put_user(clipcount, &p64->clipcount)) - return -EFAULT; - if (clipcount > 2048) - return -EINVAL; - if (!clipcount) - return put_user(NULL, &p64->clips); + struct v4l2_window32 w32; - if (get_user(p, &p32->clips)) - return -EFAULT; - uclips = compat_ptr(p); - if (aux_space < clipcount * sizeof(*kclips)) - return -EFAULT; - kclips = aux_buf; - if (put_user(kclips, &p64->clips)) + if (copy_from_user(&w32, p32, sizeof(w32))) return -EFAULT; - while (clipcount--) { - if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c))) - return -EFAULT; - if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next)) - return -EFAULT; - uclips++; - kclips++; - } + *p64 = (struct v4l2_window) { + .w = w32.w, + .field = w32.field, + .chromakey = w32.chromakey, + .clips = (void __force *)compat_ptr(w32.clips), + .clipcount = w32.clipcount, + .bitmap = compat_ptr(w32.bitmap), + .global_alpha = w32.global_alpha, + }; + + if (p64->clipcount > 2048) + return -EINVAL; + if (!p64->clipcount) + p64->clips = NULL; + return 0; } -static int put_v4l2_window32(struct v4l2_window __user *p64, +static int put_v4l2_window32(struct v4l2_window *p64, struct v4l2_window32 __user *p32) { - struct v4l2_clip __user *kclips; - struct v4l2_clip32 __user *uclips; - compat_caddr_t p; - u32 clipcount; - - if (copy_in_user(&p32->w, &p64->w, sizeof(p64->w)) || - assign_in_user(&p32->field, &p64->field) || - assign_in_user(&p32->chromakey, &p64->chromakey) || - assign_in_user(&p32->global_alpha, &p64->global_alpha) || - get_user(clipcount, &p64->clipcount) || - put_user(clipcount, &p32->clipcount)) - return -EFAULT; - if (!clipcount) - return 0; + struct v4l2_window32 w32; + + memset(&w32, 0, sizeof(w32)); + w32 = (struct v4l2_window32) { + .w = p64->w, + .field = p64->field, + .chromakey = p64->chromakey, + .clips = (uintptr_t)p64->clips, + .clipcount = p64->clipcount, + .bitmap = ptr_to_compat(p64->bitmap), + .global_alpha = p64->global_alpha, + }; - if (get_user(kclips, &p64->clips)) + /* copy everything except the clips pointer */ + if (copy_to_user(p32, &w32, offsetof(struct v4l2_window32, clips)) || + copy_to_user(&p32->clipcount, &w32.clipcount, + sizeof(w32) - offsetof(struct v4l2_window32, clipcount))) return -EFAULT; - if (get_user(p, &p32->clips)) - return -EFAULT; - uclips = compat_ptr(p); - while (clipcount--) { - if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c))) - return -EFAULT; - uclips++; - kclips++; - } + return 0; } @@ -257,169 +137,99 @@ struct v4l2_create_buffers32 { __u32 reserved[7]; }; -static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) -{ - u32 type; - - if (get_user(type, &p32->type)) - return -EFAULT; - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { - u32 clipcount; - - if (get_user(clipcount, &p32->fmt.win.clipcount)) - return -EFAULT; - if (clipcount > 2048) - return -EINVAL; - *size = clipcount * sizeof(struct v4l2_clip); - return 0; - } - default: - *size = 0; - return 0; - } -} - -static int bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) -{ - if (!access_ok(p32, sizeof(*p32))) - return -EFAULT; - return __bufsize_v4l2_format(p32, size); -} - -static int __get_v4l2_format32(struct v4l2_format __user *p64, - struct v4l2_format32 __user *p32, - void __user *aux_buf, u32 aux_space) +static int get_v4l2_format32(struct v4l2_format *p64, + struct v4l2_format32 __user *p32) { - u32 type; - - if (get_user(type, &p32->type) || put_user(type, &p64->type)) + if (get_user(p64->type, &p32->type)) return -EFAULT; - switch (type) { + switch (p64->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return copy_in_user(&p64->fmt.pix, &p32->fmt.pix, - sizeof(p64->fmt.pix)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.pix, &p32->fmt.pix, + sizeof(p64->fmt.pix)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return copy_in_user(&p64->fmt.pix_mp, &p32->fmt.pix_mp, - sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.pix_mp, &p32->fmt.pix_mp, + sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return get_v4l2_window32(&p64->fmt.win, &p32->fmt.win, - aux_buf, aux_space); + return get_v4l2_window32(&p64->fmt.win, &p32->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: - return copy_in_user(&p64->fmt.vbi, &p32->fmt.vbi, - sizeof(p64->fmt.vbi)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.vbi, &p32->fmt.vbi, + sizeof(p64->fmt.vbi)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return copy_in_user(&p64->fmt.sliced, &p32->fmt.sliced, - sizeof(p64->fmt.sliced)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.sliced, &p32->fmt.sliced, + sizeof(p64->fmt.sliced)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: - return copy_in_user(&p64->fmt.sdr, &p32->fmt.sdr, - sizeof(p64->fmt.sdr)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.sdr, &p32->fmt.sdr, + sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: - return copy_in_user(&p64->fmt.meta, &p32->fmt.meta, - sizeof(p64->fmt.meta)) ? -EFAULT : 0; + return copy_from_user(&p64->fmt.meta, &p32->fmt.meta, + sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: return -EINVAL; } } -static int get_v4l2_format32(struct v4l2_format __user *p64, - struct v4l2_format32 __user *p32, - void __user *aux_buf, u32 aux_space) -{ - if (!access_ok(p32, sizeof(*p32))) - return -EFAULT; - return __get_v4l2_format32(p64, p32, aux_buf, aux_space); -} - -static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *p32, - u32 *size) -{ - if (!access_ok(p32, sizeof(*p32))) - return -EFAULT; - return __bufsize_v4l2_format(&p32->format, size); -} - -static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, - struct v4l2_create_buffers32 __user *p32, - void __user *aux_buf, u32 aux_space) +static int get_v4l2_create32(struct v4l2_create_buffers *p64, + struct v4l2_create_buffers32 __user *p32) { - if (!access_ok(p32, sizeof(*p32)) || - copy_in_user(p64, p32, - offsetof(struct v4l2_create_buffers32, format))) + if (copy_from_user(p64, p32, + offsetof(struct v4l2_create_buffers32, format))) return -EFAULT; - return __get_v4l2_format32(&p64->format, &p32->format, - aux_buf, aux_space); + return get_v4l2_format32(&p64->format, &p32->format); } -static int __put_v4l2_format32(struct v4l2_format __user *p64, - struct v4l2_format32 __user *p32) +static int put_v4l2_format32(struct v4l2_format *p64, + struct v4l2_format32 __user *p32) { - u32 type; - - if (get_user(type, &p64->type)) - return -EFAULT; - - switch (type) { + switch (p64->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return copy_in_user(&p32->fmt.pix, &p64->fmt.pix, + return copy_to_user(&p32->fmt.pix, &p64->fmt.pix, sizeof(p64->fmt.pix)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return copy_in_user(&p32->fmt.pix_mp, &p64->fmt.pix_mp, + return copy_to_user(&p32->fmt.pix_mp, &p64->fmt.pix_mp, sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return put_v4l2_window32(&p64->fmt.win, &p32->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: - return copy_in_user(&p32->fmt.vbi, &p64->fmt.vbi, + return copy_to_user(&p32->fmt.vbi, &p64->fmt.vbi, sizeof(p64->fmt.vbi)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return copy_in_user(&p32->fmt.sliced, &p64->fmt.sliced, + return copy_to_user(&p32->fmt.sliced, &p64->fmt.sliced, sizeof(p64->fmt.sliced)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: - return copy_in_user(&p32->fmt.sdr, &p64->fmt.sdr, + return copy_to_user(&p32->fmt.sdr, &p64->fmt.sdr, sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: case V4L2_BUF_TYPE_META_OUTPUT: - return copy_in_user(&p32->fmt.meta, &p64->fmt.meta, + return copy_to_user(&p32->fmt.meta, &p64->fmt.meta, sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: return -EINVAL; } } -static int put_v4l2_format32(struct v4l2_format __user *p64, - struct v4l2_format32 __user *p32) -{ - if (!access_ok(p32, sizeof(*p32))) - return -EFAULT; - return __put_v4l2_format32(p64, p32); -} - -static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, +static int put_v4l2_create32(struct v4l2_create_buffers *p64, struct v4l2_create_buffers32 __user *p32) { - if (!access_ok(p32, sizeof(*p32)) || - copy_in_user(p32, p64, + if (copy_to_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || - assign_in_user(&p32->capabilities, &p64->capabilities) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) + put_user(p64->capabilities, &p32->capabilities) || + copy_to_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; - return __put_v4l2_format32(&p64->format, &p32->format); + return put_v4l2_format32(&p64->format, &p32->format); } struct v4l2_standard32 { @@ -431,27 +241,23 @@ struct v4l2_standard32 { __u32 reserved[4]; }; -static int get_v4l2_standard32(struct v4l2_standard __user *p64, +static int get_v4l2_standard32(struct v4l2_standard *p64, struct v4l2_standard32 __user *p32) { /* other fields are not set by the user, nor used by the driver */ - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p64->index, &p32->index)) - return -EFAULT; - return 0; + return get_user(p64->index, &p32->index); } -static int put_v4l2_standard32(struct v4l2_standard __user *p64, +static int put_v4l2_standard32(struct v4l2_standard *p64, struct v4l2_standard32 __user *p32) { - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->index, &p64->index) || - assign_in_user(&p32->id, &p64->id) || - copy_in_user(p32->name, p64->name, sizeof(p32->name)) || - copy_in_user(&p32->frameperiod, &p64->frameperiod, + if (put_user(p64->index, &p32->index) || + put_user(p64->id, &p32->id) || + copy_to_user(p32->name, p64->name, sizeof(p32->name)) || + copy_to_user(&p32->frameperiod, &p64->frameperiod, sizeof(p32->frameperiod)) || - assign_in_user(&p32->framelines, &p64->framelines) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) + put_user(p64->framelines, &p32->framelines) || + copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } @@ -498,6 +304,7 @@ struct v4l2_buffer32 { __s32 request_fd; }; +#ifdef CONFIG_COMPAT_32BIT_TIME struct v4l2_buffer32_time32 { __u32 index; __u32 type; /* enum v4l2_buf_type */ @@ -520,481 +327,252 @@ struct v4l2_buffer32_time32 { __u32 reserved2; __s32 request_fd; }; +#endif -static int get_v4l2_plane32(struct v4l2_plane __user *p64, +static int get_v4l2_plane32(struct v4l2_plane *p64, struct v4l2_plane32 __user *p32, enum v4l2_memory memory) { - compat_ulong_t p; + struct v4l2_plane32 plane32; + typeof(p64->m) m = {}; - if (copy_in_user(p64, p32, 2 * sizeof(__u32)) || - copy_in_user(&p64->data_offset, &p32->data_offset, - sizeof(p64->data_offset))) + if (copy_from_user(&plane32, p32, sizeof(plane32))) return -EFAULT; switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (copy_in_user(&p64->m.mem_offset, &p32->m.mem_offset, - sizeof(p32->m.mem_offset))) - return -EFAULT; + m.mem_offset = plane32.m.mem_offset; break; case V4L2_MEMORY_USERPTR: - if (get_user(p, &p32->m.userptr) || - put_user((unsigned long)compat_ptr(p), &p64->m.userptr)) - return -EFAULT; + m.userptr = (unsigned long)compat_ptr(plane32.m.userptr); break; case V4L2_MEMORY_DMABUF: - if (copy_in_user(&p64->m.fd, &p32->m.fd, sizeof(p32->m.fd))) - return -EFAULT; + m.fd = plane32.m.fd; break; } + memset(p64, 0, sizeof(*p64)); + *p64 = (struct v4l2_plane) { + .bytesused = plane32.bytesused, + .length = plane32.length, + .m = m, + .data_offset = plane32.data_offset, + }; + return 0; } -static int put_v4l2_plane32(struct v4l2_plane __user *p64, +static int put_v4l2_plane32(struct v4l2_plane *p64, struct v4l2_plane32 __user *p32, enum v4l2_memory memory) { - unsigned long p; + struct v4l2_plane32 plane32; - if (copy_in_user(p32, p64, 2 * sizeof(__u32)) || - copy_in_user(&p32->data_offset, &p64->data_offset, - sizeof(p64->data_offset))) - return -EFAULT; + memset(&plane32, 0, sizeof(plane32)); + plane32 = (struct v4l2_plane32) { + .bytesused = p64->bytesused, + .length = p64->length, + .data_offset = p64->data_offset, + }; switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (copy_in_user(&p32->m.mem_offset, &p64->m.mem_offset, - sizeof(p64->m.mem_offset))) - return -EFAULT; + plane32.m.mem_offset = p64->m.mem_offset; break; case V4L2_MEMORY_USERPTR: - if (get_user(p, &p64->m.userptr) || - put_user((compat_ulong_t)ptr_to_compat((void __user *)p), - &p32->m.userptr)) - return -EFAULT; + plane32.m.userptr = (uintptr_t)(p64->m.userptr); break; case V4L2_MEMORY_DMABUF: - if (copy_in_user(&p32->m.fd, &p64->m.fd, sizeof(p64->m.fd))) - return -EFAULT; + plane32.m.fd = p64->m.fd; break; } - return 0; -} - -static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *p32, u32 *size) -{ - u32 type; - u32 length; - - if (!access_ok(p32, sizeof(*p32)) || - get_user(type, &p32->type) || - get_user(length, &p32->length)) + if (copy_to_user(p32, &plane32, sizeof(plane32))) return -EFAULT; - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - if (length > VIDEO_MAX_PLANES) - return -EINVAL; - - /* - * We don't really care if userspace decides to kill itself - * by passing a very big length value - */ - *size = length * sizeof(struct v4l2_plane); - } else { - *size = 0; - } return 0; } -static int bufsize_v4l2_buffer_time32(struct v4l2_buffer32_time32 __user *p32, u32 *size) +static int get_v4l2_buffer32(struct v4l2_buffer *vb, + struct v4l2_buffer32 __user *arg) { - u32 type; - u32 length; + struct v4l2_buffer32 vb32; - if (!access_ok(p32, sizeof(*p32)) || - get_user(type, &p32->type) || - get_user(length, &p32->length)) + if (copy_from_user(&vb32, arg, sizeof(vb32))) return -EFAULT; - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - if (length > VIDEO_MAX_PLANES) - return -EINVAL; + memset(vb, 0, sizeof(*vb)); + *vb = (struct v4l2_buffer) { + .index = vb32.index, + .type = vb32.type, + .bytesused = vb32.bytesused, + .flags = vb32.flags, + .field = vb32.field, + .timestamp.tv_sec = vb32.timestamp.tv_sec, + .timestamp.tv_usec = vb32.timestamp.tv_usec, + .timecode = vb32.timecode, + .sequence = vb32.sequence, + .memory = vb32.memory, + .m.offset = vb32.m.offset, + .length = vb32.length, + .request_fd = vb32.request_fd, + }; - /* - * We don't really care if userspace decides to kill itself - * by passing a very big length value - */ - *size = length * sizeof(struct v4l2_plane); - } else { - *size = 0; + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_OVERLAY: + vb->m.offset = vb32.m.offset; + break; + case V4L2_MEMORY_USERPTR: + vb->m.userptr = (unsigned long)compat_ptr(vb32.m.userptr); + break; + case V4L2_MEMORY_DMABUF: + vb->m.fd = vb32.m.fd; + break; } - return 0; -} - -static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, - struct v4l2_buffer32 __user *p32, - void __user *aux_buf, u32 aux_space) -{ - u32 type; - u32 length; - s32 request_fd; - enum v4l2_memory memory; - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane __user *uplane; - compat_caddr_t p; - int ret; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p64->index, &p32->index) || - get_user(type, &p32->type) || - put_user(type, &p64->type) || - assign_in_user(&p64->flags, &p32->flags) || - get_user(memory, &p32->memory) || - put_user(memory, &p64->memory) || - get_user(length, &p32->length) || - put_user(length, &p64->length) || - get_user(request_fd, &p32->request_fd) || - put_user(request_fd, &p64->request_fd)) - return -EFAULT; - - if (V4L2_TYPE_IS_OUTPUT(type)) - if (assign_in_user(&p64->bytesused, &p32->bytesused) || - assign_in_user(&p64->field, &p32->field) || - assign_in_user(&p64->timestamp.tv_sec, - &p32->timestamp.tv_sec) || - assign_in_user(&p64->timestamp.tv_usec, - &p32->timestamp.tv_usec)) - return -EFAULT; - - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - u32 num_planes = length; - if (num_planes == 0) { - /* - * num_planes == 0 is legal, e.g. when userspace doesn't - * need planes array on DQBUF - */ - return put_user(NULL, &p64->m.planes); - } - if (num_planes > VIDEO_MAX_PLANES) - return -EINVAL; - - if (get_user(p, &p32->m.planes)) - return -EFAULT; - - uplane32 = compat_ptr(p); - if (!access_ok(uplane32, - num_planes * sizeof(*uplane32))) - return -EFAULT; - - /* - * We don't really care if userspace decides to kill itself - * by passing a very big num_planes value - */ - if (aux_space < num_planes * sizeof(*uplane)) - return -EFAULT; - - uplane = aux_buf; - if (put_user_force(uplane, &p64->m.planes)) - return -EFAULT; - - while (num_planes--) { - ret = get_v4l2_plane32(uplane, uplane32, memory); - if (ret) - return ret; - uplane++; - uplane32++; - } - } else { - switch (memory) { - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&p64->m.offset, &p32->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: { - compat_ulong_t userptr; - - if (get_user(userptr, &p32->m.userptr) || - put_user((unsigned long)compat_ptr(userptr), - &p64->m.userptr)) - return -EFAULT; - break; - } - case V4L2_MEMORY_DMABUF: - if (assign_in_user(&p64->m.fd, &p32->m.fd)) - return -EFAULT; - break; - } - } + if (V4L2_TYPE_IS_MULTIPLANAR(vb->type)) + vb->m.planes = (void __force *) + compat_ptr(vb32.m.planes); return 0; } -static int get_v4l2_buffer32_time32(struct v4l2_buffer_time32 __user *p64, - struct v4l2_buffer32_time32 __user *p32, - void __user *aux_buf, u32 aux_space) +#ifdef CONFIG_COMPAT_32BIT_TIME +static int get_v4l2_buffer32_time32(struct v4l2_buffer *vb, + struct v4l2_buffer32_time32 __user *arg) { - u32 type; - u32 length; - s32 request_fd; - enum v4l2_memory memory; - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane __user *uplane; - compat_caddr_t p; - int ret; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p64->index, &p32->index) || - get_user(type, &p32->type) || - put_user(type, &p64->type) || - assign_in_user(&p64->flags, &p32->flags) || - get_user(memory, &p32->memory) || - put_user(memory, &p64->memory) || - get_user(length, &p32->length) || - put_user(length, &p64->length) || - get_user(request_fd, &p32->request_fd) || - put_user(request_fd, &p64->request_fd)) - return -EFAULT; + struct v4l2_buffer32_time32 vb32; - if (V4L2_TYPE_IS_OUTPUT(type)) - if (assign_in_user(&p64->bytesused, &p32->bytesused) || - assign_in_user(&p64->field, &p32->field) || - assign_in_user(&p64->timestamp.tv_sec, - &p32->timestamp.tv_sec) || - assign_in_user(&p64->timestamp.tv_usec, - &p32->timestamp.tv_usec)) - return -EFAULT; - - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - u32 num_planes = length; - - if (num_planes == 0) { - /* - * num_planes == 0 is legal, e.g. when userspace doesn't - * need planes array on DQBUF - */ - return put_user(NULL, &p64->m.planes); - } - if (num_planes > VIDEO_MAX_PLANES) - return -EINVAL; - - if (get_user(p, &p32->m.planes)) - return -EFAULT; - - uplane32 = compat_ptr(p); - if (!access_ok(uplane32, - num_planes * sizeof(*uplane32))) - return -EFAULT; - - /* - * We don't really care if userspace decides to kill itself - * by passing a very big num_planes value - */ - if (aux_space < num_planes * sizeof(*uplane)) - return -EFAULT; - - uplane = aux_buf; - if (put_user_force(uplane, &p64->m.planes)) - return -EFAULT; - - while (num_planes--) { - ret = get_v4l2_plane32(uplane, uplane32, memory); - if (ret) - return ret; - uplane++; - uplane32++; - } - } else { - switch (memory) { - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&p64->m.offset, &p32->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: { - compat_ulong_t userptr; + if (copy_from_user(&vb32, arg, sizeof(vb32))) + return -EFAULT; - if (get_user(userptr, &p32->m.userptr) || - put_user((unsigned long)compat_ptr(userptr), - &p64->m.userptr)) - return -EFAULT; - break; - } - case V4L2_MEMORY_DMABUF: - if (assign_in_user(&p64->m.fd, &p32->m.fd)) - return -EFAULT; - break; - } + *vb = (struct v4l2_buffer) { + .index = vb32.index, + .type = vb32.type, + .bytesused = vb32.bytesused, + .flags = vb32.flags, + .field = vb32.field, + .timestamp.tv_sec = vb32.timestamp.tv_sec, + .timestamp.tv_usec = vb32.timestamp.tv_usec, + .timecode = vb32.timecode, + .sequence = vb32.sequence, + .memory = vb32.memory, + .m.offset = vb32.m.offset, + .length = vb32.length, + .request_fd = vb32.request_fd, + }; + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_OVERLAY: + vb->m.offset = vb32.m.offset; + break; + case V4L2_MEMORY_USERPTR: + vb->m.userptr = (unsigned long)compat_ptr(vb32.m.userptr); + break; + case V4L2_MEMORY_DMABUF: + vb->m.fd = vb32.m.fd; + break; } + if (V4L2_TYPE_IS_MULTIPLANAR(vb->type)) + vb->m.planes = (void __force *) + compat_ptr(vb32.m.planes); + return 0; } +#endif -static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, - struct v4l2_buffer32 __user *p32) +static int put_v4l2_buffer32(struct v4l2_buffer *vb, + struct v4l2_buffer32 __user *arg) { - u32 type; - u32 length; - enum v4l2_memory memory; - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane *uplane; - compat_caddr_t p; - int ret; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->index, &p64->index) || - get_user(type, &p64->type) || - put_user(type, &p32->type) || - assign_in_user(&p32->flags, &p64->flags) || - get_user(memory, &p64->memory) || - put_user(memory, &p32->memory)) - return -EFAULT; + struct v4l2_buffer32 vb32; + + memset(&vb32, 0, sizeof(vb32)); + vb32 = (struct v4l2_buffer32) { + .index = vb->index, + .type = vb->type, + .bytesused = vb->bytesused, + .flags = vb->flags, + .field = vb->field, + .timestamp.tv_sec = vb->timestamp.tv_sec, + .timestamp.tv_usec = vb->timestamp.tv_usec, + .timecode = vb->timecode, + .sequence = vb->sequence, + .memory = vb->memory, + .m.offset = vb->m.offset, + .length = vb->length, + .request_fd = vb->request_fd, + }; - if (assign_in_user(&p32->bytesused, &p64->bytesused) || - assign_in_user(&p32->field, &p64->field) || - assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || - assign_in_user(&p32->timestamp.tv_usec, &p64->timestamp.tv_usec) || - copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) || - assign_in_user(&p32->sequence, &p64->sequence) || - assign_in_user(&p32->reserved2, &p64->reserved2) || - assign_in_user(&p32->request_fd, &p64->request_fd) || - get_user(length, &p64->length) || - put_user(length, &p32->length)) - return -EFAULT; + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_OVERLAY: + vb32.m.offset = vb->m.offset; + break; + case V4L2_MEMORY_USERPTR: + vb32.m.userptr = (uintptr_t)(vb->m.userptr); + break; + case V4L2_MEMORY_DMABUF: + vb32.m.fd = vb->m.fd; + break; + } - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - u32 num_planes = length; + if (V4L2_TYPE_IS_MULTIPLANAR(vb->type)) + vb32.m.planes = (uintptr_t)vb->m.planes; - if (num_planes == 0) - return 0; - /* We need to define uplane without __user, even though - * it does point to data in userspace here. The reason is - * that v4l2-ioctl.c copies it from userspace to kernelspace, - * so its definition in videodev2.h doesn't have a - * __user markup. Defining uplane with __user causes - * smatch warnings, so instead declare it without __user - * and cast it as a userspace pointer to put_v4l2_plane32(). - */ - if (get_user(uplane, &p64->m.planes)) - return -EFAULT; - if (get_user(p, &p32->m.planes)) - return -EFAULT; - uplane32 = compat_ptr(p); - - while (num_planes--) { - ret = put_v4l2_plane32((void __user *)uplane, - uplane32, memory); - if (ret) - return ret; - ++uplane; - ++uplane32; - } - } else { - switch (memory) { - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&p32->m.offset, &p64->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - if (assign_in_user(&p32->m.userptr, &p64->m.userptr)) - return -EFAULT; - break; - case V4L2_MEMORY_DMABUF: - if (assign_in_user(&p32->m.fd, &p64->m.fd)) - return -EFAULT; - break; - } - } + if (copy_to_user(arg, &vb32, sizeof(vb32))) + return -EFAULT; return 0; } -static int put_v4l2_buffer32_time32(struct v4l2_buffer_time32 __user *p64, - struct v4l2_buffer32_time32 __user *p32) +#ifdef CONFIG_COMPAT_32BIT_TIME +static int put_v4l2_buffer32_time32(struct v4l2_buffer *vb, + struct v4l2_buffer32_time32 __user *arg) { - u32 type; - u32 length; - enum v4l2_memory memory; - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane *uplane; - compat_caddr_t p; - int ret; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->index, &p64->index) || - get_user(type, &p64->type) || - put_user(type, &p32->type) || - assign_in_user(&p32->flags, &p64->flags) || - get_user(memory, &p64->memory) || - put_user(memory, &p32->memory)) - return -EFAULT; - - if (assign_in_user(&p32->bytesused, &p64->bytesused) || - assign_in_user(&p32->field, &p64->field) || - assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || - assign_in_user(&p32->timestamp.tv_usec, &p64->timestamp.tv_usec) || - copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) || - assign_in_user(&p32->sequence, &p64->sequence) || - assign_in_user(&p32->reserved2, &p64->reserved2) || - assign_in_user(&p32->request_fd, &p64->request_fd) || - get_user(length, &p64->length) || - put_user(length, &p32->length)) - return -EFAULT; + struct v4l2_buffer32_time32 vb32; + + memset(&vb32, 0, sizeof(vb32)); + vb32 = (struct v4l2_buffer32_time32) { + .index = vb->index, + .type = vb->type, + .bytesused = vb->bytesused, + .flags = vb->flags, + .field = vb->field, + .timestamp.tv_sec = vb->timestamp.tv_sec, + .timestamp.tv_usec = vb->timestamp.tv_usec, + .timecode = vb->timecode, + .sequence = vb->sequence, + .memory = vb->memory, + .m.offset = vb->m.offset, + .length = vb->length, + .request_fd = vb->request_fd, + }; + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_OVERLAY: + vb32.m.offset = vb->m.offset; + break; + case V4L2_MEMORY_USERPTR: + vb32.m.userptr = (uintptr_t)(vb->m.userptr); + break; + case V4L2_MEMORY_DMABUF: + vb32.m.fd = vb->m.fd; + break; + } - if (V4L2_TYPE_IS_MULTIPLANAR(type)) { - u32 num_planes = length; + if (V4L2_TYPE_IS_MULTIPLANAR(vb->type)) + vb32.m.planes = (uintptr_t)vb->m.planes; - if (num_planes == 0) - return 0; - /* We need to define uplane without __user, even though - * it does point to data in userspace here. The reason is - * that v4l2-ioctl.c copies it from userspace to kernelspace, - * so its definition in videodev2.h doesn't have a - * __user markup. Defining uplane with __user causes - * smatch warnings, so instead declare it without __user - * and cast it as a userspace pointer to put_v4l2_plane32(). - */ - if (get_user(uplane, &p64->m.planes)) - return -EFAULT; - if (get_user(p, &p32->m.planes)) - return -EFAULT; - uplane32 = compat_ptr(p); - - while (num_planes--) { - ret = put_v4l2_plane32((void __user *)uplane, - uplane32, memory); - if (ret) - return ret; - ++uplane; - ++uplane32; - } - } else { - switch (memory) { - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&p32->m.offset, &p64->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - if (assign_in_user(&p32->m.userptr, &p64->m.userptr)) - return -EFAULT; - break; - case V4L2_MEMORY_DMABUF: - if (assign_in_user(&p32->m.fd, &p64->m.fd)) - return -EFAULT; - break; - } - } + if (copy_to_user(arg, &vb32, sizeof(vb32))) + return -EFAULT; return 0; } +#endif struct v4l2_framebuffer32 { __u32 capability; @@ -1012,33 +590,30 @@ struct v4l2_framebuffer32 { } fmt; }; -static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, +static int get_v4l2_framebuffer32(struct v4l2_framebuffer *p64, struct v4l2_framebuffer32 __user *p32) { compat_caddr_t tmp; - if (!access_ok(p32, sizeof(*p32)) || - get_user(tmp, &p32->base) || - put_user_force(compat_ptr(tmp), &p64->base) || - assign_in_user(&p64->capability, &p32->capability) || - assign_in_user(&p64->flags, &p32->flags) || - copy_in_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt))) + if (get_user(tmp, &p32->base) || + get_user(p64->capability, &p32->capability) || + get_user(p64->flags, &p32->flags) || + copy_from_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt))) return -EFAULT; + p64->base = (void __force *)compat_ptr(tmp); + return 0; } -static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, +static int put_v4l2_framebuffer32(struct v4l2_framebuffer *p64, struct v4l2_framebuffer32 __user *p32) { - void *base; - - if (!access_ok(p32, sizeof(*p32)) || - get_user(base, &p64->base) || - put_user(ptr_to_compat((void __user *)base), &p32->base) || - assign_in_user(&p32->capability, &p64->capability) || - assign_in_user(&p32->flags, &p64->flags) || - copy_in_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt))) + if (put_user((uintptr_t)p64->base, &p32->base) || + put_user(p64->capability, &p32->capability) || + put_user(p64->flags, &p32->flags) || + copy_to_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt))) return -EFAULT; + return 0; } @@ -1058,18 +633,18 @@ struct v4l2_input32 { * The 64-bit v4l2_input struct has extra padding at the end of the struct. * Otherwise it is identical to the 32-bit version. */ -static inline int get_v4l2_input32(struct v4l2_input __user *p64, +static inline int get_v4l2_input32(struct v4l2_input *p64, struct v4l2_input32 __user *p32) { - if (copy_in_user(p64, p32, sizeof(*p32))) + if (copy_from_user(p64, p32, sizeof(*p32))) return -EFAULT; return 0; } -static inline int put_v4l2_input32(struct v4l2_input __user *p64, +static inline int put_v4l2_input32(struct v4l2_input *p64, struct v4l2_input32 __user *p32) { - if (copy_in_user(p32, p64, sizeof(*p32))) + if (copy_to_user(p32, p64, sizeof(*p32))) return -EFAULT; return 0; } @@ -1124,142 +699,44 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id) (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD); } -static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *p32, - u32 *size) -{ - u32 count; - - if (!access_ok(p32, sizeof(*p32)) || - get_user(count, &p32->count)) - return -EFAULT; - if (count > V4L2_CID_MAX_CTRLS) - return -EINVAL; - *size = count * sizeof(struct v4l2_ext_control); - return 0; -} - -static int get_v4l2_ext_controls32(struct file *file, - struct v4l2_ext_controls __user *p64, - struct v4l2_ext_controls32 __user *p32, - void __user *aux_buf, u32 aux_space) +static int get_v4l2_ext_controls32(struct v4l2_ext_controls *p64, + struct v4l2_ext_controls32 __user *p32) { - struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols; - u32 count; - u32 n; - compat_caddr_t p; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p64->which, &p32->which) || - get_user(count, &p32->count) || - put_user(count, &p64->count) || - assign_in_user(&p64->error_idx, &p32->error_idx) || - assign_in_user(&p64->request_fd, &p32->request_fd) || - copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved))) - return -EFAULT; + struct v4l2_ext_controls32 ec32; - if (count == 0) - return put_user(NULL, &p64->controls); - if (count > V4L2_CID_MAX_CTRLS) - return -EINVAL; - if (get_user(p, &p32->controls)) - return -EFAULT; - ucontrols = compat_ptr(p); - if (!access_ok(ucontrols, count * sizeof(*ucontrols))) + if (copy_from_user(&ec32, p32, sizeof(ec32))) return -EFAULT; - if (aux_space < count * sizeof(*kcontrols)) - return -EFAULT; - kcontrols = aux_buf; - if (put_user_force(kcontrols, &p64->controls)) - return -EFAULT; - - for (n = 0; n < count; n++) { - u32 id; - - if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) - return -EFAULT; - if (get_user(id, &kcontrols->id)) - return -EFAULT; - - if (ctrl_is_pointer(file, id)) { - void __user *s; + *p64 = (struct v4l2_ext_controls) { + .which = ec32.which, + .count = ec32.count, + .error_idx = ec32.error_idx, + .request_fd = ec32.request_fd, + .reserved[0] = ec32.reserved[0], + .controls = (void __force *)compat_ptr(ec32.controls), + }; - if (get_user(p, &ucontrols->string)) - return -EFAULT; - s = compat_ptr(p); - if (put_user(s, &kcontrols->string)) - return -EFAULT; - } - ucontrols++; - kcontrols++; - } return 0; } -static int put_v4l2_ext_controls32(struct file *file, - struct v4l2_ext_controls __user *p64, +static int put_v4l2_ext_controls32(struct v4l2_ext_controls *p64, struct v4l2_ext_controls32 __user *p32) { - struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control *kcontrols; - u32 count; - u32 n; - compat_caddr_t p; - - /* - * We need to define kcontrols without __user, even though it does - * point to data in userspace here. The reason is that v4l2-ioctl.c - * copies it from userspace to kernelspace, so its definition in - * videodev2.h doesn't have a __user markup. Defining kcontrols - * with __user causes smatch warnings, so instead declare it - * without __user and cast it as a userspace pointer where needed. - */ - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->which, &p64->which) || - get_user(count, &p64->count) || - put_user(count, &p32->count) || - assign_in_user(&p32->error_idx, &p64->error_idx) || - assign_in_user(&p32->request_fd, &p64->request_fd) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) || - get_user(kcontrols, &p64->controls)) - return -EFAULT; + struct v4l2_ext_controls32 ec32; + + memset(&ec32, 0, sizeof(ec32)); + ec32 = (struct v4l2_ext_controls32) { + .which = p64->which, + .count = p64->count, + .error_idx = p64->error_idx, + .request_fd = p64->request_fd, + .reserved[0] = p64->reserved[0], + .controls = (uintptr_t)p64->controls, + }; - if (!count || count > (U32_MAX/sizeof(*ucontrols))) - return 0; - if (get_user(p, &p32->controls)) - return -EFAULT; - ucontrols = compat_ptr(p); - if (!access_ok(ucontrols, count * sizeof(*ucontrols))) + if (copy_to_user(p32, &ec32, sizeof(ec32))) return -EFAULT; - for (n = 0; n < count; n++) { - unsigned int size = sizeof(*ucontrols); - u32 id; - - if (get_user_cast(id, &kcontrols->id) || - put_user(id, &ucontrols->id) || - assign_in_user_cast(&ucontrols->size, &kcontrols->size) || - copy_in_user(&ucontrols->reserved2, - (void __user *)&kcontrols->reserved2, - sizeof(ucontrols->reserved2))) - return -EFAULT; - - /* - * Do not modify the pointer when copying a pointer control. - * The contents of the pointer was changed, not the pointer - * itself. - */ - if (ctrl_is_pointer(file, id)) - size -= sizeof(ucontrols->value64); - - if (copy_in_user(ucontrols, - (void __user *)kcontrols, size)) - return -EFAULT; - - ucontrols++; - kcontrols++; - } return 0; } @@ -1288,6 +765,7 @@ struct v4l2_event32 { __u32 reserved[8]; }; +#ifdef CONFIG_COMPAT_32BIT_TIME struct v4l2_event32_time32 { __u32 type; union { @@ -1300,39 +778,40 @@ struct v4l2_event32_time32 { __u32 id; __u32 reserved[8]; }; +#endif -static int put_v4l2_event32(struct v4l2_event __user *p64, +static int put_v4l2_event32(struct v4l2_event *p64, struct v4l2_event32 __user *p32) { - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->type, &p64->type) || - copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) || - assign_in_user(&p32->pending, &p64->pending) || - assign_in_user(&p32->sequence, &p64->sequence) || - assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || - assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) || - assign_in_user(&p32->id, &p64->id) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) + if (put_user(p64->type, &p32->type) || + copy_to_user(&p32->u, &p64->u, sizeof(p64->u)) || + put_user(p64->pending, &p32->pending) || + put_user(p64->sequence, &p32->sequence) || + put_user(p64->timestamp.tv_sec, &p32->timestamp.tv_sec) || + put_user(p64->timestamp.tv_nsec, &p32->timestamp.tv_nsec) || + put_user(p64->id, &p32->id) || + copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } -static int put_v4l2_event32_time32(struct v4l2_event_time32 __user *p64, +#ifdef CONFIG_COMPAT_32BIT_TIME +static int put_v4l2_event32_time32(struct v4l2_event *p64, struct v4l2_event32_time32 __user *p32) { - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->type, &p64->type) || - copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) || - assign_in_user(&p32->pending, &p64->pending) || - assign_in_user(&p32->sequence, &p64->sequence) || - assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || - assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) || - assign_in_user(&p32->id, &p64->id) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) + if (put_user(p64->type, &p32->type) || + copy_to_user(&p32->u, &p64->u, sizeof(p64->u)) || + put_user(p64->pending, &p32->pending) || + put_user(p64->sequence, &p32->sequence) || + put_user(p64->timestamp.tv_sec, &p32->timestamp.tv_sec) || + put_user(p64->timestamp.tv_nsec, &p32->timestamp.tv_nsec) || + put_user(p64->id, &p32->id) || + copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } #endif +#endif struct v4l2_edid32 { __u32 pad; @@ -1342,34 +821,23 @@ struct v4l2_edid32 { compat_caddr_t edid; }; -static int get_v4l2_edid32(struct v4l2_edid __user *p64, +static int get_v4l2_edid32(struct v4l2_edid *p64, struct v4l2_edid32 __user *p32) { - compat_uptr_t tmp; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p64->pad, &p32->pad) || - assign_in_user(&p64->start_block, &p32->start_block) || - assign_in_user_cast(&p64->blocks, &p32->blocks) || - get_user(tmp, &p32->edid) || - put_user_force(compat_ptr(tmp), &p64->edid) || - copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved))) + compat_uptr_t edid; + + if (copy_from_user(p64, p32, offsetof(struct v4l2_edid32, edid)) || + get_user(edid, &p32->edid)) return -EFAULT; + + p64->edid = (void __force *)compat_ptr(edid); return 0; } -static int put_v4l2_edid32(struct v4l2_edid __user *p64, +static int put_v4l2_edid32(struct v4l2_edid *p64, struct v4l2_edid32 __user *p32) { - void *edid; - - if (!access_ok(p32, sizeof(*p32)) || - assign_in_user(&p32->pad, &p64->pad) || - assign_in_user(&p32->start_block, &p64->start_block) || - assign_in_user(&p32->blocks, &p64->blocks) || - get_user(edid, &p64->edid) || - put_user(ptr_to_compat((void __user *)edid), &p32->edid) || - copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) + if (copy_to_user(p32, p64, offsetof(struct v4l2_edid32, edid))) return -EFAULT; return 0; } @@ -1385,13 +853,10 @@ static int put_v4l2_edid32(struct v4l2_edid __user *p64, #define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) #define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) #define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) -#define VIDIOC_QUERYBUF32_TIME32 _IOWR('V', 9, struct v4l2_buffer32_time32) #define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) #define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) #define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) -#define VIDIOC_QBUF32_TIME32 _IOWR('V', 15, struct v4l2_buffer32_time32) #define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) -#define VIDIOC_DQBUF32_TIME32 _IOWR('V', 17, struct v4l2_buffer32_time32) #define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) #define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) #define VIDIOC_G_EDID32 _IOWR('V', 40, struct v4l2_edid32) @@ -1401,366 +866,359 @@ static int put_v4l2_edid32(struct v4l2_edid __user *p64, #define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) -#define VIDIOC_DQEVENT32_TIME32 _IOR ('V', 89, struct v4l2_event32_time32) #define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) #define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) -#define VIDIOC_PREPARE_BUF32_TIME32 _IOWR('V', 93, struct v4l2_buffer32_time32) -#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) -#define VIDIOC_STREAMON32 _IOW ('V', 18, s32) -#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) -#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) -#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) -#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) -#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) - -/** - * alloc_userspace() - Allocates a 64-bits userspace pointer compatible - * for calling the native 64-bits version of an ioctl. - * - * @size: size of the structure itself to be allocated. - * @aux_space: extra size needed to store "extra" data, e.g. space for - * other __user data that is pointed to fields inside the - * structure. - * @new_p64: pointer to a pointer to be filled with the allocated struct. - * - * Return: - * - * if it can't allocate memory, either -ENOMEM or -EFAULT will be returned. - * Zero otherwise. - */ -static int alloc_userspace(unsigned int size, u32 aux_space, - void __user **new_p64) -{ - *new_p64 = compat_alloc_user_space(size + aux_space); - if (!*new_p64) - return -ENOMEM; - if (clear_user(*new_p64, size)) - return -EFAULT; - return 0; -} +#ifdef CONFIG_COMPAT_32BIT_TIME +#define VIDIOC_QUERYBUF32_TIME32 _IOWR('V', 9, struct v4l2_buffer32_time32) +#define VIDIOC_QBUF32_TIME32 _IOWR('V', 15, struct v4l2_buffer32_time32) +#define VIDIOC_DQBUF32_TIME32 _IOWR('V', 17, struct v4l2_buffer32_time32) +#ifdef CONFIG_X86_64 +#define VIDIOC_DQEVENT32_TIME32 _IOR ('V', 89, struct v4l2_event32_time32) +#endif +#define VIDIOC_PREPARE_BUF32_TIME32 _IOWR('V', 93, struct v4l2_buffer32_time32) +#endif -/** - * do_video_ioctl() - Ancillary function with handles a compat32 ioctl call - * - * @file: pointer to &struct file with the file handler - * @cmd: ioctl to be called - * @arg: arguments passed from/to the ioctl handler - * - * This function is called when a 32 bits application calls a V4L2 ioctl - * and the Kernel is compiled with 64 bits. - * - * This function is called by v4l2_compat_ioctl32() when the function is - * not private to some specific driver. - * - * It converts a 32-bits struct into a 64 bits one, calls the native 64-bits - * ioctl handler and fills back the 32-bits struct with the results of the - * native call. - */ -static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +unsigned int v4l2_compat_translate_cmd(unsigned int cmd) { - void __user *p32 = compat_ptr(arg); - void __user *new_p64 = NULL; - void __user *aux_buf; - u32 aux_space; - int compatible_arg = 1; - long err = 0; - unsigned int ncmd; - - /* - * 1. When struct size is different, converts the command. - */ switch (cmd) { - case VIDIOC_G_FMT32: ncmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: ncmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: ncmd = VIDIOC_QUERYBUF; break; - case VIDIOC_QUERYBUF32_TIME32: ncmd = VIDIOC_QUERYBUF_TIME32; break; - case VIDIOC_G_FBUF32: ncmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: ncmd = VIDIOC_S_FBUF; break; - case VIDIOC_QBUF32: ncmd = VIDIOC_QBUF; break; - case VIDIOC_QBUF32_TIME32: ncmd = VIDIOC_QBUF_TIME32; break; - case VIDIOC_DQBUF32: ncmd = VIDIOC_DQBUF; break; - case VIDIOC_DQBUF32_TIME32: ncmd = VIDIOC_DQBUF_TIME32; break; - case VIDIOC_ENUMSTD32: ncmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: ncmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_TRY_FMT32: ncmd = VIDIOC_TRY_FMT; break; - case VIDIOC_G_EXT_CTRLS32: ncmd = VIDIOC_G_EXT_CTRLS; break; - case VIDIOC_S_EXT_CTRLS32: ncmd = VIDIOC_S_EXT_CTRLS; break; - case VIDIOC_TRY_EXT_CTRLS32: ncmd = VIDIOC_TRY_EXT_CTRLS; break; + case VIDIOC_G_FMT32: + return VIDIOC_G_FMT; + case VIDIOC_S_FMT32: + return VIDIOC_S_FMT; + case VIDIOC_TRY_FMT32: + return VIDIOC_TRY_FMT; + case VIDIOC_G_FBUF32: + return VIDIOC_G_FBUF; + case VIDIOC_S_FBUF32: + return VIDIOC_S_FBUF; +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF32_TIME32: + return VIDIOC_QUERYBUF; + case VIDIOC_QBUF32_TIME32: + return VIDIOC_QBUF; + case VIDIOC_DQBUF32_TIME32: + return VIDIOC_DQBUF; + case VIDIOC_PREPARE_BUF32_TIME32: + return VIDIOC_PREPARE_BUF; +#endif + case VIDIOC_QUERYBUF32: + return VIDIOC_QUERYBUF; + case VIDIOC_QBUF32: + return VIDIOC_QBUF; + case VIDIOC_DQBUF32: + return VIDIOC_DQBUF; + case VIDIOC_CREATE_BUFS32: + return VIDIOC_CREATE_BUFS; + case VIDIOC_G_EXT_CTRLS32: + return VIDIOC_G_EXT_CTRLS; + case VIDIOC_S_EXT_CTRLS32: + return VIDIOC_S_EXT_CTRLS; + case VIDIOC_TRY_EXT_CTRLS32: + return VIDIOC_TRY_EXT_CTRLS; + case VIDIOC_PREPARE_BUF32: + return VIDIOC_PREPARE_BUF; + case VIDIOC_ENUMSTD32: + return VIDIOC_ENUMSTD; + case VIDIOC_ENUMINPUT32: + return VIDIOC_ENUMINPUT; + case VIDIOC_G_EDID32: + return VIDIOC_G_EDID; + case VIDIOC_S_EDID32: + return VIDIOC_S_EDID; #ifdef CONFIG_X86_64 - case VIDIOC_DQEVENT32: ncmd = VIDIOC_DQEVENT; break; - case VIDIOC_DQEVENT32_TIME32: ncmd = VIDIOC_DQEVENT_TIME32; break; + case VIDIOC_DQEVENT32: + return VIDIOC_DQEVENT; +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_DQEVENT32_TIME32: + return VIDIOC_DQEVENT; +#endif #endif - case VIDIOC_OVERLAY32: ncmd = VIDIOC_OVERLAY; break; - case VIDIOC_STREAMON32: ncmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: ncmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_INPUT32: ncmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: ncmd = VIDIOC_S_INPUT; break; - case VIDIOC_G_OUTPUT32: ncmd = VIDIOC_G_OUTPUT; break; - case VIDIOC_S_OUTPUT32: ncmd = VIDIOC_S_OUTPUT; break; - case VIDIOC_CREATE_BUFS32: ncmd = VIDIOC_CREATE_BUFS; break; - case VIDIOC_PREPARE_BUF32: ncmd = VIDIOC_PREPARE_BUF; break; - case VIDIOC_PREPARE_BUF32_TIME32: ncmd = VIDIOC_PREPARE_BUF_TIME32; break; - case VIDIOC_G_EDID32: ncmd = VIDIOC_G_EDID; break; - case VIDIOC_S_EDID32: ncmd = VIDIOC_S_EDID; break; - default: ncmd = cmd; break; } + return cmd; +} - /* - * 2. Allocates a 64-bits userspace pointer to store the - * values of the ioctl and copy data from the 32-bits __user - * argument into it. - */ +int v4l2_compat_get_user(void __user *arg, void *parg, unsigned int cmd) +{ switch (cmd) { - case VIDIOC_OVERLAY32: - case VIDIOC_STREAMON32: - case VIDIOC_STREAMOFF32: - case VIDIOC_S_INPUT32: - case VIDIOC_S_OUTPUT32: - err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); - if (!err && assign_in_user((unsigned int __user *)new_p64, - (compat_uint_t __user *)p32)) - err = -EFAULT; - compatible_arg = 0; - break; + case VIDIOC_G_FMT32: + case VIDIOC_S_FMT32: + case VIDIOC_TRY_FMT32: + return get_v4l2_format32(parg, arg); - case VIDIOC_G_INPUT32: - case VIDIOC_G_OUTPUT32: - err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); - compatible_arg = 0; - break; + case VIDIOC_S_FBUF32: + return get_v4l2_framebuffer32(parg, arg); +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF32_TIME32: + case VIDIOC_QBUF32_TIME32: + case VIDIOC_DQBUF32_TIME32: + case VIDIOC_PREPARE_BUF32_TIME32: + return get_v4l2_buffer32_time32(parg, arg); +#endif + case VIDIOC_QUERYBUF32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: + case VIDIOC_PREPARE_BUF32: + return get_v4l2_buffer32(parg, arg); + + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: + return get_v4l2_ext_controls32(parg, arg); + + case VIDIOC_CREATE_BUFS32: + return get_v4l2_create32(parg, arg); + + case VIDIOC_ENUMSTD32: + return get_v4l2_standard32(parg, arg); + + case VIDIOC_ENUMINPUT32: + return get_v4l2_input32(parg, arg); case VIDIOC_G_EDID32: case VIDIOC_S_EDID32: - err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64); - if (!err) - err = get_v4l2_edid32(new_p64, p32); - compatible_arg = 0; - break; + return get_v4l2_edid32(parg, arg); + } + return 0; +} +int v4l2_compat_put_user(void __user *arg, void *parg, unsigned int cmd) +{ + switch (cmd) { case VIDIOC_G_FMT32: case VIDIOC_S_FMT32: case VIDIOC_TRY_FMT32: - err = bufsize_v4l2_format(p32, &aux_space); - if (!err) - err = alloc_userspace(sizeof(struct v4l2_format), - aux_space, &new_p64); - if (!err) { - aux_buf = new_p64 + sizeof(struct v4l2_format); - err = get_v4l2_format32(new_p64, p32, - aux_buf, aux_space); - } - compatible_arg = 0; - break; - - case VIDIOC_CREATE_BUFS32: - err = bufsize_v4l2_create(p32, &aux_space); - if (!err) - err = alloc_userspace(sizeof(struct v4l2_create_buffers), - aux_space, &new_p64); - if (!err) { - aux_buf = new_p64 + sizeof(struct v4l2_create_buffers); - err = get_v4l2_create32(new_p64, p32, - aux_buf, aux_space); - } - compatible_arg = 0; - break; + return put_v4l2_format32(parg, arg); - case VIDIOC_PREPARE_BUF32: - case VIDIOC_QUERYBUF32: - case VIDIOC_QBUF32: - case VIDIOC_DQBUF32: - err = bufsize_v4l2_buffer(p32, &aux_space); - if (!err) - err = alloc_userspace(sizeof(struct v4l2_buffer), - aux_space, &new_p64); - if (!err) { - aux_buf = new_p64 + sizeof(struct v4l2_buffer); - err = get_v4l2_buffer32(new_p64, p32, - aux_buf, aux_space); - } - compatible_arg = 0; - break; - - case VIDIOC_PREPARE_BUF32_TIME32: + case VIDIOC_G_FBUF32: + return put_v4l2_framebuffer32(parg, arg); +#ifdef CONFIG_COMPAT_32BIT_TIME case VIDIOC_QUERYBUF32_TIME32: case VIDIOC_QBUF32_TIME32: case VIDIOC_DQBUF32_TIME32: - err = bufsize_v4l2_buffer_time32(p32, &aux_space); - if (!err) - err = alloc_userspace(sizeof(struct v4l2_buffer), - aux_space, &new_p64); - if (!err) { - aux_buf = new_p64 + sizeof(struct v4l2_buffer); - err = get_v4l2_buffer32_time32(new_p64, p32, - aux_buf, aux_space); - } - compatible_arg = 0; - break; + case VIDIOC_PREPARE_BUF32_TIME32: + return put_v4l2_buffer32_time32(parg, arg); +#endif + case VIDIOC_QUERYBUF32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: + case VIDIOC_PREPARE_BUF32: + return put_v4l2_buffer32(parg, arg); - case VIDIOC_S_FBUF32: - err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, - &new_p64); - if (!err) - err = get_v4l2_framebuffer32(new_p64, p32); - compatible_arg = 0; - break; + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: + return put_v4l2_ext_controls32(parg, arg); - case VIDIOC_G_FBUF32: - err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, - &new_p64); - compatible_arg = 0; - break; + case VIDIOC_CREATE_BUFS32: + return put_v4l2_create32(parg, arg); case VIDIOC_ENUMSTD32: - err = alloc_userspace(sizeof(struct v4l2_standard), 0, - &new_p64); - if (!err) - err = get_v4l2_standard32(new_p64, p32); - compatible_arg = 0; - break; + return put_v4l2_standard32(parg, arg); case VIDIOC_ENUMINPUT32: - err = alloc_userspace(sizeof(struct v4l2_input), 0, &new_p64); - if (!err) - err = get_v4l2_input32(new_p64, p32); - compatible_arg = 0; - break; + return put_v4l2_input32(parg, arg); - case VIDIOC_G_EXT_CTRLS32: - case VIDIOC_S_EXT_CTRLS32: - case VIDIOC_TRY_EXT_CTRLS32: - err = bufsize_v4l2_ext_controls(p32, &aux_space); - if (!err) - err = alloc_userspace(sizeof(struct v4l2_ext_controls), - aux_space, &new_p64); - if (!err) { - aux_buf = new_p64 + sizeof(struct v4l2_ext_controls); - err = get_v4l2_ext_controls32(file, new_p64, p32, - aux_buf, aux_space); - } - compatible_arg = 0; - break; + case VIDIOC_G_EDID32: + case VIDIOC_S_EDID32: + return put_v4l2_edid32(parg, arg); #ifdef CONFIG_X86_64 case VIDIOC_DQEVENT32: - err = alloc_userspace(sizeof(struct v4l2_event), 0, &new_p64); - compatible_arg = 0; - break; + return put_v4l2_event32(parg, arg); +#ifdef CONFIG_COMPAT_32BIT_TIME case VIDIOC_DQEVENT32_TIME32: - err = alloc_userspace(sizeof(struct v4l2_event_time32), 0, &new_p64); - compatible_arg = 0; - break; + return put_v4l2_event32_time32(parg, arg); +#endif #endif } - if (err) - return err; - - /* - * 3. Calls the native 64-bits ioctl handler. - * - * For the functions where a conversion was not needed, - * compatible_arg is true, and it will call it with the arguments - * provided by userspace and stored at @p32 var. - * - * Otherwise, it will pass the newly allocated @new_p64 argument. - */ - if (compatible_arg) - err = native_ioctl(file, ncmd, (unsigned long)p32); - else - err = native_ioctl(file, ncmd, (unsigned long)new_p64); - - if (err == -ENOTTY) - return err; - - /* - * 4. Special case: even after an error we need to put the - * results back for some ioctls. - * - * In the case of EXT_CTRLS, the error_idx will contain information - * on which control failed. - * - * In the case of S_EDID, the driver can return E2BIG and set - * the blocks to maximum allowed value. - */ + return 0; +} + +int v4l2_compat_get_array_args(struct file *file, void *mbuf, + void __user *user_ptr, size_t array_size, + unsigned int cmd, void *arg) +{ + int err = 0; + switch (cmd) { - case VIDIOC_G_EXT_CTRLS32: - case VIDIOC_S_EXT_CTRLS32: - case VIDIOC_TRY_EXT_CTRLS32: - if (put_v4l2_ext_controls32(file, new_p64, p32)) - err = -EFAULT; + case VIDIOC_G_FMT32: + case VIDIOC_S_FMT32: + case VIDIOC_TRY_FMT32: { + struct v4l2_format *f64 = arg; + struct v4l2_clip *c64 = mbuf; + struct v4l2_clip32 __user *c32 = user_ptr; + u32 clipcount = f64->fmt.win.clipcount; + + if ((f64->type != V4L2_BUF_TYPE_VIDEO_OVERLAY && + f64->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) || + clipcount == 0) + return 0; + if (clipcount > 2048) + return -EINVAL; + while (clipcount--) { + if (copy_from_user(c64, c32, sizeof(c64->c))) + return -EFAULT; + c64->next = NULL; + c64++; + c32++; + } break; - case VIDIOC_S_EDID32: - if (put_v4l2_edid32(new_p64, p32)) - err = -EFAULT; + } +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF32_TIME32: + case VIDIOC_QBUF32_TIME32: + case VIDIOC_DQBUF32_TIME32: + case VIDIOC_PREPARE_BUF32_TIME32: +#endif + case VIDIOC_QUERYBUF32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: + case VIDIOC_PREPARE_BUF32: { + struct v4l2_buffer *b64 = arg; + struct v4l2_plane *p64 = mbuf; + struct v4l2_plane32 __user *p32 = user_ptr; + + if (V4L2_TYPE_IS_MULTIPLANAR(b64->type)) { + u32 num_planes = b64->length; + + if (num_planes == 0) + return 0; + + while (num_planes--) { + err = get_v4l2_plane32(p64, p32, b64->memory); + if (err) + return err; + ++p64; + ++p32; + } + } break; } - if (err) - return err; + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: { + struct v4l2_ext_controls *ecs64 = arg; + struct v4l2_ext_control *ec64 = mbuf; + struct v4l2_ext_control32 __user *ec32 = user_ptr; + int n; + + for (n = 0; n < ecs64->count; n++) { + if (copy_from_user(ec64, ec32, sizeof(*ec32))) + return -EFAULT; - /* - * 5. Copy the data returned at the 64 bits userspace pointer to - * the original 32 bits structure. - */ - switch (cmd) { - case VIDIOC_S_INPUT32: - case VIDIOC_S_OUTPUT32: - case VIDIOC_G_INPUT32: - case VIDIOC_G_OUTPUT32: - if (assign_in_user((compat_uint_t __user *)p32, - ((unsigned int __user *)new_p64))) - err = -EFAULT; - break; + if (ctrl_is_pointer(file, ec64->id)) { + compat_uptr_t p; - case VIDIOC_G_FBUF32: - err = put_v4l2_framebuffer32(new_p64, p32); + if (get_user(p, &ec32->string)) + return -EFAULT; + ec64->string = compat_ptr(p); + } + ec32++; + ec64++; + } break; - -#ifdef CONFIG_X86_64 - case VIDIOC_DQEVENT32: - err = put_v4l2_event32(new_p64, p32); + } + default: + if (copy_from_user(mbuf, user_ptr, array_size)) + err = -EFAULT; break; + } - case VIDIOC_DQEVENT32_TIME32: - err = put_v4l2_event32_time32(new_p64, p32); - break; -#endif + return err; +} - case VIDIOC_G_EDID32: - err = put_v4l2_edid32(new_p64, p32); - break; +int v4l2_compat_put_array_args(struct file *file, void __user *user_ptr, + void *mbuf, size_t array_size, + unsigned int cmd, void *arg) +{ + int err = 0; + switch (cmd) { case VIDIOC_G_FMT32: case VIDIOC_S_FMT32: - case VIDIOC_TRY_FMT32: - err = put_v4l2_format32(new_p64, p32); - break; - - case VIDIOC_CREATE_BUFS32: - err = put_v4l2_create32(new_p64, p32); + case VIDIOC_TRY_FMT32: { + struct v4l2_format *f64 = arg; + struct v4l2_clip *c64 = mbuf; + struct v4l2_clip32 __user *c32 = user_ptr; + u32 clipcount = f64->fmt.win.clipcount; + + if ((f64->type != V4L2_BUF_TYPE_VIDEO_OVERLAY && + f64->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) || + clipcount == 0) + return 0; + if (clipcount > 2048) + return -EINVAL; + while (clipcount--) { + if (copy_to_user(c32, c64, sizeof(c64->c))) + return -EFAULT; + c64++; + c32++; + } break; - - case VIDIOC_PREPARE_BUF32: + } +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF32_TIME32: + case VIDIOC_QBUF32_TIME32: + case VIDIOC_DQBUF32_TIME32: + case VIDIOC_PREPARE_BUF32_TIME32: +#endif case VIDIOC_QUERYBUF32: case VIDIOC_QBUF32: case VIDIOC_DQBUF32: - err = put_v4l2_buffer32(new_p64, p32); + case VIDIOC_PREPARE_BUF32: { + struct v4l2_buffer *b64 = arg; + struct v4l2_plane *p64 = mbuf; + struct v4l2_plane32 __user *p32 = user_ptr; + + if (V4L2_TYPE_IS_MULTIPLANAR(b64->type)) { + u32 num_planes = b64->length; + + if (num_planes == 0) + return 0; + + while (num_planes--) { + err = put_v4l2_plane32(p64, p32, b64->memory); + if (err) + return err; + ++p64; + ++p32; + } + } break; + } + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: { + struct v4l2_ext_controls *ecs64 = arg; + struct v4l2_ext_control *ec64 = mbuf; + struct v4l2_ext_control32 __user *ec32 = user_ptr; + int n; + + for (n = 0; n < ecs64->count; n++) { + unsigned int size = sizeof(*ec32); + /* + * Do not modify the pointer when copying a pointer + * control. The contents of the pointer was changed, + * not the pointer itself. + * The structures are otherwise compatible. + */ + if (ctrl_is_pointer(file, ec64->id)) + size -= sizeof(ec32->value64); - case VIDIOC_PREPARE_BUF32_TIME32: - case VIDIOC_QUERYBUF32_TIME32: - case VIDIOC_QBUF32_TIME32: - case VIDIOC_DQBUF32_TIME32: - err = put_v4l2_buffer32_time32(new_p64, p32); - break; + if (copy_to_user(ec32, ec64, size)) + return -EFAULT; - case VIDIOC_ENUMSTD32: - err = put_v4l2_standard32(new_p64, p32); + ec32++; + ec64++; + } break; - - case VIDIOC_ENUMINPUT32: - err = put_v4l2_input32(new_p64, p32); + } + default: + if (copy_to_user(user_ptr, mbuf, array_size)) + err = -EFAULT; break; } + return err; } @@ -1787,7 +1245,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) return ret; if (_IOC_TYPE(cmd) == 'V' && _IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) - ret = do_video_ioctl(file, cmd, arg); + ret = file->f_op->unlocked_ioctl(file, cmd, + (unsigned long)compat_ptr(arg)); else if (vdev->fops->compat_ioctl32) ret = vdev->fops->compat_ioctl32(file, cmd, arg); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index bd7f330c941c..5cbe0ffbf501 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -693,9 +693,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return h264_fp_arrangement_type; case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE: return h264_fmo_map_type; - case V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE: + case V4L2_CID_STATELESS_H264_DECODE_MODE: return h264_decode_mode; - case V4L2_CID_MPEG_VIDEO_H264_START_CODE: + case V4L2_CID_STATELESS_H264_START_CODE: return h264_start_code; case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return mpeg_mpeg2_level; @@ -830,7 +830,7 @@ const char *v4l2_ctrl_get_name(u32 id) /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_MPEG_CLASS: return "Codec Controls"; + case V4L2_CID_CODEC_CLASS: return "Codec Controls"; case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; @@ -920,14 +920,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP: return "H264 I-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP: return "H264 P-Frame Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: return "H264 P-Frame Maximum QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_SPS: return "H264 Sequence Parameter Set"; - case V4L2_CID_MPEG_VIDEO_H264_PPS: return "H264 Picture Parameter Set"; - case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: return "H264 Scaling Matrix"; - case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS: return "H264 Slice Parameters"; - case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: return "H264 Decode Parameters"; - case V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE: return "H264 Decode Mode"; - case V4L2_CID_MPEG_VIDEO_H264_START_CODE: return "H264 Start Code"; - case V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS: return "H264 Prediction Weight Table"; case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return "MPEG2 Level"; case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: return "MPEG2 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; @@ -951,7 +943,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: return "Force Key Frame"; case V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS: return "MPEG-2 Slice Parameters"; case V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION: return "MPEG-2 Quantization Matrices"; - case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: return "FWHT Stateless Parameters"; case V4L2_CID_FWHT_I_FRAME_QP: return "FWHT I-Frame QP Value"; case V4L2_CID_FWHT_P_FRAME_QP: return "FWHT P-Frame QP Value"; @@ -1181,6 +1172,19 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; + + /* Stateless Codec controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ + case V4L2_CID_CODEC_STATELESS_CLASS: return "Stateless Codec Controls"; + case V4L2_CID_STATELESS_H264_DECODE_MODE: return "H264 Decode Mode"; + case V4L2_CID_STATELESS_H264_START_CODE: return "H264 Start Code"; + case V4L2_CID_STATELESS_H264_SPS: return "H264 Sequence Parameter Set"; + case V4L2_CID_STATELESS_H264_PPS: return "H264 Picture Parameter Set"; + case V4L2_CID_STATELESS_H264_SCALING_MATRIX: return "H264 Scaling Matrix"; + case V4L2_CID_STATELESS_H264_PRED_WEIGHTS: return "H264 Prediction Weight Table"; + case V4L2_CID_STATELESS_H264_SLICE_PARAMS: return "H264 Slice Parameters"; + case V4L2_CID_STATELESS_H264_DECODE_PARAMS: return "H264 Decode Parameters"; + case V4L2_CID_STATELESS_FWHT_PARAMS: return "FWHT Stateless Parameters"; default: return NULL; } @@ -1306,8 +1310,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE: - case V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE: - case V4L2_CID_MPEG_VIDEO_H264_START_CODE: case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: @@ -1338,6 +1340,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE: + case V4L2_CID_STATELESS_H264_DECODE_MODE: + case V4L2_CID_STATELESS_H264_START_CODE: case V4L2_CID_CAMERA_ORIENTATION: *type = V4L2_CTRL_TYPE_MENU; break; @@ -1358,7 +1362,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: - case V4L2_CID_MPEG_CLASS: + case V4L2_CID_CODEC_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: case V4L2_CID_JPEG_CLASS: @@ -1368,6 +1372,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FM_RX_CLASS: case V4L2_CID_RF_TUNER_CLASS: case V4L2_CID_DETECT_CLASS: + case V4L2_CID_CODEC_STATELESS_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1428,25 +1433,25 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION: *type = V4L2_CTRL_TYPE_MPEG2_QUANTIZATION; break; - case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: + case V4L2_CID_STATELESS_FWHT_PARAMS: *type = V4L2_CTRL_TYPE_FWHT_PARAMS; break; - case V4L2_CID_MPEG_VIDEO_H264_SPS: + case V4L2_CID_STATELESS_H264_SPS: *type = V4L2_CTRL_TYPE_H264_SPS; break; - case V4L2_CID_MPEG_VIDEO_H264_PPS: + case V4L2_CID_STATELESS_H264_PPS: *type = V4L2_CTRL_TYPE_H264_PPS; break; - case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: + case V4L2_CID_STATELESS_H264_SCALING_MATRIX: *type = V4L2_CTRL_TYPE_H264_SCALING_MATRIX; break; - case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS: + case V4L2_CID_STATELESS_H264_SLICE_PARAMS: *type = V4L2_CTRL_TYPE_H264_SLICE_PARAMS; break; - case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: + case V4L2_CID_STATELESS_H264_DECODE_PARAMS: *type = V4L2_CTRL_TYPE_H264_DECODE_PARAMS; break; - case V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS: + case V4L2_CID_STATELESS_H264_PRED_WEIGHTS: *type = V4L2_CTRL_TYPE_H264_PRED_WEIGHTS; break; case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: @@ -1621,6 +1626,8 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; + struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; + struct v4l2_ctrl_fwht_params *p_fwht_params; void *p = ptr.p + idx * ctrl->elem_size; if (ctrl->p_def.p_const) @@ -1643,6 +1650,16 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, p_mpeg2_slice_params->picture.picture_coding_type = V4L2_MPEG2_PICTURE_CODING_TYPE_I; break; + case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: + p_vp8_frame_header = p; + p_vp8_frame_header->num_dct_parts = 1; + break; + case V4L2_CTRL_TYPE_FWHT_PARAMS: + p_fwht_params = p; + p_fwht_params->version = V4L2_FWHT_VERSION; + p_fwht_params->width = 1280; + p_fwht_params->height = 720; + break; } } @@ -1727,6 +1744,27 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_U32: pr_cont("%u", (unsigned)*ptr.p_u32); break; + case V4L2_CTRL_TYPE_H264_SPS: + pr_cont("H264_SPS"); + break; + case V4L2_CTRL_TYPE_H264_PPS: + pr_cont("H264_PPS"); + break; + case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + pr_cont("H264_SCALING_MATRIX"); + break; + case V4L2_CTRL_TYPE_H264_SLICE_PARAMS: + pr_cont("H264_SLICE_PARAMS"); + break; + case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: + pr_cont("H264_DECODE_PARAMS"); + break; + case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS: + pr_cont("H264_PRED_WEIGHTS"); + break; + case V4L2_CTRL_TYPE_FWHT_PARAMS: + pr_cont("FWHT_PARAMS"); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1770,6 +1808,10 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; + struct v4l2_ctrl_fwht_params *p_fwht_params; + struct v4l2_ctrl_h264_sps *p_h264_sps; + struct v4l2_ctrl_h264_pps *p_h264_pps; + struct v4l2_ctrl_h264_pred_weights *p_h264_pred_weights; struct v4l2_ctrl_h264_slice_params *p_h264_slice_params; struct v4l2_ctrl_h264_decode_params *p_h264_dec_params; struct v4l2_ctrl_hevc_sps *p_hevc_sps; @@ -1826,23 +1868,159 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, break; case V4L2_CTRL_TYPE_FWHT_PARAMS: + p_fwht_params = p; + if (p_fwht_params->version < V4L2_FWHT_VERSION) + return -EINVAL; + if (!p_fwht_params->width || !p_fwht_params->height) + return -EINVAL; break; case V4L2_CTRL_TYPE_H264_SPS: + p_h264_sps = p; + + /* Some syntax elements are only conditionally valid */ + if (p_h264_sps->pic_order_cnt_type != 0) { + p_h264_sps->log2_max_pic_order_cnt_lsb_minus4 = 0; + } else if (p_h264_sps->pic_order_cnt_type != 1) { + p_h264_sps->num_ref_frames_in_pic_order_cnt_cycle = 0; + p_h264_sps->offset_for_non_ref_pic = 0; + p_h264_sps->offset_for_top_to_bottom_field = 0; + memset(&p_h264_sps->offset_for_ref_frame, 0, + sizeof(p_h264_sps->offset_for_ref_frame)); + } + + if (!V4L2_H264_SPS_HAS_CHROMA_FORMAT(p_h264_sps)) { + p_h264_sps->chroma_format_idc = 1; + p_h264_sps->bit_depth_luma_minus8 = 0; + p_h264_sps->bit_depth_chroma_minus8 = 0; + + p_h264_sps->flags &= + ~V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS; + + if (p_h264_sps->chroma_format_idc < 3) + p_h264_sps->flags &= + ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE; + } + + if (p_h264_sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) + p_h264_sps->flags &= + ~V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD; + + /* + * Chroma 4:2:2 format require at least High 4:2:2 profile. + * + * The H264 specification and well-known parser implementations + * use profile-idc values directly, as that is clearer and + * less ambiguous. We do the same here. + */ + if (p_h264_sps->profile_idc < 122 && + p_h264_sps->chroma_format_idc > 1) + return -EINVAL; + /* Chroma 4:4:4 format require at least High 4:2:2 profile */ + if (p_h264_sps->profile_idc < 244 && + p_h264_sps->chroma_format_idc > 2) + return -EINVAL; + if (p_h264_sps->chroma_format_idc > 3) + return -EINVAL; + + if (p_h264_sps->bit_depth_luma_minus8 > 6) + return -EINVAL; + if (p_h264_sps->bit_depth_chroma_minus8 > 6) + return -EINVAL; + if (p_h264_sps->log2_max_frame_num_minus4 > 12) + return -EINVAL; + if (p_h264_sps->pic_order_cnt_type > 2) + return -EINVAL; + if (p_h264_sps->log2_max_pic_order_cnt_lsb_minus4 > 12) + return -EINVAL; + if (p_h264_sps->max_num_ref_frames > V4L2_H264_REF_LIST_LEN) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_H264_PPS: + p_h264_pps = p; + + if (p_h264_pps->num_slice_groups_minus1 > 7) + return -EINVAL; + if (p_h264_pps->num_ref_idx_l0_default_active_minus1 > + (V4L2_H264_REF_LIST_LEN - 1)) + return -EINVAL; + if (p_h264_pps->num_ref_idx_l1_default_active_minus1 > + (V4L2_H264_REF_LIST_LEN - 1)) + return -EINVAL; + if (p_h264_pps->weighted_bipred_idc > 2) + return -EINVAL; + /* + * pic_init_qp_minus26 shall be in the range of + * -(26 + QpBdOffset_y) to +25, inclusive, + * where QpBdOffset_y is 6 * bit_depth_luma_minus8 + */ + if (p_h264_pps->pic_init_qp_minus26 < -62 || + p_h264_pps->pic_init_qp_minus26 > 25) + return -EINVAL; + if (p_h264_pps->pic_init_qs_minus26 < -26 || + p_h264_pps->pic_init_qs_minus26 > 25) + return -EINVAL; + if (p_h264_pps->chroma_qp_index_offset < -12 || + p_h264_pps->chroma_qp_index_offset > 12) + return -EINVAL; + if (p_h264_pps->second_chroma_qp_index_offset < -12 || + p_h264_pps->second_chroma_qp_index_offset > 12) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + break; + case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS: + p_h264_pred_weights = p; + + if (p_h264_pred_weights->luma_log2_weight_denom > 7) + return -EINVAL; + if (p_h264_pred_weights->chroma_log2_weight_denom > 7) + return -EINVAL; break; case V4L2_CTRL_TYPE_H264_SLICE_PARAMS: p_h264_slice_params = p; + if (p_h264_slice_params->slice_type != V4L2_H264_SLICE_TYPE_B) + p_h264_slice_params->flags &= + ~V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED; + + if (p_h264_slice_params->colour_plane_id > 2) + return -EINVAL; + if (p_h264_slice_params->cabac_init_idc > 2) + return -EINVAL; + if (p_h264_slice_params->disable_deblocking_filter_idc > 2) + return -EINVAL; + if (p_h264_slice_params->slice_alpha_c0_offset_div2 < -6 || + p_h264_slice_params->slice_alpha_c0_offset_div2 > 6) + return -EINVAL; + if (p_h264_slice_params->slice_beta_offset_div2 < -6 || + p_h264_slice_params->slice_beta_offset_div2 > 6) + return -EINVAL; + + if (p_h264_slice_params->slice_type == V4L2_H264_SLICE_TYPE_I || + p_h264_slice_params->slice_type == V4L2_H264_SLICE_TYPE_SI) + p_h264_slice_params->num_ref_idx_l0_active_minus1 = 0; + if (p_h264_slice_params->slice_type != V4L2_H264_SLICE_TYPE_B) + p_h264_slice_params->num_ref_idx_l1_active_minus1 = 0; + + if (p_h264_slice_params->num_ref_idx_l0_active_minus1 > + (V4L2_H264_REF_LIST_LEN - 1)) + return -EINVAL; + if (p_h264_slice_params->num_ref_idx_l1_active_minus1 > + (V4L2_H264_REF_LIST_LEN - 1)) + return -EINVAL; zero_reserved(*p_h264_slice_params); break; case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: p_h264_dec_params = p; + if (p_h264_dec_params->nal_ref_idc > 3) + return -EINVAL; for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { struct v4l2_h264_dpb_entry *dpb_entry = &p_h264_dec_params->dpb[i]; @@ -3363,9 +3541,6 @@ static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl, /* Skip refs inherited from other devices */ if (ref->from_other_dev) continue; - /* And buttons */ - if (ctrl->type == V4L2_CTRL_TYPE_BUTTON) - continue; err = handler_new_ref(hdl, ctrl, &new_ref, false, true); if (err) break; @@ -4110,8 +4285,13 @@ static int try_set_ext_ctrls_common(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl = helpers[idx].ref->ctrl; ret = user_to_new(cs->controls + idx, ctrl); - if (!ret && ctrl->is_ptr) + if (!ret && ctrl->is_ptr) { ret = validate_new(ctrl, ctrl->p_new); + if (ret) + dprintk(vdev, + "failed to validate control %s (%d)\n", + v4l2_ctrl_get_name(ctrl->id), ret); + } idx = helpers[idx].next; } while (!ret && idx); @@ -4447,8 +4627,7 @@ int v4l2_ctrl_request_setup(struct media_request *req, * Skip if this control was already handled by a cluster. * Skip button controls and read-only controls. */ - if (ref->req_done || ctrl->type == V4L2_CTRL_TYPE_BUTTON || - (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) + if (ref->req_done || (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) continue; v4l2_ctrl_lock(master); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index de4287251a89..d2e58ae91f9b 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -218,13 +218,14 @@ int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev, vdev->ctrl_handler = sd->ctrl_handler; if (read_only) set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags); + sd->devnode = vdev; err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, sd->owner); if (err < 0) { + sd->devnode = NULL; kfree(vdev); goto clean_up; } - sd->devnode = vdev; #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.info.dev.major = VIDEO_MAJOR; sd->entity.info.dev.minor = vdev->minor; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index d7bbe33840cb..5353e37eb950 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -93,7 +93,7 @@ v4l2_fwnode_bus_type_to_mbus(enum v4l2_fwnode_bus_type type) const struct v4l2_fwnode_bus_conv *conv = get_v4l2_fwnode_bus_conv_by_fwnode_bus(type); - return conv ? conv->mbus_type : V4L2_MBUS_UNKNOWN; + return conv ? conv->mbus_type : V4L2_MBUS_INVALID; } static const char * @@ -416,26 +416,18 @@ static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, enum v4l2_mbus_type mbus_type; int rval; - if (vep->bus_type == V4L2_MBUS_UNKNOWN) { - /* Zero fields from bus union to until the end */ - memset(&vep->bus, 0, - sizeof(*vep) - offsetof(typeof(*vep), bus)); - } - pr_debug("===== begin parsing endpoint %pfw\n", fwnode); - /* - * Zero the fwnode graph endpoint memory in case we don't end up parsing - * the endpoint. - */ - memset(&vep->base, 0, sizeof(vep->base)); - fwnode_property_read_u32(fwnode, "bus-type", &bus_type); pr_debug("fwnode video bus type %s (%u), mbus type %s (%u)\n", v4l2_fwnode_bus_type_to_string(bus_type), bus_type, v4l2_fwnode_mbus_type_to_string(vep->bus_type), vep->bus_type); mbus_type = v4l2_fwnode_bus_type_to_mbus(bus_type); + if (mbus_type == V4L2_MBUS_INVALID) { + pr_debug("unsupported bus type %u\n", bus_type); + return -EINVAL; + } if (vep->bus_type != V4L2_MBUS_UNKNOWN) { if (mbus_type != V4L2_MBUS_UNKNOWN && @@ -919,20 +911,6 @@ v4l2_async_notifier_parse_fwnode_endpoints(struct device *dev, } EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints); -int -v4l2_async_notifier_parse_fwnode_endpoints_by_port(struct device *dev, - struct v4l2_async_notifier *notifier, - size_t asd_struct_size, - unsigned int port, - parse_endpoint_func parse_endpoint) -{ - return __v4l2_async_notifier_parse_fwnode_ep(dev, notifier, - asd_struct_size, - port, true, - parse_endpoint); -} -EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port); - /* * v4l2_fwnode_reference_parse - parse references for async sub-devices * @dev: the device node the properties of which are parsed for references diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index eeff398fbdcc..3198abdd538c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -8,6 +8,7 @@ * Mauro Carvalho Chehab <mchehab@kernel.org> (version 2) */ +#include <linux/compat.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/slab.h> @@ -1402,6 +1403,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; + case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break; + case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break; default: /* Compressed formats */ @@ -1581,7 +1584,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { - struct v4l2_clip __user *clips = p->fmt.win.clips; + struct v4l2_clip *clips = p->fmt.win.clips; u32 clipcount = p->fmt.win.clipcount; void __user *bitmap = p->fmt.win.bitmap; @@ -3083,6 +3086,27 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, } break; } + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: { + struct v4l2_format *fmt = parg; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_OVERLAY && + fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) + break; + if (fmt->fmt.win.clipcount > 2048) + return -EINVAL; + if (!fmt->fmt.win.clipcount) + break; + + *user_ptr = (void __user *)fmt->fmt.win.clips; + *kernel_ptr = (void **)&fmt->fmt.win.clips; + *array_size = sizeof(struct v4l2_clip) + * fmt->fmt.win.clipcount; + + ret = 1; + break; + } } return ret; @@ -3104,14 +3128,18 @@ static unsigned int video_translate_cmd(unsigned int cmd) return VIDIOC_PREPARE_BUF; #endif } + if (in_compat_syscall()) + return v4l2_compat_translate_cmd(cmd); return cmd; } -static int video_get_user(void __user *arg, void *parg, unsigned int cmd, +static int video_get_user(void __user *arg, void *parg, + unsigned int real_cmd, unsigned int cmd, bool *always_copy) { - unsigned int n = _IOC_SIZE(cmd); + unsigned int n = _IOC_SIZE(real_cmd); + int err = 0; if (!(_IOC_DIR(cmd) & _IOC_WRITE)) { /* read-only ioctl */ @@ -3119,73 +3147,82 @@ static int video_get_user(void __user *arg, void *parg, unsigned int cmd, return 0; } - switch (cmd) { -#ifdef CONFIG_COMPAT_32BIT_TIME - case VIDIOC_QUERYBUF_TIME32: - case VIDIOC_QBUF_TIME32: - case VIDIOC_DQBUF_TIME32: - case VIDIOC_PREPARE_BUF_TIME32: { - struct v4l2_buffer_time32 vb32; - struct v4l2_buffer *vb = parg; - - if (copy_from_user(&vb32, arg, sizeof(vb32))) - return -EFAULT; - - *vb = (struct v4l2_buffer) { - .index = vb32.index, - .type = vb32.type, - .bytesused = vb32.bytesused, - .flags = vb32.flags, - .field = vb32.field, - .timestamp.tv_sec = vb32.timestamp.tv_sec, - .timestamp.tv_usec = vb32.timestamp.tv_usec, - .timecode = vb32.timecode, - .sequence = vb32.sequence, - .memory = vb32.memory, - .m.userptr = vb32.m.userptr, - .length = vb32.length, - .request_fd = vb32.request_fd, - }; - - if (cmd == VIDIOC_QUERYBUF_TIME32) - vb->request_fd = 0; + /* + * In some cases, only a few fields are used as input, + * i.e. when the app sets "index" and then the driver + * fills in the rest of the structure for the thing + * with that index. We only need to copy up the first + * non-input field. + */ + if (v4l2_is_known_ioctl(real_cmd)) { + u32 flags = v4l2_ioctls[_IOC_NR(real_cmd)].flags; - break; + if (flags & INFO_FL_CLEAR_MASK) + n = (flags & INFO_FL_CLEAR_MASK) >> 16; + *always_copy = flags & INFO_FL_ALWAYS_COPY; } -#endif - default: - /* - * In some cases, only a few fields are used as input, - * i.e. when the app sets "index" and then the driver - * fills in the rest of the structure for the thing - * with that index. We only need to copy up the first - * non-input field. - */ - if (v4l2_is_known_ioctl(cmd)) { - u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; - - if (flags & INFO_FL_CLEAR_MASK) - n = (flags & INFO_FL_CLEAR_MASK) >> 16; - *always_copy = flags & INFO_FL_ALWAYS_COPY; - } + if (cmd == real_cmd) { if (copy_from_user(parg, (void __user *)arg, n)) - return -EFAULT; - - /* zero out anything we don't copy from userspace */ - if (n < _IOC_SIZE(cmd)) - memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); - break; + err = -EFAULT; + } else if (in_compat_syscall()) { + err = v4l2_compat_get_user(arg, parg, cmd); + } else { + switch (cmd) { +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF_TIME32: + case VIDIOC_QBUF_TIME32: + case VIDIOC_DQBUF_TIME32: + case VIDIOC_PREPARE_BUF_TIME32: { + struct v4l2_buffer_time32 vb32; + struct v4l2_buffer *vb = parg; + + if (copy_from_user(&vb32, arg, sizeof(vb32))) + return -EFAULT; + + *vb = (struct v4l2_buffer) { + .index = vb32.index, + .type = vb32.type, + .bytesused = vb32.bytesused, + .flags = vb32.flags, + .field = vb32.field, + .timestamp.tv_sec = vb32.timestamp.tv_sec, + .timestamp.tv_usec = vb32.timestamp.tv_usec, + .timecode = vb32.timecode, + .sequence = vb32.sequence, + .memory = vb32.memory, + .m.userptr = vb32.m.userptr, + .length = vb32.length, + .request_fd = vb32.request_fd, + }; + break; + } +#endif + } } - return 0; + /* zero out anything we don't copy from userspace */ + if (!err && n < _IOC_SIZE(real_cmd)) + memset((u8 *)parg + n, 0, _IOC_SIZE(real_cmd) - n); + return err; } -static int video_put_user(void __user *arg, void *parg, unsigned int cmd) +static int video_put_user(void __user *arg, void *parg, + unsigned int real_cmd, unsigned int cmd) { if (!(_IOC_DIR(cmd) & _IOC_READ)) return 0; + if (cmd == real_cmd) { + /* Copy results into user buffer */ + if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) + return -EFAULT; + return 0; + } + + if (in_compat_syscall()) + return v4l2_compat_put_user(arg, parg, cmd); + switch (cmd) { #ifdef CONFIG_COMPAT_32BIT_TIME case VIDIOC_DQEVENT_TIME32: { @@ -3236,11 +3273,6 @@ static int video_put_user(void __user *arg, void *parg, unsigned int cmd) break; } #endif - default: - /* Copy results into user buffer */ - if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) - return -EFAULT; - break; } return 0; @@ -3274,8 +3306,8 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, parg = mbuf; } - err = video_get_user((void __user *)arg, parg, orig_cmd, - &always_copy); + err = video_get_user((void __user *)arg, parg, cmd, + orig_cmd, &always_copy); if (err) goto out; } @@ -3297,7 +3329,14 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, if (NULL == mbuf) goto out_array_args; err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, array_size)) + if (in_compat_syscall()) + err = v4l2_compat_get_array_args(file, mbuf, user_ptr, + array_size, orig_cmd, + parg); + else + err = copy_from_user(mbuf, user_ptr, array_size) ? + -EFAULT : 0; + if (err) goto out_array_args; *kernel_ptr = mbuf; } @@ -3318,8 +3357,17 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, if (has_array_args) { *kernel_ptr = (void __force *)user_ptr; - if (copy_to_user(user_ptr, mbuf, array_size)) + if (in_compat_syscall()) { + int put_err; + + put_err = v4l2_compat_put_array_args(file, user_ptr, mbuf, + array_size, orig_cmd, + parg); + if (put_err) + err = put_err; + } else if (copy_to_user(user_ptr, mbuf, array_size)) { err = -EFAULT; + } goto out_array_args; } /* @@ -3330,7 +3378,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, goto out; out_array_args: - if (video_put_user((void __user *)arg, parg, orig_cmd)) + if (video_put_user((void __user *)arg, parg, cmd, orig_cmd)) err = -EFAULT; out: kvfree(mbuf); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index a7d508e74d6b..956dafab43d4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -792,21 +792,55 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { + bool pass = true; + /* The width, height and code must match. */ - if (source_fmt->format.width != sink_fmt->format.width - || source_fmt->format.height != sink_fmt->format.height - || source_fmt->format.code != sink_fmt->format.code) - return -EPIPE; + if (source_fmt->format.width != sink_fmt->format.width) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: width does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.width, sink_fmt->format.width); + pass = false; + } + + if (source_fmt->format.height != sink_fmt->format.height) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: height does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.height, sink_fmt->format.height); + pass = false; + } + + if (source_fmt->format.code != sink_fmt->format.code) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: media bus code does not match (source 0x%8.8x, sink 0x%8.8x)\n", + __func__, + source_fmt->format.code, sink_fmt->format.code); + pass = false; + } /* The field order must match, or the sink field order must be NONE * to support interlaced hardware connected to bridges that support * progressive formats only. */ if (source_fmt->format.field != sink_fmt->format.field && - sink_fmt->format.field != V4L2_FIELD_NONE) - return -EPIPE; + sink_fmt->format.field != V4L2_FIELD_NONE) { + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: field does not match (source %u, sink %u)\n", + __func__, + source_fmt->format.field, sink_fmt->format.field); + pass = false; + } - return 0; + if (pass) + return 0; + + dev_dbg(sd->entity.graph_obj.mdev->dev, + "%s: link was \"%s\":%u -> \"%s\":%u\n", __func__, + link->source->entity->name, link->source->index, + link->sink->entity->name, link->sink->index); + + return -EPIPE; } EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c index 072299b14b8d..47d9268a7e3c 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c @@ -51,7 +51,8 @@ #include <crypto/aes.h> #include <crypto/algapi.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/authenc.h> #include <crypto/internal/aead.h> #include <crypto/null.h> diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h index 2d3dfdd2a716..65617752c630 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h @@ -9,7 +9,8 @@ #include <crypto/aes.h> #include <crypto/algapi.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/authenc.h> #include <crypto/ctr.h> #include <crypto/gf128mul.h> diff --git a/drivers/nfc/s3fwrn5/firmware.c b/drivers/nfc/s3fwrn5/firmware.c index ec930ee2c847..5d5ad8307211 100644 --- a/drivers/nfc/s3fwrn5/firmware.c +++ b/drivers/nfc/s3fwrn5/firmware.c @@ -9,7 +9,7 @@ #include <linux/completion.h> #include <linux/firmware.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include "s3fwrn5.h" #include "firmware.h" diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0d91d136bc3b..ba34153571b8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -78,6 +78,17 @@ config HUAWEI_WMI To compile this driver as a module, choose M here: the module will be called huawei-wmi. +config UV_SYSFS + tristate "Sysfs structure for UV systems" + depends on X86_UV + depends on SYSFS + help + This driver supports a sysfs tree describing information about + UV systems at /sys/firmware/sgi_uv/. + + To compile this driver as a module, choose M here: the module will + be called uv_sysfs. + config INTEL_WMI_SBL_FW_UPDATE tristate "Intel WMI Slim Bootloader firmware update signaling driver" depends on ACPI_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5f823f7eff45..a34875d833dd 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -62,6 +62,9 @@ obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o +# Hewlett Packard Enterprise +obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o + # IBM Thinkpad and Lenovo obj-$(CONFIG_IBM_RTL) += ibm_rtl.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o diff --git a/drivers/platform/x86/uv_sysfs.c b/drivers/platform/x86/uv_sysfs.c new file mode 100644 index 000000000000..7badcfa3f384 --- /dev/null +++ b/drivers/platform/x86/uv_sysfs.c @@ -0,0 +1,929 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file supports the /sys/firmware/sgi_uv topology tree on HPE UV. + * + * Copyright (c) 2020 Hewlett Packard Enterprise. All Rights Reserved. + * Copyright (c) Justin Ernst + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kobject.h> +#include <asm/uv/bios.h> +#include <asm/uv/uv.h> +#include <asm/uv/uv_hub.h> +#include <asm/uv/uv_geo.h> + +#define INVALID_CNODE -1 + +struct kobject *sgi_uv_kobj; +static struct kset *uv_pcibus_kset; +static struct kset *uv_hubs_kset; +static struct uv_bios_hub_info *hub_buf; +static struct uv_bios_port_info **port_buf; +static struct uv_hub **uv_hubs; +static struct uv_pci_top_obj **uv_pci_objs; +static int num_pci_lines; +static int num_cnodes; +static int *prev_obj_to_cnode; +static int uv_bios_obj_cnt; +static signed short uv_master_nasid = -1; +static void *uv_biosheap; + +static const char *uv_type_string(void) +{ + if (is_uv5_hub()) + return "9.0"; + else if (is_uv4a_hub()) + return "7.1"; + else if (is_uv4_hub()) + return "7.0"; + else if (is_uv3_hub()) + return "5.0"; + else if (is_uv2_hub()) + return "3.0"; + else if (uv_get_hubless_system()) + return "0.1"; + else + return "unknown"; +} + +static int ordinal_to_nasid(int ordinal) +{ + if (ordinal < num_cnodes && ordinal >= 0) + return UV_PNODE_TO_NASID(uv_blade_to_pnode(ordinal)); + else + return -1; +} + +static union geoid_u cnode_to_geoid(int cnode) +{ + union geoid_u geoid; + + uv_bios_get_geoinfo(ordinal_to_nasid(cnode), (u64)sizeof(union geoid_u), (u64 *)&geoid); + return geoid; +} + +static int location_to_bpos(char *location, int *rack, int *slot, int *blade) +{ + char type, r, b, h; + int idb, idh; + + if (sscanf(location, "%c%03d%c%02d%c%2d%c%d", + &r, rack, &type, slot, &b, &idb, &h, &idh) != 8) + return -1; + *blade = idb * 2 + idh; + + return 0; +} + +static int cache_obj_to_cnode(struct uv_bios_hub_info *obj) +{ + int cnode; + union geoid_u geoid; + int obj_rack, obj_slot, obj_blade; + int rack, slot, blade; + + if (!obj->f.fields.this_part && !obj->f.fields.is_shared) + return 0; + + if (location_to_bpos(obj->location, &obj_rack, &obj_slot, &obj_blade)) + return -1; + + for (cnode = 0; cnode < num_cnodes; cnode++) { + geoid = cnode_to_geoid(cnode); + rack = geo_rack(geoid); + slot = geo_slot(geoid); + blade = geo_blade(geoid); + if (obj_rack == rack && obj_slot == slot && obj_blade == blade) + prev_obj_to_cnode[obj->id] = cnode; + } + + return 0; +} + +static int get_obj_to_cnode(int obj_id) +{ + return prev_obj_to_cnode[obj_id]; +} + +struct uv_hub { + struct kobject kobj; + struct uv_bios_hub_info *hub_info; + struct uv_port **ports; +}; + +#define to_uv_hub(kobj_ptr) container_of(kobj_ptr, struct uv_hub, kobj) + +static ssize_t hub_name_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->name); +} + +static ssize_t hub_location_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", hub_info->location); +} + +static ssize_t hub_partition_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + return sprintf(buf, "%d\n", hub_info->f.fields.this_part); +} + +static ssize_t hub_shared_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + return sprintf(buf, "%d\n", hub_info->f.fields.is_shared); +} +static ssize_t hub_nasid_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + int cnode = get_obj_to_cnode(hub_info->id); + + return sprintf(buf, "%d\n", ordinal_to_nasid(cnode)); +} +static ssize_t hub_cnode_show(struct uv_bios_hub_info *hub_info, char *buf) +{ + return sprintf(buf, "%d\n", get_obj_to_cnode(hub_info->id)); +} + +struct hub_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct uv_bios_hub_info *hub_info, char *buf); + ssize_t (*store)(struct uv_bios_hub_info *hub_info, const char *buf, size_t sz); +}; + +static struct hub_sysfs_entry name_attribute = + __ATTR(name, 0444, hub_name_show, NULL); +static struct hub_sysfs_entry location_attribute = + __ATTR(location, 0444, hub_location_show, NULL); +static struct hub_sysfs_entry partition_attribute = + __ATTR(this_partition, 0444, hub_partition_show, NULL); +static struct hub_sysfs_entry shared_attribute = + __ATTR(shared, 0444, hub_shared_show, NULL); +static struct hub_sysfs_entry nasid_attribute = + __ATTR(nasid, 0444, hub_nasid_show, NULL); +static struct hub_sysfs_entry cnode_attribute = + __ATTR(cnode, 0444, hub_cnode_show, NULL); + +static struct attribute *uv_hub_attrs[] = { + &name_attribute.attr, + &location_attribute.attr, + &partition_attribute.attr, + &shared_attribute.attr, + &nasid_attribute.attr, + &cnode_attribute.attr, + NULL, +}; + +static void hub_release(struct kobject *kobj) +{ + struct uv_hub *hub = to_uv_hub(kobj); + + kfree(hub); +} + +static ssize_t hub_type_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uv_hub *hub = to_uv_hub(kobj); + struct uv_bios_hub_info *bios_hub_info = hub->hub_info; + struct hub_sysfs_entry *entry; + + entry = container_of(attr, struct hub_sysfs_entry, attr); + + if (!entry->show) + return -EIO; + + return entry->show(bios_hub_info, buf); +} + +static const struct sysfs_ops hub_sysfs_ops = { + .show = hub_type_show, +}; + +static struct kobj_type hub_attr_type = { + .release = hub_release, + .sysfs_ops = &hub_sysfs_ops, + .default_attrs = uv_hub_attrs, +}; + +static int uv_hubs_init(void) +{ + s64 biosr; + u64 sz; + int i, ret; + + prev_obj_to_cnode = kmalloc_array(uv_bios_obj_cnt, sizeof(*prev_obj_to_cnode), + GFP_KERNEL); + if (!prev_obj_to_cnode) + return -ENOMEM; + + for (i = 0; i < uv_bios_obj_cnt; i++) + prev_obj_to_cnode[i] = INVALID_CNODE; + + uv_hubs_kset = kset_create_and_add("hubs", NULL, sgi_uv_kobj); + if (!uv_hubs_kset) { + ret = -ENOMEM; + goto err_hubs_kset; + } + sz = uv_bios_obj_cnt * sizeof(*hub_buf); + hub_buf = kzalloc(sz, GFP_KERNEL); + if (!hub_buf) { + ret = -ENOMEM; + goto err_hub_buf; + } + + biosr = uv_bios_enum_objs((u64)uv_master_nasid, sz, (u64 *)hub_buf); + if (biosr) { + ret = -EINVAL; + goto err_enum_objs; + } + + uv_hubs = kcalloc(uv_bios_obj_cnt, sizeof(*uv_hubs), GFP_KERNEL); + if (!uv_hubs) { + ret = -ENOMEM; + goto err_enum_objs; + } + + for (i = 0; i < uv_bios_obj_cnt; i++) { + uv_hubs[i] = kzalloc(sizeof(*uv_hubs[i]), GFP_KERNEL); + if (!uv_hubs[i]) { + i--; + ret = -ENOMEM; + goto err_hubs; + } + + uv_hubs[i]->hub_info = &hub_buf[i]; + cache_obj_to_cnode(uv_hubs[i]->hub_info); + + uv_hubs[i]->kobj.kset = uv_hubs_kset; + + ret = kobject_init_and_add(&uv_hubs[i]->kobj, &hub_attr_type, + NULL, "hub_%u", hub_buf[i].id); + if (ret) + goto err_hubs; + kobject_uevent(&uv_hubs[i]->kobj, KOBJ_ADD); + } + return 0; + +err_hubs: + for (; i >= 0; i--) + kobject_put(&uv_hubs[i]->kobj); + kfree(uv_hubs); +err_enum_objs: + kfree(hub_buf); +err_hub_buf: + kset_unregister(uv_hubs_kset); +err_hubs_kset: + kfree(prev_obj_to_cnode); + return ret; + +} + +static void uv_hubs_exit(void) +{ + int i; + + for (i = 0; i < uv_bios_obj_cnt; i++) + kobject_put(&uv_hubs[i]->kobj); + + kfree(uv_hubs); + kfree(hub_buf); + kset_unregister(uv_hubs_kset); + kfree(prev_obj_to_cnode); +} + +struct uv_port { + struct kobject kobj; + struct uv_bios_port_info *port_info; +}; + +#define to_uv_port(kobj_ptr) container_of(kobj_ptr, struct uv_port, kobj) + +static ssize_t uv_port_conn_hub_show(struct uv_bios_port_info *port, char *buf) +{ + return sprintf(buf, "%d\n", port->conn_id); +} + +static ssize_t uv_port_conn_port_show(struct uv_bios_port_info *port, char *buf) +{ + return sprintf(buf, "%d\n", port->conn_port); +} + +struct uv_port_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct uv_bios_port_info *port_info, char *buf); + ssize_t (*store)(struct uv_bios_port_info *port_info, const char *buf, size_t size); +}; + +static struct uv_port_sysfs_entry uv_port_conn_hub_attribute = + __ATTR(conn_hub, 0444, uv_port_conn_hub_show, NULL); +static struct uv_port_sysfs_entry uv_port_conn_port_attribute = + __ATTR(conn_port, 0444, uv_port_conn_port_show, NULL); + +static struct attribute *uv_port_attrs[] = { + &uv_port_conn_hub_attribute.attr, + &uv_port_conn_port_attribute.attr, + NULL, +}; + +static void uv_port_release(struct kobject *kobj) +{ + struct uv_port *port = to_uv_port(kobj); + + kfree(port); +} + +static ssize_t uv_port_type_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uv_port *port = to_uv_port(kobj); + struct uv_bios_port_info *port_info = port->port_info; + struct uv_port_sysfs_entry *entry; + + entry = container_of(attr, struct uv_port_sysfs_entry, attr); + + if (!entry->show) + return -EIO; + + return entry->show(port_info, buf); +} + +static const struct sysfs_ops uv_port_sysfs_ops = { + .show = uv_port_type_show, +}; + +static struct kobj_type uv_port_attr_type = { + .release = uv_port_release, + .sysfs_ops = &uv_port_sysfs_ops, + .default_attrs = uv_port_attrs, +}; + +static int uv_ports_init(void) +{ + s64 biosr; + int j = 0, k = 0, ret, sz; + + port_buf = kcalloc(uv_bios_obj_cnt, sizeof(*port_buf), GFP_KERNEL); + if (!port_buf) + return -ENOMEM; + + for (j = 0; j < uv_bios_obj_cnt; j++) { + sz = hub_buf[j].ports * sizeof(*port_buf[j]); + port_buf[j] = kzalloc(sz, GFP_KERNEL); + if (!port_buf[j]) { + ret = -ENOMEM; + j--; + goto err_port_info; + } + biosr = uv_bios_enum_ports((u64)uv_master_nasid, (u64)hub_buf[j].id, sz, + (u64 *)port_buf[j]); + if (biosr) { + ret = -EINVAL; + goto err_port_info; + } + } + for (j = 0; j < uv_bios_obj_cnt; j++) { + uv_hubs[j]->ports = kcalloc(hub_buf[j].ports, + sizeof(*uv_hubs[j]->ports), GFP_KERNEL); + if (!uv_hubs[j]->ports) { + ret = -ENOMEM; + j--; + goto err_ports; + } + } + for (j = 0; j < uv_bios_obj_cnt; j++) { + for (k = 0; k < hub_buf[j].ports; k++) { + uv_hubs[j]->ports[k] = kzalloc(sizeof(*uv_hubs[j]->ports[k]), GFP_KERNEL); + if (!uv_hubs[j]->ports[k]) { + ret = -ENOMEM; + k--; + goto err_kobj_ports; + } + uv_hubs[j]->ports[k]->port_info = &port_buf[j][k]; + ret = kobject_init_and_add(&uv_hubs[j]->ports[k]->kobj, &uv_port_attr_type, + &uv_hubs[j]->kobj, "port_%d", port_buf[j][k].port); + if (ret) + goto err_kobj_ports; + kobject_uevent(&uv_hubs[j]->ports[k]->kobj, KOBJ_ADD); + } + } + return 0; + +err_kobj_ports: + for (; j >= 0; j--) { + for (; k >= 0; k--) + kobject_put(&uv_hubs[j]->ports[k]->kobj); + if (j > 0) + k = hub_buf[j-1].ports - 1; + } + j = uv_bios_obj_cnt - 1; +err_ports: + for (; j >= 0; j--) + kfree(uv_hubs[j]->ports); + j = uv_bios_obj_cnt - 1; +err_port_info: + for (; j >= 0; j--) + kfree(port_buf[j]); + kfree(port_buf); + return ret; +} + +static void uv_ports_exit(void) +{ + int j, k; + + for (j = 0; j < uv_bios_obj_cnt; j++) { + for (k = hub_buf[j].ports - 1; k >= 0; k--) + kobject_put(&uv_hubs[j]->ports[k]->kobj); + } + for (j = 0; j < uv_bios_obj_cnt; j++) { + kfree(uv_hubs[j]->ports); + kfree(port_buf[j]); + } + kfree(port_buf); +} + +struct uv_pci_top_obj { + struct kobject kobj; + char *type; + char *location; + int iio_stack; + char *ppb_addr; + int slot; +}; + +#define to_uv_pci_top_obj(kobj_ptr) container_of(kobj_ptr, struct uv_pci_top_obj, kobj) + +static ssize_t uv_pci_type_show(struct uv_pci_top_obj *top_obj, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->type); +} + +static ssize_t uv_pci_location_show(struct uv_pci_top_obj *top_obj, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->location); +} + +static ssize_t uv_pci_iio_stack_show(struct uv_pci_top_obj *top_obj, char *buf) +{ + return sprintf(buf, "%d\n", top_obj->iio_stack); +} + +static ssize_t uv_pci_ppb_addr_show(struct uv_pci_top_obj *top_obj, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", top_obj->ppb_addr); +} + +static ssize_t uv_pci_slot_show(struct uv_pci_top_obj *top_obj, char *buf) +{ + return sprintf(buf, "%d\n", top_obj->slot); +} + +struct uv_pci_top_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct uv_pci_top_obj *top_obj, char *buf); + ssize_t (*store)(struct uv_pci_top_obj *top_obj, const char *buf, size_t size); +}; + +static struct uv_pci_top_sysfs_entry uv_pci_type_attribute = + __ATTR(type, 0444, uv_pci_type_show, NULL); +static struct uv_pci_top_sysfs_entry uv_pci_location_attribute = + __ATTR(location, 0444, uv_pci_location_show, NULL); +static struct uv_pci_top_sysfs_entry uv_pci_iio_stack_attribute = + __ATTR(iio_stack, 0444, uv_pci_iio_stack_show, NULL); +static struct uv_pci_top_sysfs_entry uv_pci_ppb_addr_attribute = + __ATTR(ppb_addr, 0444, uv_pci_ppb_addr_show, NULL); +static struct uv_pci_top_sysfs_entry uv_pci_slot_attribute = + __ATTR(slot, 0444, uv_pci_slot_show, NULL); + +static void uv_pci_top_release(struct kobject *kobj) +{ + struct uv_pci_top_obj *top_obj = to_uv_pci_top_obj(kobj); + + kfree(top_obj->type); + kfree(top_obj->location); + kfree(top_obj->ppb_addr); + kfree(top_obj); +} + +static ssize_t pci_top_type_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct uv_pci_top_obj *top_obj = to_uv_pci_top_obj(kobj); + struct uv_pci_top_sysfs_entry *entry; + + entry = container_of(attr, struct uv_pci_top_sysfs_entry, attr); + + if (!entry->show) + return -EIO; + + return entry->show(top_obj, buf); +} + +static const struct sysfs_ops uv_pci_top_sysfs_ops = { + .show = pci_top_type_show, +}; + +static struct kobj_type uv_pci_top_attr_type = { + .release = uv_pci_top_release, + .sysfs_ops = &uv_pci_top_sysfs_ops, +}; + +static int init_pci_top_obj(struct uv_pci_top_obj *top_obj, char *line) +{ + char *start; + char type[11], location[14], ppb_addr[15]; + int str_cnt, ret; + unsigned int tmp_match[2]; + + // Minimum line length + if (strlen(line) < 36) + return -EINVAL; + + //Line must match format "pcibus %4x:%2x" to be valid + str_cnt = sscanf(line, "pcibus %4x:%2x", &tmp_match[0], &tmp_match[1]); + if (str_cnt < 2) + return -EINVAL; + + /* Connect pcibus to segment:bus number with '_' + * to concatenate name tokens. + * pcibus 0000:00 ... -> pcibus_0000:00 ... + */ + line[6] = '_'; + + /* Null terminate after the concatencated name tokens + * to produce kobj name string. + */ + line[14] = '\0'; + + // Use start to index after name tokens string for remainder of line info. + start = &line[15]; + + top_obj->iio_stack = -1; + top_obj->slot = -1; + + /* r001i01b00h0 BASE IO (IIO Stack 0) + * r001i01b00h1 PCIe IO (IIO Stack 1) + * r001i01b03h1 PCIe SLOT + * r001i01b00h0 NODE IO + * r001i01b00h0 Riser + * (IIO Stack #) may not be present. + */ + if (start[0] == 'r') { + str_cnt = sscanf(start, "%13s %10[^(] %*s %*s %d)", + location, type, &top_obj->iio_stack); + if (str_cnt < 2) + return -EINVAL; + top_obj->type = kstrdup(type, GFP_KERNEL); + if (!top_obj->type) + return -ENOMEM; + top_obj->location = kstrdup(location, GFP_KERNEL); + if (!top_obj->location) { + kfree(top_obj->type); + return -ENOMEM; + } + } + /* PPB at 0000:80:00.00 (slot 3) + * (slot #) may not be present. + */ + else if (start[0] == 'P') { + str_cnt = sscanf(start, "%10s %*s %14s %*s %d)", + type, ppb_addr, &top_obj->slot); + if (str_cnt < 2) + return -EINVAL; + top_obj->type = kstrdup(type, GFP_KERNEL); + if (!top_obj->type) + return -ENOMEM; + top_obj->ppb_addr = kstrdup(ppb_addr, GFP_KERNEL); + if (!top_obj->ppb_addr) { + kfree(top_obj->type); + return -ENOMEM; + } + } else + return -EINVAL; + + top_obj->kobj.kset = uv_pcibus_kset; + + ret = kobject_init_and_add(&top_obj->kobj, &uv_pci_top_attr_type, NULL, "%s", line); + if (ret) + goto err_add_sysfs; + + if (top_obj->type) { + ret = sysfs_create_file(&top_obj->kobj, &uv_pci_type_attribute.attr); + if (ret) + goto err_add_sysfs; + } + if (top_obj->location) { + ret = sysfs_create_file(&top_obj->kobj, &uv_pci_location_attribute.attr); + if (ret) + goto err_add_sysfs; + } + if (top_obj->iio_stack >= 0) { + ret = sysfs_create_file(&top_obj->kobj, &uv_pci_iio_stack_attribute.attr); + if (ret) + goto err_add_sysfs; + } + if (top_obj->ppb_addr) { + ret = sysfs_create_file(&top_obj->kobj, &uv_pci_ppb_addr_attribute.attr); + if (ret) + goto err_add_sysfs; + } + if (top_obj->slot >= 0) { + ret = sysfs_create_file(&top_obj->kobj, &uv_pci_slot_attribute.attr); + if (ret) + goto err_add_sysfs; + } + + kobject_uevent(&top_obj->kobj, KOBJ_ADD); + return 0; + +err_add_sysfs: + kobject_put(&top_obj->kobj); + return ret; +} + +static int pci_topology_init(void) +{ + char *pci_top_str, *start, *found, *count; + size_t sz; + s64 biosr; + int l = 0, k = 0; + int len, ret; + + uv_pcibus_kset = kset_create_and_add("pcibuses", NULL, sgi_uv_kobj); + if (!uv_pcibus_kset) + return -ENOMEM; + + for (sz = PAGE_SIZE; sz < 16 * PAGE_SIZE; sz += PAGE_SIZE) { + pci_top_str = kmalloc(sz, GFP_KERNEL); + if (!pci_top_str) { + ret = -ENOMEM; + goto err_pci_top_str; + } + biosr = uv_bios_get_pci_topology((u64)sz, (u64 *)pci_top_str); + if (biosr == BIOS_STATUS_SUCCESS) { + len = strnlen(pci_top_str, sz); + for (count = pci_top_str; count < pci_top_str + len; count++) { + if (*count == '\n') + l++; + } + num_pci_lines = l; + + uv_pci_objs = kcalloc(num_pci_lines, + sizeof(*uv_pci_objs), GFP_KERNEL); + if (!uv_pci_objs) { + kfree(pci_top_str); + ret = -ENOMEM; + goto err_pci_top_str; + } + start = pci_top_str; + while ((found = strsep(&start, "\n")) != NULL) { + uv_pci_objs[k] = kzalloc(sizeof(*uv_pci_objs[k]), GFP_KERNEL); + if (!uv_pci_objs[k]) { + ret = -ENOMEM; + goto err_pci_obj; + } + ret = init_pci_top_obj(uv_pci_objs[k], found); + if (ret) + goto err_pci_obj; + k++; + if (k == num_pci_lines) + break; + } + } + kfree(pci_top_str); + if (biosr == BIOS_STATUS_SUCCESS || biosr == BIOS_STATUS_UNIMPLEMENTED) + break; + } + + return 0; +err_pci_obj: + k--; + for (; k >= 0; k--) + kobject_put(&uv_pci_objs[k]->kobj); + kfree(uv_pci_objs); + kfree(pci_top_str); +err_pci_top_str: + kset_unregister(uv_pcibus_kset); + return ret; +} + +static void pci_topology_exit(void) +{ + int k; + + for (k = 0; k < num_pci_lines; k++) + kobject_put(&uv_pci_objs[k]->kobj); + kset_unregister(uv_pcibus_kset); + kfree(uv_pci_objs); +} + +static ssize_t partition_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%ld\n", sn_partition_id); +} + +static ssize_t coherence_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%ld\n", sn_coherency_id); +} + +static ssize_t uv_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", uv_type_string()); +} + +static ssize_t uv_archtype_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return uv_get_archtype(buf, PAGE_SIZE); +} + +static ssize_t uv_hub_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", uv_hub_type()); +} + +static ssize_t uv_hubless_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", uv_get_hubless_system()); +} + +static struct kobj_attribute partition_id_attr = + __ATTR(partition_id, 0444, partition_id_show, NULL); +static struct kobj_attribute coherence_id_attr = + __ATTR(coherence_id, 0444, coherence_id_show, NULL); +static struct kobj_attribute uv_type_attr = + __ATTR(uv_type, 0444, uv_type_show, NULL); +static struct kobj_attribute uv_archtype_attr = + __ATTR(archtype, 0444, uv_archtype_show, NULL); +static struct kobj_attribute uv_hub_type_attr = + __ATTR(hub_type, 0444, uv_hub_type_show, NULL); +static struct kobj_attribute uv_hubless_attr = + __ATTR(hubless, 0444, uv_hubless_show, NULL); + +static struct attribute *base_attrs[] = { + &partition_id_attr.attr, + &coherence_id_attr.attr, + &uv_type_attr.attr, + &uv_archtype_attr.attr, + &uv_hub_type_attr.attr, + NULL, +}; + +static struct attribute_group base_attr_group = { + .attrs = base_attrs +}; + +static int initial_bios_setup(void) +{ + u64 v; + s64 biosr; + + biosr = uv_bios_get_master_nasid((u64)sizeof(uv_master_nasid), (u64 *)&uv_master_nasid); + if (biosr) + return -EINVAL; + + biosr = uv_bios_get_heapsize((u64)uv_master_nasid, (u64)sizeof(u64), &v); + if (biosr) + return -EINVAL; + + uv_biosheap = vmalloc(v); + if (!uv_biosheap) + return -ENOMEM; + + biosr = uv_bios_install_heap((u64)uv_master_nasid, v, (u64 *)uv_biosheap); + if (biosr) { + vfree(uv_biosheap); + return -EINVAL; + } + + biosr = uv_bios_obj_count((u64)uv_master_nasid, sizeof(u64), &v); + if (biosr) { + vfree(uv_biosheap); + return -EINVAL; + } + uv_bios_obj_cnt = (int)v; + + return 0; +} + +static struct attribute *hubless_base_attrs[] = { + &partition_id_attr.attr, + &uv_type_attr.attr, + &uv_archtype_attr.attr, + &uv_hubless_attr.attr, + NULL, +}; + +static struct attribute_group hubless_base_attr_group = { + .attrs = hubless_base_attrs +}; + + +static int __init uv_sysfs_hubless_init(void) +{ + int ret; + + ret = sysfs_create_group(sgi_uv_kobj, &hubless_base_attr_group); + if (ret) { + pr_warn("sysfs_create_group hubless_base_attr_group failed\n"); + kobject_put(sgi_uv_kobj); + } + return ret; +} + +static int __init uv_sysfs_init(void) +{ + int ret = 0; + + if (!is_uv_system() && !uv_get_hubless_system()) + return -ENODEV; + + num_cnodes = uv_num_possible_blades(); + + if (!sgi_uv_kobj) + sgi_uv_kobj = kobject_create_and_add("sgi_uv", firmware_kobj); + if (!sgi_uv_kobj) { + pr_warn("kobject_create_and_add sgi_uv failed\n"); + return -EINVAL; + } + + if (uv_get_hubless_system()) + return uv_sysfs_hubless_init(); + + ret = sysfs_create_group(sgi_uv_kobj, &base_attr_group); + if (ret) { + pr_warn("sysfs_create_group base_attr_group failed\n"); + goto err_create_group; + } + + ret = initial_bios_setup(); + if (ret) + goto err_bios_setup; + + ret = uv_hubs_init(); + if (ret) + goto err_hubs_init; + + ret = uv_ports_init(); + if (ret) + goto err_ports_init; + + ret = pci_topology_init(); + if (ret) + goto err_pci_init; + + return 0; + +err_pci_init: + uv_ports_exit(); +err_ports_init: + uv_hubs_exit(); +err_hubs_init: + vfree(uv_biosheap); +err_bios_setup: + sysfs_remove_group(sgi_uv_kobj, &base_attr_group); +err_create_group: + kobject_put(sgi_uv_kobj); + return ret; +} + +static void __exit uv_sysfs_hubless_exit(void) +{ + sysfs_remove_group(sgi_uv_kobj, &hubless_base_attr_group); + kobject_put(sgi_uv_kobj); +} + +static void __exit uv_sysfs_exit(void) +{ + if (!is_uv_system()) { + if (uv_get_hubless_system()) + uv_sysfs_hubless_exit(); + return; + } + + pci_topology_exit(); + uv_ports_exit(); + uv_hubs_exit(); + vfree(uv_biosheap); + sysfs_remove_group(sgi_uv_kobj, &base_attr_group); + kobject_put(sgi_uv_kobj); +} + +#ifndef MODULE +device_initcall(uv_sysfs_init); +#else +module_init(uv_sysfs_init); +#endif +module_exit(uv_sysfs_exit); + +MODULE_AUTHOR("Hewlett Packard Enterprise"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 747c6cf1d795..e8996b1c3b35 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -44,6 +44,4 @@ source "drivers/staging/media/tegra-video/Kconfig" source "drivers/staging/media/ipu3/Kconfig" -source "drivers/staging/media/rkisp1/Kconfig" - endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index b59571826ba6..24b5873ff760 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,5 +10,4 @@ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/ diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index 3cd00cc0a364..e5f200e64993 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -231,7 +231,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) { - if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; if (sps->chroma_format_idc > 1) @@ -304,39 +304,39 @@ static const struct hantro_ctrl controls[] = { }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .id = V4L2_CID_STATELESS_H264_SPS, .ops = &hantro_ctrl_ops, }, }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .id = V4L2_CID_STATELESS_H264_PPS, }, }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, }, }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE, - .min = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, - .def = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, - .max = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, + .id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, }, }, { .codec = HANTRO_H264_DECODER, .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_START_CODE, - .min = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, - .def = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, - .max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, + .id = V4L2_CID_STATELESS_H264_START_CODE, + .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, }, }, { .codec = HANTRO_H264_DECODER, diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c index b1bdc00ac262..ed6eaf11d96f 100644 --- a/drivers/staging/media/hantro/hantro_h264.c +++ b/drivers/staging/media/hantro/hantro_h264.c @@ -344,22 +344,22 @@ int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx) hantro_start_prepare_run(ctx); ctrls->scaling = - hantro_get_ctrl(ctx, V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX); + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); if (WARN_ON(!ctrls->scaling)) return -EINVAL; ctrls->decode = - hantro_get_ctrl(ctx, V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS); + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); if (WARN_ON(!ctrls->decode)) return -EINVAL; ctrls->sps = - hantro_get_ctrl(ctx, V4L2_CID_MPEG_VIDEO_H264_SPS); + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SPS); if (WARN_ON(!ctrls->sps)) return -EINVAL; ctrls->pps = - hantro_get_ctrl(ctx, V4L2_CID_MPEG_VIDEO_H264_PPS); + hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_PPS); if (WARN_ON(!ctrls->pps)) return -EINVAL; diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 219283a06f52..34c9e4649a25 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -11,9 +11,7 @@ #include <linux/interrupt.h> #include <linux/v4l2-controls.h> -#include <media/h264-ctrls.h> -#include <media/mpeg2-ctrls.h> -#include <media/vp8-ctrls.h> +#include <media/v4l2-ctrls.h> #include <media/videobuf2-core.h> #define DEC_8190_ALIGN_MASK 0x07U diff --git a/drivers/staging/media/hantro/hantro_vp8.c b/drivers/staging/media/hantro/hantro_vp8.c index 0e02d147b189..91ec9a05645b 100644 --- a/drivers/staging/media/hantro/hantro_vp8.c +++ b/drivers/staging/media/hantro/hantro_vp8.c @@ -23,11 +23,11 @@ struct vp8_prob_tbl_packed { u8 padding1; /* mv prob */ - u8 prob_mv_context[2][19]; + u8 prob_mv_context[2][V4L2_VP8_MV_PROB_CNT]; u8 padding2[2]; /* coeff probs */ - u8 prob_coeffs[4][8][3][11]; + u8 prob_coeffs[4][8][3][V4L2_VP8_COEFF_PROB_CNT]; u8 padding3[96]; }; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 21ebf7769696..db77fef07654 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -1123,7 +1123,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, priv->upstream_ep = upstream_ep; is_csi2 = !is_parallel_bus(&upstream_ep); if (is_csi2) { - int vc_num = 0; /* * NOTE! It seems the virtual channels from the mipi csi-2 * receiver are used only for routing by the video mux's, @@ -1131,14 +1130,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, * enters the CSI's however, they are treated internally * in the IPU as virtual channel 0. */ -#if 0 - mutex_unlock(&priv->lock); - vc_num = imx_media_find_mipi_csi2_channel(&priv->sd.entity); - if (vc_num < 0) - return vc_num; - mutex_lock(&priv->lock); -#endif - ipu_csi_set_mipi_datatype(priv->csi, vc_num, + ipu_csi_set_mipi_datatype(priv->csi, 0, &priv->format_mbus[CSI_SINK_PAD]); } diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index f17135158029..c8b6a43d0d7c 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -65,7 +65,7 @@ enum { }; /* How long to wait for EOF interrupts in the buffer-capture subdevs */ -#define IMX_MEDIA_EOF_TIMEOUT 1000 +#define IMX_MEDIA_EOF_TIMEOUT 2000 struct imx_media_pixfmt { /* the in-memory FourCC pixel format */ 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/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 5ccb3846c879..5d4db7a5b4b5 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -1131,6 +1131,6 @@ static struct platform_driver meson_vdec_driver = { }; module_platform_driver(meson_vdec_driver); -MODULE_DESCRIPTION("Meson video decoder driver for GXBB/GXL/GXM"); +MODULE_DESCRIPTION("Meson video decoder driver for GXBB/GXL/GXM/G12/SM1"); MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/rkisp1/Kconfig b/drivers/staging/media/rkisp1/Kconfig deleted file mode 100644 index 41f5def9ea44..000000000000 --- a/drivers/staging/media/rkisp1/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config VIDEO_ROCKCHIP_ISP1 - tristate "Rockchip Image Signal Processing v1 Unit driver" - depends on VIDEO_V4L2 && OF - depends on ARCH_ROCKCHIP || COMPILE_TEST - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_FWNODE - select GENERIC_PHY_MIPI_DPHY - default n - help - Enable this to support the Image Signal Processing (ISP) module - present in RK3399 SoCs. - - To compile this driver as a module, choose M here: the module - will be called rockchip-isp1. diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO deleted file mode 100644 index e7c8398fc2ce..000000000000 --- a/drivers/staging/media/rkisp1/TODO +++ /dev/null @@ -1,13 +0,0 @@ -* Fix pad format size for statistics and parameters entities. -* Fix checkpatch errors. -* Add uapi docs. Remember to add documentation of how quantization is handled. -* streaming paths (mainpath and selfpath) check if the other path is streaming -in several places of the code, review this, specially that it doesn't seem it -supports streaming from both paths at the same time. - -NOTES: -* All v4l2-compliance test must pass. -* Stats and params can be tested with libcamera and ChromiumOS stack. - -Please CC patches to Linux Media <linux-media@vger.kernel.org> and -Helen Koike <helen.koike@collabora.com>. diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/staging/media/rkvdec/rkvdec-h264.c index 7cc3b478a5f4..76e97cbe2512 100644 --- a/drivers/staging/media/rkvdec/rkvdec-h264.c +++ b/drivers/staging/media/rkvdec/rkvdec-h264.c @@ -1067,16 +1067,16 @@ static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl; ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS); + V4L2_CID_STATELESS_H264_DECODE_PARAMS); run->decode_params = ctrl ? ctrl->p_cur.p : NULL; ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_MPEG_VIDEO_H264_SPS); + V4L2_CID_STATELESS_H264_SPS); run->sps = ctrl ? ctrl->p_cur.p : NULL; ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_MPEG_VIDEO_H264_PPS); + V4L2_CID_STATELESS_H264_PPS); run->pps = ctrl ? ctrl->p_cur.p : NULL; ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX); + V4L2_CID_STATELESS_H264_SCALING_MATRIX); run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; rkvdec_run_preamble(ctx, &run->base); diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/staging/media/rkvdec/rkvdec.c index d25c4a37e2af..aa4f8c287618 100644 --- a/drivers/staging/media/rkvdec/rkvdec.c +++ b/drivers/staging/media/rkvdec/rkvdec.c @@ -29,7 +29,7 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl) { - if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_SPS) { + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; /* * TODO: The hardware supports 10-bit and 4:2:2 profiles, @@ -56,31 +56,44 @@ static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = { static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = { { .mandatory = true, - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, { .mandatory = true, - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .cfg.id = V4L2_CID_STATELESS_H264_SPS, .cfg.ops = &rkvdec_ctrl_ops, }, { .mandatory = true, - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .cfg.id = V4L2_CID_STATELESS_H264_PPS, }, { - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, }, { - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE, - .cfg.min = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, - .cfg.max = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, - .cfg.def = V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .cfg.min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .cfg.max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, }, { - .cfg.id = V4L2_CID_MPEG_VIDEO_H264_START_CODE, - .cfg.min = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, - .cfg.def = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, - .cfg.max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, + .cfg.id = V4L2_CID_STATELESS_H264_START_CODE, + .cfg.min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .cfg.max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .cfg.menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + .cfg.def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .cfg.max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, }, }; @@ -1038,10 +1051,8 @@ static int rkvdec_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(&pdev->dev, "Could not get vdec IRQ\n"); + if (irq <= 0) return -ENXIO; - } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, rkvdec_irq_handler, IRQF_ONESHOT, diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile index 1bce49d3e7e2..a647b3690bf8 100644 --- a/drivers/staging/media/sunxi/cedrus/Makefile +++ b/drivers/staging/media/sunxi/cedrus/Makefile @@ -2,4 +2,5 @@ obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o \ - cedrus_mpeg2.o cedrus_h264.o cedrus_h265.o + cedrus_mpeg2.o cedrus_h264.o cedrus_h265.o \ + cedrus_vp8.o diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index e0e35502e34a..18d54f9fd715 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -45,60 +45,79 @@ static const struct cedrus_control cedrus_controls[] = { }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, }, .codec = CEDRUS_CODEC_H264, .required = true, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS, + .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, }, .codec = CEDRUS_CODEC_H264, .required = true, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .id = V4L2_CID_STATELESS_H264_SPS, }, .codec = CEDRUS_CODEC_H264, .required = true, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .id = V4L2_CID_STATELESS_H264_PPS, }, .codec = CEDRUS_CODEC_H264, .required = true, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, }, .codec = CEDRUS_CODEC_H264, .required = false, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS, + .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, }, .codec = CEDRUS_CODEC_H264, .required = false, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE, - .max = V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, - .def = V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, + .id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .max = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + .def = V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, }, .codec = CEDRUS_CODEC_H264, .required = false, }, { .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_START_CODE, - .max = V4L2_MPEG_VIDEO_H264_START_CODE_NONE, - .def = V4L2_MPEG_VIDEO_H264_START_CODE_NONE, + .id = V4L2_CID_STATELESS_H264_START_CODE, + .max = V4L2_STATELESS_H264_START_CODE_NONE, + .def = V4L2_STATELESS_H264_START_CODE_NONE, + }, + .codec = CEDRUS_CODEC_H264, + .required = false, + }, + /* + * We only expose supported profiles information, + * and not levels as it's not clear what is supported + * for each hardware/core version. + * In any case, TRY/S_FMT will clamp the format resolution + * to the maximum supported. + */ + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), }, .codec = CEDRUS_CODEC_H264, .required = false, @@ -142,6 +161,13 @@ static const struct cedrus_control cedrus_controls[] = { .codec = CEDRUS_CODEC_H265, .required = false, }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER, + }, + .codec = CEDRUS_CODEC_VP8, + .required = true, + }, }; #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) @@ -393,6 +419,7 @@ static int cedrus_probe(struct platform_device *pdev) dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; dev->dec_ops[CEDRUS_CODEC_H265] = &cedrus_dec_ops_h265; + dev->dec_ops[CEDRUS_CODEC_VP8] = &cedrus_dec_ops_vp8; mutex_init(&dev->dev_mutex); @@ -486,43 +513,81 @@ static int cedrus_remove(struct platform_device *pdev) } static const struct cedrus_variant sun4i_a10_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 320000000, }; static const struct cedrus_variant sun5i_a13_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 320000000, }; static const struct cedrus_variant sun7i_a20_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 320000000, }; static const struct cedrus_variant sun8i_a33_cedrus_variant = { - .capabilities = CEDRUS_CAPABILITY_UNTILED, + .capabilities = CEDRUS_CAPABILITY_UNTILED | + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 320000000, }; static const struct cedrus_variant sun8i_h3_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED | - CEDRUS_CAPABILITY_H265_DEC, + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_H265_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 402000000, }; +static const struct cedrus_variant sun8i_v3s_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_UNTILED | + CEDRUS_CAPABILITY_H264_DEC, + .mod_rate = 297000000, +}; + +static const struct cedrus_variant sun8i_r40_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_UNTILED | + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_VP8_DEC, + .mod_rate = 297000000, +}; + static const struct cedrus_variant sun50i_a64_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED | - CEDRUS_CAPABILITY_H265_DEC, + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_H265_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 402000000, }; static const struct cedrus_variant sun50i_h5_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED | - CEDRUS_CAPABILITY_H265_DEC, + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_H265_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .mod_rate = 402000000, }; static const struct cedrus_variant sun50i_h6_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED | - CEDRUS_CAPABILITY_H265_DEC, + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_H265_DEC | + CEDRUS_CAPABILITY_VP8_DEC, .quirks = CEDRUS_QUIRK_NO_DMA_OFFSET, .mod_rate = 600000000, }; @@ -549,6 +614,14 @@ static const struct of_device_id cedrus_dt_match[] = { .data = &sun8i_h3_cedrus_variant, }, { + .compatible = "allwinner,sun8i-v3s-video-engine", + .data = &sun8i_v3s_cedrus_variant, + }, + { + .compatible = "allwinner,sun8i-r40-video-engine", + .data = &sun8i_r40_cedrus_variant, + }, + { .compatible = "allwinner,sun50i-a64-video-engine", .data = &sun50i_a64_cedrus_variant, }, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 93c843ae14bb..e61c41853ba2 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -22,12 +22,16 @@ #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> +#include <linux/iopoll.h> #include <linux/platform_device.h> #define CEDRUS_NAME "cedrus" #define CEDRUS_CAPABILITY_UNTILED BIT(0) #define CEDRUS_CAPABILITY_H265_DEC BIT(1) +#define CEDRUS_CAPABILITY_H264_DEC BIT(2) +#define CEDRUS_CAPABILITY_MPEG2_DEC BIT(3) +#define CEDRUS_CAPABILITY_VP8_DEC BIT(4) #define CEDRUS_QUIRK_NO_DMA_OFFSET BIT(0) @@ -35,6 +39,7 @@ enum cedrus_codec { CEDRUS_CODEC_MPEG2, CEDRUS_CODEC_H264, CEDRUS_CODEC_H265, + CEDRUS_CODEC_VP8, CEDRUS_CODEC_LAST, }; @@ -76,6 +81,10 @@ struct cedrus_h265_run { const struct v4l2_ctrl_hevc_slice_params *slice_params; }; +struct cedrus_vp8_run { + const struct v4l2_ctrl_vp8_frame_header *frame_params; +}; + struct cedrus_run { struct vb2_v4l2_buffer *src; struct vb2_v4l2_buffer *dst; @@ -84,6 +93,7 @@ struct cedrus_run { struct cedrus_h264_run h264; struct cedrus_mpeg2_run mpeg2; struct cedrus_h265_run h265; + struct cedrus_vp8_run vp8; }; }; @@ -135,6 +145,14 @@ struct cedrus_ctx { void *neighbor_info_buf; dma_addr_t neighbor_info_buf_addr; } h265; + struct { + unsigned int last_frame_p_type; + unsigned int last_filter_type; + unsigned int last_sharpness_level; + + u8 *entropy_probs_buf; + dma_addr_t entropy_probs_buf_dma; + } vp8; } codec; }; @@ -181,6 +199,7 @@ struct cedrus_dev { extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; extern struct cedrus_dec_ops cedrus_dec_ops_h264; extern struct cedrus_dec_ops cedrus_dec_ops_h265; +extern struct cedrus_dec_ops cedrus_dec_ops_vp8; static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val) { @@ -192,6 +211,14 @@ static inline u32 cedrus_read(struct cedrus_dev *dev, u32 reg) return readl(dev->base + reg); } +static inline u32 cedrus_wait_for(struct cedrus_dev *dev, u32 reg, u32 flag) +{ + u32 value; + + return readl_poll_timeout_atomic(dev->base + reg, value, + (value & flag) == 0, 10, 1000); +} + static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, struct v4l2_pix_format *pix_fmt, unsigned int plane) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 6385026d1b6b..a9090daf626a 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -48,17 +48,17 @@ void cedrus_device_run(void *priv) case V4L2_PIX_FMT_H264_SLICE: run.h264.decode_params = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS); + V4L2_CID_STATELESS_H264_DECODE_PARAMS); run.h264.pps = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_PPS); + V4L2_CID_STATELESS_H264_PPS); run.h264.scaling_matrix = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX); + V4L2_CID_STATELESS_H264_SCALING_MATRIX); run.h264.slice_params = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS); + V4L2_CID_STATELESS_H264_SLICE_PARAMS); run.h264.sps = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_SPS); + V4L2_CID_STATELESS_H264_SPS); run.h264.pred_weights = cedrus_find_control_data(ctx, - V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS); + V4L2_CID_STATELESS_H264_PRED_WEIGHTS); break; case V4L2_PIX_FMT_HEVC_SLICE: @@ -70,6 +70,11 @@ void cedrus_device_run(void *priv) V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS); break; + case V4L2_PIX_FMT_VP8_FRAME: + run.vp8.frame_params = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER); + break; + default: break; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index bcf050a04ffc..111cb91f8fc2 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -47,7 +47,9 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx, enum cedrus_codec codec) reg |= VE_MODE_DEC_MPEG; break; + /* H.264 and VP8 both use the same decoding mode bit. */ case CEDRUS_CODEC_H264: + case CEDRUS_CODEC_VP8: reg |= VE_MODE_DEC_H264; break; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h index 66b152f18d17..7718c561823f 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h @@ -546,6 +546,7 @@ #define VE_H264_SHS_QP_SCALING_MATRIX_DEFAULT BIT(24) #define VE_H264_CTRL 0x220 +#define VE_H264_CTRL_VP8 BIT(29) #define VE_H264_CTRL_VLD_DATA_REQ_INT BIT(2) #define VE_H264_CTRL_DECODE_ERR_INT BIT(1) #define VE_H264_CTRL_SLICE_DECODE_INT BIT(0) @@ -555,7 +556,12 @@ VE_H264_CTRL_SLICE_DECODE_INT) #define VE_H264_TRIGGER_TYPE 0x224 +#define VE_H264_TRIGGER_TYPE_PROBABILITY(x) SHIFT_AND_MASK_BITS(x, 31, 24) +#define VE_H264_TRIGGER_TYPE_BIN_LENS(x) SHIFT_AND_MASK_BITS((x) - 1, 18, 16) #define VE_H264_TRIGGER_TYPE_N_BITS(x) (((x) & 0x3f) << 8) +#define VE_H264_TRIGGER_TYPE_VP8_GET_BITS (15 << 0) +#define VE_H264_TRIGGER_TYPE_VP8_UPDATE_COEF (14 << 0) +#define VE_H264_TRIGGER_TYPE_VP8_SLICE_DECODE (10 << 0) #define VE_H264_TRIGGER_TYPE_AVC_SLICE_DECODE (8 << 0) #define VE_H264_TRIGGER_TYPE_INIT_SWDEC (7 << 0) #define VE_H264_TRIGGER_TYPE_FLUSH_BITS (3 << 0) @@ -565,6 +571,7 @@ #define VE_H264_STATUS_DECODE_ERR_INT VE_H264_CTRL_DECODE_ERR_INT #define VE_H264_STATUS_SLICE_DECODE_INT VE_H264_CTRL_SLICE_DECODE_INT #define VE_H264_STATUS_VLD_BUSY BIT(8) +#define VE_H264_STATUS_VP8_UPPROB_BUSY BIT(17) #define VE_H264_STATUS_INT_MASK VE_H264_CTRL_INT_MASK @@ -583,10 +590,83 @@ #define VE_H264_OUTPUT_FRAME_IDX 0x24c #define VE_H264_EXTRA_BUFFER1 0x250 #define VE_H264_EXTRA_BUFFER2 0x254 +#define VE_H264_MB_ADDR 0x260 +#define VE_H264_ERROR_CASE 0x2b8 #define VE_H264_BASIC_BITS 0x2dc #define VE_AVC_SRAM_PORT_OFFSET 0x2e0 #define VE_AVC_SRAM_PORT_DATA 0x2e4 +#define VE_VP8_PPS 0x214 +#define VE_VP8_PPS_PIC_TYPE_P_FRAME BIT(31) +#define VE_VP8_PPS_LAST_SHARPNESS_LEVEL(v) SHIFT_AND_MASK_BITS(v, 30, 28) +#define VE_VP8_PPS_LAST_PIC_TYPE_P_FRAME BIT(27) +#define VE_VP8_PPS_ALTREF_SIGN_BIAS BIT(26) +#define VE_VP8_PPS_GOLDEN_SIGN_BIAS BIT(25) +#define VE_VP8_PPS_RELOAD_ENTROPY_PROBS BIT(24) +#define VE_VP8_PPS_REFRESH_ENTROPY_PROBS BIT(23) +#define VE_VP8_PPS_MB_NO_COEFF_SKIP BIT(22) +#define VE_VP8_PPS_TOKEN_PARTITION(v) SHIFT_AND_MASK_BITS(v, 21, 20) +#define VE_VP8_PPS_MODE_REF_LF_DELTA_UPDATE BIT(19) +#define VE_VP8_PPS_MODE_REF_LF_DELTA_ENABLE BIT(18) +#define VE_VP8_PPS_LOOP_FILTER_LEVEL(v) SHIFT_AND_MASK_BITS(v, 17, 12) +#define VE_VP8_PPS_LOOP_FILTER_SIMPLE BIT(11) +#define VE_VP8_PPS_SHARPNESS_LEVEL(v) SHIFT_AND_MASK_BITS(v, 10, 8) +#define VE_VP8_PPS_LAST_LOOP_FILTER_SIMPLE BIT(7) +#define VE_VP8_PPS_SEGMENTATION_ENABLE BIT(6) +#define VE_VP8_PPS_MB_SEGMENT_ABS_DELTA BIT(5) +#define VE_VP8_PPS_UPDATE_MB_SEGMENTATION_MAP BIT(4) +#define VE_VP8_PPS_FULL_PIXEL BIT(3) +#define VE_VP8_PPS_BILINEAR_MC_FILTER BIT(2) +#define VE_VP8_PPS_FILTER_TYPE_SIMPLE BIT(1) +#define VE_VP8_PPS_LPF_DISABLE BIT(0) + +#define VE_VP8_QP_INDEX_DELTA 0x218 +#define VE_VP8_QP_INDEX_DELTA_UVAC(v) SHIFT_AND_MASK_BITS(v, 31, 27) +#define VE_VP8_QP_INDEX_DELTA_UVDC(v) SHIFT_AND_MASK_BITS(v, 26, 22) +#define VE_VP8_QP_INDEX_DELTA_Y2AC(v) SHIFT_AND_MASK_BITS(v, 21, 17) +#define VE_VP8_QP_INDEX_DELTA_Y2DC(v) SHIFT_AND_MASK_BITS(v, 16, 12) +#define VE_VP8_QP_INDEX_DELTA_Y1DC(v) SHIFT_AND_MASK_BITS(v, 11, 7) +#define VE_VP8_QP_INDEX_DELTA_BASE_QINDEX(v) SHIFT_AND_MASK_BITS(v, 6, 0) + +#define VE_VP8_PART_SIZE_OFFSET 0x21c +#define VE_VP8_ENTROPY_PROBS_ADDR 0x250 +#define VE_VP8_FIRST_DATA_PART_LEN 0x254 + +#define VE_VP8_FSIZE 0x258 +#define VE_VP8_FSIZE_WIDTH(w) \ + SHIFT_AND_MASK_BITS(DIV_ROUND_UP(w, 16), 15, 8) +#define VE_VP8_FSIZE_HEIGHT(h) \ + SHIFT_AND_MASK_BITS(DIV_ROUND_UP(h, 16), 7, 0) + +#define VE_VP8_PICSIZE 0x25c +#define VE_VP8_PICSIZE_WIDTH(w) SHIFT_AND_MASK_BITS(w, 27, 16) +#define VE_VP8_PICSIZE_HEIGHT(h) SHIFT_AND_MASK_BITS(h, 11, 0) + +#define VE_VP8_REC_LUMA 0x2ac +#define VE_VP8_FWD_LUMA 0x2b0 +#define VE_VP8_BWD_LUMA 0x2b4 +#define VE_VP8_REC_CHROMA 0x2d0 +#define VE_VP8_FWD_CHROMA 0x2d4 +#define VE_VP8_BWD_CHROMA 0x2d8 +#define VE_VP8_ALT_LUMA 0x2e8 +#define VE_VP8_ALT_CHROMA 0x2ec + +#define VE_VP8_SEGMENT_FEAT_MB_LV0 0x2f0 +#define VE_VP8_SEGMENT_FEAT_MB_LV1 0x2f4 + +#define VE_VP8_SEGMENT3(v) SHIFT_AND_MASK_BITS(v, 31, 24) +#define VE_VP8_SEGMENT2(v) SHIFT_AND_MASK_BITS(v, 23, 16) +#define VE_VP8_SEGMENT1(v) SHIFT_AND_MASK_BITS(v, 15, 8) +#define VE_VP8_SEGMENT0(v) SHIFT_AND_MASK_BITS(v, 7, 0) + +#define VE_VP8_REF_LF_DELTA 0x2f8 +#define VE_VP8_MODE_LF_DELTA 0x2fc + +#define VE_VP8_LF_DELTA3(v) SHIFT_AND_MASK_BITS(v, 30, 24) +#define VE_VP8_LF_DELTA2(v) SHIFT_AND_MASK_BITS(v, 22, 16) +#define VE_VP8_LF_DELTA1(v) SHIFT_AND_MASK_BITS(v, 14, 8) +#define VE_VP8_LF_DELTA0(v) SHIFT_AND_MASK_BITS(v, 6, 0) + #define VE_ISP_INPUT_SIZE 0xa00 #define VE_ISP_INPUT_STRIDE 0xa04 #define VE_ISP_CTRL 0xa08 diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 667b86dde1ee..b62eb8e84057 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -38,10 +38,12 @@ static struct cedrus_format cedrus_formats[] = { { .pixelformat = V4L2_PIX_FMT_MPEG2_SLICE, .directions = CEDRUS_DECODE_SRC, + .capabilities = CEDRUS_CAPABILITY_MPEG2_DEC, }, { .pixelformat = V4L2_PIX_FMT_H264_SLICE, .directions = CEDRUS_DECODE_SRC, + .capabilities = CEDRUS_CAPABILITY_H264_DEC, }, { .pixelformat = V4L2_PIX_FMT_HEVC_SLICE, @@ -49,6 +51,11 @@ static struct cedrus_format cedrus_formats[] = { .capabilities = CEDRUS_CAPABILITY_H265_DEC, }, { + .pixelformat = V4L2_PIX_FMT_VP8_FRAME, + .directions = CEDRUS_DECODE_SRC, + .capabilities = CEDRUS_CAPABILITY_VP8_DEC, + }, + { .pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12, .directions = CEDRUS_DECODE_DST, }, @@ -110,6 +117,7 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) case V4L2_PIX_FMT_MPEG2_SLICE: case V4L2_PIX_FMT_H264_SLICE: case V4L2_PIX_FMT_HEVC_SLICE: + case V4L2_PIX_FMT_VP8_FRAME: /* Zero bytes per line for encoded source. */ bytesperline = 0; /* Choose some minimum size since this can't be 0 */ @@ -473,14 +481,20 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) ctx->current_codec = CEDRUS_CODEC_H265; break; + case V4L2_PIX_FMT_VP8_FRAME: + ctx->current_codec = CEDRUS_CODEC_VP8; + break; + default: return -EINVAL; } if (V4L2_TYPE_IS_OUTPUT(vq->type)) { ret = pm_runtime_get_sync(dev->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(dev->dev); goto err_cleanup; + } if (dev->dec_ops[ctx->current_codec]->start) { ret = dev->dec_ops[ctx->current_codec]->start(ctx); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c new file mode 100644 index 000000000000..ff613ebd1180 --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c @@ -0,0 +1,907 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cedrus VPU driver + * + * Copyright (c) 2019 Jernej Skrabec <jernej.skrabec@siol.net> + */ + +/* + * VP8 in Cedrus shares same engine as H264. + * + * Note that it seems necessary to call bitstream parsing functions, + * to parse frame header, otherwise decoded image is garbage. This is + * contrary to what is driver supposed to do. However, values are not + * really used, so this might be acceptable. It's possible that bitstream + * parsing functions set some internal VPU state, which is later necessary + * for proper decoding. Biggest suspect is "VP8 probs update" trigger. + */ + +#include <linux/delay.h> +#include <linux/types.h> + +#include <media/videobuf2-dma-contig.h> + +#include "cedrus.h" +#include "cedrus_hw.h" +#include "cedrus_regs.h" + +#define CEDRUS_ENTROPY_PROBS_SIZE 0x2400 +#define VP8_PROB_HALF 128 +#define QUANT_DELTA_COUNT 5 + +/* + * This table comes from the concatenation of k_coeff_entropy_update_probs, + * kf_ymode_prob, default_mv_context, etc. It is provided in this form in + * order to avoid computing it every time the driver is initialised, and is + * suitable for direct consumption by the hardware. + */ +static const u8 prob_table_init[] = { + /* k_coeff_entropy_update_probs */ + /* block 0 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xB0, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xDF, 0xF1, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF9, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xF4, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xF6, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* block 1 */ + 0xD9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE1, 0xFC, 0xF1, 0xFD, 0xFF, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0xFA, 0xF1, 0xFA, 0xFD, 0xFF, 0xFD, 0xFE, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xDF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEE, 0xFD, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xF8, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF9, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* block 2 */ + 0xBA, 0xFB, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0xFB, 0xF4, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0xFB, 0xF3, 0xFD, 0xFE, 0xFF, 0xFE, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEC, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFB, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* block 3 */ + 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0xFE, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0xFE, 0xF9, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF6, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFC, 0xFE, 0xFB, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0xFE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFD, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF5, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFD, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFC, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF9, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* kf_y_mode_probs */ + 0x91, 0x9C, 0xA3, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* split_mv_probs */ + 0x6E, 0x6F, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* bmode_prob */ + 0x78, 0x5A, 0x4F, 0x85, 0x57, 0x55, 0x50, 0x6F, + 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* sub_mv_ref_prob */ + 0x93, 0x88, 0x12, 0x00, + 0x6A, 0x91, 0x01, 0x00, + 0xB3, 0x79, 0x01, 0x00, + 0xDF, 0x01, 0x22, 0x00, + 0xD0, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* mv_counts_to_probs */ + 0x07, 0x01, 0x01, 0x8F, + 0x0E, 0x12, 0x0E, 0x6B, + 0x87, 0x40, 0x39, 0x44, + 0x3C, 0x38, 0x80, 0x41, + 0x9F, 0x86, 0x80, 0x22, + 0xEA, 0xBC, 0x80, 0x1C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* kf_y_mode_tree */ + 0x84, 0x02, 0x04, 0x06, 0x80, 0x81, 0x82, 0x83, + + /* y_mode_tree */ + 0x80, 0x02, 0x04, 0x06, 0x81, 0x82, 0x83, 0x84, + + /* uv_mode_tree */ + 0x80, 0x02, 0x81, 0x04, 0x82, 0x83, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + /* small_mv_tree */ + 0x02, 0x08, 0x04, 0x06, 0x80, 0x81, 0x82, 0x83, + 0x0A, 0x0C, 0x84, 0x85, 0x86, 0x87, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* small_mv_tree again */ + 0x02, 0x08, 0x04, 0x06, 0x80, 0x81, 0x82, 0x83, + 0x0A, 0x0C, 0x84, 0x85, 0x86, 0x87, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* split_mv_tree */ + 0x83, 0x02, 0x82, 0x04, 0x80, 0x81, 0x00, 0x00, + + /* b_mode_tree */ + 0x80, 0x02, 0x81, 0x04, 0x82, 0x06, 0x08, 0x0C, + 0x83, 0x0A, 0x85, 0x86, 0x84, 0x0E, 0x87, 0x10, + 0x88, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* submv_ref_tree */ + 0x8A, 0x02, 0x8B, 0x04, 0x8C, 0x8D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* mv_ref_tree */ + 0x87, 0x02, 0x85, 0x04, 0x86, 0x06, 0x88, 0x89, +}; + +/* + * This table is a copy of k_mv_entropy_update_probs from the VP8 + * specification. + * + * FIXME: If any other driver uses it, move this table to media/vp8-ctrls.h + */ +static const u8 k_mv_entropy_update_probs[2][V4L2_VP8_MV_PROB_CNT] = { + { 237, 246, 253, 253, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 250, 250, 252, 254, 254 }, + { 231, 243, 245, 253, 254, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 251, 251, 254, 254, 254 } +}; + +static uint8_t read_bits(struct cedrus_dev *dev, unsigned int bits_count, + unsigned int probability) +{ + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_VP8_GET_BITS | + VE_H264_TRIGGER_TYPE_BIN_LENS(bits_count) | + VE_H264_TRIGGER_TYPE_PROBABILITY(probability)); + + cedrus_wait_for(dev, VE_H264_STATUS, VE_H264_STATUS_VLD_BUSY); + + return cedrus_read(dev, VE_H264_BASIC_BITS); +} + +static void get_delta_q(struct cedrus_dev *dev) +{ + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 4, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } +} + +static void process_segmentation_info(struct cedrus_dev *dev) +{ + int update, i; + + update = read_bits(dev, 1, VP8_PROB_HALF); + + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 1, VP8_PROB_HALF); + + for (i = 0; i < 4; i++) + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 7, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } + + for (i = 0; i < 4; i++) + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 6, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } + } + + if (update) + for (i = 0; i < 3; i++) + if (read_bits(dev, 1, VP8_PROB_HALF)) + read_bits(dev, 8, VP8_PROB_HALF); +} + +static void process_ref_lf_delta_info(struct cedrus_dev *dev) +{ + if (read_bits(dev, 1, VP8_PROB_HALF)) { + int i; + + for (i = 0; i < 4; i++) + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 6, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } + + for (i = 0; i < 4; i++) + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 6, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } + } +} + +static void process_ref_frame_info(struct cedrus_dev *dev) +{ + u8 refresh_golden_frame = read_bits(dev, 1, VP8_PROB_HALF); + u8 refresh_alt_ref_frame = read_bits(dev, 1, VP8_PROB_HALF); + + if (!refresh_golden_frame) + read_bits(dev, 2, VP8_PROB_HALF); + + if (!refresh_alt_ref_frame) + read_bits(dev, 2, VP8_PROB_HALF); + + read_bits(dev, 1, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); +} + +static void cedrus_irq_clear(struct cedrus_dev *dev) +{ + cedrus_write(dev, VE_H264_STATUS, + VE_H264_STATUS_INT_MASK); +} + +static void cedrus_read_header(struct cedrus_dev *dev, + const struct v4l2_ctrl_vp8_frame_header *slice) +{ + int i, j; + + if (VP8_FRAME_IS_KEY_FRAME(slice)) { + read_bits(dev, 1, VP8_PROB_HALF); + read_bits(dev, 1, VP8_PROB_HALF); + } + + if (read_bits(dev, 1, VP8_PROB_HALF)) + process_segmentation_info(dev); + + read_bits(dev, 1, VP8_PROB_HALF); + read_bits(dev, 6, VP8_PROB_HALF); + read_bits(dev, 3, VP8_PROB_HALF); + + if (read_bits(dev, 1, VP8_PROB_HALF)) + process_ref_lf_delta_info(dev); + + read_bits(dev, 2, VP8_PROB_HALF); + + /* y_ac_qi */ + read_bits(dev, 7, VP8_PROB_HALF); + + /* Parses y_dc_delta, y2_dc_delta, etc. */ + for (i = 0; i < QUANT_DELTA_COUNT; i++) + get_delta_q(dev); + + if (!VP8_FRAME_IS_KEY_FRAME(slice)) + process_ref_frame_info(dev); + + read_bits(dev, 1, VP8_PROB_HALF); + + if (!VP8_FRAME_IS_KEY_FRAME(slice)) + read_bits(dev, 1, VP8_PROB_HALF); + + cedrus_write(dev, VE_H264_TRIGGER_TYPE, VE_H264_TRIGGER_TYPE_VP8_UPDATE_COEF); + cedrus_wait_for(dev, VE_H264_STATUS, VE_H264_STATUS_VP8_UPPROB_BUSY); + cedrus_irq_clear(dev); + + if (read_bits(dev, 1, VP8_PROB_HALF)) + read_bits(dev, 8, VP8_PROB_HALF); + + if (!VP8_FRAME_IS_KEY_FRAME(slice)) { + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + } + + if (read_bits(dev, 1, VP8_PROB_HALF)) { + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + read_bits(dev, 8, VP8_PROB_HALF); + } + + for (i = 0; i < 2; i++) + for (j = 0; j < V4L2_VP8_MV_PROB_CNT; j++) + if (read_bits(dev, 1, k_mv_entropy_update_probs[i][j])) + read_bits(dev, 7, VP8_PROB_HALF); + } +} + +static void cedrus_vp8_update_probs(const struct v4l2_ctrl_vp8_frame_header *slice, + u8 *prob_table) +{ + int i, j, k; + + memcpy(&prob_table[0x1008], slice->entropy_header.y_mode_probs, + sizeof(slice->entropy_header.y_mode_probs)); + memcpy(&prob_table[0x1010], slice->entropy_header.uv_mode_probs, + sizeof(slice->entropy_header.uv_mode_probs)); + + memcpy(&prob_table[0x1018], slice->segment_header.segment_probs, + sizeof(slice->segment_header.segment_probs)); + + prob_table[0x101c] = slice->prob_skip_false; + prob_table[0x101d] = slice->prob_intra; + prob_table[0x101e] = slice->prob_last; + prob_table[0x101f] = slice->prob_gf; + + memcpy(&prob_table[0x1020], slice->entropy_header.mv_probs[0], + V4L2_VP8_MV_PROB_CNT); + memcpy(&prob_table[0x1040], slice->entropy_header.mv_probs[1], + V4L2_VP8_MV_PROB_CNT); + + for (i = 0; i < 4; ++i) + for (j = 0; j < 8; ++j) + for (k = 0; k < 3; ++k) + memcpy(&prob_table[i * 512 + j * 64 + k * 16], + slice->entropy_header.coeff_probs[i][j][k], 11); +} + +static enum cedrus_irq_status +cedrus_vp8_irq_status(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg = cedrus_read(dev, VE_H264_STATUS); + + if (reg & (VE_H264_STATUS_DECODE_ERR_INT | + VE_H264_STATUS_VLD_DATA_REQ_INT)) + return CEDRUS_IRQ_ERROR; + + if (reg & VE_H264_CTRL_SLICE_DECODE_INT) + return CEDRUS_IRQ_OK; + + return CEDRUS_IRQ_NONE; +} + +static void cedrus_vp8_irq_clear(struct cedrus_ctx *ctx) +{ + cedrus_irq_clear(ctx->dev); +} + +static void cedrus_vp8_irq_disable(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg = cedrus_read(dev, VE_H264_CTRL); + + cedrus_write(dev, VE_H264_CTRL, + reg & ~VE_H264_CTRL_INT_MASK); +} + +static void cedrus_vp8_setup(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_vp8_frame_header *slice = run->vp8.frame_params; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *src_buf = &run->src->vb2_buf; + struct cedrus_dev *dev = ctx->dev; + dma_addr_t luma_addr, chroma_addr; + dma_addr_t src_buf_addr; + int header_size; + int qindex; + u32 reg; + + cedrus_engine_enable(ctx, CEDRUS_CODEC_VP8); + + cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8); + + cedrus_vp8_update_probs(slice, ctx->codec.vp8.entropy_probs_buf); + + reg = slice->first_part_size * 8; + cedrus_write(dev, VE_VP8_FIRST_DATA_PART_LEN, reg); + + header_size = VP8_FRAME_IS_KEY_FRAME(slice) ? 10 : 3; + + reg = slice->first_part_size + header_size; + cedrus_write(dev, VE_VP8_PART_SIZE_OFFSET, reg); + + reg = vb2_plane_size(src_buf, 0) * 8; + cedrus_write(dev, VE_H264_VLD_LEN, reg); + + /* + * FIXME: There is a problem if frame header is skipped (adding + * first_part_header_bits to offset). It seems that functions + * for parsing bitstreams change internal state of VPU in some + * way that can't be otherwise set. Maybe this can be bypassed + * by somehow fixing probability table buffer? + */ + reg = header_size * 8; + cedrus_write(dev, VE_H264_VLD_OFFSET, reg); + + src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + cedrus_write(dev, VE_H264_VLD_END, + src_buf_addr + vb2_get_plane_payload(src_buf, 0)); + cedrus_write(dev, VE_H264_VLD_ADDR, + VE_H264_VLD_ADDR_VAL(src_buf_addr) | + VE_H264_VLD_ADDR_FIRST | VE_H264_VLD_ADDR_VALID | + VE_H264_VLD_ADDR_LAST); + + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_INIT_SWDEC); + + cedrus_write(dev, VE_VP8_ENTROPY_PROBS_ADDR, + ctx->codec.vp8.entropy_probs_buf_dma); + + reg = 0; + switch (slice->version) { + case 1: + reg |= VE_VP8_PPS_FILTER_TYPE_SIMPLE; + reg |= VE_VP8_PPS_BILINEAR_MC_FILTER; + break; + case 2: + reg |= VE_VP8_PPS_LPF_DISABLE; + reg |= VE_VP8_PPS_BILINEAR_MC_FILTER; + break; + case 3: + reg |= VE_VP8_PPS_LPF_DISABLE; + reg |= VE_VP8_PPS_FULL_PIXEL; + break; + } + if (slice->segment_header.flags & V4L2_VP8_SEGMENT_HEADER_FLAG_UPDATE_MAP) + reg |= VE_VP8_PPS_UPDATE_MB_SEGMENTATION_MAP; + if (!(slice->segment_header.flags & V4L2_VP8_SEGMENT_HEADER_FLAG_DELTA_VALUE_MODE)) + reg |= VE_VP8_PPS_MB_SEGMENT_ABS_DELTA; + if (slice->segment_header.flags & V4L2_VP8_SEGMENT_HEADER_FLAG_ENABLED) + reg |= VE_VP8_PPS_SEGMENTATION_ENABLE; + if (ctx->codec.vp8.last_filter_type) + reg |= VE_VP8_PPS_LAST_LOOP_FILTER_SIMPLE; + reg |= VE_VP8_PPS_SHARPNESS_LEVEL(slice->lf_header.sharpness_level); + if (slice->lf_header.flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) + reg |= VE_VP8_PPS_LOOP_FILTER_SIMPLE; + reg |= VE_VP8_PPS_LOOP_FILTER_LEVEL(slice->lf_header.level); + if (slice->lf_header.flags & V4L2_VP8_LF_HEADER_ADJ_ENABLE) + reg |= VE_VP8_PPS_MODE_REF_LF_DELTA_ENABLE; + if (slice->lf_header.flags & V4L2_VP8_LF_HEADER_DELTA_UPDATE) + reg |= VE_VP8_PPS_MODE_REF_LF_DELTA_UPDATE; + reg |= VE_VP8_PPS_TOKEN_PARTITION(ilog2(slice->num_dct_parts)); + if (slice->flags & V4L2_VP8_FRAME_HEADER_FLAG_MB_NO_SKIP_COEFF) + reg |= VE_VP8_PPS_MB_NO_COEFF_SKIP; + reg |= VE_VP8_PPS_RELOAD_ENTROPY_PROBS; + if (slice->flags & V4L2_VP8_FRAME_HEADER_FLAG_SIGN_BIAS_GOLDEN) + reg |= VE_VP8_PPS_GOLDEN_SIGN_BIAS; + if (slice->flags & V4L2_VP8_FRAME_HEADER_FLAG_SIGN_BIAS_ALT) + reg |= VE_VP8_PPS_ALTREF_SIGN_BIAS; + if (ctx->codec.vp8.last_frame_p_type) + reg |= VE_VP8_PPS_LAST_PIC_TYPE_P_FRAME; + reg |= VE_VP8_PPS_LAST_SHARPNESS_LEVEL(ctx->codec.vp8.last_sharpness_level); + if (!(slice->flags & V4L2_VP8_FRAME_HEADER_FLAG_KEY_FRAME)) + reg |= VE_VP8_PPS_PIC_TYPE_P_FRAME; + cedrus_write(dev, VE_VP8_PPS, reg); + + cedrus_read_header(dev, slice); + + /* reset registers changed by HW */ + cedrus_write(dev, VE_H264_CUR_MB_NUM, 0); + cedrus_write(dev, VE_H264_MB_ADDR, 0); + cedrus_write(dev, VE_H264_ERROR_CASE, 0); + + reg = 0; + reg |= VE_VP8_QP_INDEX_DELTA_UVAC(slice->quant_header.uv_ac_delta); + reg |= VE_VP8_QP_INDEX_DELTA_UVDC(slice->quant_header.uv_dc_delta); + reg |= VE_VP8_QP_INDEX_DELTA_Y2AC(slice->quant_header.y2_ac_delta); + reg |= VE_VP8_QP_INDEX_DELTA_Y2DC(slice->quant_header.y2_dc_delta); + reg |= VE_VP8_QP_INDEX_DELTA_Y1DC(slice->quant_header.y_dc_delta); + reg |= VE_VP8_QP_INDEX_DELTA_BASE_QINDEX(slice->quant_header.y_ac_qi); + cedrus_write(dev, VE_VP8_QP_INDEX_DELTA, reg); + + reg = 0; + reg |= VE_VP8_FSIZE_WIDTH(slice->width); + reg |= VE_VP8_FSIZE_HEIGHT(slice->height); + cedrus_write(dev, VE_VP8_FSIZE, reg); + + reg = 0; + reg |= VE_VP8_PICSIZE_WIDTH(slice->width); + reg |= VE_VP8_PICSIZE_HEIGHT(slice->height); + cedrus_write(dev, VE_VP8_PICSIZE, reg); + + reg = 0; + reg |= VE_VP8_SEGMENT3(slice->segment_header.quant_update[3]); + reg |= VE_VP8_SEGMENT2(slice->segment_header.quant_update[2]); + reg |= VE_VP8_SEGMENT1(slice->segment_header.quant_update[1]); + reg |= VE_VP8_SEGMENT0(slice->segment_header.quant_update[0]); + cedrus_write(dev, VE_VP8_SEGMENT_FEAT_MB_LV0, reg); + + reg = 0; + reg |= VE_VP8_SEGMENT3(slice->segment_header.lf_update[3]); + reg |= VE_VP8_SEGMENT2(slice->segment_header.lf_update[2]); + reg |= VE_VP8_SEGMENT1(slice->segment_header.lf_update[1]); + reg |= VE_VP8_SEGMENT0(slice->segment_header.lf_update[0]); + cedrus_write(dev, VE_VP8_SEGMENT_FEAT_MB_LV1, reg); + + reg = 0; + reg |= VE_VP8_LF_DELTA3(slice->lf_header.ref_frm_delta[3]); + reg |= VE_VP8_LF_DELTA2(slice->lf_header.ref_frm_delta[2]); + reg |= VE_VP8_LF_DELTA1(slice->lf_header.ref_frm_delta[1]); + reg |= VE_VP8_LF_DELTA0(slice->lf_header.ref_frm_delta[0]); + cedrus_write(dev, VE_VP8_REF_LF_DELTA, reg); + + reg = 0; + reg |= VE_VP8_LF_DELTA3(slice->lf_header.mb_mode_delta[3]); + reg |= VE_VP8_LF_DELTA2(slice->lf_header.mb_mode_delta[2]); + reg |= VE_VP8_LF_DELTA1(slice->lf_header.mb_mode_delta[1]); + reg |= VE_VP8_LF_DELTA0(slice->lf_header.mb_mode_delta[0]); + cedrus_write(dev, VE_VP8_MODE_LF_DELTA, reg); + + luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + cedrus_write(dev, VE_VP8_REC_LUMA, luma_addr); + cedrus_write(dev, VE_VP8_REC_CHROMA, chroma_addr); + + qindex = vb2_find_timestamp(cap_q, slice->last_frame_ts, 0); + if (qindex >= 0) { + luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); + cedrus_write(dev, VE_VP8_FWD_LUMA, luma_addr); + cedrus_write(dev, VE_VP8_FWD_CHROMA, chroma_addr); + } else { + cedrus_write(dev, VE_VP8_FWD_LUMA, 0); + cedrus_write(dev, VE_VP8_FWD_CHROMA, 0); + } + + qindex = vb2_find_timestamp(cap_q, slice->golden_frame_ts, 0); + if (qindex >= 0) { + luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); + cedrus_write(dev, VE_VP8_BWD_LUMA, luma_addr); + cedrus_write(dev, VE_VP8_BWD_CHROMA, chroma_addr); + } else { + cedrus_write(dev, VE_VP8_BWD_LUMA, 0); + cedrus_write(dev, VE_VP8_BWD_CHROMA, 0); + } + + qindex = vb2_find_timestamp(cap_q, slice->alt_frame_ts, 0); + if (qindex >= 0) { + luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); + cedrus_write(dev, VE_VP8_ALT_LUMA, luma_addr); + cedrus_write(dev, VE_VP8_ALT_CHROMA, chroma_addr); + } else { + cedrus_write(dev, VE_VP8_ALT_LUMA, 0); + cedrus_write(dev, VE_VP8_ALT_CHROMA, 0); + } + + cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8 | + VE_H264_CTRL_DECODE_ERR_INT | + VE_H264_CTRL_SLICE_DECODE_INT); + + if (slice->lf_header.level) { + ctx->codec.vp8.last_filter_type = + !!(slice->lf_header.flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE); + ctx->codec.vp8.last_frame_p_type = + !VP8_FRAME_IS_KEY_FRAME(slice); + ctx->codec.vp8.last_sharpness_level = + slice->lf_header.sharpness_level; + } +} + +static int cedrus_vp8_start(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + ctx->codec.vp8.entropy_probs_buf = + dma_alloc_coherent(dev->dev, CEDRUS_ENTROPY_PROBS_SIZE, + &ctx->codec.vp8.entropy_probs_buf_dma, + GFP_KERNEL); + if (!ctx->codec.vp8.entropy_probs_buf) + return -ENOMEM; + + /* + * This offset has been discovered by reverse engineering, we don’t know + * what it actually means. + */ + memcpy(&ctx->codec.vp8.entropy_probs_buf[2048], + prob_table_init, sizeof(prob_table_init)); + + return 0; +} + +static void cedrus_vp8_stop(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_engine_disable(dev); + + dma_free_coherent(dev->dev, CEDRUS_ENTROPY_PROBS_SIZE, + ctx->codec.vp8.entropy_probs_buf, + ctx->codec.vp8.entropy_probs_buf_dma); +} + +static void cedrus_vp8_trigger(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_VP8_SLICE_DECODE); +} + +struct cedrus_dec_ops cedrus_dec_ops_vp8 = { + .irq_clear = cedrus_vp8_irq_clear, + .irq_disable = cedrus_vp8_irq_disable, + .irq_status = cedrus_vp8_irq_status, + .setup = cedrus_vp8_setup, + .start = cedrus_vp8_start, + .stop = cedrus_vp8_stop, + .trigger = cedrus_vp8_trigger, +}; diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c index 808196ea5b81..d9f8b21edf6a 100644 --- a/drivers/staging/media/zoran/zoran_driver.c +++ b/drivers/staging/media/zoran/zoran_driver.c @@ -666,7 +666,7 @@ static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selectio if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pci_err(zr->pci_dev, "%s invalid combinaison\n", __func__); + pci_err(zr->pci_dev, "%s invalid selection type combination\n", __func__); return -EINVAL; } diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 6ade4a5c4840..480d294a23ab 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -14,7 +14,7 @@ #include <linux/tee_drv.h> #include <linux/uaccess.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include "tee_private.h" #define TEE_NUM_DEVICES 32 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index fa50e8936f5f..ac0b5fc30ea6 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1246,7 +1246,7 @@ out_free_interp: set_binfmt(&elf_format); #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES - retval = arch_setup_additional_pages(bprm, !!interpreter); + retval = ARCH_SETUP_ADDITIONAL_PAGES(bprm, elf_ex, !!interpreter); if (retval < 0) goto out; #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */ @@ -1307,7 +1307,7 @@ out_free_interp: #endif finalize_exec(bprm); - start_thread(regs, elf_entry, bprm->p); + START_THREAD(elf_ex, regs, elf_entry, bprm->p); retval = 0; out: return retval; diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index 2d24c765cbd7..2c557229696a 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -106,15 +106,25 @@ #endif #ifdef compat_start_thread -#undef start_thread -#define start_thread compat_start_thread +#define COMPAT_START_THREAD(ex, regs, new_ip, new_sp) \ + compat_start_thread(regs, new_ip, new_sp) #endif -#ifdef compat_arch_setup_additional_pages +#ifdef COMPAT_START_THREAD +#undef START_THREAD +#define START_THREAD COMPAT_START_THREAD +#endif + +#ifdef compat_arch_setup_additional_pages +#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ + compat_arch_setup_additional_pages(bprm, interpreter) +#endif + +#ifdef COMPAT_ARCH_SETUP_ADDITIONAL_PAGES #undef ARCH_HAS_SETUP_ADDITIONAL_PAGES #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 -#undef arch_setup_additional_pages -#define arch_setup_additional_pages compat_arch_setup_additional_pages +#undef ARCH_SETUP_ADDITIONAL_PAGES +#define ARCH_SETUP_ADDITIONAL_PAGES COMPAT_ARCH_SETUP_ADDITIONAL_PAGES #endif #ifdef compat_elf_read_implies_exec diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index 1fbe6c24d705..3414d35ccbb6 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -14,7 +14,7 @@ #include <linux/namei.h> #include <linux/scatterlist.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/skcipher.h> #include "fscrypt_private.h" @@ -404,7 +404,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, fname->disk_name.len = iname->len; return 0; } - ret = fscrypt_get_encryption_info(dir); + ret = fscrypt_get_encryption_info(dir, lookup); if (ret) return ret; @@ -560,7 +560,11 @@ int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return -ECHILD; dir = dget_parent(dentry); - err = fscrypt_get_encryption_info(d_inode(dir)); + /* + * Pass allow_unsupported=true, so that files with an unsupported + * encryption policy can be deleted. + */ + err = fscrypt_get_encryption_info(d_inode(dir), true); valid = !fscrypt_has_encryption_key(d_inode(dir)); dput(dir); diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 4f5806a3b73d..f0bed6b06fa6 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -25,6 +25,9 @@ #define FSCRYPT_CONTEXT_V1 1 #define FSCRYPT_CONTEXT_V2 2 +/* Keep this in sync with include/uapi/linux/fscrypt.h */ +#define FSCRYPT_MODE_MAX FSCRYPT_MODE_ADIANTUM + struct fscrypt_context_v1 { u8 version; /* FSCRYPT_CONTEXT_V1 */ u8 contents_encryption_mode; @@ -436,16 +439,9 @@ struct fscrypt_master_key { * FS_IOC_REMOVE_ENCRYPTION_KEY can be retried, or * FS_IOC_ADD_ENCRYPTION_KEY can add the secret again. * - * Locking: protected by key->sem (outer) and mk_secret_sem (inner). - * The reason for two locks is that key->sem also protects modifying - * mk_users, which ranks it above the semaphore for the keyring key - * type, which is in turn above page faults (via keyring_read). But - * sometimes filesystems call fscrypt_get_encryption_info() from within - * a transaction, which ranks it below page faults. So we need a - * separate lock which protects mk_secret but not also mk_users. + * Locking: protected by this master key's key->sem. */ struct fscrypt_master_key_secret mk_secret; - struct rw_semaphore mk_secret_sem; /* * For v1 policy keys: an arbitrary key descriptor which was assigned by @@ -464,8 +460,8 @@ struct fscrypt_master_key { * * This is NULL for v1 policy keys; those can only be added by root. * - * Locking: in addition to this keyrings own semaphore, this is - * protected by the master key's key->sem, so we can do atomic + * Locking: in addition to this keyring's own semaphore, this is + * protected by this master key's key->sem, so we can do atomic * search+insert. It can also be searched without taking any locks, but * in that case the returned key may have already been removed. */ @@ -491,9 +487,9 @@ struct fscrypt_master_key { * Per-mode encryption keys for the various types of encryption policies * that use them. Allocated and derived on-demand. */ - struct fscrypt_prepared_key mk_direct_keys[__FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[__FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[__FSCRYPT_MODE_MAX + 1]; + struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; + struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; + struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; @@ -507,9 +503,9 @@ is_master_key_secret_present(const struct fscrypt_master_key_secret *secret) /* * The READ_ONCE() is only necessary for fscrypt_drop_inode() and * fscrypt_key_describe(). These run in atomic context, so they can't - * take ->mk_secret_sem and thus 'secret' can change concurrently which - * would be a data race. But they only need to know whether the secret - * *was* present at the time of check, so READ_ONCE() suffices. + * take the key semaphore and thus 'secret' can change concurrently + * which would be a data race. But they only need to know whether the + * secret *was* present at the time of check, so READ_ONCE() suffices. */ return READ_ONCE(secret->size) != 0; } @@ -575,6 +571,34 @@ int fscrypt_derive_dirhash_key(struct fscrypt_info *ci, void fscrypt_hash_inode_number(struct fscrypt_info *ci, const struct fscrypt_master_key *mk); +int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported); + +/** + * fscrypt_require_key() - require an inode's encryption key + * @inode: the inode we need the key for + * + * If the inode is encrypted, set up its encryption key if not already done. + * Then require that the key be present and return -ENOKEY otherwise. + * + * No locks are needed, and the key will live as long as the struct inode --- so + * it won't go away from under you. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_require_key(struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) { + int err = fscrypt_get_encryption_info(inode, false); + + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } + return 0; +} + /* keysetup_v1.c */ void fscrypt_put_direct_key(struct fscrypt_direct_key *dk); diff --git a/fs/crypto/hkdf.c b/fs/crypto/hkdf.c index 0cba7928446d..e0ec21055505 100644 --- a/fs/crypto/hkdf.c +++ b/fs/crypto/hkdf.c @@ -10,7 +10,7 @@ */ #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include "fscrypt_private.h" diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 20b0df47fe6a..79570e0e8e61 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -54,15 +54,12 @@ EXPORT_SYMBOL_GPL(fscrypt_file_open); int __fscrypt_prepare_link(struct inode *inode, struct inode *dir, struct dentry *dentry) { - int err; - - err = fscrypt_require_key(dir); - if (err) - return err; - - /* ... in case we looked up no-key name before key was added */ - if (dentry->d_flags & DCACHE_NOKEY_NAME) + if (fscrypt_is_nokey_name(dentry)) return -ENOKEY; + /* + * We don't need to separately check that the directory inode's key is + * available, as it's implied by the dentry not being a no-key name. + */ if (!fscrypt_has_permitted_context(dir, inode)) return -EXDEV; @@ -75,19 +72,13 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { - int err; - - err = fscrypt_require_key(old_dir); - if (err) - return err; - - err = fscrypt_require_key(new_dir); - if (err) - return err; - - /* ... in case we looked up no-key name(s) before key was added */ - if ((old_dentry->d_flags | new_dentry->d_flags) & DCACHE_NOKEY_NAME) + if (fscrypt_is_nokey_name(old_dentry) || + fscrypt_is_nokey_name(new_dentry)) return -ENOKEY; + /* + * We don't need to separately check that the directory inodes' keys are + * available, as it's implied by the dentries not being no-key names. + */ if (old_dir != new_dir) { if (IS_ENCRYPTED(new_dir) && @@ -123,6 +114,20 @@ int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, } EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); +int __fscrypt_prepare_readdir(struct inode *dir) +{ + return fscrypt_get_encryption_info(dir, true); +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_readdir); + +int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr) +{ + if (attr->ia_valid & ATTR_SIZE) + return fscrypt_require_key(d_inode(dentry)); + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_setattr); + /** * fscrypt_prepare_setflags() - prepare to change flags with FS_IOC_SETFLAGS * @inode: the inode on which flags are being changed @@ -138,6 +143,7 @@ int fscrypt_prepare_setflags(struct inode *inode, unsigned int oldflags, unsigned int flags) { struct fscrypt_info *ci; + struct key *key; struct fscrypt_master_key *mk; int err; @@ -153,13 +159,14 @@ int fscrypt_prepare_setflags(struct inode *inode, ci = inode->i_crypt_info; if (ci->ci_policy.version != FSCRYPT_POLICY_V2) return -EINVAL; - mk = ci->ci_master_key->payload.data[0]; - down_read(&mk->mk_secret_sem); + key = ci->ci_master_key; + mk = key->payload.data[0]; + down_read(&key->sem); if (is_master_key_secret_present(&mk->mk_secret)) err = fscrypt_derive_dirhash_key(ci, mk); else err = -ENOKEY; - up_read(&mk->mk_secret_sem); + up_read(&key->sem); return err; } return 0; @@ -325,7 +332,7 @@ const char *fscrypt_get_symlink(struct inode *inode, const void *caddr, * Try to set up the symlink's encryption key, but we can continue * regardless of whether the key is available or not. */ - err = fscrypt_get_encryption_info(inode); + err = fscrypt_get_encryption_info(inode, false); if (err) return ERR_PTR(err); has_key = fscrypt_has_encryption_key(inode); diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index 53cc552a7b8f..0b3ffbb4faf4 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -44,7 +44,7 @@ static void free_master_key(struct fscrypt_master_key *mk) wipe_master_key_secret(&mk->mk_secret); - for (i = 0; i <= __FSCRYPT_MODE_MAX; i++) { + for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { fscrypt_destroy_prepared_key(&mk->mk_direct_keys[i]); fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_64_keys[i]); fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_32_keys[i]); @@ -347,7 +347,6 @@ static int add_new_master_key(struct fscrypt_master_key_secret *secret, mk->mk_spec = *mk_spec; move_master_key_secret(&mk->mk_secret, secret); - init_rwsem(&mk->mk_secret_sem); refcount_set(&mk->mk_refcount, 1); /* secret is present */ INIT_LIST_HEAD(&mk->mk_decrypted_inodes); @@ -427,11 +426,8 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, } /* Re-add the secret if needed. */ - if (rekey) { - down_write(&mk->mk_secret_sem); + if (rekey) move_master_key_secret(&mk->mk_secret, secret); - up_write(&mk->mk_secret_sem); - } return 0; } @@ -975,10 +971,8 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) /* No user claims remaining. Go ahead and wipe the secret. */ dead = false; if (is_master_key_secret_present(&mk->mk_secret)) { - down_write(&mk->mk_secret_sem); wipe_master_key_secret(&mk->mk_secret); dead = refcount_dec_and_test(&mk->mk_refcount); - up_write(&mk->mk_secret_sem); } up_write(&key->sem); if (dead) { diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index d595abb8ef90..261293fb7097 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -56,6 +56,8 @@ static struct fscrypt_mode * select_encryption_mode(const union fscrypt_policy *policy, const struct inode *inode) { + BUILD_BUG_ON(ARRAY_SIZE(fscrypt_modes) != FSCRYPT_MODE_MAX + 1); + if (S_ISREG(inode->i_mode)) return &fscrypt_modes[fscrypt_policy_contents_mode(policy)]; @@ -168,7 +170,7 @@ static int setup_per_mode_enc_key(struct fscrypt_info *ci, unsigned int hkdf_infolen = 0; int err; - if (WARN_ON(mode_num > __FSCRYPT_MODE_MAX)) + if (WARN_ON(mode_num > FSCRYPT_MODE_MAX)) return -EINVAL; prep_key = &keys[mode_num]; @@ -335,11 +337,11 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_info *ci, * Find the master key, then set up the inode's actual encryption key. * * If the master key is found in the filesystem-level keyring, then the - * corresponding 'struct key' is returned in *master_key_ret with - * ->mk_secret_sem read-locked. This is needed to ensure that only one task - * links the fscrypt_info into ->mk_decrypted_inodes (as multiple tasks may race - * to create an fscrypt_info for the same inode), and to synchronize the master - * key being removed with a new inode starting to use it. + * corresponding 'struct key' is returned in *master_key_ret with its semaphore + * read-locked. This is needed to ensure that only one task links the + * fscrypt_info into ->mk_decrypted_inodes (as multiple tasks may race to create + * an fscrypt_info for the same inode), and to synchronize the master key being + * removed with a new inode starting to use it. */ static int setup_file_encryption_key(struct fscrypt_info *ci, bool need_dirhash_key, @@ -388,7 +390,7 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, } mk = key->payload.data[0]; - down_read(&mk->mk_secret_sem); + down_read(&key->sem); /* Has the secret been removed (via FS_IOC_REMOVE_ENCRYPTION_KEY)? */ if (!is_master_key_secret_present(&mk->mk_secret)) { @@ -431,7 +433,7 @@ static int setup_file_encryption_key(struct fscrypt_info *ci, return 0; out_release_key: - up_read(&mk->mk_secret_sem); + up_read(&key->sem); key_put(key); return err; } @@ -534,9 +536,7 @@ fscrypt_setup_encryption_info(struct inode *inode, res = 0; out: if (master_key) { - struct fscrypt_master_key *mk = master_key->payload.data[0]; - - up_read(&mk->mk_secret_sem); + up_read(&master_key->sem); key_put(master_key); } put_crypt_info(crypt_info); @@ -546,6 +546,11 @@ out: /** * fscrypt_get_encryption_info() - set up an inode's encryption key * @inode: the inode to set up the key for. Must be encrypted. + * @allow_unsupported: if %true, treat an unsupported encryption policy (or + * unrecognized encryption context) the same way as the key + * being unavailable, instead of returning an error. Use + * %false unless the operation being performed is needed in + * order for files (or directories) to be deleted. * * Set up ->i_crypt_info, if it hasn't already been done. * @@ -556,7 +561,7 @@ out: * encryption key is unavailable. (Use fscrypt_has_encryption_key() to * distinguish these cases.) Also can return another -errno code. */ -int fscrypt_get_encryption_info(struct inode *inode) +int fscrypt_get_encryption_info(struct inode *inode, bool allow_unsupported) { int res; union fscrypt_context ctx; @@ -567,29 +572,38 @@ int fscrypt_get_encryption_info(struct inode *inode) res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { + if (res == -ERANGE && allow_unsupported) + return 0; fscrypt_warn(inode, "Error %d getting encryption context", res); return res; } res = fscrypt_policy_from_context(&policy, &ctx, res); if (res) { + if (allow_unsupported) + return 0; fscrypt_warn(inode, "Unrecognized or corrupt encryption context"); return res; } - if (!fscrypt_supported_policy(&policy, inode)) + if (!fscrypt_supported_policy(&policy, inode)) { + if (allow_unsupported) + return 0; return -EINVAL; + } res = fscrypt_setup_encryption_info(inode, &policy, fscrypt_context_nonce(&ctx), IS_CASEFOLDED(inode) && S_ISDIR(inode->i_mode)); + + if (res == -ENOPKG && allow_unsupported) /* Algorithm unavailable? */ + res = 0; if (res == -ENOKEY) res = 0; return res; } -EXPORT_SYMBOL(fscrypt_get_encryption_info); /** * fscrypt_prepare_new_inode() - prepare to create a new inode in a directory @@ -710,7 +724,7 @@ int fscrypt_drop_inode(struct inode *inode) return 0; /* - * Note: since we aren't holding ->mk_secret_sem, the result here can + * Note: since we aren't holding the key semaphore, the result here can * immediately become outdated. But there's no correctness problem with * unnecessarily evicting. Nor is there a correctness problem with not * evicting while iput() is racing with the key being removed, since diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 4441d9944b9e..a51cef6bd27f 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -175,7 +175,10 @@ static bool fscrypt_supported_v2_policy(const struct fscrypt_policy_v2 *policy, return false; } - if (policy->flags & ~FSCRYPT_POLICY_FLAGS_VALID) { + if (policy->flags & ~(FSCRYPT_POLICY_FLAGS_PAD_MASK | + FSCRYPT_POLICY_FLAG_DIRECT_KEY | + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | + FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) { fscrypt_warn(inode, "Unsupported encryption flags (0x%02x)", policy->flags); return false; @@ -587,7 +590,7 @@ EXPORT_SYMBOL_GPL(fscrypt_ioctl_get_nonce); int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) { union fscrypt_policy parent_policy, child_policy; - int err; + int err, err1, err2; /* No restrictions on file types which are never encrypted */ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && @@ -617,19 +620,25 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) * In any case, if an unexpected error occurs, fall back to "forbidden". */ - err = fscrypt_get_encryption_info(parent); + err = fscrypt_get_encryption_info(parent, true); if (err) return 0; - err = fscrypt_get_encryption_info(child); + err = fscrypt_get_encryption_info(child, true); if (err) return 0; - err = fscrypt_get_policy(parent, &parent_policy); - if (err) - return 0; + err1 = fscrypt_get_policy(parent, &parent_policy); + err2 = fscrypt_get_policy(child, &child_policy); - err = fscrypt_get_policy(child, &child_policy); - if (err) + /* + * Allow the case where the parent and child both have an unrecognized + * encryption policy, so that files with an unrecognized encryption + * policy can be deleted. + */ + if (err1 == -EINVAL && err2 == -EINVAL) + return 1; + + if (err1 || err2) return 0; return fscrypt_policies_equal(&parent_policy, &child_policy); diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ca50c90adc4c..c6d16353326a 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -118,11 +118,9 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx) struct buffer_head *bh = NULL; struct fscrypt_str fstr = FSTR_INIT(NULL, 0); - if (IS_ENCRYPTED(inode)) { - err = fscrypt_get_encryption_info(inode); - if (err) - return err; - } + err = fscrypt_prepare_readdir(inode); + if (err) + return err; if (is_dx_dir(inode)) { err = ext4_dx_readdir(file, ctx); @@ -616,13 +614,6 @@ finished: return 0; } -static int ext4_dir_open(struct inode * inode, struct file * filp) -{ - if (IS_ENCRYPTED(inode)) - return fscrypt_get_encryption_info(inode) ? -EACCES : 0; - return 0; -} - static int ext4_release_dir(struct inode *inode, struct file *filp) { if (filp->private_data) @@ -664,7 +655,6 @@ const struct file_operations ext4_dir_operations = { .compat_ioctl = ext4_compat_ioctl, #endif .fsync = ext4_sync_file, - .open = ext4_dir_open, .release = ext4_release_dir, }; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 33509266f5a0..5fa8436cd5fa 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -643,13 +643,7 @@ static struct stats dx_show_leaf(struct inode *dir, name = de->name; len = de->name_len; - if (IS_ENCRYPTED(dir)) - res = fscrypt_get_encryption_info(dir); - if (res) { - printk(KERN_WARNING "Error setting up" - " fname crypto: %d\n", res); - } - if (!fscrypt_has_encryption_key(dir)) { + if (!IS_ENCRYPTED(dir)) { /* Directory is not encrypted */ ext4fs_dirhash(dir, de->name, de->name_len, &h); @@ -1010,7 +1004,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, EXT4_DIR_REC_LEN(0)); /* Check if the directory is encrypted */ if (IS_ENCRYPTED(dir)) { - err = fscrypt_get_encryption_info(dir); + err = fscrypt_prepare_readdir(dir); if (err < 0) { brelse(bh); return err; @@ -2195,6 +2189,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, if (!dentry->d_name.len) return -EINVAL; + if (fscrypt_is_nokey_name(dentry)) + return -ENOKEY; + #ifdef CONFIG_UNICODE if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) && sb->s_encoding && utf8_validate(sb->s_encoding, &dentry->d_name)) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 4b9ef8bbfa4a..049500f1e764 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -1022,7 +1022,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) int err = 0; if (IS_ENCRYPTED(inode)) { - err = fscrypt_get_encryption_info(inode); + err = fscrypt_prepare_readdir(inode); if (err) goto out; @@ -1081,19 +1081,11 @@ out: return err < 0 ? err : 0; } -static int f2fs_dir_open(struct inode *inode, struct file *filp) -{ - if (IS_ENCRYPTED(inode)) - return fscrypt_get_encryption_info(inode) ? -EACCES : 0; - return 0; -} - const struct file_operations f2fs_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .iterate_shared = f2fs_readdir, .fsync = f2fs_sync_file, - .open = f2fs_dir_open, .unlocked_ioctl = f2fs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = f2fs_compat_ioctl, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cb700d797296..9a321c52face 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3251,6 +3251,8 @@ bool f2fs_empty_dir(struct inode *dir); static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) { + if (fscrypt_is_nokey_name(dentry)) + return -ENOKEY; return f2fs_do_add_link(d_inode(dentry->d_parent), &dentry->d_name, inode, inode->i_ino, inode->i_mode); } diff --git a/fs/ubifs/auth.c b/fs/ubifs/auth.c index b93b3cd10bfd..0886d835f597 100644 --- a/fs/ubifs/auth.c +++ b/fs/ubifs/auth.c @@ -12,7 +12,6 @@ #include <linux/crypto.h> #include <linux/verification.h> #include <crypto/hash.h> -#include <crypto/sha.h> #include <crypto/algapi.h> #include <keys/user-type.h> #include <keys/asymmetric-type.h> diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 155521e51ac5..1f33a5598b93 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -270,6 +270,15 @@ done: return d_splice_alias(inode, dentry); } +static int ubifs_prepare_create(struct inode *dir, struct dentry *dentry, + struct fscrypt_name *nm) +{ + if (fscrypt_is_nokey_name(dentry)) + return -ENOKEY; + + return fscrypt_setup_filename(dir, &dentry->d_name, 0, nm); +} + static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { @@ -293,7 +302,7 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, if (err) return err; - err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); + err = ubifs_prepare_create(dir, dentry, &nm); if (err) goto out_budg; @@ -505,7 +514,7 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx) return 0; if (encrypted) { - err = fscrypt_get_encryption_info(dir); + err = fscrypt_prepare_readdir(dir); if (err) return err; @@ -953,7 +962,7 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) if (err) return err; - err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); + err = ubifs_prepare_create(dir, dentry, &nm); if (err) goto out_budg; @@ -1038,7 +1047,7 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry, return err; } - err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); + err = ubifs_prepare_create(dir, dentry, &nm); if (err) { kfree(dev); goto out_budg; @@ -1122,7 +1131,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, if (err) return err; - err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm); + err = ubifs_prepare_create(dir, dentry, &nm); if (err) goto out_budg; @@ -1610,14 +1619,6 @@ int ubifs_getattr(const struct path *path, struct kstat *stat, return 0; } -static int ubifs_dir_open(struct inode *dir, struct file *file) -{ - if (IS_ENCRYPTED(dir)) - return fscrypt_get_encryption_info(dir) ? -EACCES : 0; - - return 0; -} - const struct inode_operations ubifs_dir_inode_operations = { .lookup = ubifs_lookup, .create = ubifs_create, @@ -1644,7 +1645,6 @@ const struct file_operations ubifs_dir_operations = { .iterate_shared = ubifs_readdir, .fsync = ubifs_fsync, .unlocked_ioctl = ubifs_ioctl, - .open = ubifs_dir_open, #ifdef CONFIG_COMPAT .compat_ioctl = ubifs_compat_ioctl, #endif diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 5ab3bbec8108..f7e997a01ad0 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/enable.c: ioctl to enable verity on a file + * Ioctl to enable verity on a file * * Copyright 2019 Google LLC */ @@ -398,9 +398,9 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) * Some pages of the file may have been evicted from pagecache after * being used in the Merkle tree construction, then read into pagecache * again by another process reading from the file concurrently. Since - * these pages didn't undergo verification against the file measurement - * which fs-verity now claims to be enforcing, we have to wipe the - * pagecache to ensure that all future reads are verified. + * these pages didn't undergo verification against the file digest which + * fs-verity now claims to be enforcing, we have to wipe the pagecache + * to ensure that all future reads are verified. */ filemap_write_and_wait(inode->i_mapping); invalidate_inode_pages2(inode->i_mapping); diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index e96d99d5145e..6413d28664d6 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -14,7 +14,7 @@ #define pr_fmt(fmt) "fs-verity: " fmt -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/fsverity.h> #include <linux/mempool.h> @@ -67,52 +67,22 @@ struct merkle_tree_params { * When a verity file is first opened, an instance of this struct is allocated * and stored in ->i_verity_info; it remains until the inode is evicted. It * caches information about the Merkle tree that's needed to efficiently verify - * data read from the file. It also caches the file measurement. The Merkle - * tree pages themselves are not cached here, but the filesystem may cache them. + * data read from the file. It also caches the file digest. The Merkle tree + * pages themselves are not cached here, but the filesystem may cache them. */ struct fsverity_info { struct merkle_tree_params tree_params; u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE]; - u8 measurement[FS_VERITY_MAX_DIGEST_SIZE]; + u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE]; const struct inode *inode; }; -/* - * Merkle tree properties. The file measurement is the hash of this structure - * excluding the signature and with the sig_size field set to 0. - */ -struct fsverity_descriptor { - __u8 version; /* must be 1 */ - __u8 hash_algorithm; /* Merkle tree hash algorithm */ - __u8 log_blocksize; /* log2 of size of data and tree blocks */ - __u8 salt_size; /* size of salt in bytes; 0 if none */ - __le32 sig_size; /* size of signature in bytes; 0 if none */ - __le64 data_size; /* size of file the Merkle tree is built over */ - __u8 root_hash[64]; /* Merkle tree root hash */ - __u8 salt[32]; /* salt prepended to each hashed block */ - __u8 __reserved[144]; /* must be 0's */ - __u8 signature[]; /* optional PKCS#7 signature */ -}; - /* Arbitrary limit to bound the kmalloc() size. Can be changed. */ #define FS_VERITY_MAX_DESCRIPTOR_SIZE 16384 #define FS_VERITY_MAX_SIGNATURE_SIZE (FS_VERITY_MAX_DESCRIPTOR_SIZE - \ sizeof(struct fsverity_descriptor)) -/* - * Format in which verity file measurements are signed. This is the same as - * 'struct fsverity_digest', except here some magic bytes are prepended to - * provide some context about what is being signed in case the same key is used - * for non-fsverity purposes, and here the fields have fixed endianness. - */ -struct fsverity_signed_digest { - char magic[8]; /* must be "FSVerity" */ - __le16 digest_algorithm; - __le16 digest_size; - __u8 digest[]; -}; - /* hash_algs.c */ extern struct fsverity_hash_alg fsverity_hash_algs[]; diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c index c37e186ebeb6..71d0fccb6d4c 100644 --- a/fs/verity/hash_algs.c +++ b/fs/verity/hash_algs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/hash_algs.c: fs-verity hash algorithms + * fs-verity hash algorithms * * Copyright 2019 Google LLC */ diff --git a/fs/verity/init.c b/fs/verity/init.c index 94c104e00861..c98b7016f446 100644 --- a/fs/verity/init.c +++ b/fs/verity/init.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/init.c: fs-verity module initialization and logging + * fs-verity module initialization and logging * * Copyright 2019 Google LLC */ diff --git a/fs/verity/measure.c b/fs/verity/measure.c index df409a5682ed..f0d7b30c62db 100644 --- a/fs/verity/measure.c +++ b/fs/verity/measure.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/measure.c: ioctl to get a verity file's measurement + * Ioctl to get a verity file's digest * * Copyright 2019 Google LLC */ @@ -10,12 +10,12 @@ #include <linux/uaccess.h> /** - * fsverity_ioctl_measure() - get a verity file's measurement - * @filp: file to get measurement of + * fsverity_ioctl_measure() - get a verity file's digest + * @filp: file to get digest of * @_uarg: user pointer to fsverity_digest * - * Retrieve the file measurement that the kernel is enforcing for reads from a - * verity file. See the "FS_IOC_MEASURE_VERITY" section of + * Retrieve the file digest that the kernel is enforcing for reads from a verity + * file. See the "FS_IOC_MEASURE_VERITY" section of * Documentation/filesystems/fsverity.rst for the documentation. * * Return: 0 on success, -errno on failure @@ -51,7 +51,7 @@ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg) if (copy_to_user(uarg, &arg, sizeof(arg))) return -EFAULT; - if (copy_to_user(uarg->digest, vi->measurement, hash_alg->digest_size)) + if (copy_to_user(uarg->digest, vi->file_digest, hash_alg->digest_size)) return -EFAULT; return 0; diff --git a/fs/verity/open.c b/fs/verity/open.c index bfe0280c14e4..228d0eca3e2e 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/open.c: opening fs-verity files + * Opening fs-verity files * * Copyright 2019 Google LLC */ @@ -124,18 +124,18 @@ out_err: } /* - * Compute the file measurement by hashing the fsverity_descriptor excluding the + * Compute the file digest by hashing the fsverity_descriptor excluding the * signature and with the sig_size field set to 0. */ -static int compute_file_measurement(struct fsverity_hash_alg *hash_alg, - struct fsverity_descriptor *desc, - u8 *measurement) +static int compute_file_digest(struct fsverity_hash_alg *hash_alg, + struct fsverity_descriptor *desc, + u8 *file_digest) { __le32 sig_size = desc->sig_size; int err; desc->sig_size = 0; - err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), measurement); + err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), file_digest); desc->sig_size = sig_size; return err; @@ -199,15 +199,15 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, memcpy(vi->root_hash, desc->root_hash, vi->tree_params.digest_size); - err = compute_file_measurement(vi->tree_params.hash_alg, desc, - vi->measurement); + err = compute_file_digest(vi->tree_params.hash_alg, desc, + vi->file_digest); if (err) { - fsverity_err(inode, "Error %d computing file measurement", err); + fsverity_err(inode, "Error %d computing file digest", err); goto out; } - pr_debug("Computed file measurement: %s:%*phN\n", + pr_debug("Computed file digest: %s:%*phN\n", vi->tree_params.hash_alg->name, - vi->tree_params.digest_size, vi->measurement); + vi->tree_params.digest_size, vi->file_digest); err = fsverity_verify_signature(vi, desc, desc_size); out: @@ -354,7 +354,7 @@ int __init fsverity_init_info_cache(void) { fsverity_info_cachep = KMEM_CACHE_USERCOPY(fsverity_info, SLAB_RECLAIM_ACCOUNT, - measurement); + file_digest); if (!fsverity_info_cachep) return -ENOMEM; return 0; diff --git a/fs/verity/signature.c b/fs/verity/signature.c index b14ed96387ec..012468eda2a7 100644 --- a/fs/verity/signature.c +++ b/fs/verity/signature.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/signature.c: verification of builtin signatures + * Verification of builtin signatures * * Copyright 2019 Google LLC */ @@ -32,8 +32,8 @@ static struct key *fsverity_keyring; * @desc: the file's fsverity_descriptor * @desc_size: size of @desc * - * If the file's fs-verity descriptor includes a signature of the file - * measurement, verify it against the certificates in the fs-verity keyring. + * If the file's fs-verity descriptor includes a signature of the file digest, + * verify it against the certificates in the fs-verity keyring. * * Return: 0 on success (signature valid or not required); -errno on failure */ @@ -44,7 +44,7 @@ int fsverity_verify_signature(const struct fsverity_info *vi, const struct inode *inode = vi->inode; const struct fsverity_hash_alg *hash_alg = vi->tree_params.hash_alg; const u32 sig_size = le32_to_cpu(desc->sig_size); - struct fsverity_signed_digest *d; + struct fsverity_formatted_digest *d; int err; if (sig_size == 0) { @@ -67,7 +67,7 @@ int fsverity_verify_signature(const struct fsverity_info *vi, memcpy(d->magic, "FSVerity", 8); d->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs); d->digest_size = cpu_to_le16(hash_alg->digest_size); - memcpy(d->digest, vi->measurement, hash_alg->digest_size); + memcpy(d->digest, vi->file_digest, hash_alg->digest_size); err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size, desc->signature, sig_size, @@ -90,8 +90,8 @@ int fsverity_verify_signature(const struct fsverity_info *vi, return err; } - pr_debug("Valid signature for file measurement %s:%*phN\n", - hash_alg->name, hash_alg->digest_size, vi->measurement); + pr_debug("Valid signature for file digest %s:%*phN\n", + hash_alg->name, hash_alg->digest_size, vi->file_digest); return 0; } diff --git a/fs/verity/verify.c b/fs/verity/verify.c index a8b68c6f663d..0adb970f4e73 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * fs/verity/verify.c: data verification functions, i.e. hooks for ->readpages() + * Data verification functions, i.e. hooks for ->readpages() * * Copyright 2019 Google LLC */ diff --git a/include/crypto/aead.h b/include/crypto/aead.h index c32a6f5664e9..fcc12c593ef8 100644 --- a/include/crypto/aead.h +++ b/include/crypto/aead.h @@ -191,6 +191,11 @@ static inline void crypto_free_aead(struct crypto_aead *tfm) crypto_destroy_tfm(tfm, crypto_aead_tfm(tfm)); } +static inline const char *crypto_aead_driver_name(struct crypto_aead *tfm) +{ + return crypto_tfm_alg_driver_name(crypto_aead_tfm(tfm)); +} + static inline struct aead_alg *crypto_aead_alg(struct crypto_aead *tfm) { return container_of(crypto_aead_tfm(tfm)->__crt_alg, diff --git a/include/crypto/curve25519.h b/include/crypto/curve25519.h index 4e6dc840b159..ece6a9b5fafc 100644 --- a/include/crypto/curve25519.h +++ b/include/crypto/curve25519.h @@ -28,6 +28,8 @@ void curve25519_arch(u8 out[CURVE25519_KEY_SIZE], void curve25519_base_arch(u8 pub[CURVE25519_KEY_SIZE], const u8 secret[CURVE25519_KEY_SIZE]); +bool curve25519_selftest(void); + static inline bool __must_check curve25519(u8 mypublic[CURVE25519_KEY_SIZE], const u8 secret[CURVE25519_KEY_SIZE], diff --git a/include/crypto/hash_info.h b/include/crypto/hash_info.h index eb9d2e368969..dd4f06785049 100644 --- a/include/crypto/hash_info.h +++ b/include/crypto/hash_info.h @@ -8,7 +8,8 @@ #ifndef _CRYPTO_HASH_INFO_H #define _CRYPTO_HASH_INFO_H -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> #include <crypto/md5.h> #include <crypto/streebog.h> diff --git a/include/crypto/internal/blake2s.h b/include/crypto/internal/blake2s.h index 74ff77032e52..6e376ae6b6b5 100644 --- a/include/crypto/internal/blake2s.h +++ b/include/crypto/internal/blake2s.h @@ -16,6 +16,8 @@ void blake2s_compress_generic(struct blake2s_state *state,const u8 *block, void blake2s_compress_arch(struct blake2s_state *state,const u8 *block, size_t nblocks, const u32 inc); +bool blake2s_selftest(void); + static inline void blake2s_set_lastblock(struct blake2s_state *state) { state->f[0] = -1; diff --git a/include/crypto/sha1.h b/include/crypto/sha1.h new file mode 100644 index 000000000000..044ecea60ac8 --- /dev/null +++ b/include/crypto/sha1.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Common values for SHA-1 algorithms + */ + +#ifndef _CRYPTO_SHA1_H +#define _CRYPTO_SHA1_H + +#include <linux/types.h> + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_BLOCK_SIZE 64 + +#define SHA1_H0 0x67452301UL +#define SHA1_H1 0xefcdab89UL +#define SHA1_H2 0x98badcfeUL +#define SHA1_H3 0x10325476UL +#define SHA1_H4 0xc3d2e1f0UL + +extern const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE]; + +struct sha1_state { + u32 state[SHA1_DIGEST_SIZE / 4]; + u64 count; + u8 buffer[SHA1_BLOCK_SIZE]; +}; + +struct shash_desc; + +extern int crypto_sha1_update(struct shash_desc *desc, const u8 *data, + unsigned int len); + +extern int crypto_sha1_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); + +/* + * An implementation of SHA-1's compression function. Don't use in new code! + * You shouldn't be using SHA-1, and even if you *have* to use SHA-1, this isn't + * the correct way to hash something with SHA-1 (use crypto_shash instead). + */ +#define SHA1_DIGEST_WORDS (SHA1_DIGEST_SIZE / 4) +#define SHA1_WORKSPACE_WORDS 16 +void sha1_init(__u32 *buf); +void sha1_transform(__u32 *digest, const char *data, __u32 *W); + +#endif /* _CRYPTO_SHA1_H */ diff --git a/include/crypto/sha1_base.h b/include/crypto/sha1_base.h index 20fd1f7468af..2e0e7c3827d1 100644 --- a/include/crypto/sha1_base.h +++ b/include/crypto/sha1_base.h @@ -9,9 +9,10 @@ #define _CRYPTO_SHA1_BASE_H #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <linux/crypto.h> #include <linux/module.h> +#include <linux/string.h> #include <asm/unaligned.h> @@ -101,7 +102,7 @@ static inline int sha1_base_finish(struct shash_desc *desc, u8 *out) for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) put_unaligned_be32(sctx->state[i], digest++); - *sctx = (struct sha1_state){}; + memzero_explicit(sctx, sizeof(*sctx)); return 0; } diff --git a/include/crypto/sha.h b/include/crypto/sha2.h index 4ff3da816630..2838f529f31e 100644 --- a/include/crypto/sha.h +++ b/include/crypto/sha2.h @@ -1,16 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Common values for SHA algorithms + * Common values for SHA-2 algorithms */ -#ifndef _CRYPTO_SHA_H -#define _CRYPTO_SHA_H +#ifndef _CRYPTO_SHA2_H +#define _CRYPTO_SHA2_H #include <linux/types.h> -#define SHA1_DIGEST_SIZE 20 -#define SHA1_BLOCK_SIZE 64 - #define SHA224_DIGEST_SIZE 28 #define SHA224_BLOCK_SIZE 64 @@ -23,12 +20,6 @@ #define SHA512_DIGEST_SIZE 64 #define SHA512_BLOCK_SIZE 128 -#define SHA1_H0 0x67452301UL -#define SHA1_H1 0xefcdab89UL -#define SHA1_H2 0x98badcfeUL -#define SHA1_H3 0x10325476UL -#define SHA1_H4 0xc3d2e1f0UL - #define SHA224_H0 0xc1059ed8UL #define SHA224_H1 0x367cd507UL #define SHA224_H2 0x3070dd17UL @@ -65,8 +56,6 @@ #define SHA512_H6 0x1f83d9abfb41bd6bULL #define SHA512_H7 0x5be0cd19137e2179ULL -extern const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE]; - extern const u8 sha224_zero_message_hash[SHA224_DIGEST_SIZE]; extern const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE]; @@ -75,12 +64,6 @@ extern const u8 sha384_zero_message_hash[SHA384_DIGEST_SIZE]; extern const u8 sha512_zero_message_hash[SHA512_DIGEST_SIZE]; -struct sha1_state { - u32 state[SHA1_DIGEST_SIZE / 4]; - u64 count; - u8 buffer[SHA1_BLOCK_SIZE]; -}; - struct sha256_state { u32 state[SHA256_DIGEST_SIZE / 4]; u64 count; @@ -95,12 +78,6 @@ struct sha512_state { struct shash_desc; -extern int crypto_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len); - -extern int crypto_sha1_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *hash); - extern int crypto_sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len); @@ -114,16 +91,6 @@ extern int crypto_sha512_finup(struct shash_desc *desc, const u8 *data, unsigned int len, u8 *hash); /* - * An implementation of SHA-1's compression function. Don't use in new code! - * You shouldn't be using SHA-1, and even if you *have* to use SHA-1, this isn't - * the correct way to hash something with SHA-1 (use crypto_shash instead). - */ -#define SHA1_DIGEST_WORDS (SHA1_DIGEST_SIZE / 4) -#define SHA1_WORKSPACE_WORDS 16 -void sha1_init(__u32 *buf); -void sha1_transform(__u32 *digest, const char *data, __u32 *W); - -/* * Stand-alone implementation of the SHA256 algorithm. It is designed to * have as little dependencies as possible so it can be used in the * kexec_file purgatory. In other cases you should generally use the @@ -164,4 +131,4 @@ static inline void sha224_init(struct sha256_state *sctx) void sha224_update(struct sha256_state *sctx, const u8 *data, unsigned int len); void sha224_final(struct sha256_state *sctx, u8 *out); -#endif +#endif /* _CRYPTO_SHA2_H */ diff --git a/include/crypto/sha256_base.h b/include/crypto/sha256_base.h index 6ded110783ae..76173c613058 100644 --- a/include/crypto/sha256_base.h +++ b/include/crypto/sha256_base.h @@ -9,9 +9,10 @@ #define _CRYPTO_SHA256_BASE_H #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/crypto.h> #include <linux/module.h> +#include <linux/string.h> #include <asm/unaligned.h> @@ -105,7 +106,7 @@ static inline int sha256_base_finish(struct shash_desc *desc, u8 *out) for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be32)) put_unaligned_be32(sctx->state[i], digest++); - *sctx = (struct sha256_state){}; + memzero_explicit(sctx, sizeof(*sctx)); return 0; } diff --git a/include/crypto/sha512_base.h b/include/crypto/sha512_base.h index fb19c77494dc..b370b3340b16 100644 --- a/include/crypto/sha512_base.h +++ b/include/crypto/sha512_base.h @@ -9,9 +9,10 @@ #define _CRYPTO_SHA512_BASE_H #include <crypto/internal/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/crypto.h> #include <linux/module.h> +#include <linux/string.h> #include <asm/unaligned.h> @@ -126,7 +127,7 @@ static inline int sha512_base_finish(struct shash_desc *desc, u8 *out) for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be64)) put_unaligned_be64(sctx->state[i], digest++); - *sctx = (struct sha512_state){}; + memzero_explicit(sctx, sizeof(*sctx)); return 0; } diff --git a/include/crypto/sm3_base.h b/include/crypto/sm3_base.h index 1cbf9aa1fe52..2f3a32ab97bb 100644 --- a/include/crypto/sm3_base.h +++ b/include/crypto/sm3_base.h @@ -13,6 +13,7 @@ #include <crypto/sm3.h> #include <linux/crypto.h> #include <linux/module.h> +#include <linux/string.h> #include <asm/unaligned.h> typedef void (sm3_block_fn)(struct sm3_state *sst, u8 const *src, int blocks); @@ -104,7 +105,7 @@ static inline int sm3_base_finish(struct shash_desc *desc, u8 *out) for (i = 0; i < SM3_DIGEST_SIZE / sizeof(__be32); i++) put_unaligned_be32(sctx->state[i], digest++); - *sctx = (struct sm3_state){}; + memzero_explicit(sctx, sizeof(*sctx)); return 0; } diff --git a/include/linux/ccp.h b/include/linux/ccp.h index a5dfbaf2470d..868924dec5a1 100644 --- a/include/linux/ccp.h +++ b/include/linux/ccp.h @@ -15,7 +15,8 @@ #include <linux/workqueue.h> #include <linux/list.h> #include <crypto/aes.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> +#include <crypto/sha2.h> struct ccp_device; struct ccp_cmd; diff --git a/include/linux/edac.h b/include/linux/edac.h index 15e8f3d8a895..76d3562d3006 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -175,11 +175,15 @@ static inline char *mc_event_error_type(const unsigned int err_type) * @MEM_RDDR3: Registered DDR3 RAM * This is a variant of the DDR3 memories. * @MEM_LRDDR3: Load-Reduced DDR3 memory. + * @MEM_LPDDR3: Low-Power DDR3 memory. * @MEM_DDR4: Unbuffered DDR4 RAM * @MEM_RDDR4: Registered DDR4 RAM * This is a variant of the DDR4 memories. * @MEM_LRDDR4: Load-Reduced DDR4 memory. + * @MEM_LPDDR4: Low-Power DDR4 memory. + * @MEM_DDR5: Unbuffered DDR5 RAM * @MEM_NVDIMM: Non-volatile RAM + * @MEM_WIO2: Wide I/O 2. */ enum mem_type { MEM_EMPTY = 0, @@ -200,10 +204,14 @@ enum mem_type { MEM_DDR3, MEM_RDDR3, MEM_LRDDR3, + MEM_LPDDR3, MEM_DDR4, MEM_RDDR4, MEM_LRDDR4, + MEM_LPDDR4, + MEM_DDR5, MEM_NVDIMM, + MEM_WIO2, }; #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) @@ -223,13 +231,17 @@ enum mem_type { #define MEM_FLAG_XDR BIT(MEM_XDR) #define MEM_FLAG_DDR3 BIT(MEM_DDR3) #define MEM_FLAG_RDDR3 BIT(MEM_RDDR3) +#define MEM_FLAG_LPDDR3 BIT(MEM_LPDDR3) #define MEM_FLAG_DDR4 BIT(MEM_DDR4) #define MEM_FLAG_RDDR4 BIT(MEM_RDDR4) #define MEM_FLAG_LRDDR4 BIT(MEM_LRDDR4) +#define MEM_FLAG_LPDDR4 BIT(MEM_LPDDR4) +#define MEM_FLAG_DDR5 BIT(MEM_DDR5) #define MEM_FLAG_NVDIMM BIT(MEM_NVDIMM) +#define MEM_FLAG_WIO2 BIT(MEM_WIO2) /** - * enum edac-type - Error Detection and Correction capabilities and mode + * enum edac_type - Error Detection and Correction capabilities and mode * @EDAC_UNKNOWN: Unknown if ECC is available * @EDAC_NONE: Doesn't support ECC * @EDAC_RESERVED: Reserved ECC type @@ -309,7 +321,7 @@ enum scrub_type { #define OP_OFFLINE 0x300 /** - * enum edac_mc_layer - memory controller hierarchy layer + * enum edac_mc_layer_type - memory controller hierarchy layer * * @EDAC_MC_LAYER_BRANCH: memory layer is named "branch" * @EDAC_MC_LAYER_CHANNEL: memory layer is named "channel" diff --git a/include/linux/elf.h b/include/linux/elf.h index 5d5b0321da0b..c9a46c4e183b 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -22,6 +22,16 @@ SET_PERSONALITY(ex) #endif +#ifndef START_THREAD +#define START_THREAD(elf_ex, regs, elf_entry, start_stack) \ + start_thread(regs, elf_entry, start_stack) +#endif + +#if defined(ARCH_HAS_SETUP_ADDITIONAL_PAGES) && !defined(ARCH_SETUP_ADDITIONAL_PAGES) +#define ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ + arch_setup_additional_pages(bprm, interpreter) +#endif + #define ELF32_GNU_PROPERTY_ALIGN 4 #define ELF64_GNU_PROPERTY_ALIGN 8 diff --git a/include/linux/filter.h b/include/linux/filter.h index 1b62397bd124..29c27656165b 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -21,7 +21,7 @@ #include <linux/if_vlan.h> #include <linux/vmalloc.h> #include <linux/sockptr.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <net/sch_generic.h> diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index a8f7a43f031b..d23156d1ac94 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -75,7 +75,7 @@ struct fscrypt_operations { static inline struct fscrypt_info *fscrypt_get_info(const struct inode *inode) { /* - * Pairs with the cmpxchg_release() in fscrypt_get_encryption_info(). + * Pairs with the cmpxchg_release() in fscrypt_setup_encryption_info(). * I.e., another task may publish ->i_crypt_info concurrently, executing * a RELEASE barrier. We need to use smp_load_acquire() here to safely * ACQUIRE the memory the other task published. @@ -111,6 +111,35 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) dentry->d_flags &= ~DCACHE_NOKEY_NAME; } +/** + * fscrypt_is_nokey_name() - test whether a dentry is a no-key name + * @dentry: the dentry to check + * + * This returns true if the dentry is a no-key dentry. A no-key dentry is a + * dentry that was created in an encrypted directory that hasn't had its + * encryption key added yet. Such dentries may be either positive or negative. + * + * When a filesystem is asked to create a new filename in an encrypted directory + * and the new filename's dentry is a no-key dentry, it must fail the operation + * with ENOKEY. This includes ->create(), ->mkdir(), ->mknod(), ->symlink(), + * ->rename(), and ->link(). (However, ->rename() and ->link() are already + * handled by fscrypt_prepare_rename() and fscrypt_prepare_link().) + * + * This is necessary because creating a filename requires the directory's + * encryption key, but just checking for the key on the directory inode during + * the final filesystem operation doesn't guarantee that the key was available + * during the preceding dentry lookup. And the key must have already been + * available during the dentry lookup in order for it to have been checked + * whether the filename already exists in the directory and for the new file's + * dentry not to be invalidated due to it incorrectly having the no-key flag. + * + * Return: %true if the dentry is a no-key name + */ +static inline bool fscrypt_is_nokey_name(const struct dentry *dentry) +{ + return dentry->d_flags & DCACHE_NOKEY_NAME; +} + /* crypto.c */ void fscrypt_enqueue_decrypt_work(struct work_struct *); @@ -171,7 +200,6 @@ int fscrypt_ioctl_remove_key_all_users(struct file *filp, void __user *arg); int fscrypt_ioctl_get_key_status(struct file *filp, void __user *arg); /* keysetup.c */ -int fscrypt_get_encryption_info(struct inode *inode); int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode, bool *encrypt_ret); void fscrypt_put_encryption_info(struct inode *inode); @@ -213,6 +241,8 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, unsigned int flags); int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry, struct fscrypt_name *fname); +int __fscrypt_prepare_readdir(struct inode *dir); +int __fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr); int fscrypt_prepare_setflags(struct inode *inode, unsigned int oldflags, unsigned int flags); int fscrypt_prepare_symlink(struct inode *dir, const char *target, @@ -244,6 +274,11 @@ static inline void fscrypt_handle_d_move(struct dentry *dentry) { } +static inline bool fscrypt_is_nokey_name(const struct dentry *dentry) +{ + return false; +} + /* crypto.c */ static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work) { @@ -372,10 +407,6 @@ static inline int fscrypt_ioctl_get_key_status(struct file *filp, } /* keysetup.c */ -static inline int fscrypt_get_encryption_info(struct inode *inode) -{ - return -EOPNOTSUPP; -} static inline int fscrypt_prepare_new_inode(struct inode *dir, struct inode *inode, @@ -503,6 +534,17 @@ static inline int __fscrypt_prepare_lookup(struct inode *dir, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_readdir(struct inode *dir) +{ + return -EOPNOTSUPP; +} + +static inline int __fscrypt_prepare_setattr(struct dentry *dentry, + struct iattr *attr) +{ + return -EOPNOTSUPP; +} + static inline int fscrypt_prepare_setflags(struct inode *inode, unsigned int oldflags, unsigned int flags) @@ -642,32 +684,6 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) } /** - * fscrypt_require_key() - require an inode's encryption key - * @inode: the inode we need the key for - * - * If the inode is encrypted, set up its encryption key if not already done. - * Then require that the key be present and return -ENOKEY otherwise. - * - * No locks are needed, and the key will live as long as the struct inode --- so - * it won't go away from under you. - * - * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code - * if a problem occurred while setting up the encryption key. - */ -static inline int fscrypt_require_key(struct inode *inode) -{ - if (IS_ENCRYPTED(inode)) { - int err = fscrypt_get_encryption_info(inode); - - if (err) - return err; - if (!fscrypt_has_encryption_key(inode)) - return -ENOKEY; - } - return 0; -} - -/** * fscrypt_prepare_link() - prepare to link an inode into a possibly-encrypted * directory * @old_dentry: an existing dentry for the inode being linked @@ -676,8 +692,7 @@ static inline int fscrypt_require_key(struct inode *inode) * * A new link can only be added to an encrypted directory if the directory's * encryption key is available --- since otherwise we'd have no way to encrypt - * the filename. Therefore, we first set up the directory's encryption key (if - * not already done) and return an error if it's unavailable. + * the filename. * * We also verify that the link will not violate the constraint that all files * in an encrypted directory tree use the same encryption policy. @@ -738,8 +753,9 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir, * * Prepare for ->lookup() in a directory which may be encrypted by determining * the name that will actually be used to search the directory on-disk. If the - * directory's encryption key is available, then the lookup is assumed to be by - * plaintext name; otherwise, it is assumed to be by no-key name. + * directory's encryption policy is supported by this kernel and its encryption + * key is available, then the lookup is assumed to be by plaintext name; + * otherwise, it is assumed to be by no-key name. * * This also installs a custom ->d_revalidate() method which will invalidate the * dentry if it was created without the key and the key is later added. @@ -763,6 +779,26 @@ static inline int fscrypt_prepare_lookup(struct inode *dir, } /** + * fscrypt_prepare_readdir() - prepare to read a possibly-encrypted directory + * @dir: the directory inode + * + * If the directory is encrypted and it doesn't already have its encryption key + * set up, try to set it up so that the filenames will be listed in plaintext + * form rather than in no-key form. + * + * Return: 0 on success; -errno on error. Note that the encryption key being + * unavailable is not considered an error. It is also not an error if + * the encryption policy is unsupported by this kernel; that is treated + * like the key being unavailable, so that files can still be deleted. + */ +static inline int fscrypt_prepare_readdir(struct inode *dir) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_readdir(dir); + return 0; +} + +/** * fscrypt_prepare_setattr() - prepare to change a possibly-encrypted inode's * attributes * @dentry: dentry through which the inode is being changed @@ -783,8 +819,8 @@ static inline int fscrypt_prepare_lookup(struct inode *dir, static inline int fscrypt_prepare_setattr(struct dentry *dentry, struct iattr *attr) { - if (attr->ia_valid & ATTR_SIZE) - return fscrypt_require_key(d_inode(dentry)); + if (IS_ENCRYPTED(d_inode(dentry))) + return __fscrypt_prepare_setattr(dentry, attr); return 0; } diff --git a/include/linux/mm.h b/include/linux/mm.h index db6ae4d3fb4e..1813fa86b981 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -559,6 +559,13 @@ struct vm_operations_struct { void (*close)(struct vm_area_struct * area); int (*split)(struct vm_area_struct * area, unsigned long addr); int (*mremap)(struct vm_area_struct * area); + /* + * Called by mprotect() to make driver-specific permission + * checks before mprotect() is finalised. The VMA must not + * be modified. Returns 0 if eprotect() can proceed. + */ + int (*mprotect)(struct vm_area_struct *vma, unsigned long start, + unsigned long end, unsigned long newflags); vm_fault_t (*fault)(struct vm_fault *vmf); vm_fault_t (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size); diff --git a/include/linux/platform_data/media/coda.h b/include/linux/platform_data/media/coda.h deleted file mode 100644 index 293b61b60c9d..000000000000 --- a/include/linux/platform_data/media/coda.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2013 Philipp Zabel, Pengutronix - */ -#ifndef PLATFORM_CODA_H -#define PLATFORM_CODA_H - -struct device; - -struct coda_platform_data { - struct device *iram_dev; -}; - -#endif diff --git a/include/linux/purgatory.h b/include/linux/purgatory.h index b950e961cfa8..d7dc1559427f 100644 --- a/include/linux/purgatory.h +++ b/include/linux/purgatory.h @@ -3,7 +3,7 @@ #define _LINUX_PURGATORY_H #include <linux/types.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <uapi/linux/kexec.h> struct kexec_sha_region { 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/fwht-ctrls.h b/include/media/fwht-ctrls.h deleted file mode 100644 index 615027410e47..000000000000 --- a/include/media/fwht-ctrls.h +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * These are the FWHT state controls for use with stateless FWHT - * codec drivers. - * - * It turns out that these structs are not stable yet and will undergo - * more changes. So keep them private until they are stable and ready to - * become part of the official public API. - */ - -#ifndef _FWHT_CTRLS_H_ -#define _FWHT_CTRLS_H_ - -#define V4L2_CTRL_TYPE_FWHT_PARAMS 0x0105 - -#define V4L2_CID_MPEG_VIDEO_FWHT_PARAMS (V4L2_CID_MPEG_BASE + 292) - -struct v4l2_ctrl_fwht_params { - __u64 backward_ref_ts; - __u32 version; - __u32 width; - __u32 height; - __u32 flags; - __u32 colorspace; - __u32 xfer_func; - __u32 ycbcr_enc; - __u32 quantization; -}; - - -#endif diff --git a/include/media/h264-ctrls.h b/include/media/h264-ctrls.h deleted file mode 100644 index ec4799154438..000000000000 --- a/include/media/h264-ctrls.h +++ /dev/null @@ -1,231 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * These are the H.264 state controls for use with stateless H.264 - * codec drivers. - * - * It turns out that these structs are not stable yet and will undergo - * more changes. So keep them private until they are stable and ready to - * become part of the official public API. - */ - -#ifndef _H264_CTRLS_H_ -#define _H264_CTRLS_H_ - -#include <linux/videodev2.h> - -/* - * Maximum DPB size, as specified by section 'A.3.1 Level limits - * common to the Baseline, Main, and Extended profiles'. - */ -#define V4L2_H264_NUM_DPB_ENTRIES 16 - -#define V4L2_H264_REF_LIST_LEN (2 * V4L2_H264_NUM_DPB_ENTRIES) - -/* Our pixel format isn't stable at the moment */ -#define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ - -/* - * This is put insanely high to avoid conflicting with controls that - * would be added during the phase where those controls are not - * stable. It should be fixed eventually. - */ -#define V4L2_CID_MPEG_VIDEO_H264_SPS (V4L2_CID_MPEG_BASE+1000) -#define V4L2_CID_MPEG_VIDEO_H264_PPS (V4L2_CID_MPEG_BASE+1001) -#define V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX (V4L2_CID_MPEG_BASE+1002) -#define V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS (V4L2_CID_MPEG_BASE+1003) -#define V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS (V4L2_CID_MPEG_BASE+1004) -#define V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (V4L2_CID_MPEG_BASE+1005) -#define V4L2_CID_MPEG_VIDEO_H264_START_CODE (V4L2_CID_MPEG_BASE+1006) -#define V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS (V4L2_CID_MPEG_BASE+1007) - -/* enum v4l2_ctrl_type type values */ -#define V4L2_CTRL_TYPE_H264_SPS 0x0110 -#define V4L2_CTRL_TYPE_H264_PPS 0x0111 -#define V4L2_CTRL_TYPE_H264_SCALING_MATRIX 0x0112 -#define V4L2_CTRL_TYPE_H264_SLICE_PARAMS 0x0113 -#define V4L2_CTRL_TYPE_H264_DECODE_PARAMS 0x0114 -#define V4L2_CTRL_TYPE_H264_PRED_WEIGHTS 0x0115 - -enum v4l2_mpeg_video_h264_decode_mode { - V4L2_MPEG_VIDEO_H264_DECODE_MODE_SLICE_BASED, - V4L2_MPEG_VIDEO_H264_DECODE_MODE_FRAME_BASED, -}; - -enum v4l2_mpeg_video_h264_start_code { - V4L2_MPEG_VIDEO_H264_START_CODE_NONE, - V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, -}; - -#define V4L2_H264_SPS_CONSTRAINT_SET0_FLAG 0x01 -#define V4L2_H264_SPS_CONSTRAINT_SET1_FLAG 0x02 -#define V4L2_H264_SPS_CONSTRAINT_SET2_FLAG 0x04 -#define V4L2_H264_SPS_CONSTRAINT_SET3_FLAG 0x08 -#define V4L2_H264_SPS_CONSTRAINT_SET4_FLAG 0x10 -#define V4L2_H264_SPS_CONSTRAINT_SET5_FLAG 0x20 - -#define V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE 0x01 -#define V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS 0x02 -#define V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO 0x04 -#define V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED 0x08 -#define V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY 0x10 -#define V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD 0x20 -#define V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE 0x40 - -struct v4l2_ctrl_h264_sps { - __u8 profile_idc; - __u8 constraint_set_flags; - __u8 level_idc; - __u8 seq_parameter_set_id; - __u8 chroma_format_idc; - __u8 bit_depth_luma_minus8; - __u8 bit_depth_chroma_minus8; - __u8 log2_max_frame_num_minus4; - __u8 pic_order_cnt_type; - __u8 log2_max_pic_order_cnt_lsb_minus4; - __u8 max_num_ref_frames; - __u8 num_ref_frames_in_pic_order_cnt_cycle; - __s32 offset_for_ref_frame[255]; - __s32 offset_for_non_ref_pic; - __s32 offset_for_top_to_bottom_field; - __u16 pic_width_in_mbs_minus1; - __u16 pic_height_in_map_units_minus1; - __u32 flags; -}; - -#define V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE 0x0001 -#define V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT 0x0002 -#define V4L2_H264_PPS_FLAG_WEIGHTED_PRED 0x0004 -#define V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT 0x0008 -#define V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED 0x0010 -#define V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT 0x0020 -#define V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE 0x0040 -#define V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT 0x0080 - -struct v4l2_ctrl_h264_pps { - __u8 pic_parameter_set_id; - __u8 seq_parameter_set_id; - __u8 num_slice_groups_minus1; - __u8 num_ref_idx_l0_default_active_minus1; - __u8 num_ref_idx_l1_default_active_minus1; - __u8 weighted_bipred_idc; - __s8 pic_init_qp_minus26; - __s8 pic_init_qs_minus26; - __s8 chroma_qp_index_offset; - __s8 second_chroma_qp_index_offset; - __u16 flags; -}; - -struct v4l2_ctrl_h264_scaling_matrix { - __u8 scaling_list_4x4[6][16]; - __u8 scaling_list_8x8[6][64]; -}; - -struct v4l2_h264_weight_factors { - __s16 luma_weight[32]; - __s16 luma_offset[32]; - __s16 chroma_weight[32][2]; - __s16 chroma_offset[32][2]; -}; - -#define V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(pps, slice) \ - ((((pps)->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) && \ - ((slice)->slice_type == V4L2_H264_SLICE_TYPE_P || \ - (slice)->slice_type == V4L2_H264_SLICE_TYPE_SP)) || \ - ((pps)->weighted_bipred_idc == 1 && \ - (slice)->slice_type == V4L2_H264_SLICE_TYPE_B)) - -struct v4l2_ctrl_h264_pred_weights { - __u16 luma_log2_weight_denom; - __u16 chroma_log2_weight_denom; - struct v4l2_h264_weight_factors weight_factors[2]; -}; - -#define V4L2_H264_SLICE_TYPE_P 0 -#define V4L2_H264_SLICE_TYPE_B 1 -#define V4L2_H264_SLICE_TYPE_I 2 -#define V4L2_H264_SLICE_TYPE_SP 3 -#define V4L2_H264_SLICE_TYPE_SI 4 - -#define V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED 0x01 -#define V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH 0x02 - -#define V4L2_H264_TOP_FIELD_REF 0x1 -#define V4L2_H264_BOTTOM_FIELD_REF 0x2 -#define V4L2_H264_FRAME_REF 0x3 - -struct v4l2_h264_reference { - __u8 fields; - - /* Index into v4l2_ctrl_h264_decode_params.dpb[] */ - __u8 index; -}; - -struct v4l2_ctrl_h264_slice_params { - /* Offset in bits to slice_data() from the beginning of this slice. */ - __u32 header_bit_size; - - __u32 first_mb_in_slice; - - __u8 slice_type; - __u8 colour_plane_id; - __u8 redundant_pic_cnt; - __u8 cabac_init_idc; - __s8 slice_qp_delta; - __s8 slice_qs_delta; - __u8 disable_deblocking_filter_idc; - __s8 slice_alpha_c0_offset_div2; - __s8 slice_beta_offset_div2; - __u8 num_ref_idx_l0_active_minus1; - __u8 num_ref_idx_l1_active_minus1; - - __u8 reserved; - - struct v4l2_h264_reference ref_pic_list0[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference ref_pic_list1[V4L2_H264_REF_LIST_LEN]; - - __u32 flags; -}; - -#define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 -#define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 -#define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 -#define V4L2_H264_DPB_ENTRY_FLAG_FIELD 0x08 - -struct v4l2_h264_dpb_entry { - __u64 reference_ts; - __u32 pic_num; - __u16 frame_num; - __u8 fields; - __u8 reserved[5]; - /* Note that field is indicated by v4l2_buffer.field */ - __s32 top_field_order_cnt; - __s32 bottom_field_order_cnt; - __u32 flags; /* V4L2_H264_DPB_ENTRY_FLAG_* */ -}; - -#define V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC 0x01 -#define V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC 0x02 -#define V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD 0x04 - -struct v4l2_ctrl_h264_decode_params { - struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]; - __u16 nal_ref_idc; - __u16 frame_num; - __s32 top_field_order_cnt; - __s32 bottom_field_order_cnt; - __u16 idr_pic_id; - __u16 pic_order_cnt_lsb; - __s32 delta_pic_order_cnt_bottom; - __s32 delta_pic_order_cnt0; - __s32 delta_pic_order_cnt1; - /* Size in bits of dec_ref_pic_marking() syntax element. */ - __u32 dec_ref_pic_marking_bit_size; - /* Size in bits of pic order count syntax. */ - __u32 pic_order_cnt_bit_size; - __u32 slice_group_change_cycle; - - __u32 reserved; - __u32 flags; /* V4L2_H264_DECODE_PARAM_FLAG_* */ -}; - -#endif diff --git a/include/media/hevc-ctrls.h b/include/media/hevc-ctrls.h index 1009cf0891cc..b4cb2ef02f17 100644 --- a/include/media/hevc-ctrls.h +++ b/include/media/hevc-ctrls.h @@ -16,11 +16,11 @@ /* The pixel format isn't stable at the moment and will likely be renamed. */ #define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */ -#define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_MPEG_BASE + 1008) -#define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_MPEG_BASE + 1009) -#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010) -#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_MPEG_BASE + 1015) -#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_MPEG_BASE + 1016) +#define V4L2_CID_MPEG_VIDEO_HEVC_SPS (V4L2_CID_CODEC_BASE + 1008) +#define V4L2_CID_MPEG_VIDEO_HEVC_PPS (V4L2_CID_CODEC_BASE + 1009) +#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_CODEC_BASE + 1010) +#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (V4L2_CID_CODEC_BASE + 1015) +#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (V4L2_CID_CODEC_BASE + 1016) /* enum v4l2_ctrl_type type values */ #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120 diff --git a/include/media/mpeg2-ctrls.h b/include/media/mpeg2-ctrls.h index 6601455b3d5e..2a4ae6701166 100644 --- a/include/media/mpeg2-ctrls.h +++ b/include/media/mpeg2-ctrls.h @@ -11,8 +11,8 @@ #ifndef _MPEG2_CTRLS_H_ #define _MPEG2_CTRLS_H_ -#define V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (V4L2_CID_MPEG_BASE+250) -#define V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (V4L2_CID_MPEG_BASE+251) +#define V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS (V4L2_CID_CODEC_BASE+250) +#define V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION (V4L2_CID_CODEC_BASE+251) /* enum v4l2_ctrl_type type values */ #define V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS 0x0103 diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 7dbb91c601a7..999b750bc6b8 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -263,6 +263,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_IT913X_V2 "rc-it913x-v2" #define RC_MAP_KAIOMY "rc-kaiomy" #define RC_MAP_KHADAS "rc-khadas" +#define RC_MAP_KHAMSIN "rc-khamsin" #define RC_MAP_KWORLD_315U "rc-kworld-315u" #define RC_MAP_KWORLD_PC150U "rc-kworld-pc150u" #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" @@ -282,6 +283,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_NPGTECH "rc-npgtech" #define RC_MAP_ODROID "rc-odroid" #define RC_MAP_PCTV_SEDNA "rc-pctv-sedna" +#define RC_MAP_PINE64 "rc-pine64" #define RC_MAP_PINNACLE_COLOR "rc-pinnacle-color" #define RC_MAP_PINNACLE_GREY "rc-pinnacle-grey" #define RC_MAP_PINNACLE_PCTV_HD "rc-pinnacle-pctv-hd" diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index d6e31234826f..0e04b5b2ebb0 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -27,7 +27,7 @@ struct v4l2_async_notifier; * @V4L2_ASYNC_MATCH_I2C: Match will check for I2C adapter ID and address * @V4L2_ASYNC_MATCH_FWNODE: Match will use firmware node * - * This enum is used by the asyncrhronous sub-device logic to define the + * This enum is used by the asynchronous sub-device logic to define the * algorithm that will be used to match an asynchronous device. */ enum v4l2_async_match_type { diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index a3083529b698..be36cbdcc1bd 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -519,6 +519,27 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, u32 width, u32 height); +/** + * v4l2_get_link_rate - Get link rate from transmitter + * + * @handler: The transmitter's control handler + * @mul: The multiplier between pixel rate and link frequency. Bits per pixel on + * D-PHY, samples per clock on parallel. 0 otherwise. + * @div: The divisor between pixel rate and link frequency. Number of data lanes + * times two on D-PHY, 1 on parallel. 0 otherwise. + * + * This function is intended for obtaining the link frequency from the + * transmitter sub-devices. It returns the link rate, either from the + * V4L2_CID_LINK_FREQ control implemented by the transmitter, or value + * calculated based on the V4L2_CID_PIXEL_RATE implemented by the transmitter. + * + * Returns link frequency on success, otherwise a negative error code: + * -ENOENT: Link frequency or pixel rate control not found + * -EINVAL: Invalid link frequency value + */ +s64 v4l2_get_link_rate(struct v4l2_ctrl_handler *handler, unsigned int mul, + unsigned int div); + static inline u64 v4l2_buffer_get_timestamp(const struct v4l2_buffer *buf) { /* diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index cb25f345e9ad..167ca8c8424f 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -18,8 +18,6 @@ * This will move to the public headers once this API is fully stable. */ #include <media/mpeg2-ctrls.h> -#include <media/fwht-ctrls.h> -#include <media/h264-ctrls.h> #include <media/vp8-ctrls.h> #include <media/hevc-ctrls.h> @@ -1292,7 +1290,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 c09074276543..4365430eea6f 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -219,23 +219,35 @@ struct v4l2_fwnode_connector { * @vep: pointer to the V4L2 fwnode data structure * * This function parses the V4L2 fwnode endpoint specific parameters from the - * firmware. The caller is responsible for assigning @vep.bus_type to a valid - * media bus type. The caller may also set the default configuration for the - * endpoint --- a configuration that shall be in line with the DT binding - * documentation. Should a device support multiple bus types, the caller may - * call this function once the correct type is found --- with a default - * configuration valid for that type. - * - * It is also allowed to set @vep.bus_type to V4L2_MBUS_UNKNOWN. USING THIS - * FEATURE REQUIRES "bus-type" PROPERTY IN DT BINDINGS. For old drivers, - * guessing @vep.bus_type between CSI-2 D-PHY, parallel and BT.656 busses is - * supported. NEVER RELY ON GUESSING @vep.bus_type IN NEW DRIVERS! + * firmware. There are two ways to use this function, either by letting it + * obtain the type of the bus (by setting the @vep.bus_type field to + * V4L2_MBUS_UNKNOWN) or specifying the bus type explicitly to one of the &enum + * v4l2_mbus_type types. + * + * When @vep.bus_type is V4L2_MBUS_UNKNOWN, the function will use the "bus-type" + * property to determine the type when it is available. The caller is + * responsible for validating the contents of @vep.bus_type field after the call + * returns. + * + * As a deprecated functionality to support older DT bindings without "bus-type" + * property for devices that support multiple types, if the "bus-type" property + * does not exist, the function will attempt to guess the type based on the + * endpoint properties available. NEVER RELY ON GUESSING THE BUS TYPE IN NEW + * DRIVERS OR BINDINGS. + * + * It is also possible to set @vep.bus_type corresponding to an actual bus. In + * this case the function will only attempt to parse properties related to this + * bus, and it will return an error if the value of the "bus-type" property + * corresponds to a different bus. + * + * The caller is required to initialise all fields of @vep, either with + * explicitly values, or by zeroing them. * * The function does not change the V4L2 fwnode endpoint state if it fails. * - * NOTE: This function does not parse properties the size of which is variable - * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in - * new drivers instead. + * NOTE: This function does not parse "link-frequencies" property as its size is + * not known in advance. Please use v4l2_fwnode_endpoint_alloc_parse() if you + * need properties of variable size. * * Return: %0 on success or a negative error code on failure: * %-ENOMEM on memory allocation failure @@ -261,17 +273,29 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); * @vep: pointer to the V4L2 fwnode data structure * * This function parses the V4L2 fwnode endpoint specific parameters from the - * firmware. The caller is responsible for assigning @vep.bus_type to a valid - * media bus type. The caller may also set the default configuration for the - * endpoint --- a configuration that shall be in line with the DT binding - * documentation. Should a device support multiple bus types, the caller may - * call this function once the correct type is found --- with a default - * configuration valid for that type. - * - * It is also allowed to set @vep.bus_type to V4L2_MBUS_UNKNOWN. USING THIS - * FEATURE REQUIRES "bus-type" PROPERTY IN DT BINDINGS. For old drivers, - * guessing @vep.bus_type between CSI-2 D-PHY, parallel and BT.656 busses is - * supported. NEVER RELY ON GUESSING @vep.bus_type IN NEW DRIVERS! + * firmware. There are two ways to use this function, either by letting it + * obtain the type of the bus (by setting the @vep.bus_type field to + * V4L2_MBUS_UNKNOWN) or specifying the bus type explicitly to one of the &enum + * v4l2_mbus_type types. + * + * When @vep.bus_type is V4L2_MBUS_UNKNOWN, the function will use the "bus-type" + * property to determine the type when it is available. The caller is + * responsible for validating the contents of @vep.bus_type field after the call + * returns. + * + * As a deprecated functionality to support older DT bindings without "bus-type" + * property for devices that support multiple types, if the "bus-type" property + * does not exist, the function will attempt to guess the type based on the + * endpoint properties available. NEVER RELY ON GUESSING THE BUS TYPE IN NEW + * DRIVERS OR BINDINGS. + * + * It is also possible to set @vep.bus_type corresponding to an actual bus. In + * this case the function will only attempt to parse properties related to this + * bus, and it will return an error if the value of the "bus-type" property + * corresponds to a different bus. + * + * The caller is required to initialise all fields of @vep, either with + * explicitly values, or by zeroing them. * * The function does not change the V4L2 fwnode endpoint state if it fails. * @@ -461,60 +485,7 @@ v4l2_async_notifier_parse_fwnode_endpoints(struct device *dev, parse_endpoint_func parse_endpoint); /** - * v4l2_async_notifier_parse_fwnode_endpoints_by_port - Parse V4L2 fwnode - * endpoints of a port in a - * device node - * @dev: the device the endpoints of which are to be parsed - * @notifier: notifier for @dev - * @asd_struct_size: size of the driver's async sub-device struct, including - * sizeof(struct v4l2_async_subdev). The &struct - * v4l2_async_subdev shall be the first member of - * the driver's async sub-device struct, i.e. both - * begin at the same memory address. - * @port: port number where endpoints are to be parsed - * @parse_endpoint: Driver's callback function called on each V4L2 fwnode - * endpoint. Optional. - * - * This function is just like v4l2_async_notifier_parse_fwnode_endpoints() with - * the exception that it only parses endpoints in a given port. This is useful - * on devices that have both sinks and sources: the async sub-devices connected - * to sources have already been configured by another driver (on capture - * devices). In this case the driver must know which ports to parse. - * - * Parse the fwnode endpoints of the @dev device on a given @port and populate - * the async sub-devices list of the notifier. The @parse_endpoint callback - * function is called for each endpoint with the corresponding async sub-device - * pointer to let the caller initialize the driver-specific part of the async - * sub-device structure. - * - * The notifier memory shall be zeroed before this function is called on the - * notifier the first time. - * - * This function may not be called on a registered notifier and may be called on - * a notifier only once per port. - * - * The &struct v4l2_fwnode_endpoint passed to the callback function - * @parse_endpoint is released once the function is finished. If there is a need - * to retain that configuration, the user needs to allocate memory for it. - * - * Any notifier populated using this function must be released with a call to - * v4l2_async_notifier_cleanup() after it has been unregistered and the async - * sub-devices are no longer in use, even if the function returned an error. - * - * Return: %0 on success, including when no async sub-devices are found - * %-ENOMEM if memory allocation failed - * %-EINVAL if graph or endpoint parsing failed - * Other error codes as returned by @parse_endpoint - */ -int -v4l2_async_notifier_parse_fwnode_endpoints_by_port(struct device *dev, - struct v4l2_async_notifier *notifier, - size_t asd_struct_size, - unsigned int port, - 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..d2314f4d4490 100644 --- a/include/media/v4l2-h264.h +++ b/include/media/v4l2-h264.h @@ -10,7 +10,7 @@ #ifndef _MEDIA_V4L2_H264_H #define _MEDIA_V4L2_H264_H -#include <media/h264-ctrls.h> +#include <media/v4l2-ctrls.h> /** * struct v4l2_h264_reflist_builder - Reference list builder object diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 86878fba332b..edb733f21604 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -686,6 +686,16 @@ long int v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); #endif +unsigned int v4l2_compat_translate_cmd(unsigned int cmd); +int v4l2_compat_get_user(void __user *arg, void *parg, unsigned int cmd); +int v4l2_compat_put_user(void __user *arg, void *parg, unsigned int cmd); +int v4l2_compat_get_array_args(struct file *file, void *mbuf, + void __user *user_ptr, size_t array_size, + unsigned int cmd, void *arg); +int v4l2_compat_put_array_args(struct file *file, void __user *user_ptr, + void *mbuf, size_t array_size, + unsigned int cmd, void *arg); + /** * typedef v4l2_kioctl - Typedef used to pass an ioctl handler. * diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 59b1de197114..841e190aedd9 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -103,6 +103,7 @@ * @V4L2_MBUS_CCP2: CCP2 (Compact Camera Port 2) * @V4L2_MBUS_CSI2_DPHY: MIPI CSI-2 serial interface, with D-PHY * @V4L2_MBUS_CSI2_CPHY: MIPI CSI-2 serial interface, with C-PHY + * @V4L2_MBUS_INVALID: invalid bus type (keep as last) */ enum v4l2_mbus_type { V4L2_MBUS_UNKNOWN, @@ -112,6 +113,7 @@ enum v4l2_mbus_type { V4L2_MBUS_CCP2, V4L2_MBUS_CSI2_DPHY, V4L2_MBUS_CSI2_CPHY, + V4L2_MBUS_INVALID, }; /** @@ -145,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. * @@ -168,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 @@ -188,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 1de960bfcab9..d0e9a5bdb08b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -309,7 +309,7 @@ struct v4l2_subdev_audio_ops { }; /** - * enum v4l2_mbus_frame_desc_entry - media bus frame description flags + * enum v4l2_mbus_frame_desc_flags - media bus frame description flags * * @V4L2_MBUS_FRAME_DESC_FL_LEN_MAX: * Indicates that &struct v4l2_mbus_frame_desc_entry->length field diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index bbb3f26fbde9..61969402a0e3 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/media/vp8-ctrls.h b/include/media/vp8-ctrls.h index 53cba826e482..3969550df148 100644 --- a/include/media/vp8-ctrls.h +++ b/include/media/vp8-ctrls.h @@ -15,7 +15,7 @@ #define V4L2_PIX_FMT_VP8_FRAME v4l2_fourcc('V', 'P', '8', 'F') -#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER (V4L2_CID_MPEG_BASE + 2000) +#define V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER (V4L2_CID_CODEC_BASE + 2000) #define V4L2_CTRL_TYPE_VP8_FRAME_HEADER 0x301 #define V4L2_VP8_SEGMENT_HEADER_FLAG_ENABLED 0x01 @@ -53,11 +53,13 @@ struct v4l2_vp8_quantization_header { __u16 padding; }; +#define V4L2_VP8_COEFF_PROB_CNT 11 +#define V4L2_VP8_MV_PROB_CNT 19 struct v4l2_vp8_entropy_header { - __u8 coeff_probs[4][8][3][11]; + __u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT]; __u8 y_mode_probs[4]; __u8 uv_mode_probs[3]; - __u8 mv_probs[2][19]; + __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT]; __u8 padding[3]; }; diff --git a/include/uapi/linux/fscrypt.h b/include/uapi/linux/fscrypt.h index e5de60336938..9f4428be3e36 100644 --- a/include/uapi/linux/fscrypt.h +++ b/include/uapi/linux/fscrypt.h @@ -20,7 +20,6 @@ #define FSCRYPT_POLICY_FLAG_DIRECT_KEY 0x04 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 #define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 0x10 -#define FSCRYPT_POLICY_FLAGS_VALID 0x1F /* Encryption algorithms */ #define FSCRYPT_MODE_AES_256_XTS 1 @@ -28,7 +27,7 @@ #define FSCRYPT_MODE_AES_128_CBC 5 #define FSCRYPT_MODE_AES_128_CTS 6 #define FSCRYPT_MODE_ADIANTUM 9 -#define __FSCRYPT_MODE_MAX 9 +/* If adding a mode number > 9, update FSCRYPT_MODE_MAX in fscrypt_private.h */ /* * Legacy policy version; ad-hoc KDF and no key verification. @@ -177,7 +176,7 @@ struct fscrypt_get_key_status_arg { #define FS_POLICY_FLAGS_PAD_32 FSCRYPT_POLICY_FLAGS_PAD_32 #define FS_POLICY_FLAGS_PAD_MASK FSCRYPT_POLICY_FLAGS_PAD_MASK #define FS_POLICY_FLAG_DIRECT_KEY FSCRYPT_POLICY_FLAG_DIRECT_KEY -#define FS_POLICY_FLAGS_VALID FSCRYPT_POLICY_FLAGS_VALID +#define FS_POLICY_FLAGS_VALID 0x07 /* contains old flags only */ #define FS_ENCRYPTION_MODE_INVALID 0 /* never used */ #define FS_ENCRYPTION_MODE_AES_256_XTS FSCRYPT_MODE_AES_256_XTS #define FS_ENCRYPTION_MODE_AES_256_GCM 2 /* never used */ diff --git a/include/uapi/linux/fsverity.h b/include/uapi/linux/fsverity.h index da0daf6c193b..33f44156f8ea 100644 --- a/include/uapi/linux/fsverity.h +++ b/include/uapi/linux/fsverity.h @@ -34,6 +34,55 @@ struct fsverity_digest { __u8 digest[]; }; +/* + * Struct containing a file's Merkle tree properties. The fs-verity file digest + * is the hash of this struct. A userspace program needs this struct only if it + * needs to compute fs-verity file digests itself, e.g. in order to sign files. + * It isn't needed just to enable fs-verity on a file. + * + * Note: when computing the file digest, 'sig_size' and 'signature' must be left + * zero and empty, respectively. These fields are present only because some + * filesystems reuse this struct as part of their on-disk format. + */ +struct fsverity_descriptor { + __u8 version; /* must be 1 */ + __u8 hash_algorithm; /* Merkle tree hash algorithm */ + __u8 log_blocksize; /* log2 of size of data and tree blocks */ + __u8 salt_size; /* size of salt in bytes; 0 if none */ +#ifdef __KERNEL__ + __le32 sig_size; +#else + __le32 __reserved_0x04; /* must be 0 */ +#endif + __le64 data_size; /* size of file the Merkle tree is built over */ + __u8 root_hash[64]; /* Merkle tree root hash */ + __u8 salt[32]; /* salt prepended to each hashed block */ + __u8 __reserved[144]; /* must be 0's */ +#ifdef __KERNEL__ + __u8 signature[]; +#endif +}; + +/* + * Format in which fs-verity file digests are signed in built-in signatures. + * This is the same as 'struct fsverity_digest', except here some magic bytes + * are prepended to provide some context about what is being signed in case the + * same key is used for non-fsverity purposes, and here the fields have fixed + * endianness. + * + * This struct is specific to the built-in signature verification support, which + * is optional. fs-verity users may also verify signatures in userspace, in + * which case userspace is responsible for deciding on what bytes are signed. + * This struct may still be used, but it doesn't have to be. For example, + * userspace could instead use a string like "sha256:$digest_as_hex_string". + */ +struct fsverity_formatted_digest { + char magic[8]; /* must be "FSVerity" */ + __le16 digest_algorithm; + __le16 digest_size; + __u8 digest[]; +}; + #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) #define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h index 60b7c2efd921..dc52a11ba6d1 100644 --- a/include/uapi/linux/if_alg.h +++ b/include/uapi/linux/if_alg.h @@ -24,6 +24,22 @@ struct sockaddr_alg { __u8 salg_name[64]; }; +/* + * Linux v4.12 and later removed the 64-byte limit on salg_name[]; it's now an + * arbitrary-length field. We had to keep the original struct above for source + * compatibility with existing userspace programs, though. Use the new struct + * below if support for very long algorithm names is needed. To do this, + * allocate 'sizeof(struct sockaddr_alg_new) + strlen(algname) + 1' bytes, and + * copy algname (including the null terminator) into salg_name. + */ +struct sockaddr_alg_new { + __u16 salg_family; + __u8 salg_type[14]; + __u32 salg_feat; + __u32 salg_mask; + __u8 salg_name[]; +}; + struct af_alg_iv { __u32 ivlen; __u8 iv[0]; diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index f99d9dcae667..c45a4eaea667 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h @@ -139,7 +139,7 @@ */ #define LIRC_GET_REC_TIMEOUT _IOR('i', 0x00000024, __u32) -/* +/** * struct lirc_scancode - decoded scancode with protocol for use with * LIRC_MODE_SCANCODE * @@ -196,6 +196,7 @@ struct lirc_scancode { * @RC_PROTO_RCMM24: RC-MM protocol 24 bits * @RC_PROTO_RCMM32: RC-MM protocol 32 bits * @RC_PROTO_XBOX_DVD: Xbox DVD Movie Playback Kit protocol + * @RC_PROTO_MAX: Maximum value of enum rc_proto */ enum rc_proto { RC_PROTO_UNKNOWN = 0, @@ -226,6 +227,7 @@ enum rc_proto { RC_PROTO_RCMM24 = 25, RC_PROTO_RCMM32 = 26, RC_PROTO_XBOX_DVD = 27, + RC_PROTO_MAX = RC_PROTO_XBOX_DVD, }; #endif diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 5d905ad6dbb2..0dfc11ee243a 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -157,4 +157,12 @@ /* HSV - next is 0x6002 */ #define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001 +/* + * This format should be used when the same driver handles + * both sides of the link and the bus format is a fixed + * metadata format that is not configurable from userspace. + * Width and height will be set to 0 for this format. + */ +#define MEDIA_BUS_FMT_METADATA_FIXED 0x7001 + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ diff --git a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index 432cb6be55b4..6e449e784260 100644 --- a/drivers/staging/media/rkisp1/uapi/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */ /* * Rockchip ISP1 userspace API * Copyright (C) 2017 Rockchip Electronics Co., Ltd. @@ -9,10 +9,6 @@ #include <linux/types.h> -/* Vendor specific - used for RK_ISP1 camera sub-system */ -#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */ -#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A statistics */ - /* Defect Pixel Cluster Detection */ #define RKISP1_CIF_ISP_MODULE_DPCC (1U << 0) /* Black Level Subtraction */ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index a184c4939438..823b214aac0c 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -54,7 +54,7 @@ /* Control classes */ #define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ -#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */ +#define V4L2_CTRL_CLASS_CODEC 0x00990000 /* Stateful codec controls */ #define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */ #define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator controls */ #define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */ @@ -65,6 +65,7 @@ #define V4L2_CTRL_CLASS_FM_RX 0x00a10000 /* FM Receiver controls */ #define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */ #define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */ +#define V4L2_CTRL_CLASS_CODEC_STATELESS 0x00a40000 /* Stateless codecs controls */ /* User-class control IDs */ @@ -198,15 +199,21 @@ enum v4l2_colorfx { */ #define V4L2_CID_USER_ATMEL_ISC_BASE (V4L2_CID_USER_BASE + 0x10c0) +/* + * The base for the CODA driver controls. + * We reserve 16 controls for this driver. + */ +#define V4L2_CID_USER_CODA_BASE (V4L2_CID_USER_BASE + 0x10e0) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -#define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -#define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1) +#define V4L2_CID_CODEC_BASE (V4L2_CTRL_CLASS_CODEC | 0x900) +#define V4L2_CID_CODEC_CLASS (V4L2_CTRL_CLASS_CODEC | 1) /* MPEG streams, specific to multiplexed streams */ -#define V4L2_CID_MPEG_STREAM_TYPE (V4L2_CID_MPEG_BASE+0) +#define V4L2_CID_MPEG_STREAM_TYPE (V4L2_CID_CODEC_BASE+0) enum v4l2_mpeg_stream_type { V4L2_MPEG_STREAM_TYPE_MPEG2_PS = 0, /* MPEG-2 program stream */ V4L2_MPEG_STREAM_TYPE_MPEG2_TS = 1, /* MPEG-2 transport stream */ @@ -215,26 +222,26 @@ enum v4l2_mpeg_stream_type { V4L2_MPEG_STREAM_TYPE_MPEG1_VCD = 4, /* MPEG-1 VCD-compatible stream */ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */ }; -#define V4L2_CID_MPEG_STREAM_PID_PMT (V4L2_CID_MPEG_BASE+1) -#define V4L2_CID_MPEG_STREAM_PID_AUDIO (V4L2_CID_MPEG_BASE+2) -#define V4L2_CID_MPEG_STREAM_PID_VIDEO (V4L2_CID_MPEG_BASE+3) -#define V4L2_CID_MPEG_STREAM_PID_PCR (V4L2_CID_MPEG_BASE+4) -#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (V4L2_CID_MPEG_BASE+5) -#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (V4L2_CID_MPEG_BASE+6) -#define V4L2_CID_MPEG_STREAM_VBI_FMT (V4L2_CID_MPEG_BASE+7) +#define V4L2_CID_MPEG_STREAM_PID_PMT (V4L2_CID_CODEC_BASE+1) +#define V4L2_CID_MPEG_STREAM_PID_AUDIO (V4L2_CID_CODEC_BASE+2) +#define V4L2_CID_MPEG_STREAM_PID_VIDEO (V4L2_CID_CODEC_BASE+3) +#define V4L2_CID_MPEG_STREAM_PID_PCR (V4L2_CID_CODEC_BASE+4) +#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (V4L2_CID_CODEC_BASE+5) +#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (V4L2_CID_CODEC_BASE+6) +#define V4L2_CID_MPEG_STREAM_VBI_FMT (V4L2_CID_CODEC_BASE+7) enum v4l2_mpeg_stream_vbi_fmt { V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */ V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV format */ }; /* MPEG audio controls specific to multiplexed streams */ -#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ (V4L2_CID_MPEG_BASE+100) +#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ (V4L2_CID_CODEC_BASE+100) enum v4l2_mpeg_audio_sampling_freq { V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0, V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1, V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2, }; -#define V4L2_CID_MPEG_AUDIO_ENCODING (V4L2_CID_MPEG_BASE+101) +#define V4L2_CID_MPEG_AUDIO_ENCODING (V4L2_CID_CODEC_BASE+101) enum v4l2_mpeg_audio_encoding { V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0, V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1, @@ -242,7 +249,7 @@ enum v4l2_mpeg_audio_encoding { V4L2_MPEG_AUDIO_ENCODING_AAC = 3, V4L2_MPEG_AUDIO_ENCODING_AC3 = 4, }; -#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102) +#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_CODEC_BASE+102) enum v4l2_mpeg_audio_l1_bitrate { V4L2_MPEG_AUDIO_L1_BITRATE_32K = 0, V4L2_MPEG_AUDIO_L1_BITRATE_64K = 1, @@ -259,7 +266,7 @@ enum v4l2_mpeg_audio_l1_bitrate { V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12, V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13, }; -#define V4L2_CID_MPEG_AUDIO_L2_BITRATE (V4L2_CID_MPEG_BASE+103) +#define V4L2_CID_MPEG_AUDIO_L2_BITRATE (V4L2_CID_CODEC_BASE+103) enum v4l2_mpeg_audio_l2_bitrate { V4L2_MPEG_AUDIO_L2_BITRATE_32K = 0, V4L2_MPEG_AUDIO_L2_BITRATE_48K = 1, @@ -276,7 +283,7 @@ enum v4l2_mpeg_audio_l2_bitrate { V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12, V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13, }; -#define V4L2_CID_MPEG_AUDIO_L3_BITRATE (V4L2_CID_MPEG_BASE+104) +#define V4L2_CID_MPEG_AUDIO_L3_BITRATE (V4L2_CID_CODEC_BASE+104) enum v4l2_mpeg_audio_l3_bitrate { V4L2_MPEG_AUDIO_L3_BITRATE_32K = 0, V4L2_MPEG_AUDIO_L3_BITRATE_40K = 1, @@ -293,34 +300,34 @@ enum v4l2_mpeg_audio_l3_bitrate { V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12, V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13, }; -#define V4L2_CID_MPEG_AUDIO_MODE (V4L2_CID_MPEG_BASE+105) +#define V4L2_CID_MPEG_AUDIO_MODE (V4L2_CID_CODEC_BASE+105) enum v4l2_mpeg_audio_mode { V4L2_MPEG_AUDIO_MODE_STEREO = 0, V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1, V4L2_MPEG_AUDIO_MODE_DUAL = 2, V4L2_MPEG_AUDIO_MODE_MONO = 3, }; -#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION (V4L2_CID_MPEG_BASE+106) +#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION (V4L2_CID_CODEC_BASE+106) enum v4l2_mpeg_audio_mode_extension { V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4 = 0, V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8 = 1, V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2, V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3, }; -#define V4L2_CID_MPEG_AUDIO_EMPHASIS (V4L2_CID_MPEG_BASE+107) +#define V4L2_CID_MPEG_AUDIO_EMPHASIS (V4L2_CID_CODEC_BASE+107) enum v4l2_mpeg_audio_emphasis { V4L2_MPEG_AUDIO_EMPHASIS_NONE = 0, V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1, V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17 = 2, }; -#define V4L2_CID_MPEG_AUDIO_CRC (V4L2_CID_MPEG_BASE+108) +#define V4L2_CID_MPEG_AUDIO_CRC (V4L2_CID_CODEC_BASE+108) enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_NONE = 0, V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; -#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) -#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110) -#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111) +#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_CODEC_BASE+109) +#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_CODEC_BASE+110) +#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_CODEC_BASE+111) enum v4l2_mpeg_audio_ac3_bitrate { V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0, V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1, @@ -342,7 +349,7 @@ enum v4l2_mpeg_audio_ac3_bitrate { V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17, V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18, }; -#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_MPEG_BASE+112) +#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_CODEC_BASE+112) enum v4l2_mpeg_audio_dec_playback { V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO = 0, V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO = 1, @@ -351,52 +358,52 @@ enum v4l2_mpeg_audio_dec_playback { V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO = 4, V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO = 5, }; -#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_MPEG_BASE+113) +#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_CODEC_BASE+113) /* MPEG video controls specific to multiplexed streams */ -#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) +#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_CODEC_BASE+200) enum v4l2_mpeg_video_encoding { V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2, }; -#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201) +#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_CODEC_BASE+201) enum v4l2_mpeg_video_aspect { V4L2_MPEG_VIDEO_ASPECT_1x1 = 0, V4L2_MPEG_VIDEO_ASPECT_4x3 = 1, V4L2_MPEG_VIDEO_ASPECT_16x9 = 2, V4L2_MPEG_VIDEO_ASPECT_221x100 = 3, }; -#define V4L2_CID_MPEG_VIDEO_B_FRAMES (V4L2_CID_MPEG_BASE+202) -#define V4L2_CID_MPEG_VIDEO_GOP_SIZE (V4L2_CID_MPEG_BASE+203) -#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (V4L2_CID_MPEG_BASE+204) -#define V4L2_CID_MPEG_VIDEO_PULLDOWN (V4L2_CID_MPEG_BASE+205) -#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_MPEG_BASE+206) +#define V4L2_CID_MPEG_VIDEO_B_FRAMES (V4L2_CID_CODEC_BASE+202) +#define V4L2_CID_MPEG_VIDEO_GOP_SIZE (V4L2_CID_CODEC_BASE+203) +#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (V4L2_CID_CODEC_BASE+204) +#define V4L2_CID_MPEG_VIDEO_PULLDOWN (V4L2_CID_CODEC_BASE+205) +#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_CODEC_BASE+206) enum v4l2_mpeg_video_bitrate_mode { V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1, V4L2_MPEG_VIDEO_BITRATE_MODE_CQ = 2, }; -#define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207) -#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208) -#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209) -#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) -#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) -#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (V4L2_CID_MPEG_BASE+212) -#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (V4L2_CID_MPEG_BASE+213) -#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (V4L2_CID_MPEG_BASE+214) -#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (V4L2_CID_MPEG_BASE+215) -#define V4L2_CID_MPEG_VIDEO_HEADER_MODE (V4L2_CID_MPEG_BASE+216) +#define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_CODEC_BASE+207) +#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_CODEC_BASE+208) +#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_CODEC_BASE+209) +#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_CODEC_BASE+210) +#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_CODEC_BASE+211) +#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (V4L2_CID_CODEC_BASE+212) +#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (V4L2_CID_CODEC_BASE+213) +#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (V4L2_CID_CODEC_BASE+214) +#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (V4L2_CID_CODEC_BASE+215) +#define V4L2_CID_MPEG_VIDEO_HEADER_MODE (V4L2_CID_CODEC_BASE+216) enum v4l2_mpeg_video_header_mode { V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE = 0, V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME = 1, }; -#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_MPEG_BASE+217) -#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (V4L2_CID_MPEG_BASE+218) -#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (V4L2_CID_MPEG_BASE+219) -#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (V4L2_CID_MPEG_BASE+220) -#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_MPEG_BASE+221) +#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_CODEC_BASE+217) +#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (V4L2_CID_CODEC_BASE+218) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (V4L2_CID_CODEC_BASE+219) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (V4L2_CID_CODEC_BASE+220) +#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_CODEC_BASE+221) enum v4l2_mpeg_video_multi_slice_mode { V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB = 1, @@ -407,24 +414,24 @@ enum v4l2_mpeg_video_multi_slice_mode { V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2, #endif }; -#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222) -#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223) -#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224) -#define V4L2_CID_MPEG_VIDEO_VBV_DELAY (V4L2_CID_MPEG_BASE+225) -#define V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (V4L2_CID_MPEG_BASE+226) -#define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (V4L2_CID_MPEG_BASE+227) -#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_MPEG_BASE+228) -#define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_MPEG_BASE+229) +#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_CODEC_BASE+222) +#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_CODEC_BASE+223) +#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_CODEC_BASE+224) +#define V4L2_CID_MPEG_VIDEO_VBV_DELAY (V4L2_CID_CODEC_BASE+225) +#define V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER (V4L2_CID_CODEC_BASE+226) +#define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE (V4L2_CID_CODEC_BASE+227) +#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_CODEC_BASE+228) +#define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_CODEC_BASE+229) /* CIDs for the MPEG-2 Part 2 (H.262) codec */ -#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_MPEG_BASE+270) +#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270) enum v4l2_mpeg_video_mpeg2_level { V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW = 0, V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN = 1, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440 = 2, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH = 3, }; -#define V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE (V4L2_CID_MPEG_BASE+271) +#define V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE (V4L2_CID_CODEC_BASE+271) enum v4l2_mpeg_video_mpeg2_profile { V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE = 0, V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN = 1, @@ -435,28 +442,28 @@ enum v4l2_mpeg_video_mpeg2_profile { }; /* CIDs for the FWHT codec as used by the vicodec driver. */ -#define V4L2_CID_FWHT_I_FRAME_QP (V4L2_CID_MPEG_BASE + 290) -#define V4L2_CID_FWHT_P_FRAME_QP (V4L2_CID_MPEG_BASE + 291) - -#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300) -#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301) -#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302) -#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP (V4L2_CID_MPEG_BASE+303) -#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP (V4L2_CID_MPEG_BASE+304) -#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (V4L2_CID_MPEG_BASE+350) -#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (V4L2_CID_MPEG_BASE+351) -#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (V4L2_CID_MPEG_BASE+352) -#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP (V4L2_CID_MPEG_BASE+353) -#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP (V4L2_CID_MPEG_BASE+354) -#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (V4L2_CID_MPEG_BASE+355) -#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (V4L2_CID_MPEG_BASE+356) -#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE (V4L2_CID_MPEG_BASE+357) +#define V4L2_CID_FWHT_I_FRAME_QP (V4L2_CID_CODEC_BASE + 290) +#define V4L2_CID_FWHT_P_FRAME_QP (V4L2_CID_CODEC_BASE + 291) + +#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_CODEC_BASE+300) +#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_CODEC_BASE+301) +#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_CODEC_BASE+302) +#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP (V4L2_CID_CODEC_BASE+303) +#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP (V4L2_CID_CODEC_BASE+304) +#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (V4L2_CID_CODEC_BASE+350) +#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (V4L2_CID_CODEC_BASE+351) +#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (V4L2_CID_CODEC_BASE+352) +#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP (V4L2_CID_CODEC_BASE+353) +#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP (V4L2_CID_CODEC_BASE+354) +#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (V4L2_CID_CODEC_BASE+355) +#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (V4L2_CID_CODEC_BASE+356) +#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE (V4L2_CID_CODEC_BASE+357) enum v4l2_mpeg_video_h264_entropy_mode { V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC = 0, V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC = 1, }; -#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (V4L2_CID_MPEG_BASE+358) -#define V4L2_CID_MPEG_VIDEO_H264_LEVEL (V4L2_CID_MPEG_BASE+359) +#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (V4L2_CID_CODEC_BASE+358) +#define V4L2_CID_MPEG_VIDEO_H264_LEVEL (V4L2_CID_CODEC_BASE+359) enum v4l2_mpeg_video_h264_level { V4L2_MPEG_VIDEO_H264_LEVEL_1_0 = 0, V4L2_MPEG_VIDEO_H264_LEVEL_1B = 1, @@ -479,15 +486,15 @@ enum v4l2_mpeg_video_h264_level { V4L2_MPEG_VIDEO_H264_LEVEL_6_1 = 18, V4L2_MPEG_VIDEO_H264_LEVEL_6_2 = 19, }; -#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360) -#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361) -#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE+362) +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_CODEC_BASE+360) +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_CODEC_BASE+361) +#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE (V4L2_CID_CODEC_BASE+362) enum v4l2_mpeg_video_h264_loop_filter_mode { V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED = 0, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED = 1, V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2, }; -#define V4L2_CID_MPEG_VIDEO_H264_PROFILE (V4L2_CID_MPEG_BASE+363) +#define V4L2_CID_MPEG_VIDEO_H264_PROFILE (V4L2_CID_CODEC_BASE+363) enum v4l2_mpeg_video_h264_profile { V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE = 0, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE = 1, @@ -508,10 +515,10 @@ enum v4l2_mpeg_video_h264_profile { V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH = 16, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH = 17, }; -#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364) -#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365) -#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (V4L2_CID_MPEG_BASE+366) -#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC (V4L2_CID_MPEG_BASE+367) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_CODEC_BASE+364) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_CODEC_BASE+365) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (V4L2_CID_CODEC_BASE+366) +#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC (V4L2_CID_CODEC_BASE+367) enum v4l2_mpeg_video_h264_vui_sar_idc { V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED = 0, V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 = 1, @@ -532,9 +539,9 @@ enum v4l2_mpeg_video_h264_vui_sar_idc { V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 = 16, V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED = 17, }; -#define V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (V4L2_CID_MPEG_BASE+368) -#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (V4L2_CID_MPEG_BASE+369) -#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE (V4L2_CID_MPEG_BASE+370) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING (V4L2_CID_CODEC_BASE+368) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_CURRENT_FRAME_0 (V4L2_CID_CODEC_BASE+369) +#define V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE (V4L2_CID_CODEC_BASE+370) enum v4l2_mpeg_video_h264_sei_fp_arrangement_type { V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_CHECKERBOARD = 0, V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_COLUMN = 1, @@ -543,8 +550,8 @@ enum v4l2_mpeg_video_h264_sei_fp_arrangement_type { V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM = 4, V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TEMPORAL = 5, }; -#define V4L2_CID_MPEG_VIDEO_H264_FMO (V4L2_CID_MPEG_BASE+371) -#define V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE (V4L2_CID_MPEG_BASE+372) +#define V4L2_CID_MPEG_VIDEO_H264_FMO (V4L2_CID_CODEC_BASE+371) +#define V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE (V4L2_CID_CODEC_BASE+372) enum v4l2_mpeg_video_h264_fmo_map_type { V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_INTERLEAVED_SLICES = 0, V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES = 1, @@ -554,36 +561,36 @@ enum v4l2_mpeg_video_h264_fmo_map_type { V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_WIPE_SCAN = 5, V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_EXPLICIT = 6, }; -#define V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (V4L2_CID_MPEG_BASE+373) -#define V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION (V4L2_CID_MPEG_BASE+374) +#define V4L2_CID_MPEG_VIDEO_H264_FMO_SLICE_GROUP (V4L2_CID_CODEC_BASE+373) +#define V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_DIRECTION (V4L2_CID_CODEC_BASE+374) enum v4l2_mpeg_video_h264_fmo_change_dir { V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_RIGHT = 0, V4L2_MPEG_VIDEO_H264_FMO_CHANGE_DIR_LEFT = 1, }; -#define V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (V4L2_CID_MPEG_BASE+375) -#define V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (V4L2_CID_MPEG_BASE+376) -#define V4L2_CID_MPEG_VIDEO_H264_ASO (V4L2_CID_MPEG_BASE+377) -#define V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (V4L2_CID_MPEG_BASE+378) -#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (V4L2_CID_MPEG_BASE+379) -#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE (V4L2_CID_MPEG_BASE+380) +#define V4L2_CID_MPEG_VIDEO_H264_FMO_CHANGE_RATE (V4L2_CID_CODEC_BASE+375) +#define V4L2_CID_MPEG_VIDEO_H264_FMO_RUN_LENGTH (V4L2_CID_CODEC_BASE+376) +#define V4L2_CID_MPEG_VIDEO_H264_ASO (V4L2_CID_CODEC_BASE+377) +#define V4L2_CID_MPEG_VIDEO_H264_ASO_SLICE_ORDER (V4L2_CID_CODEC_BASE+378) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING (V4L2_CID_CODEC_BASE+379) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE (V4L2_CID_CODEC_BASE+380) enum v4l2_mpeg_video_h264_hierarchical_coding_type { V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B = 0, V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P = 1, }; -#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_MPEG_BASE+381) -#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_MPEG_BASE+382) -#define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_MPEG_BASE+383) -#define V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (V4L2_CID_MPEG_BASE+384) -#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP (V4L2_CID_MPEG_BASE+385) -#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP (V4L2_CID_MPEG_BASE+386) -#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP (V4L2_CID_MPEG_BASE+387) -#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP (V4L2_CID_MPEG_BASE+388) -#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400) -#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401) -#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402) -#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (V4L2_CID_MPEG_BASE+403) -#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (V4L2_CID_MPEG_BASE+404) -#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL (V4L2_CID_MPEG_BASE+405) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER (V4L2_CID_CODEC_BASE+381) +#define V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER_QP (V4L2_CID_CODEC_BASE+382) +#define V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION (V4L2_CID_CODEC_BASE+383) +#define V4L2_CID_MPEG_VIDEO_H264_CHROMA_QP_INDEX_OFFSET (V4L2_CID_CODEC_BASE+384) +#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP (V4L2_CID_CODEC_BASE+385) +#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+386) +#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP (V4L2_CID_CODEC_BASE+387) +#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP (V4L2_CID_CODEC_BASE+388) +#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_CODEC_BASE+400) +#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_CODEC_BASE+401) +#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_CODEC_BASE+402) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (V4L2_CID_CODEC_BASE+403) +#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (V4L2_CID_CODEC_BASE+404) +#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL (V4L2_CID_CODEC_BASE+405) enum v4l2_mpeg_video_mpeg4_level { V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 = 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B = 1, @@ -594,7 +601,7 @@ enum v4l2_mpeg_video_mpeg4_level { V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 = 6, V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 = 7, }; -#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE (V4L2_CID_MPEG_BASE+406) +#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE (V4L2_CID_CODEC_BASE+406) enum v4l2_mpeg_video_mpeg4_profile { V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE = 0, V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE = 1, @@ -602,40 +609,40 @@ enum v4l2_mpeg_video_mpeg4_profile { V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE = 3, V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY = 4, }; -#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407) +#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_CODEC_BASE+407) /* Control IDs for VP8 streams * Although VP8 is not part of MPEG we add these controls to the MPEG class * as that class is already handling other video compression standards */ -#define V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS (V4L2_CID_MPEG_BASE+500) +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS (V4L2_CID_CODEC_BASE+500) enum v4l2_vp8_num_partitions { V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION = 0, V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS = 1, V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS = 2, V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS = 3, }; -#define V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (V4L2_CID_MPEG_BASE+501) -#define V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES (V4L2_CID_MPEG_BASE+502) +#define V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (V4L2_CID_CODEC_BASE+501) +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES (V4L2_CID_CODEC_BASE+502) enum v4l2_vp8_num_ref_frames { V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME = 0, V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME = 1, V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME = 2, }; -#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (V4L2_CID_MPEG_BASE+503) -#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (V4L2_CID_MPEG_BASE+504) -#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (V4L2_CID_MPEG_BASE+505) -#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL (V4L2_CID_MPEG_BASE+506) +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (V4L2_CID_CODEC_BASE+503) +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (V4L2_CID_CODEC_BASE+504) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (V4L2_CID_CODEC_BASE+505) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL (V4L2_CID_CODEC_BASE+506) enum v4l2_vp8_golden_frame_sel { V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, }; -#define V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (V4L2_CID_MPEG_BASE+507) -#define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) -#define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) -#define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) +#define V4L2_CID_MPEG_VIDEO_VPX_MIN_QP (V4L2_CID_CODEC_BASE+507) +#define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_CODEC_BASE+508) +#define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_CODEC_BASE+509) +#define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_CODEC_BASE+510) -#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_MPEG_BASE+511) +#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_CODEC_BASE+511) enum v4l2_mpeg_video_vp8_profile { V4L2_MPEG_VIDEO_VP8_PROFILE_0 = 0, V4L2_MPEG_VIDEO_VP8_PROFILE_1 = 1, @@ -644,14 +651,14 @@ enum v4l2_mpeg_video_vp8_profile { }; /* Deprecated alias for compatibility reasons. */ #define V4L2_CID_MPEG_VIDEO_VPX_PROFILE V4L2_CID_MPEG_VIDEO_VP8_PROFILE -#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_MPEG_BASE+512) +#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_CODEC_BASE+512) enum v4l2_mpeg_video_vp9_profile { V4L2_MPEG_VIDEO_VP9_PROFILE_0 = 0, V4L2_MPEG_VIDEO_VP9_PROFILE_1 = 1, V4L2_MPEG_VIDEO_VP9_PROFILE_2 = 2, V4L2_MPEG_VIDEO_VP9_PROFILE_3 = 3, }; -#define V4L2_CID_MPEG_VIDEO_VP9_LEVEL (V4L2_CID_MPEG_BASE+513) +#define V4L2_CID_MPEG_VIDEO_VP9_LEVEL (V4L2_CID_CODEC_BASE+513) enum v4l2_mpeg_video_vp9_level { V4L2_MPEG_VIDEO_VP9_LEVEL_1_0 = 0, V4L2_MPEG_VIDEO_VP9_LEVEL_1_1 = 1, @@ -671,32 +678,32 @@ enum v4l2_mpeg_video_vp9_level { /* CIDs for HEVC encoding. */ -#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (V4L2_CID_MPEG_BASE + 600) -#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (V4L2_CID_MPEG_BASE + 601) -#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (V4L2_CID_MPEG_BASE + 602) -#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (V4L2_CID_MPEG_BASE + 603) -#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (V4L2_CID_MPEG_BASE + 604) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (V4L2_CID_MPEG_BASE + 605) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE (V4L2_CID_MPEG_BASE + 606) +#define V4L2_CID_MPEG_VIDEO_HEVC_MIN_QP (V4L2_CID_CODEC_BASE + 600) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_QP (V4L2_CID_CODEC_BASE + 601) +#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP (V4L2_CID_CODEC_BASE + 602) +#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_QP (V4L2_CID_CODEC_BASE + 603) +#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP (V4L2_CID_CODEC_BASE + 604) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_QP (V4L2_CID_CODEC_BASE + 605) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE (V4L2_CID_CODEC_BASE + 606) enum v4l2_mpeg_video_hevc_hier_coding_type { V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B = 0, V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P = 1, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (V4L2_CID_MPEG_BASE + 607) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (V4L2_CID_MPEG_BASE + 608) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (V4L2_CID_MPEG_BASE + 609) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (V4L2_CID_MPEG_BASE + 610) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (V4L2_CID_MPEG_BASE + 611) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (V4L2_CID_MPEG_BASE + 612) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (V4L2_CID_MPEG_BASE + 613) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (V4L2_CID_MPEG_BASE + 614) -#define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE (V4L2_CID_MPEG_BASE + 615) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER (V4L2_CID_CODEC_BASE + 607) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_QP (V4L2_CID_CODEC_BASE + 608) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_QP (V4L2_CID_CODEC_BASE + 609) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_QP (V4L2_CID_CODEC_BASE + 610) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_QP (V4L2_CID_CODEC_BASE + 611) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_QP (V4L2_CID_CODEC_BASE + 612) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_QP (V4L2_CID_CODEC_BASE + 613) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_QP (V4L2_CID_CODEC_BASE + 614) +#define V4L2_CID_MPEG_VIDEO_HEVC_PROFILE (V4L2_CID_CODEC_BASE + 615) enum v4l2_mpeg_video_hevc_profile { V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_LEVEL (V4L2_CID_MPEG_BASE + 616) +#define V4L2_CID_MPEG_VIDEO_HEVC_LEVEL (V4L2_CID_CODEC_BASE + 616) enum v4l2_mpeg_video_hevc_level { V4L2_MPEG_VIDEO_HEVC_LEVEL_1 = 0, V4L2_MPEG_VIDEO_HEVC_LEVEL_2 = 1, @@ -712,56 +719,56 @@ enum v4l2_mpeg_video_hevc_level { V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 = 11, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 = 12, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (V4L2_CID_MPEG_BASE + 617) -#define V4L2_CID_MPEG_VIDEO_HEVC_TIER (V4L2_CID_MPEG_BASE + 618) +#define V4L2_CID_MPEG_VIDEO_HEVC_FRAME_RATE_RESOLUTION (V4L2_CID_CODEC_BASE + 617) +#define V4L2_CID_MPEG_VIDEO_HEVC_TIER (V4L2_CID_CODEC_BASE + 618) enum v4l2_mpeg_video_hevc_tier { V4L2_MPEG_VIDEO_HEVC_TIER_MAIN = 0, V4L2_MPEG_VIDEO_HEVC_TIER_HIGH = 1, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (V4L2_CID_MPEG_BASE + 619) -#define V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE + 620) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_PARTITION_DEPTH (V4L2_CID_CODEC_BASE + 619) +#define V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE (V4L2_CID_CODEC_BASE + 620) enum v4l2_cid_mpeg_video_hevc_loop_filter_mode { V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED = 0, V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_ENABLED = 1, V4L2_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (V4L2_CID_MPEG_BASE + 621) -#define V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (V4L2_CID_MPEG_BASE + 622) -#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE (V4L2_CID_MPEG_BASE + 623) +#define V4L2_CID_MPEG_VIDEO_HEVC_LF_BETA_OFFSET_DIV2 (V4L2_CID_CODEC_BASE + 621) +#define V4L2_CID_MPEG_VIDEO_HEVC_LF_TC_OFFSET_DIV2 (V4L2_CID_CODEC_BASE + 622) +#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_TYPE (V4L2_CID_CODEC_BASE + 623) enum v4l2_cid_mpeg_video_hevc_refresh_type { V4L2_MPEG_VIDEO_HEVC_REFRESH_NONE = 0, V4L2_MPEG_VIDEO_HEVC_REFRESH_CRA = 1, V4L2_MPEG_VIDEO_HEVC_REFRESH_IDR = 2, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (V4L2_CID_MPEG_BASE + 624) -#define V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (V4L2_CID_MPEG_BASE + 625) -#define V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (V4L2_CID_MPEG_BASE + 626) -#define V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (V4L2_CID_MPEG_BASE + 627) -#define V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (V4L2_CID_MPEG_BASE + 628) -#define V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (V4L2_CID_MPEG_BASE + 629) -#define V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (V4L2_CID_MPEG_BASE + 630) -#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (V4L2_CID_MPEG_BASE + 631) -#define V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT (V4L2_CID_MPEG_BASE + 632) -#define V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (V4L2_CID_MPEG_BASE + 633) -#define V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (V4L2_CID_MPEG_BASE + 634) -#define V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD (V4L2_CID_MPEG_BASE + 635) +#define V4L2_CID_MPEG_VIDEO_HEVC_REFRESH_PERIOD (V4L2_CID_CODEC_BASE + 624) +#define V4L2_CID_MPEG_VIDEO_HEVC_LOSSLESS_CU (V4L2_CID_CODEC_BASE + 625) +#define V4L2_CID_MPEG_VIDEO_HEVC_CONST_INTRA_PRED (V4L2_CID_CODEC_BASE + 626) +#define V4L2_CID_MPEG_VIDEO_HEVC_WAVEFRONT (V4L2_CID_CODEC_BASE + 627) +#define V4L2_CID_MPEG_VIDEO_HEVC_GENERAL_PB (V4L2_CID_CODEC_BASE + 628) +#define V4L2_CID_MPEG_VIDEO_HEVC_TEMPORAL_ID (V4L2_CID_CODEC_BASE + 629) +#define V4L2_CID_MPEG_VIDEO_HEVC_STRONG_SMOOTHING (V4L2_CID_CODEC_BASE + 630) +#define V4L2_CID_MPEG_VIDEO_HEVC_MAX_NUM_MERGE_MV_MINUS1 (V4L2_CID_CODEC_BASE + 631) +#define V4L2_CID_MPEG_VIDEO_HEVC_INTRA_PU_SPLIT (V4L2_CID_CODEC_BASE + 632) +#define V4L2_CID_MPEG_VIDEO_HEVC_TMV_PREDICTION (V4L2_CID_CODEC_BASE + 633) +#define V4L2_CID_MPEG_VIDEO_HEVC_WITHOUT_STARTCODE (V4L2_CID_CODEC_BASE + 634) +#define V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD (V4L2_CID_CODEC_BASE + 635) enum v4l2_cid_mpeg_video_hevc_size_of_length_field { V4L2_MPEG_VIDEO_HEVC_SIZE_0 = 0, V4L2_MPEG_VIDEO_HEVC_SIZE_1 = 1, V4L2_MPEG_VIDEO_HEVC_SIZE_2 = 2, V4L2_MPEG_VIDEO_HEVC_SIZE_4 = 3, }; -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (V4L2_CID_MPEG_BASE + 636) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (V4L2_CID_MPEG_BASE + 637) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (V4L2_CID_MPEG_BASE + 638) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (V4L2_CID_MPEG_BASE + 639) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (V4L2_CID_MPEG_BASE + 640) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (V4L2_CID_MPEG_BASE + 641) -#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (V4L2_CID_MPEG_BASE + 642) -#define V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (V4L2_CID_MPEG_BASE + 643) -#define V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (V4L2_CID_MPEG_BASE + 644) -#define V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY (V4L2_CID_MPEG_BASE + 645) -#define V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE (V4L2_CID_MPEG_BASE + 646) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR (V4L2_CID_CODEC_BASE + 636) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR (V4L2_CID_CODEC_BASE + 637) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR (V4L2_CID_CODEC_BASE + 638) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR (V4L2_CID_CODEC_BASE + 639) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR (V4L2_CID_CODEC_BASE + 640) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR (V4L2_CID_CODEC_BASE + 641) +#define V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L6_BR (V4L2_CID_CODEC_BASE + 642) +#define V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES (V4L2_CID_CODEC_BASE + 643) +#define V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR (V4L2_CID_CODEC_BASE + 644) +#define V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY (V4L2_CID_CODEC_BASE + 645) +#define V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE (V4L2_CID_CODEC_BASE + 646) enum v4l2_mpeg_video_frame_skip_mode { V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED = 0, V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT = 1, @@ -769,14 +776,14 @@ enum v4l2_mpeg_video_frame_skip_mode { }; /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ -#define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) -#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) +#define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000) +#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0) enum v4l2_mpeg_cx2341x_video_spatial_filter_mode { V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0, V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO = 1, }; -#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+1) -#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+2) +#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (V4L2_CID_CODEC_CX2341X_BASE+1) +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE (V4L2_CID_CODEC_CX2341X_BASE+2) enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type { V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF = 0, V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR = 1, @@ -784,18 +791,18 @@ enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type { V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE = 3, V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4, }; -#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+3) +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE (V4L2_CID_CODEC_CX2341X_BASE+3) enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type { V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF = 0, V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1, }; -#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+4) +#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+4) enum v4l2_mpeg_cx2341x_video_temporal_filter_mode { V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0, V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO = 1, }; -#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+5) -#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+6) +#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (V4L2_CID_CODEC_CX2341X_BASE+5) +#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE (V4L2_CID_CODEC_CX2341X_BASE+6) enum v4l2_mpeg_cx2341x_video_median_filter_type { V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF = 0, V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR = 1, @@ -803,38 +810,38 @@ enum v4l2_mpeg_cx2341x_video_median_filter_type { V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3, V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG = 4, }; -#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+7) -#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8) -#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9) -#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10) -#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11) +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_CODEC_CX2341X_BASE+7) +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_CODEC_CX2341X_BASE+8) +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_CODEC_CX2341X_BASE+9) +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_CODEC_CX2341X_BASE+10) +#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_CODEC_CX2341X_BASE+11) /* MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */ -#define V4L2_CID_MPEG_MFC51_BASE (V4L2_CTRL_CLASS_MPEG | 0x1100) +#define V4L2_CID_CODEC_MFC51_BASE (V4L2_CTRL_CLASS_CODEC | 0x1100) -#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_MPEG_MFC51_BASE+0) -#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_MPEG_MFC51_BASE+1) -#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE (V4L2_CID_MPEG_MFC51_BASE+2) +#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_CODEC_MFC51_BASE+0) +#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_CODEC_MFC51_BASE+1) +#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE (V4L2_CID_CODEC_MFC51_BASE+2) enum v4l2_mpeg_mfc51_video_frame_skip_mode { V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED = 0, V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT = 1, V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT = 2, }; -#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE (V4L2_CID_MPEG_MFC51_BASE+3) +#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE (V4L2_CID_CODEC_MFC51_BASE+3) enum v4l2_mpeg_mfc51_video_force_frame_type { V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED = 0, V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME = 1, V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED = 2, }; -#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING (V4L2_CID_MPEG_MFC51_BASE+4) -#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (V4L2_CID_MPEG_MFC51_BASE+5) -#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (V4L2_CID_MPEG_MFC51_BASE+6) -#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (V4L2_CID_MPEG_MFC51_BASE+7) -#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (V4L2_CID_MPEG_MFC51_BASE+50) -#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (V4L2_CID_MPEG_MFC51_BASE+51) -#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (V4L2_CID_MPEG_MFC51_BASE+52) -#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_MPEG_MFC51_BASE+53) -#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_MPEG_MFC51_BASE+54) +#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING (V4L2_CID_CODEC_MFC51_BASE+4) +#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (V4L2_CID_CODEC_MFC51_BASE+5) +#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (V4L2_CID_CODEC_MFC51_BASE+6) +#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (V4L2_CID_CODEC_MFC51_BASE+7) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (V4L2_CID_CODEC_MFC51_BASE+50) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (V4L2_CID_CODEC_MFC51_BASE+51) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (V4L2_CID_CODEC_MFC51_BASE+52) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_CODEC_MFC51_BASE+53) +#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_CODEC_MFC51_BASE+54) /* Camera class control IDs */ @@ -1171,4 +1178,470 @@ enum v4l2_detect_md_mode { #define V4L2_CID_DETECT_MD_THRESHOLD_GRID (V4L2_CID_DETECT_CLASS_BASE + 3) #define V4L2_CID_DETECT_MD_REGION_GRID (V4L2_CID_DETECT_CLASS_BASE + 4) + +/* Stateless CODECs controls */ +#define V4L2_CID_CODEC_STATELESS_BASE (V4L2_CTRL_CLASS_CODEC_STATELESS | 0x900) +#define V4L2_CID_CODEC_STATELESS_CLASS (V4L2_CTRL_CLASS_CODEC_STATELESS | 1) + +#define V4L2_CID_STATELESS_H264_DECODE_MODE (V4L2_CID_CODEC_STATELESS_BASE + 0) +/** + * enum v4l2_stateless_h264_decode_mode - Decoding mode + * + * @V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED: indicates that decoding + * is performed one slice at a time. In this mode, + * V4L2_CID_STATELESS_H264_SLICE_PARAMS must contain the parsed slice + * parameters and the OUTPUT buffer must contain a single slice. + * V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF feature is used + * in order to support multislice frames. + * @V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED: indicates that + * decoding is performed per frame. The OUTPUT buffer must contain + * all slices and also both fields. This mode is typically supported + * by device drivers that are able to parse the slice(s) header(s) + * in hardware. When this mode is selected, + * V4L2_CID_STATELESS_H264_SLICE_PARAMS is not used. + */ +enum v4l2_stateless_h264_decode_mode { + V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED, + V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, +}; + +#define V4L2_CID_STATELESS_H264_START_CODE (V4L2_CID_CODEC_STATELESS_BASE + 1) +/** + * enum v4l2_stateless_h264_start_code - Start code + * + * @V4L2_STATELESS_H264_START_CODE_NONE: slices are passed + * to the driver without any start code. + * @V4L2_STATELESS_H264_START_CODE_ANNEX_B: slices are passed + * to the driver with an Annex B start code prefix + * (legal start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001). + * This mode is typically supported by device drivers that parse + * the start code in hardware. + */ +enum v4l2_stateless_h264_start_code { + V4L2_STATELESS_H264_START_CODE_NONE, + V4L2_STATELESS_H264_START_CODE_ANNEX_B, +}; + +#define V4L2_H264_SPS_CONSTRAINT_SET0_FLAG 0x01 +#define V4L2_H264_SPS_CONSTRAINT_SET1_FLAG 0x02 +#define V4L2_H264_SPS_CONSTRAINT_SET2_FLAG 0x04 +#define V4L2_H264_SPS_CONSTRAINT_SET3_FLAG 0x08 +#define V4L2_H264_SPS_CONSTRAINT_SET4_FLAG 0x10 +#define V4L2_H264_SPS_CONSTRAINT_SET5_FLAG 0x20 + +#define V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE 0x01 +#define V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS 0x02 +#define V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO 0x04 +#define V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED 0x08 +#define V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY 0x10 +#define V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD 0x20 +#define V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE 0x40 + +#define V4L2_H264_SPS_HAS_CHROMA_FORMAT(sps) \ + ((sps)->profile_idc == 100 || (sps)->profile_idc == 110 || \ + (sps)->profile_idc == 122 || (sps)->profile_idc == 244 || \ + (sps)->profile_idc == 44 || (sps)->profile_idc == 83 || \ + (sps)->profile_idc == 86 || (sps)->profile_idc == 118 || \ + (sps)->profile_idc == 128 || (sps)->profile_idc == 138 || \ + (sps)->profile_idc == 139 || (sps)->profile_idc == 134 || \ + (sps)->profile_idc == 135) + +#define V4L2_CID_STATELESS_H264_SPS (V4L2_CID_CODEC_STATELESS_BASE + 2) +/** + * struct v4l2_ctrl_h264_sps - H264 sequence parameter set + * + * All the members on this sequence parameter set structure match the + * sequence parameter set syntax as specified by the H264 specification. + * + * @profile_idc: see H264 specification. + * @constraint_set_flags: see H264 specification. + * @level_idc: see H264 specification. + * @seq_parameter_set_id: see H264 specification. + * @chroma_format_idc: see H264 specification. + * @bit_depth_luma_minus8: see H264 specification. + * @bit_depth_chroma_minus8: see H264 specification. + * @log2_max_frame_num_minus4: see H264 specification. + * @pic_order_cnt_type: see H264 specification. + * @log2_max_pic_order_cnt_lsb_minus4: see H264 specification. + * @max_num_ref_frames: see H264 specification. + * @num_ref_frames_in_pic_order_cnt_cycle: see H264 specification. + * @offset_for_ref_frame: see H264 specification. + * @offset_for_non_ref_pic: see H264 specification. + * @offset_for_top_to_bottom_field: see H264 specification. + * @pic_width_in_mbs_minus1: see H264 specification. + * @pic_height_in_map_units_minus1: see H264 specification. + * @flags: see V4L2_H264_SPS_FLAG_{}. + */ +struct v4l2_ctrl_h264_sps { + __u8 profile_idc; + __u8 constraint_set_flags; + __u8 level_idc; + __u8 seq_parameter_set_id; + __u8 chroma_format_idc; + __u8 bit_depth_luma_minus8; + __u8 bit_depth_chroma_minus8; + __u8 log2_max_frame_num_minus4; + __u8 pic_order_cnt_type; + __u8 log2_max_pic_order_cnt_lsb_minus4; + __u8 max_num_ref_frames; + __u8 num_ref_frames_in_pic_order_cnt_cycle; + __s32 offset_for_ref_frame[255]; + __s32 offset_for_non_ref_pic; + __s32 offset_for_top_to_bottom_field; + __u16 pic_width_in_mbs_minus1; + __u16 pic_height_in_map_units_minus1; + __u32 flags; +}; + +#define V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE 0x0001 +#define V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT 0x0002 +#define V4L2_H264_PPS_FLAG_WEIGHTED_PRED 0x0004 +#define V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT 0x0008 +#define V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED 0x0010 +#define V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT 0x0020 +#define V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE 0x0040 +#define V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT 0x0080 + +#define V4L2_CID_STATELESS_H264_PPS (V4L2_CID_CODEC_STATELESS_BASE + 3) +/** + * struct v4l2_ctrl_h264_pps - H264 picture parameter set + * + * Except where noted, all the members on this picture parameter set + * structure match the sequence parameter set syntax as specified + * by the H264 specification. + * + * In particular, V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT flag + * has a specific meaning. This flag should be set if a non-flat + * scaling matrix applies to the picture. In this case, applications + * are expected to use V4L2_CID_STATELESS_H264_SCALING_MATRIX, + * to pass the values of the non-flat matrices. + * + * @pic_parameter_set_id: see H264 specification. + * @seq_parameter_set_id: see H264 specification. + * @num_slice_groups_minus1: see H264 specification. + * @num_ref_idx_l0_default_active_minus1: see H264 specification. + * @num_ref_idx_l1_default_active_minus1: see H264 specification. + * @weighted_bipred_idc: see H264 specification. + * @pic_init_qp_minus26: see H264 specification. + * @pic_init_qs_minus26: see H264 specification. + * @chroma_qp_index_offset: see H264 specification. + * @second_chroma_qp_index_offset: see H264 specification. + * @flags: see V4L2_H264_PPS_FLAG_{}. + */ +struct v4l2_ctrl_h264_pps { + __u8 pic_parameter_set_id; + __u8 seq_parameter_set_id; + __u8 num_slice_groups_minus1; + __u8 num_ref_idx_l0_default_active_minus1; + __u8 num_ref_idx_l1_default_active_minus1; + __u8 weighted_bipred_idc; + __s8 pic_init_qp_minus26; + __s8 pic_init_qs_minus26; + __s8 chroma_qp_index_offset; + __s8 second_chroma_qp_index_offset; + __u16 flags; +}; + +#define V4L2_CID_STATELESS_H264_SCALING_MATRIX (V4L2_CID_CODEC_STATELESS_BASE + 4) +/** + * struct v4l2_ctrl_h264_scaling_matrix - H264 scaling matrices + * + * @scaling_list_4x4: scaling matrix after applying the inverse + * scanning process. Expected list order is Intra Y, Intra Cb, + * Intra Cr, Inter Y, Inter Cb, Inter Cr. The values on each + * scaling list are expected in raster scan order. + * @scaling_list_8x8: scaling matrix after applying the inverse + * scanning process. Expected list order is Intra Y, Inter Y, + * Intra Cb, Inter Cb, Intra Cr, Inter Cr. The values on each + * scaling list are expected in raster scan order. + * + * Note that the list order is different for the 4x4 and 8x8 + * matrices as per the H264 specification, see table 7-2 "Assignment + * of mnemonic names to scaling list indices and specification of + * fall-back rule". + */ +struct v4l2_ctrl_h264_scaling_matrix { + __u8 scaling_list_4x4[6][16]; + __u8 scaling_list_8x8[6][64]; +}; + +struct v4l2_h264_weight_factors { + __s16 luma_weight[32]; + __s16 luma_offset[32]; + __s16 chroma_weight[32][2]; + __s16 chroma_offset[32][2]; +}; + +#define V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(pps, slice) \ + ((((pps)->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) && \ + ((slice)->slice_type == V4L2_H264_SLICE_TYPE_P || \ + (slice)->slice_type == V4L2_H264_SLICE_TYPE_SP)) || \ + ((pps)->weighted_bipred_idc == 1 && \ + (slice)->slice_type == V4L2_H264_SLICE_TYPE_B)) + +#define V4L2_CID_STATELESS_H264_PRED_WEIGHTS (V4L2_CID_CODEC_STATELESS_BASE + 5) +/** + * struct v4l2_ctrl_h264_pred_weights - Prediction weight table + * + * Prediction weight table, which matches the syntax specified + * by the H264 specification. + * + * @luma_log2_weight_denom: see H264 specification. + * @chroma_log2_weight_denom: see H264 specification. + * @weight_factors: luma and chroma weight factors. + */ +struct v4l2_ctrl_h264_pred_weights { + __u16 luma_log2_weight_denom; + __u16 chroma_log2_weight_denom; + struct v4l2_h264_weight_factors weight_factors[2]; +}; + +#define V4L2_H264_SLICE_TYPE_P 0 +#define V4L2_H264_SLICE_TYPE_B 1 +#define V4L2_H264_SLICE_TYPE_I 2 +#define V4L2_H264_SLICE_TYPE_SP 3 +#define V4L2_H264_SLICE_TYPE_SI 4 + +#define V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED 0x01 +#define V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH 0x02 + +#define V4L2_H264_TOP_FIELD_REF 0x1 +#define V4L2_H264_BOTTOM_FIELD_REF 0x2 +#define V4L2_H264_FRAME_REF 0x3 + +/** + * struct v4l2_h264_reference - H264 picture reference + * + * @fields: indicates how the picture is referenced. + * Valid values are V4L2_H264_{}_REF. + * @index: index into v4l2_ctrl_h264_decode_params.dpb[]. + */ +struct v4l2_h264_reference { + __u8 fields; + __u8 index; +}; + +/* + * Maximum DPB size, as specified by section 'A.3.1 Level limits + * common to the Baseline, Main, and Extended profiles'. + */ +#define V4L2_H264_NUM_DPB_ENTRIES 16 +#define V4L2_H264_REF_LIST_LEN (2 * V4L2_H264_NUM_DPB_ENTRIES) + +#define V4L2_CID_STATELESS_H264_SLICE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 6) +/** + * struct v4l2_ctrl_h264_slice_params - H264 slice parameters + * + * This structure holds the H264 syntax elements that are specified + * as non-invariant for the slices in a given frame. + * + * Slice invariant syntax elements are contained in struct + * v4l2_ctrl_h264_decode_params. This is done to reduce the API surface + * on frame-based decoders, where slice header parsing is done by the + * hardware. + * + * Slice invariant syntax elements are specified in specification section + * "7.4.3 Slice header semantics". + * + * Except where noted, the members on this struct match the slice header syntax. + * + * @header_bit_size: offset in bits to slice_data() from the beginning of this slice. + * @first_mb_in_slice: see H264 specification. + * @slice_type: see H264 specification. + * @colour_plane_id: see H264 specification. + * @redundant_pic_cnt: see H264 specification. + * @cabac_init_idc: see H264 specification. + * @slice_qp_delta: see H264 specification. + * @slice_qs_delta: see H264 specification. + * @disable_deblocking_filter_idc: see H264 specification. + * @slice_alpha_c0_offset_div2: see H264 specification. + * @slice_beta_offset_div2: see H264 specification. + * @num_ref_idx_l0_active_minus1: see H264 specification. + * @num_ref_idx_l1_active_minus1: see H264 specification. + * @reserved: padding field. Should be zeroed by applications. + * @ref_pic_list0: reference picture list 0 after applying the per-slice modifications. + * @ref_pic_list1: reference picture list 1 after applying the per-slice modifications. + * @flags: see V4L2_H264_SLICE_FLAG_{}. + */ +struct v4l2_ctrl_h264_slice_params { + __u32 header_bit_size; + __u32 first_mb_in_slice; + __u8 slice_type; + __u8 colour_plane_id; + __u8 redundant_pic_cnt; + __u8 cabac_init_idc; + __s8 slice_qp_delta; + __s8 slice_qs_delta; + __u8 disable_deblocking_filter_idc; + __s8 slice_alpha_c0_offset_div2; + __s8 slice_beta_offset_div2; + __u8 num_ref_idx_l0_active_minus1; + __u8 num_ref_idx_l1_active_minus1; + + __u8 reserved; + + struct v4l2_h264_reference ref_pic_list0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference ref_pic_list1[V4L2_H264_REF_LIST_LEN]; + + __u32 flags; +}; + +#define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 +#define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 +#define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 +#define V4L2_H264_DPB_ENTRY_FLAG_FIELD 0x08 + +/** + * struct v4l2_h264_dpb_entry - H264 decoded picture buffer entry + * + * @reference_ts: timestamp of the V4L2 capture buffer to use as reference. + * The timestamp refers to the timestamp field in struct v4l2_buffer. + * Use v4l2_timeval_to_ns() to convert the struct timeval to a __u64. + * @pic_num: matches PicNum variable assigned during the reference + * picture lists construction process. + * @frame_num: frame identifier which matches frame_num syntax element. + * @fields: indicates how the DPB entry is referenced. Valid values are + * V4L2_H264_{}_REF. + * @reserved: padding field. Should be zeroed by applications. + * @top_field_order_cnt: matches TopFieldOrderCnt picture value. + * @bottom_field_order_cnt: matches BottomFieldOrderCnt picture value. + * Note that picture field is indicated by v4l2_buffer.field. + * @flags: see V4L2_H264_DPB_ENTRY_FLAG_{}. + */ +struct v4l2_h264_dpb_entry { + __u64 reference_ts; + __u32 pic_num; + __u16 frame_num; + __u8 fields; + __u8 reserved[5]; + __s32 top_field_order_cnt; + __s32 bottom_field_order_cnt; + __u32 flags; +}; + +#define V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC 0x01 +#define V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC 0x02 +#define V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD 0x04 + +#define V4L2_CID_STATELESS_H264_DECODE_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 7) +/** + * struct v4l2_ctrl_h264_decode_params - H264 decoding parameters + * + * @dpb: decoded picture buffer. + * @nal_ref_idc: slice header syntax element. + * @frame_num: slice header syntax element. + * @top_field_order_cnt: matches TopFieldOrderCnt picture value. + * @bottom_field_order_cnt: matches BottomFieldOrderCnt picture value. + * Note that picture field is indicated by v4l2_buffer.field. + * @idr_pic_id: slice header syntax element. + * @pic_order_cnt_lsb: slice header syntax element. + * @delta_pic_order_cnt_bottom: slice header syntax element. + * @delta_pic_order_cnt0: slice header syntax element. + * @delta_pic_order_cnt1: slice header syntax element. + * @dec_ref_pic_marking_bit_size: size in bits of dec_ref_pic_marking() + * syntax element. + * @pic_order_cnt_bit_size: size in bits of pic order count syntax. + * @slice_group_change_cycle: slice header syntax element. + * @reserved: padding field. Should be zeroed by applications. + * @flags: see V4L2_H264_DECODE_PARAM_FLAG_{}. + */ +struct v4l2_ctrl_h264_decode_params { + struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]; + __u16 nal_ref_idc; + __u16 frame_num; + __s32 top_field_order_cnt; + __s32 bottom_field_order_cnt; + __u16 idr_pic_id; + __u16 pic_order_cnt_lsb; + __s32 delta_pic_order_cnt_bottom; + __s32 delta_pic_order_cnt0; + __s32 delta_pic_order_cnt1; + __u32 dec_ref_pic_marking_bit_size; + __u32 pic_order_cnt_bit_size; + __u32 slice_group_change_cycle; + + __u32 reserved; + __u32 flags; +}; + + +/* Stateless FWHT control, used by the vicodec driver */ + +/* Current FWHT version */ +#define V4L2_FWHT_VERSION 3 + +/* Set if this is an interlaced format */ +#define V4L2_FWHT_FL_IS_INTERLACED BIT(0) +/* Set if this is a bottom-first (NTSC) interlaced format */ +#define V4L2_FWHT_FL_IS_BOTTOM_FIRST BIT(1) +/* Set if each 'frame' contains just one field */ +#define V4L2_FWHT_FL_IS_ALTERNATE BIT(2) +/* + * If V4L2_FWHT_FL_IS_ALTERNATE was set, then this is set if this + * 'frame' is the bottom field, else it is the top field. + */ +#define V4L2_FWHT_FL_IS_BOTTOM_FIELD BIT(3) +/* Set if the Y' plane is uncompressed */ +#define V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED BIT(4) +/* Set if the Cb plane is uncompressed */ +#define V4L2_FWHT_FL_CB_IS_UNCOMPRESSED BIT(5) +/* Set if the Cr plane is uncompressed */ +#define V4L2_FWHT_FL_CR_IS_UNCOMPRESSED BIT(6) +/* Set if the chroma plane is full height, if cleared it is half height */ +#define V4L2_FWHT_FL_CHROMA_FULL_HEIGHT BIT(7) +/* Set if the chroma plane is full width, if cleared it is half width */ +#define V4L2_FWHT_FL_CHROMA_FULL_WIDTH BIT(8) +/* Set if the alpha plane is uncompressed */ +#define V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9) +/* Set if this is an I Frame */ +#define V4L2_FWHT_FL_I_FRAME BIT(10) + +/* A 4-values flag - the number of components - 1 */ +#define V4L2_FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16) +#define V4L2_FWHT_FL_COMPONENTS_NUM_OFFSET 16 + +/* A 4-values flag - the pixel encoding type */ +#define V4L2_FWHT_FL_PIXENC_MSK GENMASK(20, 19) +#define V4L2_FWHT_FL_PIXENC_OFFSET 19 +#define V4L2_FWHT_FL_PIXENC_YUV (1 << V4L2_FWHT_FL_PIXENC_OFFSET) +#define V4L2_FWHT_FL_PIXENC_RGB (2 << V4L2_FWHT_FL_PIXENC_OFFSET) +#define V4L2_FWHT_FL_PIXENC_HSV (3 << V4L2_FWHT_FL_PIXENC_OFFSET) + +#define V4L2_CID_STATELESS_FWHT_PARAMS (V4L2_CID_CODEC_STATELESS_BASE + 100) +/** + * struct v4l2_ctrl_fwht_params - FWHT parameters + * + * @backward_ref_ts: timestamp of the V4L2 capture buffer to use as reference. + * The timestamp refers to the timestamp field in struct v4l2_buffer. + * Use v4l2_timeval_to_ns() to convert the struct timeval to a __u64. + * @version: must be V4L2_FWHT_VERSION. + * @width: width of frame. + * @height: height of frame. + * @flags: FWHT flags (see V4L2_FWHT_FL_*). + * @colorspace: the colorspace (enum v4l2_colorspace). + * @xfer_func: the transfer function (enum v4l2_xfer_func). + * @ycbcr_enc: the Y'CbCr encoding (enum v4l2_ycbcr_encoding). + * @quantization: the quantization (enum v4l2_quantization). + */ +struct v4l2_ctrl_fwht_params { + __u64 backward_ref_ts; + __u32 version; + __u32 width; + __u32 height; + __u32 flags; + __u32 colorspace; + __u32 xfer_func; + __u32 ycbcr_enc; + __u32 quantization; +}; + +/* MPEG-compression definitions kept for backwards compatibility */ +#ifndef __KERNEL__ +#define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC +#define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS +#define V4L2_CID_MPEG_BASE V4L2_CID_CODEC_BASE +#define V4L2_CID_MPEG_CX2341X_BASE V4L2_CID_CODEC_CX2341X_BASE +#define V4L2_CID_MPEG_MFC51_BASE V4L2_CID_CODEC_MFC51_BASE +#endif + #endif diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 534eaa4d39bc..79dbde3bcf8d 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -221,9 +221,7 @@ enum v4l2_colorspace { V4L2_COLORSPACE_470_SYSTEM_M = 5, /* - * EBU Tech 3213 PAL/SECAM colorspace. This only makes sense when - * dealing with really old PAL/SECAM recordings. Superseded by - * SMPTE 170M. + * EBU Tech 3213 PAL/SECAM colorspace. */ V4L2_COLORSPACE_470_SYSTEM_BG = 6, @@ -517,7 +515,7 @@ struct v4l2_pix_format { /* Pixel format FOURCC depth Description */ -/* RGB formats */ +/* RGB formats (1 or 2 bytes per pixel) */ #define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1') /* 8 RGB-3-3-2 */ #define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4') /* 16 xxxxrrrr ggggbbbb */ #define V4L2_PIX_FMT_ARGB444 v4l2_fourcc('A', 'R', '1', '2') /* 16 aaaarrrr ggggbbbb */ @@ -526,12 +524,6 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_RGBX444 v4l2_fourcc('R', 'X', '1', '2') /* 16 rrrrgggg bbbbxxxx */ #define V4L2_PIX_FMT_ABGR444 v4l2_fourcc('A', 'B', '1', '2') /* 16 aaaabbbb ggggrrrr */ #define V4L2_PIX_FMT_XBGR444 v4l2_fourcc('X', 'B', '1', '2') /* 16 xxxxbbbb ggggrrrr */ - -/* - * Originally this had 'BA12' as fourcc, but this clashed with the older - * V4L2_PIX_FMT_SGRBG12 which inexplicably used that same fourcc. - * So use 'GA12' instead for V4L2_PIX_FMT_BGRA444. - */ #define V4L2_PIX_FMT_BGRA444 v4l2_fourcc('G', 'A', '1', '2') /* 16 bbbbgggg rrrraaaa */ #define V4L2_PIX_FMT_BGRX444 v4l2_fourcc('B', 'X', '1', '2') /* 16 bbbbgggg rrrrxxxx */ #define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O') /* 16 RGB-5-5-5 */ @@ -548,6 +540,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_ARGB555X v4l2_fourcc_be('A', 'R', '1', '5') /* 16 ARGB-5-5-5 BE */ #define V4L2_PIX_FMT_XRGB555X v4l2_fourcc_be('X', 'R', '1', '5') /* 16 XRGB-5-5-5 BE */ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ + +/* RGB formats (3 or 4 bytes per pixel) */ #define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */ #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ @@ -597,8 +591,6 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */ #define V4L2_PIX_FMT_VUYA32 v4l2_fourcc('V', 'U', 'Y', 'A') /* 32 VUYA-8-8-8-8 */ #define V4L2_PIX_FMT_VUYX32 v4l2_fourcc('V', 'U', 'Y', 'X') /* 32 VUYX-8-8-8-8 */ -#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* 8 8-bit color */ -#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ #define V4L2_PIX_FMT_M420 v4l2_fourcc('M', '4', '2', '0') /* 12 YUV 4:2:0 2 lines y, 1 line uv interleaved */ /* two planes -- one Y, one Cr + Cb interleaved */ @@ -608,6 +600,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_NV61 v4l2_fourcc('N', 'V', '6', '1') /* 16 Y/CrCb 4:2:2 */ #define V4L2_PIX_FMT_NV24 v4l2_fourcc('N', 'V', '2', '4') /* 24 Y/CbCr 4:4:4 */ #define V4L2_PIX_FMT_NV42 v4l2_fourcc('N', 'V', '4', '2') /* 24 Y/CrCb 4:4:4 */ +#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H', 'M', '1', '2') /* 8 YUV 4:2:0 16x16 macroblocks */ /* two non contiguous planes - one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ @@ -705,6 +698,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */ #define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */ #define V4L2_PIX_FMT_FWHT_STATELESS v4l2_fourcc('S', 'F', 'W', 'H') /* Stateless FWHT (vicodec) */ +#define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ @@ -740,6 +734,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_INZI v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */ #define V4L2_PIX_FMT_SUNXI_TILED_NV12 v4l2_fourcc('S', 'T', '1', '2') /* Sunxi Tiled NV12 Format */ #define V4L2_PIX_FMT_CNF4 v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */ +#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */ /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */ #define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */ @@ -770,6 +765,10 @@ struct v4l2_pix_format { #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */ #define V4L2_META_FMT_VIVID v4l2_fourcc('V', 'I', 'V', 'D') /* Vivid Metadata */ +/* Vendor specific - used for RK_ISP1 camera sub-system */ +#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */ +#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */ + /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe @@ -1185,7 +1184,7 @@ struct v4l2_window { struct v4l2_rect w; __u32 field; /* enum v4l2_field */ __u32 chromakey; - struct v4l2_clip __user *clips; + struct v4l2_clip *clips; __u32 clipcount; void __user *bitmap; __u8 global_alpha; @@ -1731,6 +1730,13 @@ struct v4l2_ext_control { __u16 __user *p_u16; __u32 __user *p_u32; struct v4l2_area __user *p_area; + struct v4l2_ctrl_h264_sps __user *p_h264_sps; + struct v4l2_ctrl_h264_pps *p_h264_pps; + struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix; + struct v4l2_ctrl_h264_pred_weights __user *p_h264_pred_weights; + struct v4l2_ctrl_h264_slice_params __user *p_h264_slice_params; + struct v4l2_ctrl_h264_decode_params __user *p_h264_decode_params; + struct v4l2_ctrl_fwht_params __user *p_fwht_params; void __user *ptr; }; } __attribute__ ((packed)); @@ -1777,6 +1783,15 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_U16 = 0x0101, V4L2_CTRL_TYPE_U32 = 0x0102, V4L2_CTRL_TYPE_AREA = 0x0106, + + V4L2_CTRL_TYPE_H264_SPS = 0x0200, + V4L2_CTRL_TYPE_H264_PPS = 0x0201, + V4L2_CTRL_TYPE_H264_SCALING_MATRIX = 0x0202, + V4L2_CTRL_TYPE_H264_SLICE_PARAMS = 0x0203, + V4L2_CTRL_TYPE_H264_DECODE_PARAMS = 0x0204, + V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205, + + V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 106e4500fd53..4fcfe0b70c4e 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -11,7 +11,7 @@ #include <asm/page.h> #include <asm/sections.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> /* vmcoreinfo stuff */ unsigned char *vmcoreinfo_data; diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index 8798a8183974..4f8efc278aa7 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -42,7 +42,6 @@ #include <asm/sections.h> #include <crypto/hash.h> -#include <crypto/sha.h> #include "kexec_internal.h" DEFINE_MUTEX(kexec_mutex); diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index e21f6b9234f7..b02086d70492 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -20,7 +20,7 @@ #include <linux/fs.h> #include <linux/ima.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/elf.h> #include <linux/elfcore.h> #include <linux/kernel.h> diff --git a/lib/crypto/blake2s-selftest.c b/lib/crypto/blake2s-selftest.c index 79ef404a990d..5d9ea53be973 100644 --- a/lib/crypto/blake2s-selftest.c +++ b/lib/crypto/blake2s-selftest.c @@ -3,7 +3,7 @@ * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */ -#include <crypto/blake2s.h> +#include <crypto/internal/blake2s.h> #include <linux/string.h> /* diff --git a/lib/crypto/blake2s.c b/lib/crypto/blake2s.c index 41025a30c524..6a4b6b78d630 100644 --- a/lib/crypto/blake2s.c +++ b/lib/crypto/blake2s.c @@ -17,8 +17,6 @@ #include <linux/bug.h> #include <asm/unaligned.h> -bool blake2s_selftest(void); - void blake2s_update(struct blake2s_state *state, const u8 *in, size_t inlen) { const size_t fill = BLAKE2S_BLOCK_SIZE - state->buflen; diff --git a/lib/crypto/curve25519.c b/lib/crypto/curve25519.c index 288a62cd29b2..fb29739e8c29 100644 --- a/lib/crypto/curve25519.c +++ b/lib/crypto/curve25519.c @@ -13,8 +13,6 @@ #include <linux/module.h> #include <linux/init.h> -bool curve25519_selftest(void); - static int __init mod_init(void) { if (!IS_ENABLED(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) && diff --git a/lib/crypto/sha256.c b/lib/crypto/sha256.c index 2321f6cb322f..72a4b0b1df28 100644 --- a/lib/crypto/sha256.c +++ b/lib/crypto/sha256.c @@ -15,9 +15,28 @@ #include <linux/export.h> #include <linux/module.h> #include <linux/string.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/unaligned.h> +static const u32 SHA256_K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +}; + static inline u32 Ch(u32 x, u32 y, u32 z) { return z ^ (x & (y ^ z)); @@ -43,173 +62,68 @@ static inline void BLEND_OP(int I, u32 *W) W[I] = s1(W[I-2]) + W[I-7] + s0(W[I-15]) + W[I-16]; } -static void sha256_transform(u32 *state, const u8 *input) +#define SHA256_ROUND(i, a, b, c, d, e, f, g, h) do { \ + u32 t1, t2; \ + t1 = h + e1(e) + Ch(e, f, g) + SHA256_K[i] + W[i]; \ + t2 = e0(a) + Maj(a, b, c); \ + d += t1; \ + h = t1 + t2; \ +} while (0) + +static void sha256_transform(u32 *state, const u8 *input, u32 *W) { - u32 a, b, c, d, e, f, g, h, t1, t2; - u32 W[64]; + u32 a, b, c, d, e, f, g, h; int i; /* load the input */ - for (i = 0; i < 16; i++) - LOAD_OP(i, W, input); + for (i = 0; i < 16; i += 8) { + LOAD_OP(i + 0, W, input); + LOAD_OP(i + 1, W, input); + LOAD_OP(i + 2, W, input); + LOAD_OP(i + 3, W, input); + LOAD_OP(i + 4, W, input); + LOAD_OP(i + 5, W, input); + LOAD_OP(i + 6, W, input); + LOAD_OP(i + 7, W, input); + } /* now blend */ - for (i = 16; i < 64; i++) - BLEND_OP(i, W); + for (i = 16; i < 64; i += 8) { + BLEND_OP(i + 0, W); + BLEND_OP(i + 1, W); + BLEND_OP(i + 2, W); + BLEND_OP(i + 3, W); + BLEND_OP(i + 4, W); + BLEND_OP(i + 5, W); + BLEND_OP(i + 6, W); + BLEND_OP(i + 7, W); + } /* load the state into our registers */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; f = state[5]; g = state[6]; h = state[7]; /* now iterate */ - t1 = h + e1(e) + Ch(e, f, g) + 0x428a2f98 + W[0]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0x71374491 + W[1]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0xb5c0fbcf + W[2]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0xe9b5dba5 + W[3]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x3956c25b + W[4]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0x59f111f1 + W[5]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x923f82a4 + W[6]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0xab1c5ed5 + W[7]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0xd807aa98 + W[8]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0x12835b01 + W[9]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0x243185be + W[10]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0x550c7dc3 + W[11]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x72be5d74 + W[12]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0x80deb1fe + W[13]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x9bdc06a7 + W[14]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0xc19bf174 + W[15]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0xe49b69c1 + W[16]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0xefbe4786 + W[17]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0x0fc19dc6 + W[18]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0x240ca1cc + W[19]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x2de92c6f + W[20]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0x4a7484aa + W[21]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x5cb0a9dc + W[22]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0x76f988da + W[23]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0x983e5152 + W[24]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0xa831c66d + W[25]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0xb00327c8 + W[26]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0xbf597fc7 + W[27]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0xc6e00bf3 + W[28]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0xd5a79147 + W[29]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x06ca6351 + W[30]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0x14292967 + W[31]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0x27b70a85 + W[32]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0x2e1b2138 + W[33]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0x4d2c6dfc + W[34]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0x53380d13 + W[35]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x650a7354 + W[36]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0x766a0abb + W[37]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x81c2c92e + W[38]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0x92722c85 + W[39]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0xa2bfe8a1 + W[40]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0xa81a664b + W[41]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0xc24b8b70 + W[42]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0xc76c51a3 + W[43]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0xd192e819 + W[44]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0xd6990624 + W[45]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0xf40e3585 + W[46]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0x106aa070 + W[47]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0x19a4c116 + W[48]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0x1e376c08 + W[49]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0x2748774c + W[50]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0x34b0bcb5 + W[51]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x391c0cb3 + W[52]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0x4ed8aa4a + W[53]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0x5b9cca4f + W[54]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0x682e6ff3 + W[55]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; - - t1 = h + e1(e) + Ch(e, f, g) + 0x748f82ee + W[56]; - t2 = e0(a) + Maj(a, b, c); d += t1; h = t1 + t2; - t1 = g + e1(d) + Ch(d, e, f) + 0x78a5636f + W[57]; - t2 = e0(h) + Maj(h, a, b); c += t1; g = t1 + t2; - t1 = f + e1(c) + Ch(c, d, e) + 0x84c87814 + W[58]; - t2 = e0(g) + Maj(g, h, a); b += t1; f = t1 + t2; - t1 = e + e1(b) + Ch(b, c, d) + 0x8cc70208 + W[59]; - t2 = e0(f) + Maj(f, g, h); a += t1; e = t1 + t2; - t1 = d + e1(a) + Ch(a, b, c) + 0x90befffa + W[60]; - t2 = e0(e) + Maj(e, f, g); h += t1; d = t1 + t2; - t1 = c + e1(h) + Ch(h, a, b) + 0xa4506ceb + W[61]; - t2 = e0(d) + Maj(d, e, f); g += t1; c = t1 + t2; - t1 = b + e1(g) + Ch(g, h, a) + 0xbef9a3f7 + W[62]; - t2 = e0(c) + Maj(c, d, e); f += t1; b = t1 + t2; - t1 = a + e1(f) + Ch(f, g, h) + 0xc67178f2 + W[63]; - t2 = e0(b) + Maj(b, c, d); e += t1; a = t1 + t2; + for (i = 0; i < 64; i += 8) { + SHA256_ROUND(i + 0, a, b, c, d, e, f, g, h); + SHA256_ROUND(i + 1, h, a, b, c, d, e, f, g); + SHA256_ROUND(i + 2, g, h, a, b, c, d, e, f); + SHA256_ROUND(i + 3, f, g, h, a, b, c, d, e); + SHA256_ROUND(i + 4, e, f, g, h, a, b, c, d); + SHA256_ROUND(i + 5, d, e, f, g, h, a, b, c); + SHA256_ROUND(i + 6, c, d, e, f, g, h, a, b); + SHA256_ROUND(i + 7, b, c, d, e, f, g, h, a); + } state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; - - /* clear any sensitive info... */ - a = b = c = d = e = f = g = h = t1 = t2 = 0; - memzero_explicit(W, 64 * sizeof(u32)); } void sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len) { unsigned int partial, done; const u8 *src; + u32 W[64]; partial = sctx->count & 0x3f; sctx->count += len; @@ -224,11 +138,13 @@ void sha256_update(struct sha256_state *sctx, const u8 *data, unsigned int len) } do { - sha256_transform(sctx->state, src); + sha256_transform(sctx->state, src, W); done += 64; src = data + done; } while (done + 63 < len); + memzero_explicit(W, sizeof(W)); + partial = 0; } memcpy(sctx->buf + partial, src, len - done); @@ -265,7 +181,7 @@ static void __sha256_final(struct sha256_state *sctx, u8 *out, int digest_words) put_unaligned_be32(sctx->state[i], &dst[i]); /* Zeroize sensitive information. */ - memset(sctx, 0, sizeof(*sctx)); + memzero_explicit(sctx, sizeof(*sctx)); } void sha256_final(struct sha256_state *sctx, u8 *out) diff --git a/lib/digsig.c b/lib/digsig.c index e0627c3e53b2..04b5e55ed95f 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -20,7 +20,7 @@ #include <linux/key.h> #include <linux/crypto.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <keys/user-type.h> #include <linux/mpi.h> #include <linux/digsig.h> diff --git a/lib/mpi/ec.c b/lib/mpi/ec.c index c21470122dfc..40f5908e57a4 100644 --- a/lib/mpi/ec.c +++ b/lib/mpi/ec.c @@ -1252,7 +1252,6 @@ void mpi_ec_mul_point(MPI_POINT result, MPI_POINT q1, q2, prd, sum; unsigned long sw; mpi_size_t rsize; - int scalar_copied = 0; /* Compute scalar point multiplication with Montgomery Ladder. * Note that we don't use Y-coordinate in the points at all. @@ -1314,8 +1313,6 @@ void mpi_ec_mul_point(MPI_POINT result, point_free(&p2); point_free(&p1_); point_free(&p2_); - if (scalar_copied) - mpi_free(scalar); return; } diff --git a/lib/sha1.c b/lib/sha1.c index 49257a915bb6..9bd1935a1472 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/export.h> #include <linux/bitops.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <asm/unaligned.h> /* diff --git a/mm/mprotect.c b/mm/mprotect.c index 56c02beb6041..ab709023e9aa 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -616,9 +616,16 @@ static int do_mprotect_pkey(unsigned long start, size_t len, tmp = vma->vm_end; if (tmp > end) tmp = end; + + if (vma->vm_ops && vma->vm_ops->mprotect) + error = vma->vm_ops->mprotect(vma, nstart, tmp, newflags); + if (error) + goto out; + error = mprotect_fixup(vma, &prev, nstart, tmp, newflags); if (error) goto out; + nstart = tmp; if (nstart < prev->vm_end) diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index 85dddfe3a2c6..687d95dce085 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -35,7 +35,6 @@ #include <net/xfrm.h> #include <crypto/hash.h> -#include <crypto/sha.h> #include <net/seg6.h> #include <net/genetlink.h> #include <net/seg6_hmac.h> diff --git a/net/mptcp/crypto.c b/net/mptcp/crypto.c index 05d398d3fde4..b472dc149856 100644 --- a/net/mptcp/crypto.c +++ b/net/mptcp/crypto.c @@ -21,7 +21,7 @@ */ #include <linux/kernel.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <asm/unaligned.h> #include "protocol.h" diff --git a/net/mptcp/options.c b/net/mptcp/options.c index a044dd43411d..90cd52df99a6 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -7,7 +7,7 @@ #define pr_fmt(fmt) "MPTCP: " fmt #include <linux/kernel.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <net/tcp.h> #include <net/mptcp.h> #include "protocol.h" diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 953906e40742..c21a852a6ffa 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/netdevice.h> #include <crypto/algapi.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <net/sock.h> #include <net/inet_common.h> #include <net/inet_hashtables.h> diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 413c803c5208..547425c20e11 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -14,7 +14,7 @@ #include <linux/types.h> #include <linux/integrity.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <linux/key.h> #include <linux/audit.h> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 192e531c146f..87432b35d771 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -29,7 +29,7 @@ #include <crypto/aes.h> #include <crypto/algapi.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <crypto/skcipher.h> #include "encrypted.h" diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index b9fe02e5f84f..74d82093cbaa 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -22,7 +22,7 @@ #include <linux/rcupdate.h> #include <linux/crypto.h> #include <crypto/hash.h> -#include <crypto/sha.h> +#include <crypto/sha1.h> #include <linux/capability.h> #include <linux/tpm.h> #include <linux/tpm_command.h> diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 3c96e8402e94..b51bad121c11 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -311,7 +311,7 @@ static LIST_HEAD(tomoyo_log); /* Lock for "struct list_head tomoyo_log". */ static DEFINE_SPINLOCK(tomoyo_log_lock); -/* Length of "stuct list_head tomoyo_log". */ +/* Length of "struct list_head tomoyo_log". */ static unsigned int tomoyo_log_count; /** diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 4bee32bfe16d..5c64927bf2b3 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -498,7 +498,7 @@ static struct tomoyo_profile *tomoyo_assign_profile ptr = ns->profile_ptr[profile]; if (ptr) return ptr; - entry = kzalloc(sizeof(*entry), GFP_NOFS); + entry = kzalloc(sizeof(*entry), GFP_NOFS | __GFP_NOWARN); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; ptr = ns->profile_ptr[profile]; @@ -635,7 +635,7 @@ static int tomoyo_set_mode(char *name, const char *value, if (strstr(value, tomoyo_mode[mode])) /* * Update lower 3 bits in order to distinguish - * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + * 'config' from 'TOMOYO_CONFIG_USE_DEFAULT'. */ config = (config & ~7) | mode; if (config != TOMOYO_CONFIG_USE_DEFAULT) { @@ -2574,7 +2574,7 @@ static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * * @head: Pointer to "struct tomoyo_io_buffer". - * @buffer: Poiner to buffer to write to. + * @buffer: Pointer to buffer to write to. * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. @@ -2608,7 +2608,7 @@ ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, /** * tomoyo_parse_policy - Parse a policy line. * - * @head: Poiter to "struct tomoyo_io_buffer". + * @head: Pointer to "struct tomoyo_io_buffer". * @line: Line to parse. * * Returns 0 on success, negative value otherwise. diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 8f6d57c15df6..f8bcc083bb0d 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -98,7 +98,7 @@ static bool tomoyo_envp(const char *env_name, const char *env_value, * @argc: Length of @argc. * @argv: Pointer to "struct tomoyo_argv". * @envc: Length of @envp. - * @envp: Poiner to "struct tomoyo_envp". + * @envp: Pointer to "struct tomoyo_envp". * * Returns true on success, false otherwise. */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index dc4ecc0b2038..98d985895ec8 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -473,9 +473,7 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) return ptr; if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname)) return NULL; - entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS); - if (!entry) - return NULL; + entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS | __GFP_NOWARN); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; ptr = tomoyo_find_namespace(domainname, len); @@ -891,7 +889,7 @@ force_jump_domain: * * @bprm: Pointer to "struct linux_binprm". * @pos: Location to dump. - * @dump: Poiner to "struct tomoyo_page_dump". + * @dump: Pointer to "struct tomoyo_page_dump". * * Returns true on success, false otherwise. */ diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 9537832fca18..026e29ea3796 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -463,7 +463,7 @@ static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, return; reinject: /* - * We can safely reinject this element here bacause + * We can safely reinject this element here because * (1) Appending list elements and removing list elements are protected * by tomoyo_policy_lock mutex. * (2) Only this function removes list elements and this function is diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 2e7fcfa923c9..1b570bde7a3b 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -73,7 +73,7 @@ bool tomoyo_memory_ok(void *ptr) */ void *tomoyo_commit_ok(void *data, const unsigned int size) { - void *ptr = kzalloc(size, GFP_NOFS); + void *ptr = kzalloc(size, GFP_NOFS | __GFP_NOWARN); if (tomoyo_memory_ok(ptr)) { memmove(ptr, data, size); @@ -170,7 +170,7 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) atomic_inc(&ptr->head.users); goto out; } - ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); + ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS | __GFP_NOWARN); if (tomoyo_memory_ok(ptr)) { ptr->entry.name = ((char *) ptr) + sizeof(*ptr); memmove((char *) ptr->entry.name, name, len); diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 546281c5b233..065f4941c4d8 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -131,8 +131,8 @@ static const struct file_operations tomoyo_self_operations = { */ static int tomoyo_open(struct inode *inode, struct file *file) { - const int key = ((u8 *) file_inode(file)->i_private) - - ((u8 *) NULL); + const u8 key = (uintptr_t) file_inode(file)->i_private; + return tomoyo_open_control(key, file); } @@ -223,7 +223,7 @@ static const struct file_operations tomoyo_operations = { static void __init tomoyo_create_entry(const char *name, const umode_t mode, struct dentry *parent, const u8 key) { - securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, + securityfs_create_file(name, mode, parent, (void *) (uintptr_t) key, &tomoyo_operations); } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index a40abb0b91ee..176b803ebcfc 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -434,59 +434,64 @@ void tomoyo_normalize_line(unsigned char *buffer) */ static bool tomoyo_correct_word2(const char *string, size_t len) { + u8 recursion = 20; const char *const start = string; bool in_repetition = false; - unsigned char c; - unsigned char d; - unsigned char e; if (!len) goto out; while (len--) { - c = *string++; + unsigned char c = *string++; + if (c == '\\') { if (!len--) goto out; c = *string++; + if (c >= '0' && c <= '3') { + unsigned char d; + unsigned char e; + + if (!len-- || !len--) + goto out; + d = *string++; + e = *string++; + if (d < '0' || d > '7' || e < '0' || e > '7') + goto out; + c = tomoyo_make_byte(c, d, e); + if (c <= ' ' || c >= 127) + continue; + goto out; + } switch (c) { case '\\': /* "\\" */ - continue; - case '$': /* "\$" */ case '+': /* "\+" */ case '?': /* "\?" */ + case 'x': /* "\x" */ + case 'a': /* "\a" */ + case '-': /* "\-" */ + continue; + } + if (!recursion--) + goto out; + switch (c) { case '*': /* "\*" */ case '@': /* "\@" */ - case 'x': /* "\x" */ + case '$': /* "\$" */ case 'X': /* "\X" */ - case 'a': /* "\a" */ case 'A': /* "\A" */ - case '-': /* "\-" */ continue; case '{': /* "/\{" */ if (string - 3 < start || *(string - 3) != '/') - break; + goto out; in_repetition = true; continue; case '}': /* "\}/" */ if (*string != '/') - break; + goto out; if (!in_repetition) - break; + goto out; in_repetition = false; continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - if (!len-- || !len--) - break; - d = *string++; - e = *string++; - if (d < '0' || d > '7' || e < '0' || e > '7') - break; - c = tomoyo_make_byte(c, d, e); - if (c <= ' ' || c >= 127) - continue; } goto out; } else if (in_repetition && c == '/') { diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 28f039adfa13..58894bf47514 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -8,7 +8,7 @@ * EC for audio function. */ -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/acpi.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/tools/power/cpupower/lib/cpupower.c b/tools/power/cpupower/lib/cpupower.c index 3656e697537e..3f7d0c0c5067 100644 --- a/tools/power/cpupower/lib/cpupower.c +++ b/tools/power/cpupower/lib/cpupower.c @@ -16,8 +16,8 @@ unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen) { - int fd; ssize_t numread; + int fd; fd = open(path, O_RDONLY); if (fd == -1) @@ -35,6 +35,27 @@ unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen) return (unsigned int) numread; } +unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen) +{ + ssize_t numwritten; + int fd; + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwritten = write(fd, buf, buflen - 1); + if (numwritten < 1) { + perror(path); + close(fd); + return -1; + } + + close(fd); + + return (unsigned int) numwritten; +} + /* * Detect whether a CPU is online * diff --git a/tools/power/cpupower/lib/cpupower_intern.h b/tools/power/cpupower/lib/cpupower_intern.h index 4887c76d23f8..ac1112b956ec 100644 --- a/tools/power/cpupower/lib/cpupower_intern.h +++ b/tools/power/cpupower/lib/cpupower_intern.h @@ -1,6 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ #define PATH_TO_CPU "/sys/devices/system/cpu/" + +#ifndef MAX_LINE_LEN #define MAX_LINE_LEN 4096 +#endif + #define SYSFS_PATH_MAX 255 unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen); +unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen); diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c index 0ba61a2c4d81..06345b543786 100644 --- a/tools/power/cpupower/utils/cpupower-info.c +++ b/tools/power/cpupower/utils/cpupower-info.c @@ -101,7 +101,7 @@ int cmd_info(int argc, char **argv) } if (params.perf_bias) { - ret = msr_intel_get_perf_bias(cpu); + ret = cpupower_intel_get_perf_bias(cpu); if (ret < 0) { fprintf(stderr, _("Could not read perf-bias value[%d]\n"), ret); diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c index 052044d7e012..180d5ba877e6 100644 --- a/tools/power/cpupower/utils/cpupower-set.c +++ b/tools/power/cpupower/utils/cpupower-set.c @@ -95,7 +95,7 @@ int cmd_set(int argc, char **argv) } if (params.perf_bias) { - ret = msr_intel_set_perf_bias(cpu, perf_bias); + ret = cpupower_intel_set_perf_bias(cpu, perf_bias); if (ret) { fprintf(stderr, _("Error setting perf-bias " "value on CPU %d\n"), cpu); diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index c258eeccd05f..37dac161f3fe 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -105,8 +105,8 @@ extern struct cpupower_cpu_info cpupower_cpu_info; extern int read_msr(int cpu, unsigned int idx, unsigned long long *val); extern int write_msr(int cpu, unsigned int idx, unsigned long long val); -extern int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val); -extern int msr_intel_get_perf_bias(unsigned int cpu); +extern int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val); +extern int cpupower_intel_get_perf_bias(unsigned int cpu); extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); /* Read/Write msr ****************************/ @@ -150,9 +150,9 @@ static inline int read_msr(int cpu, unsigned int idx, unsigned long long *val) { return -1; }; static inline int write_msr(int cpu, unsigned int idx, unsigned long long val) { return -1; }; -static inline int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) +static inline int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val) { return -1; }; -static inline int msr_intel_get_perf_bias(unsigned int cpu) +static inline int cpupower_intel_get_perf_bias(unsigned int cpu) { return -1; }; static inline unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) { return 0; }; diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index f406adc40bad..e8f8f643a627 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -1,7 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> + #if defined(__i386__) || defined(__x86_64__) #include "helpers/helpers.h" +#include "helpers/sysfs.h" + +#include "cpupower_intern.h" #define MSR_AMD_HWCR 0xc0010015 @@ -40,4 +48,44 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, *support = *active = 1; return 0; } + +int cpupower_intel_get_perf_bias(unsigned int cpu) +{ + char linebuf[MAX_LINE_LEN]; + char path[SYSFS_PATH_MAX]; + unsigned long val; + char *endp; + + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) + return -1; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu); + + if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0) + return -1; + + val = strtol(linebuf, &endp, 0); + if (endp == linebuf || errno == ERANGE) + return -1; + + return val; +} + +int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val) +{ + char path[SYSFS_PATH_MAX]; + char linebuf[3] = {}; + + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) + return -1; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu); + snprintf(linebuf, sizeof(linebuf), "%d", val); + + if (cpupower_write_sysfs(path, linebuf, 3) <= 0) + return -1; + + return 0; +} + #endif /* #if defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/msr.c b/tools/power/cpupower/utils/helpers/msr.c index ab9950748838..8b0b6be74bb8 100644 --- a/tools/power/cpupower/utils/helpers/msr.c +++ b/tools/power/cpupower/utils/helpers/msr.c @@ -11,7 +11,6 @@ /* Intel specific MSRs */ #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_MISC_ENABLES 0x1a0 -#define MSR_IA32_ENERGY_PERF_BIAS 0x1b0 #define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1ad /* @@ -73,33 +72,6 @@ int write_msr(int cpu, unsigned int idx, unsigned long long val) return -1; } -int msr_intel_get_perf_bias(unsigned int cpu) -{ - unsigned long long val; - int ret; - - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) - return -1; - - ret = read_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &val); - if (ret) - return ret; - return val; -} - -int msr_intel_set_perf_bias(unsigned int cpu, unsigned int val) -{ - int ret; - - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_PERF_BIAS)) - return -1; - - ret = write_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, val); - if (ret) - return ret; - return 0; -} - unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu) { unsigned long long val; diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index f3a1746f7f45..389ea5209a83 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -1831,6 +1831,25 @@ int get_mp(int cpu, struct msr_counter *mp, unsigned long long *counterp) return 0; } +int get_epb(int cpu) +{ + char path[128 + PATH_BYTES]; + int ret, epb = -1; + FILE *fp; + + sprintf(path, "/sys/devices/system/cpu/cpu%d/power/energy_perf_bias", cpu); + + fp = fopen_or_die(path, "r"); + + ret = fscanf(fp, "%d", &epb); + if (ret != 1) + err(1, "%s(%s)", __func__, path); + + fclose(fp); + + return epb; +} + void get_apic_id(struct thread_data *t) { unsigned int eax, ebx, ecx, edx; @@ -3917,9 +3936,8 @@ dump_sysfs_pstate_config(void) */ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) { - unsigned long long msr; char *epb_string; - int cpu; + int cpu, epb; if (!has_epb) return 0; @@ -3935,10 +3953,11 @@ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) return -1; } - if (get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &msr)) + epb = get_epb(cpu); + if (epb < 0) return 0; - switch (msr & 0xF) { + switch (epb) { case ENERGY_PERF_BIAS_PERFORMANCE: epb_string = "performance"; break; @@ -3952,7 +3971,7 @@ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) epb_string = "custom"; break; } - fprintf(outf, "cpu%d: MSR_IA32_ENERGY_PERF_BIAS: 0x%08llx (%s)\n", cpu, msr, epb_string); + fprintf(outf, "cpu%d: EPB: %d (%s)\n", cpu, epb, epb_string); return 0; } diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c index ff6c6661f075..5fd9e594079c 100644 --- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c @@ -91,6 +91,9 @@ unsigned int has_hwp_request_pkg; /* IA32_HWP_REQUEST_PKG */ unsigned int bdx_highest_ratio; +#define PATH_TO_CPU "/sys/devices/system/cpu/" +#define SYSFS_PATH_MAX 255 + /* * maintain compatibility with original implementation, but don't document it: */ @@ -721,6 +724,48 @@ int put_msr(int cpu, int offset, unsigned long long new_msr) return 0; } +static unsigned int read_sysfs(const char *path, char *buf, size_t buflen) +{ + ssize_t numread; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return 0; + + numread = read(fd, buf, buflen - 1); + if (numread < 1) { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return (unsigned int) numread; +} + +static unsigned int write_sysfs(const char *path, char *buf, size_t buflen) +{ + ssize_t numwritten; + int fd; + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwritten = write(fd, buf, buflen - 1); + if (numwritten < 1) { + perror("write failed\n"); + close(fd); + return -1; + } + + close(fd); + + return (unsigned int) numwritten; +} + void print_hwp_cap(int cpu, struct msr_hwp_cap *cap, char *str) { if (cpu != -1) @@ -798,17 +843,61 @@ void write_hwp_request(int cpu, struct msr_hwp_request *hwp_req, unsigned int ms put_msr(cpu, msr_offset, msr); } +static int get_epb(int cpu) +{ + char path[SYSFS_PATH_MAX]; + char linebuf[3]; + char *endp; + long val; + + if (!has_epb) + return -1; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu); + + if (!read_sysfs(path, linebuf, 3)) + return -1; + + val = strtol(linebuf, &endp, 0); + if (endp == linebuf || errno == ERANGE) + return -1; + + return (int)val; +} + +static int set_epb(int cpu, int val) +{ + char path[SYSFS_PATH_MAX]; + char linebuf[3]; + char *endp; + int ret; + + if (!has_epb) + return -1; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/power/energy_perf_bias", cpu); + snprintf(linebuf, sizeof(linebuf), "%d", val); + + ret = write_sysfs(path, linebuf, 3); + if (ret <= 0) + return -1; + + val = strtol(linebuf, &endp, 0); + if (endp == linebuf || errno == ERANGE) + return -1; + + return (int)val; +} + int print_cpu_msrs(int cpu) { - unsigned long long msr; struct msr_hwp_request req; struct msr_hwp_cap cap; + int epb; - if (has_epb) { - get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &msr); - - printf("cpu%d: EPB %u\n", cpu, (unsigned int) msr); - } + epb = get_epb(cpu); + if (epb >= 0) + printf("cpu%d: EPB %u\n", cpu, (unsigned int) epb); if (!has_hwp) return 0; @@ -1091,15 +1180,15 @@ int enable_hwp_on_cpu(int cpu) int update_cpu_msrs(int cpu) { unsigned long long msr; - + int epb; if (update_epb) { - get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &msr); - put_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, new_epb); + epb = get_epb(cpu); + set_epb(cpu, new_epb); if (verbose) printf("cpu%d: ENERGY_PERF_BIAS old: %d new: %d\n", - cpu, (unsigned int) msr, (unsigned int) new_epb); + cpu, epb, (unsigned int) new_epb); } if (update_turbo) { diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 2ac0fff6dad8..9b185bf82da8 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -23,7 +23,6 @@ #include "nfit_test.h" #include "../watermark.h" -#include <asm/copy_mc_test.h> #include <asm/mce.h> /* @@ -3284,107 +3283,6 @@ static struct platform_driver nfit_test_driver = { .id_table = nfit_test_id, }; -static char copy_mc_buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); - -enum INJECT { - INJECT_NONE, - INJECT_SRC, - INJECT_DST, -}; - -static void copy_mc_test_init(char *dst, char *src, size_t size) -{ - size_t i; - - memset(dst, 0xff, size); - for (i = 0; i < size; i++) - src[i] = (char) i; -} - -static bool copy_mc_test_validate(unsigned char *dst, unsigned char *src, - size_t size, unsigned long rem) -{ - size_t i; - - for (i = 0; i < size - rem; i++) - if (dst[i] != (unsigned char) i) { - pr_info_once("%s:%d: offset: %zd got: %#x expect: %#x\n", - __func__, __LINE__, i, dst[i], - (unsigned char) i); - return false; - } - for (i = size - rem; i < size; i++) - if (dst[i] != 0xffU) { - pr_info_once("%s:%d: offset: %zd got: %#x expect: 0xff\n", - __func__, __LINE__, i, dst[i]); - return false; - } - return true; -} - -void copy_mc_test(void) -{ - char *inject_desc[] = { "none", "source", "destination" }; - enum INJECT inj; - - if (IS_ENABLED(CONFIG_COPY_MC_TEST)) { - pr_info("%s: run...\n", __func__); - } else { - pr_info("%s: disabled, skip.\n", __func__); - return; - } - - for (inj = INJECT_NONE; inj <= INJECT_DST; inj++) { - int i; - - pr_info("%s: inject: %s\n", __func__, inject_desc[inj]); - for (i = 0; i < 512; i++) { - unsigned long expect, rem; - void *src, *dst; - bool valid; - - switch (inj) { - case INJECT_NONE: - copy_mc_inject_src(NULL); - copy_mc_inject_dst(NULL); - dst = ©_mc_buf[2048]; - src = ©_mc_buf[1024 - i]; - expect = 0; - break; - case INJECT_SRC: - copy_mc_inject_src(©_mc_buf[1024]); - copy_mc_inject_dst(NULL); - dst = ©_mc_buf[2048]; - src = ©_mc_buf[1024 - i]; - expect = 512 - i; - break; - case INJECT_DST: - copy_mc_inject_src(NULL); - copy_mc_inject_dst(©_mc_buf[2048]); - dst = ©_mc_buf[2048 - i]; - src = ©_mc_buf[1024]; - expect = 512 - i; - break; - } - - copy_mc_test_init(dst, src, 512); - rem = copy_mc_fragile(dst, src, 512); - valid = copy_mc_test_validate(dst, src, 512, expect); - if (rem == expect && valid) - continue; - pr_info("%s: copy(%#lx, %#lx, %d) off: %d rem: %ld %s expect: %ld\n", - __func__, - ((unsigned long) dst) & ~PAGE_MASK, - ((unsigned long ) src) & ~PAGE_MASK, - 512, i, rem, valid ? "valid" : "bad", - expect); - } - } - - copy_mc_inject_src(NULL); - copy_mc_inject_dst(NULL); -} - static __init int nfit_test_init(void) { int rc, i; @@ -3393,7 +3291,6 @@ static __init int nfit_test_init(void) libnvdimm_test(); acpi_nfit_test(); device_dax_test(); - copy_mc_test(); dax_pmem_test(); dax_pmem_core_test(); #ifdef CONFIG_DEV_DAX_PMEM_COMPAT diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index d9c283503159..2e20e30a6faa 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -50,6 +50,7 @@ TARGETS += openat2 TARGETS += rseq TARGETS += rtc TARGETS += seccomp +TARGETS += sgx TARGETS += sigaltstack TARGETS += size TARGETS += sparc64 diff --git a/tools/testing/selftests/sgx/.gitignore b/tools/testing/selftests/sgx/.gitignore new file mode 100644 index 000000000000..fbaf0bda9a92 --- /dev/null +++ b/tools/testing/selftests/sgx/.gitignore @@ -0,0 +1,2 @@ +test_sgx +test_encl.elf diff --git a/tools/testing/selftests/sgx/Makefile b/tools/testing/selftests/sgx/Makefile new file mode 100644 index 000000000000..7f12d55b97f8 --- /dev/null +++ b/tools/testing/selftests/sgx/Makefile @@ -0,0 +1,57 @@ +top_srcdir = ../../../.. + +include ../lib.mk + +.PHONY: all clean + +CAN_BUILD_X86_64 := $(shell ../x86/check_cc.sh $(CC) \ + ../x86/trivial_64bit_program.c) + +ifndef OBJCOPY +OBJCOPY := $(CROSS_COMPILE)objcopy +endif + +INCLUDES := -I$(top_srcdir)/tools/include +HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC -z noexecstack +ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \ + -fno-stack-protector -mrdrnd $(INCLUDES) + +TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx + +ifeq ($(CAN_BUILD_X86_64), 1) +all: $(TEST_CUSTOM_PROGS) $(OUTPUT)/test_encl.elf +endif + +$(OUTPUT)/test_sgx: $(OUTPUT)/main.o \ + $(OUTPUT)/load.o \ + $(OUTPUT)/sigstruct.o \ + $(OUTPUT)/call.o \ + $(OUTPUT)/sign_key.o + $(CC) $(HOST_CFLAGS) -o $@ $^ -lcrypto + +$(OUTPUT)/main.o: main.c + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/load.o: load.c + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/sigstruct.o: sigstruct.c + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/call.o: call.S + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/sign_key.o: sign_key.S + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/test_encl.elf: test_encl.lds test_encl.c test_encl_bootstrap.S + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ + +EXTRA_CLEAN := \ + $(OUTPUT)/test_encl.elf \ + $(OUTPUT)/load.o \ + $(OUTPUT)/call.o \ + $(OUTPUT)/main.o \ + $(OUTPUT)/sigstruct.o \ + $(OUTPUT)/test_sgx \ + $(OUTPUT)/test_sgx.o \ diff --git a/tools/testing/selftests/sgx/call.S b/tools/testing/selftests/sgx/call.S new file mode 100644 index 000000000000..4ecadc7490f4 --- /dev/null +++ b/tools/testing/selftests/sgx/call.S @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** +* Copyright(c) 2016-20 Intel Corporation. +*/ + + .text + + .global sgx_call_vdso +sgx_call_vdso: + .cfi_startproc + push %r15 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r15, 0 + push %r14 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r14, 0 + push %r13 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r13, 0 + push %r12 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r12, 0 + push %rbx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + push $0 + .cfi_adjust_cfa_offset 8 + push 0x38(%rsp) + .cfi_adjust_cfa_offset 8 + call *eenter(%rip) + add $0x10, %rsp + .cfi_adjust_cfa_offset -0x10 + pop %rbx + .cfi_adjust_cfa_offset -8 + pop %r12 + .cfi_adjust_cfa_offset -8 + pop %r13 + .cfi_adjust_cfa_offset -8 + pop %r14 + .cfi_adjust_cfa_offset -8 + pop %r15 + .cfi_adjust_cfa_offset -8 + ret + .cfi_endproc diff --git a/tools/testing/selftests/sgx/defines.h b/tools/testing/selftests/sgx/defines.h new file mode 100644 index 000000000000..592c1ccf4576 --- /dev/null +++ b/tools/testing/selftests/sgx/defines.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2016-20 Intel Corporation. + */ + +#ifndef DEFINES_H +#define DEFINES_H + +#include <stdint.h> + +#define PAGE_SIZE 4096 +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +#define __aligned(x) __attribute__((__aligned__(x))) +#define __packed __attribute__((packed)) + +#include "../../../../arch/x86/kernel/cpu/sgx/arch.h" +#include "../../../../arch/x86/include/asm/enclu.h" +#include "../../../../arch/x86/include/uapi/asm/sgx.h" + +#endif /* DEFINES_H */ diff --git a/tools/testing/selftests/sgx/load.c b/tools/testing/selftests/sgx/load.c new file mode 100644 index 000000000000..9d43b75aaa55 --- /dev/null +++ b/tools/testing/selftests/sgx/load.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <assert.h> +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include "defines.h" +#include "main.h" + +void encl_delete(struct encl *encl) +{ + if (encl->encl_base) + munmap((void *)encl->encl_base, encl->encl_size); + + if (encl->bin) + munmap(encl->bin, encl->bin_size); + + if (encl->fd) + close(encl->fd); + + if (encl->segment_tbl) + free(encl->segment_tbl); + + memset(encl, 0, sizeof(*encl)); +} + +static bool encl_map_bin(const char *path, struct encl *encl) +{ + struct stat sb; + void *bin; + int ret; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) { + perror("open()"); + return false; + } + + ret = stat(path, &sb); + if (ret) { + perror("stat()"); + goto err; + } + + bin = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (bin == MAP_FAILED) { + perror("mmap()"); + goto err; + } + + encl->bin = bin; + encl->bin_size = sb.st_size; + + close(fd); + return true; + +err: + close(fd); + return false; +} + +static bool encl_ioc_create(struct encl *encl) +{ + struct sgx_secs *secs = &encl->secs; + struct sgx_enclave_create ioc; + int rc; + + assert(encl->encl_base != 0); + + memset(secs, 0, sizeof(*secs)); + secs->ssa_frame_size = 1; + secs->attributes = SGX_ATTR_MODE64BIT; + secs->xfrm = 3; + secs->base = encl->encl_base; + secs->size = encl->encl_size; + + ioc.src = (unsigned long)secs; + rc = ioctl(encl->fd, SGX_IOC_ENCLAVE_CREATE, &ioc); + if (rc) { + fprintf(stderr, "SGX_IOC_ENCLAVE_CREATE failed: errno=%d\n", + errno); + munmap((void *)secs->base, encl->encl_size); + return false; + } + + return true; +} + +static bool encl_ioc_add_pages(struct encl *encl, struct encl_segment *seg) +{ + struct sgx_enclave_add_pages ioc; + struct sgx_secinfo secinfo; + int rc; + + memset(&secinfo, 0, sizeof(secinfo)); + secinfo.flags = seg->flags; + + ioc.src = (uint64_t)encl->src + seg->offset; + ioc.offset = seg->offset; + ioc.length = seg->size; + ioc.secinfo = (unsigned long)&secinfo; + ioc.flags = SGX_PAGE_MEASURE; + + rc = ioctl(encl->fd, SGX_IOC_ENCLAVE_ADD_PAGES, &ioc); + if (rc < 0) { + fprintf(stderr, "SGX_IOC_ENCLAVE_ADD_PAGES failed: errno=%d.\n", + errno); + return false; + } + + return true; +} + +bool encl_load(const char *path, struct encl *encl) +{ + Elf64_Phdr *phdr_tbl; + off_t src_offset; + Elf64_Ehdr *ehdr; + int i, j; + int ret; + + memset(encl, 0, sizeof(*encl)); + + ret = open("/dev/sgx_enclave", O_RDWR); + if (ret < 0) { + fprintf(stderr, "Unable to open /dev/sgx_enclave\n"); + goto err; + } + + encl->fd = ret; + + if (!encl_map_bin(path, encl)) + goto err; + + ehdr = encl->bin; + phdr_tbl = encl->bin + ehdr->e_phoff; + + for (i = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = &phdr_tbl[i]; + + if (phdr->p_type == PT_LOAD) + encl->nr_segments++; + } + + encl->segment_tbl = calloc(encl->nr_segments, + sizeof(struct encl_segment)); + if (!encl->segment_tbl) + goto err; + + for (i = 0, j = 0; i < ehdr->e_phnum; i++) { + Elf64_Phdr *phdr = &phdr_tbl[i]; + unsigned int flags = phdr->p_flags; + struct encl_segment *seg; + + if (phdr->p_type != PT_LOAD) + continue; + + seg = &encl->segment_tbl[j]; + + if (!!(flags & ~(PF_R | PF_W | PF_X))) { + fprintf(stderr, + "%d has invalid segment flags 0x%02x.\n", i, + phdr->p_flags); + goto err; + } + + if (j == 0 && flags != (PF_R | PF_W)) { + fprintf(stderr, + "TCS has invalid segment flags 0x%02x.\n", + phdr->p_flags); + goto err; + } + + if (j == 0) { + src_offset = phdr->p_offset & PAGE_MASK; + + seg->prot = PROT_READ | PROT_WRITE; + seg->flags = SGX_PAGE_TYPE_TCS << 8; + } else { + seg->prot = (phdr->p_flags & PF_R) ? PROT_READ : 0; + seg->prot |= (phdr->p_flags & PF_W) ? PROT_WRITE : 0; + seg->prot |= (phdr->p_flags & PF_X) ? PROT_EXEC : 0; + seg->flags = (SGX_PAGE_TYPE_REG << 8) | seg->prot; + } + + seg->offset = (phdr->p_offset & PAGE_MASK) - src_offset; + seg->size = (phdr->p_filesz + PAGE_SIZE - 1) & PAGE_MASK; + + printf("0x%016lx 0x%016lx 0x%02x\n", seg->offset, seg->size, + seg->prot); + + j++; + } + + assert(j == encl->nr_segments); + + encl->src = encl->bin + src_offset; + encl->src_size = encl->segment_tbl[j - 1].offset + + encl->segment_tbl[j - 1].size; + + for (encl->encl_size = 4096; encl->encl_size < encl->src_size; ) + encl->encl_size <<= 1; + + return true; + +err: + encl_delete(encl); + return false; +} + +static bool encl_map_area(struct encl *encl) +{ + size_t encl_size = encl->encl_size; + void *area; + + area = mmap(NULL, encl_size * 2, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (area == MAP_FAILED) { + perror("mmap"); + return false; + } + + encl->encl_base = ((uint64_t)area + encl_size - 1) & ~(encl_size - 1); + + munmap(area, encl->encl_base - (uint64_t)area); + munmap((void *)(encl->encl_base + encl_size), + (uint64_t)area + encl_size - encl->encl_base); + + return true; +} + +bool encl_build(struct encl *encl) +{ + struct sgx_enclave_init ioc; + int ret; + int i; + + if (!encl_map_area(encl)) + return false; + + if (!encl_ioc_create(encl)) + return false; + + /* + * Pages must be added before mapping VMAs because their permissions + * cap the VMA permissions. + */ + for (i = 0; i < encl->nr_segments; i++) { + struct encl_segment *seg = &encl->segment_tbl[i]; + + if (!encl_ioc_add_pages(encl, seg)) + return false; + } + + ioc.sigstruct = (uint64_t)&encl->sigstruct; + ret = ioctl(encl->fd, SGX_IOC_ENCLAVE_INIT, &ioc); + if (ret) { + fprintf(stderr, "SGX_IOC_ENCLAVE_INIT failed: errno=%d\n", + errno); + return false; + } + + return true; +} diff --git a/tools/testing/selftests/sgx/main.c b/tools/testing/selftests/sgx/main.c new file mode 100644 index 000000000000..724cec700926 --- /dev/null +++ b/tools/testing/selftests/sgx/main.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <elf.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include "defines.h" +#include "main.h" +#include "../kselftest.h" + +static const uint64_t MAGIC = 0x1122334455667788ULL; +vdso_sgx_enter_enclave_t eenter; + +struct vdso_symtab { + Elf64_Sym *elf_symtab; + const char *elf_symstrtab; + Elf64_Word *elf_hashtab; +}; + +static void *vdso_get_base_addr(char *envp[]) +{ + Elf64_auxv_t *auxv; + int i; + + for (i = 0; envp[i]; i++) + ; + + auxv = (Elf64_auxv_t *)&envp[i + 1]; + + for (i = 0; auxv[i].a_type != AT_NULL; i++) { + if (auxv[i].a_type == AT_SYSINFO_EHDR) + return (void *)auxv[i].a_un.a_val; + } + + return NULL; +} + +static Elf64_Dyn *vdso_get_dyntab(void *addr) +{ + Elf64_Ehdr *ehdr = addr; + Elf64_Phdr *phdrtab = addr + ehdr->e_phoff; + int i; + + for (i = 0; i < ehdr->e_phnum; i++) + if (phdrtab[i].p_type == PT_DYNAMIC) + return addr + phdrtab[i].p_offset; + + return NULL; +} + +static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) +{ + int i; + + for (i = 0; dyntab[i].d_tag != DT_NULL; i++) + if (dyntab[i].d_tag == tag) + return addr + dyntab[i].d_un.d_ptr; + + return NULL; +} + +static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) +{ + Elf64_Dyn *dyntab = vdso_get_dyntab(addr); + + symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); + if (!symtab->elf_symtab) + return false; + + symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB); + if (!symtab->elf_symstrtab) + return false; + + symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH); + if (!symtab->elf_hashtab) + return false; + + return true; +} + +static unsigned long elf_sym_hash(const char *name) +{ + unsigned long h = 0, high; + + while (*name) { + h = (h << 4) + *name++; + high = h & 0xf0000000; + + if (high) + h ^= high >> 24; + + h &= ~high; + } + + return h; +} + +static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name) +{ + Elf64_Word bucketnum = symtab->elf_hashtab[0]; + Elf64_Word *buckettab = &symtab->elf_hashtab[2]; + Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum]; + Elf64_Sym *sym; + Elf64_Word i; + + for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF; + i = chaintab[i]) { + sym = &symtab->elf_symtab[i]; + if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name])) + return sym; + } + + return NULL; +} + +bool report_results(struct sgx_enclave_run *run, int ret, uint64_t result, + const char *test) +{ + bool valid = true; + + if (ret) { + printf("FAIL: %s() returned: %d\n", test, ret); + valid = false; + } + + if (run->function != EEXIT) { + printf("FAIL: %s() function, expected: %u, got: %u\n", test, EEXIT, + run->function); + valid = false; + } + + if (result != MAGIC) { + printf("FAIL: %s(), expected: 0x%lx, got: 0x%lx\n", test, MAGIC, + result); + valid = false; + } + + if (run->user_data) { + printf("FAIL: %s() user data, expected: 0x0, got: 0x%llx\n", + test, run->user_data); + valid = false; + } + + return valid; +} + +static int user_handler(long rdi, long rsi, long rdx, long ursp, long r8, long r9, + struct sgx_enclave_run *run) +{ + run->user_data = 0; + return 0; +} + +int main(int argc, char *argv[], char *envp[]) +{ + struct sgx_enclave_run run; + struct vdso_symtab symtab; + Elf64_Sym *eenter_sym; + uint64_t result = 0; + struct encl encl; + unsigned int i; + void *addr; + int ret; + + memset(&run, 0, sizeof(run)); + + if (!encl_load("test_encl.elf", &encl)) { + encl_delete(&encl); + ksft_exit_skip("cannot load enclaves\n"); + } + + if (!encl_measure(&encl)) + goto err; + + if (!encl_build(&encl)) + goto err; + + /* + * An enclave consumer only must do this. + */ + for (i = 0; i < encl.nr_segments; i++) { + struct encl_segment *seg = &encl.segment_tbl[i]; + + addr = mmap((void *)encl.encl_base + seg->offset, seg->size, + seg->prot, MAP_SHARED | MAP_FIXED, encl.fd, 0); + if (addr == MAP_FAILED) { + fprintf(stderr, "mmap() failed, errno=%d.\n", errno); + exit(KSFT_FAIL); + } + } + + memset(&run, 0, sizeof(run)); + run.tcs = encl.encl_base; + + addr = vdso_get_base_addr(envp); + if (!addr) + goto err; + + if (!vdso_get_symtab(addr, &symtab)) + goto err; + + eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave"); + if (!eenter_sym) + goto err; + + eenter = addr + eenter_sym->st_value; + + ret = sgx_call_vdso((void *)&MAGIC, &result, 0, EENTER, NULL, NULL, &run); + if (!report_results(&run, ret, result, "sgx_call_vdso")) + goto err; + + + /* Invoke the vDSO directly. */ + result = 0; + ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER, + 0, 0, &run); + if (!report_results(&run, ret, result, "eenter")) + goto err; + + /* And with an exit handler. */ + run.user_handler = (__u64)user_handler; + run.user_data = 0xdeadbeef; + ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER, + 0, 0, &run); + if (!report_results(&run, ret, result, "user_handler")) + goto err; + + printf("SUCCESS\n"); + encl_delete(&encl); + exit(KSFT_PASS); + +err: + encl_delete(&encl); + exit(KSFT_FAIL); +} diff --git a/tools/testing/selftests/sgx/main.h b/tools/testing/selftests/sgx/main.h new file mode 100644 index 000000000000..67211a708f04 --- /dev/null +++ b/tools/testing/selftests/sgx/main.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2016-20 Intel Corporation. + */ + +#ifndef MAIN_H +#define MAIN_H + +struct encl_segment { + off_t offset; + size_t size; + unsigned int prot; + unsigned int flags; +}; + +struct encl { + int fd; + void *bin; + off_t bin_size; + void *src; + size_t src_size; + size_t encl_size; + off_t encl_base; + unsigned int nr_segments; + struct encl_segment *segment_tbl; + struct sgx_secs secs; + struct sgx_sigstruct sigstruct; +}; + +extern unsigned char sign_key[]; +extern unsigned char sign_key_end[]; + +void encl_delete(struct encl *ctx); +bool encl_load(const char *path, struct encl *encl); +bool encl_measure(struct encl *encl); +bool encl_build(struct encl *encl); + +int sgx_call_vdso(void *rdi, void *rsi, long rdx, u32 function, void *r8, void *r9, + struct sgx_enclave_run *run); + +#endif /* MAIN_H */ diff --git a/tools/testing/selftests/sgx/sign_key.S b/tools/testing/selftests/sgx/sign_key.S new file mode 100644 index 000000000000..e4fbe948444a --- /dev/null +++ b/tools/testing/selftests/sgx/sign_key.S @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** +* Copyright(c) 2016-20 Intel Corporation. +*/ + + .section ".rodata", "a" + +sign_key: + .globl sign_key + .incbin "sign_key.pem" +sign_key_end: + .globl sign_key_end diff --git a/tools/testing/selftests/sgx/sign_key.pem b/tools/testing/selftests/sgx/sign_key.pem new file mode 100644 index 000000000000..d76f21f19187 --- /dev/null +++ b/tools/testing/selftests/sgx/sign_key.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4wIBAAKCAYEApalGbq7Q+usM91CPtksu3D+b0Prc8gAFL6grM3mg85A5Bx8V +cfMXPgtrw8EYFwQxDAvzZWwl+9VfOX0ECrFRBkOHcOiG0SnADN8+FLj1UiNUQwbp +S6OzhNWuRcSbGraSOyUlVlV0yMQSvewyzGklOaXBe30AJqzIBc8QfdSxKuP8rs0Z +ga6k/Bl73osrYKByILJTUUeZqjLERsE6GebsdzbWgKn8qVqng4ZS4yMNg6LeRlH3 ++9CIPgg4jwpSLHcp7dq2qTIB9a0tGe9ayp+5FbucpB6U7ePold0EeRN6RlJGDF9k +L93v8P5ykz5G5gYZ2g0K1X2sHIWV4huxPgv5PXgdyQYbK+6olqj0d5rjYuwX57Ul +k6SroPS1U6UbdCjG5txM+BNGU0VpD0ZhrIRw0leQdnNcCO9sTJuInZrgYacSVJ7u +mtB+uCt+uzUesc+l+xPRYA+9e14lLkZp7AAmo9FvL816XDI09deehJ3i/LmHKCRN +tuqC5TprRjFwUr6dAgEDAoIBgG5w2Z8fNfycs0+LCnmHdJLVEotR6KFVWMpwHMz7 +wKJgJgS/Y6FMuilc8oKAuroCy11dTO5IGVKOP3uorVx2NgQtBPXwWeDGgAiU1A3Q +o4wXjYIEm4fCd63jyYPYZ2ckYXzDbjmOTdstYdPyzIhGGNEZK6eoqsRzMAPfYFPj +IMdCqHSIu6vJw1K7p+myHOsVoWshjODaZnF3LYSA0WaZ8vokjwBxUxuRxQJZjJds +s60XPtmL+qfgWtQFewoG4XL6GuD8FcXccynRRtzrLtFNPIl9BQfWfjBBhTC1/Te1 +0Z6XbZvpdUTD9OfLB7SbR2OUFNpKQgriO0iYVdbW3cr7uu38Zwp4W1TX73DPjoi6 +KNooP6SGWd4mRJW2+dUmSYS4QNG8eVVZswKcploEIXlAKRsOe4kzJJ1iETugIe85 +uX8nd1WYEp65xwoRUg8hqng0MeyveVbXqNKuJG6tzNDt9kgFYo+hmC/oouAW2Dtc +T9jdRAwKJXqA2Eg6OkgXCEv+kwKBwQDYaQiFMlFhsmLlqI+EzCUh7c941/cL7m6U +7j98+8ngl0HgCEcrc10iJVCKakQW3YbPzAx3XkKTaGjWazvvrFarXIGlOud64B8a +iWyQ7VdlnmZnNEdk+C83tI91OQeaTKqRLDGzKh29Ry/jL8Pcbazt+kDgxa0H7qJp +roADUanLQuNkYubpbhFBh3xpa2EExaVq6rF7nIVsD8W9TrbmPKA4LgH7z0iy544D +kVCNYsTjYDdUWP+WiSor8kCnnpjnN9sCgcEAw/eNezUD1UDf6OYFC9+5JZJFn4Tg +mZMyN93JKIb199ffwnjtHUSjcyiWeesXucpzwtGbTcwQnDisSW4oneYKLSEBlBaq +scqiUugyGZZOthFSCbdXYXMViK2vHrKlkse7GxVlROKcEhM/pRBrmjaGO8eWR+D4 +FO2wCXzVs3KgV6j779frw0vC54oHOxc9+Lu1rSHp4i+600koyvL/zF6U/5tZXIvN +YW2yoiQJnjCmVA1pwbwV6KAUTPDTMnBK+YjnAoHBAJBGBa4hi5Z27JkbCliIGMFJ +NPs6pLKe9GNJf6in2+sPgUAFhMeiPhbDiwbxgrnpBIqICE+ULGJFmzmc0p/IOceT +ARjR76dAFLxbnbXzj5kURETNhO36yiUjCk4mBRGIcbYddndxaSjaH+zKgpLzyJ6m +1esuc1qfFvEfAAI2cTIsl5hB70ZJYNZaUvDyQK3ZGPHxy6e9rkgKg9OJz0QoatAe +q/002yHvtAJg4F5B2JeVejg7VQ8GHB1MKxppu0TP5wKBwQCCpQj8zgKOKz/wmViy +lSYZDC5qWJW7t3bP6TDFr06lOpUsUJ4TgxeiGw778g/RMaKB4RIz3WBoJcgw9BsT +7rFza1ZiucchMcGMmswRDt8kC4wGejpA92Owc8oUdxkMhSdnY5jYlxK2t3/DYEe8 +JFl9L7mFQKVjSSAGUzkiTGrlG1Kf5UfXh9dFBq98uilQfSPIwUaWynyM23CHTKqI +Pw3/vOY9sojrnncWwrEUIG7is5vWfWPwargzSzd29YdRBe8CgcEAuRVewK/YeNOX +B7ZG6gKKsfsvrGtY7FPETzLZAHjoVXYNea4LVZ2kn4hBXXlvw/4HD+YqcTt4wmif +5JQlDvjNobUiKJZpzy7hklVhF7wZFl4pCF7Yh43q9iQ7gKTaeUG7MiaK+G8Zz8aY +HW9rsiihbdZkccMvnPfO9334XMxl3HtBRzLstjUlbLB7Sdh+7tZ3JQidCOFNs5pE +XyWwnASPu4tKfDahH1UUTp1uJcq/6716CSWg080avYxFcn75qqsb +-----END RSA PRIVATE KEY----- diff --git a/tools/testing/selftests/sgx/sigstruct.c b/tools/testing/selftests/sgx/sigstruct.c new file mode 100644 index 000000000000..dee7a3d6c5a5 --- /dev/null +++ b/tools/testing/selftests/sgx/sigstruct.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#define _GNU_SOURCE +#include <assert.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include "defines.h" +#include "main.h" + +struct q1q2_ctx { + BN_CTX *bn_ctx; + BIGNUM *m; + BIGNUM *s; + BIGNUM *q1; + BIGNUM *qr; + BIGNUM *q2; +}; + +static void free_q1q2_ctx(struct q1q2_ctx *ctx) +{ + BN_CTX_free(ctx->bn_ctx); + BN_free(ctx->m); + BN_free(ctx->s); + BN_free(ctx->q1); + BN_free(ctx->qr); + BN_free(ctx->q2); +} + +static bool alloc_q1q2_ctx(const uint8_t *s, const uint8_t *m, + struct q1q2_ctx *ctx) +{ + ctx->bn_ctx = BN_CTX_new(); + ctx->s = BN_bin2bn(s, SGX_MODULUS_SIZE, NULL); + ctx->m = BN_bin2bn(m, SGX_MODULUS_SIZE, NULL); + ctx->q1 = BN_new(); + ctx->qr = BN_new(); + ctx->q2 = BN_new(); + + if (!ctx->bn_ctx || !ctx->s || !ctx->m || !ctx->q1 || !ctx->qr || + !ctx->q2) { + free_q1q2_ctx(ctx); + return false; + } + + return true; +} + +static bool calc_q1q2(const uint8_t *s, const uint8_t *m, uint8_t *q1, + uint8_t *q2) +{ + struct q1q2_ctx ctx; + + if (!alloc_q1q2_ctx(s, m, &ctx)) { + fprintf(stderr, "Not enough memory for Q1Q2 calculation\n"); + return false; + } + + if (!BN_mul(ctx.q1, ctx.s, ctx.s, ctx.bn_ctx)) + goto out; + + if (!BN_div(ctx.q1, ctx.qr, ctx.q1, ctx.m, ctx.bn_ctx)) + goto out; + + if (BN_num_bytes(ctx.q1) > SGX_MODULUS_SIZE) { + fprintf(stderr, "Too large Q1 %d bytes\n", + BN_num_bytes(ctx.q1)); + goto out; + } + + if (!BN_mul(ctx.q2, ctx.s, ctx.qr, ctx.bn_ctx)) + goto out; + + if (!BN_div(ctx.q2, NULL, ctx.q2, ctx.m, ctx.bn_ctx)) + goto out; + + if (BN_num_bytes(ctx.q2) > SGX_MODULUS_SIZE) { + fprintf(stderr, "Too large Q2 %d bytes\n", + BN_num_bytes(ctx.q2)); + goto out; + } + + BN_bn2bin(ctx.q1, q1); + BN_bn2bin(ctx.q2, q2); + + free_q1q2_ctx(&ctx); + return true; +out: + free_q1q2_ctx(&ctx); + return false; +} + +struct sgx_sigstruct_payload { + struct sgx_sigstruct_header header; + struct sgx_sigstruct_body body; +}; + +static bool check_crypto_errors(void) +{ + int err; + bool had_errors = false; + const char *filename; + int line; + char str[256]; + + for ( ; ; ) { + if (ERR_peek_error() == 0) + break; + + had_errors = true; + err = ERR_get_error_line(&filename, &line); + ERR_error_string_n(err, str, sizeof(str)); + fprintf(stderr, "crypto: %s: %s:%d\n", str, filename, line); + } + + return had_errors; +} + +static inline const BIGNUM *get_modulus(RSA *key) +{ + const BIGNUM *n; + + RSA_get0_key(key, &n, NULL, NULL); + return n; +} + +static RSA *gen_sign_key(void) +{ + unsigned long sign_key_length; + BIO *bio; + RSA *key; + + sign_key_length = (unsigned long)&sign_key_end - + (unsigned long)&sign_key; + + bio = BIO_new_mem_buf(&sign_key, sign_key_length); + if (!bio) + return NULL; + + key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + + return key; +} + +static void reverse_bytes(void *data, int length) +{ + int i = 0; + int j = length - 1; + uint8_t temp; + uint8_t *ptr = data; + + while (i < j) { + temp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = temp; + i++; + j--; + } +} + +enum mrtags { + MRECREATE = 0x0045544145524345, + MREADD = 0x0000000044444145, + MREEXTEND = 0x00444E4554584545, +}; + +static bool mrenclave_update(EVP_MD_CTX *ctx, const void *data) +{ + if (!EVP_DigestUpdate(ctx, data, 64)) { + fprintf(stderr, "digest update failed\n"); + return false; + } + + return true; +} + +static bool mrenclave_commit(EVP_MD_CTX *ctx, uint8_t *mrenclave) +{ + unsigned int size; + + if (!EVP_DigestFinal_ex(ctx, (unsigned char *)mrenclave, &size)) { + fprintf(stderr, "digest commit failed\n"); + return false; + } + + if (size != 32) { + fprintf(stderr, "invalid digest size = %u\n", size); + return false; + } + + return true; +} + +struct mrecreate { + uint64_t tag; + uint32_t ssaframesize; + uint64_t size; + uint8_t reserved[44]; +} __attribute__((__packed__)); + + +static bool mrenclave_ecreate(EVP_MD_CTX *ctx, uint64_t blob_size) +{ + struct mrecreate mrecreate; + uint64_t encl_size; + + for (encl_size = 0x1000; encl_size < blob_size; ) + encl_size <<= 1; + + memset(&mrecreate, 0, sizeof(mrecreate)); + mrecreate.tag = MRECREATE; + mrecreate.ssaframesize = 1; + mrecreate.size = encl_size; + + if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) + return false; + + return mrenclave_update(ctx, &mrecreate); +} + +struct mreadd { + uint64_t tag; + uint64_t offset; + uint64_t flags; /* SECINFO flags */ + uint8_t reserved[40]; +} __attribute__((__packed__)); + +static bool mrenclave_eadd(EVP_MD_CTX *ctx, uint64_t offset, uint64_t flags) +{ + struct mreadd mreadd; + + memset(&mreadd, 0, sizeof(mreadd)); + mreadd.tag = MREADD; + mreadd.offset = offset; + mreadd.flags = flags; + + return mrenclave_update(ctx, &mreadd); +} + +struct mreextend { + uint64_t tag; + uint64_t offset; + uint8_t reserved[48]; +} __attribute__((__packed__)); + +static bool mrenclave_eextend(EVP_MD_CTX *ctx, uint64_t offset, + const uint8_t *data) +{ + struct mreextend mreextend; + int i; + + for (i = 0; i < 0x1000; i += 0x100) { + memset(&mreextend, 0, sizeof(mreextend)); + mreextend.tag = MREEXTEND; + mreextend.offset = offset + i; + + if (!mrenclave_update(ctx, &mreextend)) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x00])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x40])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x80])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0xC0])) + return false; + } + + return true; +} + +static bool mrenclave_segment(EVP_MD_CTX *ctx, struct encl *encl, + struct encl_segment *seg) +{ + uint64_t end = seg->offset + seg->size; + uint64_t offset; + + for (offset = seg->offset; offset < end; offset += PAGE_SIZE) { + if (!mrenclave_eadd(ctx, offset, seg->flags)) + return false; + + if (!mrenclave_eextend(ctx, offset, encl->src + offset)) + return false; + } + + return true; +} + +bool encl_measure(struct encl *encl) +{ + uint64_t header1[2] = {0x000000E100000006, 0x0000000000010000}; + uint64_t header2[2] = {0x0000006000000101, 0x0000000100000060}; + struct sgx_sigstruct *sigstruct = &encl->sigstruct; + struct sgx_sigstruct_payload payload; + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int siglen; + RSA *key = NULL; + EVP_MD_CTX *ctx; + int i; + + memset(sigstruct, 0, sizeof(*sigstruct)); + + sigstruct->header.header1[0] = header1[0]; + sigstruct->header.header1[1] = header1[1]; + sigstruct->header.header2[0] = header2[0]; + sigstruct->header.header2[1] = header2[1]; + sigstruct->exponent = 3; + sigstruct->body.attributes = SGX_ATTR_MODE64BIT; + sigstruct->body.xfrm = 3; + + /* sanity check */ + if (check_crypto_errors()) + goto err; + + key = gen_sign_key(); + if (!key) { + ERR_print_errors_fp(stdout); + goto err; + } + + BN_bn2bin(get_modulus(key), sigstruct->modulus); + + ctx = EVP_MD_CTX_create(); + if (!ctx) + goto err; + + if (!mrenclave_ecreate(ctx, encl->src_size)) + goto err; + + for (i = 0; i < encl->nr_segments; i++) { + struct encl_segment *seg = &encl->segment_tbl[i]; + + if (!mrenclave_segment(ctx, encl, seg)) + goto err; + } + + if (!mrenclave_commit(ctx, sigstruct->body.mrenclave)) + goto err; + + memcpy(&payload.header, &sigstruct->header, sizeof(sigstruct->header)); + memcpy(&payload.body, &sigstruct->body, sizeof(sigstruct->body)); + + SHA256((unsigned char *)&payload, sizeof(payload), digest); + + if (!RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, + sigstruct->signature, &siglen, key)) + goto err; + + if (!calc_q1q2(sigstruct->signature, sigstruct->modulus, sigstruct->q1, + sigstruct->q2)) + goto err; + + /* BE -> LE */ + reverse_bytes(sigstruct->signature, SGX_MODULUS_SIZE); + reverse_bytes(sigstruct->modulus, SGX_MODULUS_SIZE); + reverse_bytes(sigstruct->q1, SGX_MODULUS_SIZE); + reverse_bytes(sigstruct->q2, SGX_MODULUS_SIZE); + + EVP_MD_CTX_destroy(ctx); + RSA_free(key); + return true; + +err: + EVP_MD_CTX_destroy(ctx); + RSA_free(key); + return false; +} diff --git a/tools/testing/selftests/sgx/test_encl.c b/tools/testing/selftests/sgx/test_encl.c new file mode 100644 index 000000000000..cf25b5dc1e03 --- /dev/null +++ b/tools/testing/selftests/sgx/test_encl.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2016-20 Intel Corporation. */ + +#include <stddef.h> +#include "defines.h" + +static void *memcpy(void *dest, const void *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + ((char *)dest)[i] = ((char *)src)[i]; + + return dest; +} + +void encl_body(void *rdi, void *rsi) +{ + memcpy(rsi, rdi, 8); +} diff --git a/tools/testing/selftests/sgx/test_encl.lds b/tools/testing/selftests/sgx/test_encl.lds new file mode 100644 index 000000000000..0fbbda7e665e --- /dev/null +++ b/tools/testing/selftests/sgx/test_encl.lds @@ -0,0 +1,40 @@ +OUTPUT_FORMAT(elf64-x86-64) + +PHDRS +{ + tcs PT_LOAD; + text PT_LOAD; + data PT_LOAD; +} + +SECTIONS +{ + . = 0; + .tcs : { + *(.tcs*) + } : tcs + + . = ALIGN(4096); + .text : { + *(.text*) + *(.rodata*) + } : text + + . = ALIGN(4096); + .data : { + *(.data*) + } : data + + /DISCARD/ : { + *(.comment*) + *(.note*) + *(.debug*) + *(.eh_frame*) + } +} + +ASSERT(!DEFINED(.altinstructions), "ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.got.plt), "Libcalls are not supported in enclaves") diff --git a/tools/testing/selftests/sgx/test_encl_bootstrap.S b/tools/testing/selftests/sgx/test_encl_bootstrap.S new file mode 100644 index 000000000000..5d5680d4ea39 --- /dev/null +++ b/tools/testing/selftests/sgx/test_encl_bootstrap.S @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2016-20 Intel Corporation. + */ + + .macro ENCLU + .byte 0x0f, 0x01, 0xd7 + .endm + + .section ".tcs", "aw" + .balign 4096 + + .fill 1, 8, 0 # STATE (set by CPU) + .fill 1, 8, 0 # FLAGS + .quad encl_ssa # OSSA + .fill 1, 4, 0 # CSSA (set by CPU) + .fill 1, 4, 1 # NSSA + .quad encl_entry # OENTRY + .fill 1, 8, 0 # AEP (set by EENTER and ERESUME) + .fill 1, 8, 0 # OFSBASE + .fill 1, 8, 0 # OGSBASE + .fill 1, 4, 0xFFFFFFFF # FSLIMIT + .fill 1, 4, 0xFFFFFFFF # GSLIMIT + .fill 4024, 1, 0 # Reserved + + # Identical to the previous TCS. + .fill 1, 8, 0 # STATE (set by CPU) + .fill 1, 8, 0 # FLAGS + .quad encl_ssa # OSSA + .fill 1, 4, 0 # CSSA (set by CPU) + .fill 1, 4, 1 # NSSA + .quad encl_entry # OENTRY + .fill 1, 8, 0 # AEP (set by EENTER and ERESUME) + .fill 1, 8, 0 # OFSBASE + .fill 1, 8, 0 # OGSBASE + .fill 1, 4, 0xFFFFFFFF # FSLIMIT + .fill 1, 4, 0xFFFFFFFF # GSLIMIT + .fill 4024, 1, 0 # Reserved + + .text + +encl_entry: + # RBX contains the base address for TCS, which is also the first address + # inside the enclave. By adding the value of le_stack_end to it, we get + # the absolute address for the stack. + lea (encl_stack)(%rbx), %rax + xchg %rsp, %rax + push %rax + + push %rcx # push the address after EENTER + push %rbx # push the enclave base address + + call encl_body + + pop %rbx # pop the enclave base address + + /* Clear volatile GPRs, except RAX (EEXIT function). */ + xor %rcx, %rcx + xor %rdx, %rdx + xor %rdi, %rdi + xor %rsi, %rsi + xor %r8, %r8 + xor %r9, %r9 + xor %r10, %r10 + xor %r11, %r11 + + # Reset status flags. + add %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1 + + # Prepare EEXIT target by popping the address of the instruction after + # EENTER to RBX. + pop %rbx + + # Restore the caller stack. + pop %rax + mov %rax, %rsp + + # EEXIT + mov $4, %rax + enclu + + .section ".data", "aw" + +encl_ssa: + .space 4096 + + .balign 4096 + .space 8192 +encl_stack: diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c index 7161cfc2e60b..8c780cce941d 100644 --- a/tools/testing/selftests/x86/fsgsbase.c +++ b/tools/testing/selftests/x86/fsgsbase.c @@ -392,8 +392,8 @@ static void set_gs_and_switch_to(unsigned long local, local = read_base(GS); /* - * Signal delivery seems to mess up weird selectors. Put it - * back. + * Signal delivery is quite likely to change a selector + * of 1, 2, or 3 back to 0 due to IRET being defective. */ asm volatile ("mov %0, %%gs" : : "rm" (force_sel)); } else { @@ -411,6 +411,14 @@ static void set_gs_and_switch_to(unsigned long local, if (base == local && sel_pre_sched == sel_post_sched) { printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n", sel_pre_sched, local); + } else if (base == local && sel_pre_sched >= 1 && sel_pre_sched <= 3 && + sel_post_sched == 0) { + /* + * IRET is misdesigned and will squash selectors 1, 2, or 3 + * to zero. Don't fail the test just because this happened. + */ + printf("[OK]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx because IRET is defective\n", + sel_pre_sched, local, sel_post_sched, base); } else { nerrs++; printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n", diff --git a/tools/testing/selftests/x86/raw_syscall_helper_32.S b/tools/testing/selftests/x86/raw_syscall_helper_32.S index 94410fa2b5ed..a10d36afdca0 100644 --- a/tools/testing/selftests/x86/raw_syscall_helper_32.S +++ b/tools/testing/selftests/x86/raw_syscall_helper_32.S @@ -45,3 +45,5 @@ int80_and_ret: .type int80_and_ret, @function .size int80_and_ret, .-int80_and_ret + +.section .note.GNU-stack,"",%progbits diff --git a/tools/testing/selftests/x86/thunks.S b/tools/testing/selftests/x86/thunks.S index 1bb5d62c16a4..a2d47d8344d4 100644 --- a/tools/testing/selftests/x86/thunks.S +++ b/tools/testing/selftests/x86/thunks.S @@ -57,3 +57,5 @@ call32_from_64: ret .size call32_from_64, .-call32_from_64 + +.section .note.GNU-stack,"",%progbits |