diff options
265 files changed, 23495 insertions, 4686 deletions
diff --git a/Documentation/admin-guide/media/mgb4.rst b/Documentation/admin-guide/media/mgb4.rst index 5ac69b833a7a..0a8a56e837f7 100644 --- a/Documentation/admin-guide/media/mgb4.rst +++ b/Documentation/admin-guide/media/mgb4.rst @@ -31,9 +31,11 @@ Global (PCI card) parameters | 0 - No module present | 1 - FPDL3 - | 2 - GMSL (one serializer, two daisy chained deserializers) - | 3 - GMSL (one serializer, two deserializers) - | 4 - GMSL (two deserializers with two daisy chain outputs) + | 2 - GMSL3 (one serializer, two daisy chained deserializers) + | 3 - GMSL3 (one serializer, two deserializers) + | 4 - GMSL3 (two deserializers with two daisy chain outputs) + | 6 - GMSL1 + | 8 - GMSL3 coax **module_version** (R): Module version number. Zero in case of a missing module. @@ -42,7 +44,8 @@ Global (PCI card) parameters Firmware type. | 1 - FPDL3 - | 2 - GMSL + | 2 - GMSL3 + | 3 - GMSL1 **fw_version** (R): Firmware version number. diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml b/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml index dee8ce7cb7ba..5f8f3b3dea76 100644 --- a/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml +++ b/Documentation/devicetree/bindings/media/i2c/adi,adv7180.yaml @@ -30,7 +30,27 @@ properties: - adi,adv7282-m reg: - maxItems: 1 + minItems: 1 + items: + - description: main register map + - description: VPP or CSI register map + - description: CSI register map + description: + The ADV7180 family may have up to three register maps. All chips have + the main register map. The availability of the CSI and VPP register maps + depends on the chip variant. + + The addresses of the CSI and VPP register maps are programmable by + software. They depend on the board layout and other devices on the I2C + bus and are determined by the hardware designer to avoid address + conflicts on the I2C bus. + + reg-names: + minItems: 1 + items: + - const: main + - enum: [ csi, vpp ] + - const: csi powerdown-gpios: maxItems: 1 @@ -138,6 +158,62 @@ allOf: required: - ports + - if: + properties: + compatible: + contains: + enum: + - adi,adv7180 + - adi,adv7180cp + - adi,adv7180st + - adi,adv7182 + then: + properties: + reg: + maxItems: 1 + + reg-names: + maxItems: 1 + + - if: + properties: + compatible: + contains: + enum: + - adi,adv7281 + - adi,adv7281-m + - adi,adv7281-ma + then: + properties: + reg: + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: main + - const: csi + + - if: + properties: + compatible: + contains: + enum: + - adi,adv7280 + - adi,adv7282 + then: + properties: + reg: + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: main + - const: vpp + examples: - | i2c { @@ -187,3 +263,22 @@ examples: }; }; }; + + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + composite-in@20 { + compatible = "adi,adv7280-m"; + reg = <0x20>, <0x42>, <0x44>; + reg-names = "main", "vpp", "csi"; + + port { + adv7280_out: endpoint { + bus-width = <8>; + remote-endpoint = <&vin1ep>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml new file mode 100644 index 000000000000..b76771d81851 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,os05b10.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OS05B10 Image Sensor + +maintainers: + - Elgin Perumbilly <elgin.perumbilly@siliconsignals.io> + +description: + The OmniVision OS05B10 is a 5MP (2592x1944) color CMOS image sensor controlled + through an I2C-compatible SCCB bus. it outputs RAW10/RAW12 format and uses a + 1/2.78" optical format. + +properties: + compatible: + const: ovti,os05b10 + + reg: + maxItems: 1 + + clocks: + items: + - description: XCLK clock + + avdd-supply: + description: Analog Domain Power Supply (2.8v) + + dovdd-supply: + description: I/O Domain Power Supply (1.8v) + + dvdd-supply: + description: Digital Domain Power Supply (1.2v) + + reset-gpios: + maxItems: 1 + description: Reset Pin GPIO Control (active low) + + port: + description: MIPI CSI-2 transmitter port + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - avdd-supply + - dovdd-supply + - dvdd-supply + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera-sensor@36 { + compatible = "ovti,os05b10"; + reg = <0x36>; + clocks = <&os05b10_clk>; + reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; + + avdd-supply = <&os05b10_avdd_2v8>; + dvdd-supply = <&os05b10_dvdd_1v2>; + dovdd-supply = <&os05b10_dovdd_1v8>; + + port { + cam_out: endpoint { + remote-endpoint = <&mipi_in_cam>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <600000000>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml index a2abed06a099..2d7937a372a2 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5647.yaml @@ -14,6 +14,9 @@ description: |- The OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces and CCI (I2C compatible) control bus. +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + properties: compatible: const: ovti,ov5647 @@ -30,6 +33,15 @@ properties: description: Reference to the GPIO connected to the pwdn pin. Active high. maxItems: 1 + avdd-supply: + description: Analog voltage supply, 2.8 volts + + dvdd-supply: + description: Digital core voltage supply, 1.5 volts + + dovdd-supply: + description: Digital I/O voltage supply, 1.7 - 3.0 volts + port: $ref: /schemas/graph.yaml#/$defs/port-base additionalProperties: false @@ -48,7 +60,7 @@ required: - clocks - port -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/media/i2c/samsung,s5k3m5.yaml b/Documentation/devicetree/bindings/media/i2c/samsung,s5k3m5.yaml new file mode 100644 index 000000000000..434f15f64bcd --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/samsung,s5k3m5.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/samsung,s5k3m5.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung S5K3M5 Image Sensor + +description: + Samsung S5K3M5 (ISOCELL 3M5) image sensor is a 13MP image sensor. + The sensor is controlled over a serial camera control bus protocol, + the widest supported output image frame size is 4208x3120 at 30 frames + per second, data output format is RAW10 transferred over 4-lane + MIPI D-PHY interface. + +maintainers: + - Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: samsung,s5k3m5 + + reg: + maxItems: 1 + + clocks: + description: MCLK supply clock. + maxItems: 1 + + reset-gpios: + description: Active low GPIO connected to RESET pad of the sensor. + maxItems: 1 + + afvdd-supply: + description: Autofocus actuator voltage supply, 2.8-3.0 volts. + + vdda-supply: + description: Analogue voltage supply, 2.8 volts. + + vddd-supply: + description: Digital core voltage supply, 1.05 volts. + + vddio-supply: + description: Digital I/O voltage supply, 2.8 or 1.8 volts. + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + required: + - link-frequencies + +required: + - compatible + - reg + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@10 { + compatible = "samsung,s5k3m5"; + reg = <0x10>; + clocks = <&camera_mclk 0>; + assigned-clocks = <&camera_mclk 0>; + assigned-clock-rates = <24000000>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + vdda-supply = <&vreg_2p8>; + vddd-supply = <&vreg_1p05>; + vddio-supply = <&vreg_1p8>; + + port { + endpoint { + link-frequencies = /bits/ 64 <602500000>; + remote-endpoint = <&mipi_csi2_ep>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/samsung,s5kjn1.yaml b/Documentation/devicetree/bindings/media/i2c/samsung,s5kjn1.yaml new file mode 100644 index 000000000000..8f368ae044b4 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/samsung,s5kjn1.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/samsung,s5kjn1.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung S5KJN1 Image Sensor + +description: + Samsung S5KJN1 (ISOCELL JN1) image sensor is a 50MP image sensor. + The sensor is controlled over a serial camera control bus protocol, + the widest supported output image frame size is 8160x6144 at 10 frames + per second, data output format is RAW10 transferred over 4-lane + MIPI D-PHY interface. + +maintainers: + - Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: samsung,s5kjn1 + + reg: + maxItems: 1 + + clocks: + description: MCLK supply clock. + maxItems: 1 + + reset-gpios: + description: Active low GPIO connected to RESET pad of the sensor. + maxItems: 1 + + afvdd-supply: + description: Autofocus actuator voltage supply, 2.8-3.0 volts. + + vdda-supply: + description: Analogue voltage supply, 2.8 volts. + + vddd-supply: + description: Digital core voltage supply, 1.05 volts. + + vddio-supply: + description: Digital I/O voltage supply, 1.8 volts. + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + required: + - link-frequencies + +required: + - compatible + - reg + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@56 { + compatible = "samsung,s5kjn1"; + reg = <0x56>; + clocks = <&camera_mclk 0>; + assigned-clocks = <&camera_mclk 0>; + assigned-clock-rates = <24000000>; + reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; + vdda-supply = <&vreg_2p8>; + vddd-supply = <&vreg_1p05>; + vddio-supply = <&vreg_1p8>; + + port { + endpoint { + link-frequencies = /bits/ 64 <700000000>; + remote-endpoint = <&mipi_csi2_ep>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt deleted file mode 100644 index 8d8e40c56872..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.txt +++ /dev/null @@ -1,55 +0,0 @@ -Toshiba et8ek8 5MP sensor - -Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device - -More detailed documentation can be found in -Documentation/devicetree/bindings/media/video-interfaces.txt . - - -Mandatory properties --------------------- - -- compatible: "toshiba,et8ek8" -- reg: I2C address (0x3e, or an alternative address) -- vana-supply: Analogue voltage supply (VANA), 2.8 volts -- clocks: External clock to the sensor -- reset-gpios: XSHUTDOWN GPIO. The XSHUTDOWN signal is active low. The sensor - is in hardware standby mode when the signal is in the low state. - - -Optional properties -------------------- - -- flash-leds: See ../video-interfaces.txt -- lens-focus: See ../video-interfaces.txt - - -Endpoint node mandatory properties ----------------------------------- - -- remote-endpoint: A phandle to the bus receiver's endpoint node. - - -Example -------- - -&i2c3 { - clock-frequency = <400000>; - - cam1: camera@3e { - compatible = "toshiba,et8ek8"; - reg = <0x3e>; - vana-supply = <&vaux4>; - - clocks = <&isp 0>; - assigned-clocks = <&isp 0>; - assigned-clock-rates = <9600000>; - - reset-gpio = <&gpio4 6 GPIO_ACTIVE_HIGH>; /* 102 */ - port { - csi_cam1: endpoint { - remote-endpoint = <&csi_out1>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.yaml b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.yaml new file mode 100644 index 000000000000..f0186ae87de2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/toshiba,et8ek8.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Toshiba et8ek8 5MP sensor + +maintainers: + - Pavel Machek <pavel@ucw.cz> + - Sakari Ailus <sakari.ailus@iki.fi> + +description: + Toshiba et8ek8 5MP sensor is an image sensor found in Nokia N900 device + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + const: toshiba,et8ek8 + + reg: + description: + I2C address (0x3e, or an alternative address) + maxItems: 1 + + vana-supply: + description: + Analogue voltage supply (VANA), 2.8 volts + + clocks: + maxItems: 1 + + reset-gpios: + description: + XSHUTDOWN GPIO. The XSHUTDOWN signal is active low. The sensor + is in hardware standby mode when the signal is in the low state. + maxItems: 1 + + flash-leds: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - vana-supply + - clocks + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@3e { + compatible = "toshiba,et8ek8"; + reg = <0x3e>; + vana-supply = <&vaux4>; + clocks = <&isp 0>; + assigned-clocks = <&isp 0>; + assigned-clock-rates = <9600000>; + reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>; + flash-leds = <&led>; + + port { + csi_cam1: endpoint { + remote-endpoint = <&csi_out1>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml index b5aca3d2cc5c..18cc6315a821 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml @@ -55,6 +55,12 @@ properties: minItems: 1 # Wrapper and all slots maxItems: 5 # Wrapper and 4 slots + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Optional phandle to a reserved on-chip SRAM regions. The SRAM can + be used for descriptor storage, which may improve bus utilization. + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml b/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml index 80a4540a22dc..e5f170aa4d9e 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcs8300-camss.yaml @@ -120,6 +120,14 @@ properties: items: - const: top + vdda-phy-supply: + description: + Phandle to a 0.88V regulator supply to CSI PHYs. + + vdda-pll-supply: + description: + Phandle to 1.2V regulator supply to CSI PHYs pll block. + ports: $ref: /schemas/graph.yaml#/properties/ports @@ -160,6 +168,8 @@ required: - power-domains - power-domain-names - ports + - vdda-phy-supply + - vdda-pll-supply additionalProperties: false @@ -328,6 +338,9 @@ examples: power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>; power-domain-names = "top"; + vdda-phy-supply = <&vreg_l4a_0p88>; + vdda-pll-supply = <&vreg_l1c_1p2>; + ports { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml index 019caa2b09c3..48f280e99809 100644 --- a/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sa8775p-camss.yaml @@ -126,11 +126,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/media/qcom,sc7280-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sc7280-camss.yaml index ee35e3bc97ff..b1c54c5b01b2 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc7280-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc7280-camss.yaml @@ -125,11 +125,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml index c99fe4106eee..354130aba9fc 100644 --- a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml @@ -264,11 +264,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. required: - clock-names diff --git a/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml index 35c40fe22376..46cc7fff1599 100644 --- a/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sdm670-camss.yaml @@ -91,11 +91,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml index 82bf4689d330..be09cf3a3b3b 100644 --- a/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-camss.yaml @@ -207,11 +207,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. required: - clock-names diff --git a/Documentation/devicetree/bindings/media/qcom,sm6150-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm6150-camss.yaml new file mode 100644 index 000000000000..ba7b0acb9128 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sm6150-camss.yaml @@ -0,0 +1,439 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,sm6150-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SM6150 Camera Subsystem (CAMSS) + +maintainers: + - Wenmeng Liu <wenmeng.liu@oss.qualcomm.com> + +description: + This binding describes the camera subsystem hardware found on SM6150 + Qualcomm SoCs. It includes submodules such as CSIPHY (CSI Physical layer) + and CSID (CSI Decoder), which comply with the MIPI CSI2 protocol. + + The subsystem also integrates a set of real-time image processing engines + and their associated configuration modules, as well as non-real-time engines. + +properties: + compatible: + const: qcom,sm6150-camss + + reg: + items: + - description: Registers for CSID 0 + - description: Registers for CSID 1 + - description: Registers for CSID Lite + - description: Registers for CSIPHY 0 + - description: Registers for CSIPHY 1 + - description: Registers for CSIPHY 2 + - description: Registers for VFE 0 + - description: Registers for VFE 1 + - description: Registers for VFE Lite + - description: Registers for BPS (Bayer Processing Segment) + - description: Registers for CAMNOC + - description: Registers for CPAS CDM + - description: Registers for CPAS TOP + - description: Registers for ICP (Imaging Control Processor) CSR (Control and Status Registers) + - description: Registers for ICP QGIC (Qualcomm Generic Interrupt Controller) + - description: Registers for ICP SIERRA ((A5 subsystem communication)) + - description: Registers for IPE (Image Postprocessing Engine) 0 + - description: Registers for JPEG DMA + - description: Registers for JPEG ENC + - description: Registers for LRME (Low Resolution Motion Estimation) + + reg-names: + items: + - const: csid0 + - const: csid1 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: vfe0 + - const: vfe1 + - const: vfe_lite + - const: bps + - const: camnoc + - const: cpas_cdm + - const: cpas_top + - const: icp_csr + - const: icp_qgic + - const: icp_sierra + - const: ipe0 + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + clocks: + maxItems: 33 + + clock-names: + items: + - const: gcc_ahb + - const: gcc_axi_hf + - const: camnoc_axi + - const: cpas_ahb + - const: csiphy0 + - const: csiphy0_timer + - const: csiphy1 + - const: csiphy1_timer + - const: csiphy2 + - const: csiphy2_timer + - const: soc_ahb + - const: vfe0 + - const: vfe0_axi + - const: vfe0_cphy_rx + - const: vfe0_csid + - const: vfe1 + - const: vfe1_axi + - const: vfe1_cphy_rx + - const: vfe1_csid + - const: vfe_lite + - const: vfe_lite_cphy_rx + - const: vfe_lite_csid + - const: bps + - const: bps_ahb + - const: bps_axi + - const: bps_areg + - const: icp + - const: ipe0 + - const: ipe0_ahb + - const: ipe0_areg + - const: ipe0_axi + - const: jpeg + - const: lrme + + interrupts: + maxItems: 15 + + interrupt-names: + items: + - const: csid0 + - const: csid1 + - const: csid_lite + - const: csiphy0 + - const: csiphy1 + - const: csiphy2 + - const: vfe0 + - const: vfe1 + - const: vfe_lite + - const: camnoc + - const: cdm + - const: icp + - const: jpeg_dma + - const: jpeg_enc + - const: lrme + + interconnects: + maxItems: 4 + + interconnect-names: + items: + - const: ahb + - const: hf_0 + - const: hf_1 + - const: sf_mnoc + + iommus: + items: + - description: Camera IFE 0 non-protected stream + - description: Camera IFE 1 non-protected stream + - description: Camera IFE 3 non-protected stream + - description: Camera CDM non-protected stream + - description: Camera LRME read non-protected stream + - description: Camera IPE 0 read non-protected stream + - description: Camera BPS read non-protected stream + - description: Camera IPE 0 write non-protected stream + - description: Camera BPS write non-protected stream + - description: Camera LRME write non-protected stream + - description: Camera JPEG read non-protected stream + - description: Camera JPEG write non-protected stream + - description: Camera ICP stream + + power-domains: + items: + - description: + IFE0 GDSC - Image Front End, Global Distributed Switch Controller. + - description: + IFE1 GDSC - Image Front End, Global Distributed Switch Controller. + - description: + Titan GDSC - Titan ISP Block, Global Distributed Switch Controller. + - description: + Titan BPS - Bayer Processing Segment, Global Distributed Switch Controller. + - description: + IPE GDSC - Image Postprocessing Engine, Global Distributed Switch Controller. + + power-domain-names: + items: + - const: ife0 + - const: ife1 + - const: top + - const: bps + - const: ipe + + vdd-csiphy-1p2-supply: + description: + Phandle to a 1.2V regulator supply to CSI PHYs. + + vdd-csiphy-1p8-supply: + description: + Phandle to 1.8V regulator supply to CSI PHYs pll block. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + description: + CSI input ports. + + patternProperties: + "^port@[0-2]$": + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + + description: + Input port for receiving CSI data from a CSIPHY. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - data-lanes + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - interrupt-names + - interconnects + - interconnect-names + - iommus + - power-domains + - power-domain-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,qcs615-camcc.h> + #include <dt-bindings/clock/qcom,qcs615-gcc.h> + #include <dt-bindings/clock/qcom,rpmh.h> + #include <dt-bindings/interconnect/qcom,icc.h> + #include <dt-bindings/interconnect/qcom,qcs615-rpmh.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/qcom-rpmpd.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + camss: isp@acb3000 { + compatible = "qcom,sm6150-camss"; + + reg = <0x0 0x0acb3000 0x0 0x1000>, + <0x0 0x0acba000 0x0 0x1000>, + <0x0 0x0acc8000 0x0 0x1000>, + <0x0 0x0ac65000 0x0 0x1000>, + <0x0 0x0ac66000 0x0 0x1000>, + <0x0 0x0ac67000 0x0 0x1000>, + <0x0 0x0acaf000 0x0 0x4000>, + <0x0 0x0acb6000 0x0 0x4000>, + <0x0 0x0acc4000 0x0 0x4000>, + <0x0 0x0ac6f000 0x0 0x3000>, + <0x0 0x0ac42000 0x0 0x5000>, + <0x0 0x0ac48000 0x0 0x1000>, + <0x0 0x0ac40000 0x0 0x1000>, + <0x0 0x0ac18000 0x0 0x3000>, + <0x0 0x0ac00000 0x0 0x6000>, + <0x0 0x0ac10000 0x0 0x8000>, + <0x0 0x0ac87000 0x0 0x3000>, + <0x0 0x0ac52000 0x0 0x4000>, + <0x0 0x0ac4e000 0x0 0x4000>, + <0x0 0x0ac6b000 0x0 0x0a00>; + reg-names = "csid0", + "csid1", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "vfe0", + "vfe1", + "vfe_lite", + "bps", + "camnoc", + "cpas_cdm", + "cpas_top", + "icp_csr", + "icp_qgic", + "icp_sierra", + "ipe0", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + clocks = <&gcc GCC_CAMERA_AHB_CLK>, + <&gcc GCC_CAMERA_HF_AXI_CLK>, + <&camcc CAM_CC_CAMNOC_AXI_CLK>, + <&camcc CAM_CC_CPAS_AHB_CLK>, + <&camcc CAM_CC_CSIPHY0_CLK>, + <&camcc CAM_CC_CSI0PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY1_CLK>, + <&camcc CAM_CC_CSI1PHYTIMER_CLK>, + <&camcc CAM_CC_CSIPHY2_CLK>, + <&camcc CAM_CC_CSI2PHYTIMER_CLK>, + <&camcc CAM_CC_SOC_AHB_CLK>, + <&camcc CAM_CC_IFE_0_CLK>, + <&camcc CAM_CC_IFE_0_AXI_CLK>, + <&camcc CAM_CC_IFE_0_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_0_CSID_CLK>, + <&camcc CAM_CC_IFE_1_CLK>, + <&camcc CAM_CC_IFE_1_AXI_CLK>, + <&camcc CAM_CC_IFE_1_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_1_CSID_CLK>, + <&camcc CAM_CC_IFE_LITE_CLK>, + <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>, + <&camcc CAM_CC_IFE_LITE_CSID_CLK>, + <&camcc CAM_CC_BPS_CLK>, + <&camcc CAM_CC_BPS_AHB_CLK>, + <&camcc CAM_CC_BPS_AXI_CLK>, + <&camcc CAM_CC_BPS_AREG_CLK>, + <&camcc CAM_CC_ICP_CLK>, + <&camcc CAM_CC_IPE_0_CLK>, + <&camcc CAM_CC_IPE_0_AHB_CLK>, + <&camcc CAM_CC_IPE_0_AREG_CLK>, + <&camcc CAM_CC_IPE_0_AXI_CLK>, + <&camcc CAM_CC_JPEG_CLK>, + <&camcc CAM_CC_LRME_CLK>; + + clock-names = "gcc_ahb", + "gcc_axi_hf", + "camnoc_axi", + "cpas_ahb", + "csiphy0", + "csiphy0_timer", + "csiphy1", + "csiphy1_timer", + "csiphy2", + "csiphy2_timer", + "soc_ahb", + "vfe0", + "vfe0_axi", + "vfe0_cphy_rx", + "vfe0_csid", + "vfe1", + "vfe1_axi", + "vfe1_cphy_rx", + "vfe1_csid", + "vfe_lite", + "vfe_lite_cphy_rx", + "vfe_lite_csid", + "bps", + "bps_ahb", + "bps_axi", + "bps_areg", + "icp", + "ipe0", + "ipe0_ahb", + "ipe0_areg", + "ipe0_axi", + "jpeg", + "lrme"; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_CAMNOC_HF0 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_HF1 QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>, + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "ahb", + "hf_0", + "hf_1", + "sf_mnoc"; + + interrupts = <GIC_SPI 464 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 466 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 468 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 478 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 479 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 465 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 467 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 469 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 459 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 461 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 463 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 475 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 476 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "csid0", + "csid1", + "csid_lite", + "csiphy0", + "csiphy1", + "csiphy2", + "vfe0", + "vfe1", + "vfe_lite", + "camnoc", + "cdm", + "icp", + "jpeg_dma", + "jpeg_enc", + "lrme"; + + iommus = <&apps_smmu 0x0820 0x40>, + <&apps_smmu 0x0840 0x00>, + <&apps_smmu 0x0860 0x40>, + <&apps_smmu 0x0c00 0x00>, + <&apps_smmu 0x0cc0 0x00>, + <&apps_smmu 0x0c80 0x00>, + <&apps_smmu 0x0ca0 0x00>, + <&apps_smmu 0x0d00 0x00>, + <&apps_smmu 0x0d20 0x00>, + <&apps_smmu 0x0d40 0x00>, + <&apps_smmu 0x0d80 0x20>, + <&apps_smmu 0x0da0 0x20>, + <&apps_smmu 0x0de2 0x00>; + + power-domains = <&camcc IFE_0_GDSC>, + <&camcc IFE_1_GDSC>, + <&camcc TITAN_TOP_GDSC>, + <&camcc BPS_GDSC>, + <&camcc IPE_0_GDSC>; + power-domain-names = "ife0", + "ife1", + "top", + "bps", + "ipe"; + + vdd-csiphy-1p2-supply = <&vreg_l11a_1p2>; + vdd-csiphy-1p8-supply = <&vreg_l12a_1p8>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + csiphy_ep0: endpoint { + data-lanes = <0 1>; + remote-endpoint = <&sensor_ep>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml index ebf68ff4ab96..a509d4bbcb4a 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml @@ -296,11 +296,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.8V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. required: - clock-names diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-camss.yaml index cd34f14916b4..4b9ab1352e91 100644 --- a/Documentation/devicetree/bindings/media/qcom,sm8550-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,sm8550-camss.yaml @@ -134,11 +134,11 @@ properties: vdda-phy-supply: description: - Phandle to a regulator supply to PHY core block. + 0.88V supply to CSIPHY IP blocks. vdda-pll-supply: description: - Phandle to 1.2V regulator supply to PHY refclk pll block. + 1.2V supply to CSIPHY IP blocks. ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml index b87a13479a4b..2d1662ef522b 100644 --- a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml @@ -120,11 +120,11 @@ properties: vdd-csiphy-0p8-supply: description: - Phandle to a 0.8V regulator supply to a PHY. + 0.8V supply to a PHY. vdd-csiphy-1p2-supply: description: - Phandle to 1.2V regulator supply to a PHY. + 1.2V supply to a PHY. ports: $ref: /schemas/graph.yaml#/properties/ports diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index cf92dfe69637..b5eff6fec8a9 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -77,6 +77,7 @@ allOf: - renesas,r9a07g043u-fcpvd - renesas,r9a07g044-fcpvd - renesas,r9a07g054-fcpvd + - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd then: properties: diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml new file mode 100644 index 000000000000..2c2bd87582eb --- /dev/null +++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/rockchip,rk3568-mipi-csi2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip MIPI CSI-2 Receiver + +maintainers: + - Michael Riesch <michael.riesch@collabora.com> + +description: + The Rockchip MIPI CSI-2 Receiver is a CSI-2 bridge with one input port and + one output port. It receives the data with the help of an external MIPI PHY + (C-PHY or D-PHY) and passes it to the Rockchip Video Capture (VICAP) block. + +properties: + compatible: + enum: + - rockchip,rk3568-mipi-csi2 + + reg: + maxItems: 1 + + interrupts: + items: + - description: Interrupt that signals changes in CSI2HOST_ERR1. + - description: Interrupt that signals changes in CSI2HOST_ERR2. + + interrupt-names: + items: + - const: err1 + - const: err2 + + clocks: + maxItems: 1 + + phys: + maxItems: 1 + description: MIPI C-PHY or D-PHY. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Input port node. Connect to e.g., a MIPI CSI-2 image sensor. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + enum: + - 1 # MEDIA_BUS_TYPE_CSI2_CPHY + - 4 # MEDIA_BUS_TYPE_CSI2_DPHY + + data-lanes: + minItems: 1 + maxItems: 4 + + required: + - bus-type + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Output port connected to a Rockchip VICAP port. + + required: + - port@0 + - port@1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - phys + - ports + - power-domains + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/rk3568-cru.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/media/video-interfaces.h> + #include <dt-bindings/power/rk3568-power.h> + + soc { + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + csi: csi@fdfb0000 { + compatible = "rockchip,rk3568-mipi-csi2"; + reg = <0x0 0xfdfb0000 0x0 0x10000>; + interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "err1", "err2"; + clocks = <&cru PCLK_CSI2HOST1>; + phys = <&csi_dphy>; + power-domains = <&power RK3568_PD_VI>; + resets = <&cru SRST_P_CSI2HOST1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + csi_in: port@0 { + reg = <0>; + + csi_input: endpoint { + bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>; + data-lanes = <1 2 3 4>; + remote-endpoint = <&imx415_output>; + }; + }; + + csi_out: port@1 { + reg = <1>; + + csi_output: endpoint { + remote-endpoint = <&vicap_mipi_input>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/ti,omap3isp.txt b/Documentation/devicetree/bindings/media/ti,omap3isp.txt deleted file mode 100644 index ac23de855641..000000000000 --- a/Documentation/devicetree/bindings/media/ti,omap3isp.txt +++ /dev/null @@ -1,71 +0,0 @@ -OMAP 3 ISP Device Tree bindings -=============================== - -The DT definitions can be found in include/dt-bindings/media/omap3-isp.h. - -Required properties -=================== - -compatible : must contain "ti,omap3-isp" - -reg : the two registers sets (physical address and length) for the - ISP. The first set contains the core ISP registers up to - the end of the SBL block. The second set contains the - CSI PHYs and receivers registers. -interrupts : the ISP interrupt specifier -iommus : phandle and IOMMU specifier for the IOMMU that serves the ISP -syscon : the phandle and register offset to the Complex I/O or CSI-PHY - register -ti,phy-type : 0 -- OMAP3ISP_PHY_TYPE_COMPLEX_IO (e.g. 3430) - 1 -- OMAP3ISP_PHY_TYPE_CSIPHY (e.g. 3630) -#clock-cells : Must be 1 --- the ISP provides two external clocks, - cam_xclka and cam_xclkb, at indices 0 and 1, - respectively. Please find more information on common - clock bindings in ../clock/clock-bindings.txt. - -Port nodes (optional) ---------------------- - -More documentation on these bindings is available in -video-interfaces.txt in the same directory. - -reg : The interface: - 0 - parallel (CCDC) - 1 - CSIPHY1 -- CSI2C / CCP2B on 3630; - CSI1 -- CSIb on 3430 - 2 - CSIPHY2 -- CSI2A / CCP2B on 3630; - CSI2 -- CSIa on 3430 - -Optional properties -=================== - -vdd-csiphy1-supply : voltage supply of the CSI-2 PHY 1 -vdd-csiphy2-supply : voltage supply of the CSI-2 PHY 2 - -Endpoint nodes --------------- - -lane-polarities : lane polarity (required on CSI-2) - 0 -- not inverted; 1 -- inverted -data-lanes : an array of data lanes from 1 to 3. The length can - be either 1 or 2. (required on CSI-2) -clock-lanes : the clock lane (from 1 to 3). (required on CSI-2) - - -Example -======= - - isp@480bc000 { - compatible = "ti,omap3-isp"; - reg = <0x480bc000 0x12fc - 0x480bd800 0x0600>; - interrupts = <24>; - iommus = <&mmu_isp>; - syscon = <&scm_conf 0x2f0>; - ti,phy-type = <OMAP3ISP_PHY_TYPE_CSIPHY>; - #clock-cells = <1>; - ports { - #address-cells = <1>; - #size-cells = <0>; - }; - }; diff --git a/Documentation/devicetree/bindings/media/ti,omap3isp.yaml b/Documentation/devicetree/bindings/media/ti,omap3isp.yaml new file mode 100644 index 000000000000..7155fd3db505 --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti,omap3isp.yaml @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/ti,omap3isp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments OMAP 3 Image Signal Processor (ISP) + +maintainers: + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> + - Sakari Ailus <sakari.ailus@iki.fi> + +description: + The OMAP 3 ISP is an image signal processor present in OMAP 3 SoCs. + +properties: + compatible: + const: ti,omap3-isp + + reg: + items: + - description: Core ISP registers up to the end of the SBL block + - description: CSI PHYs and receivers registers + + interrupts: + maxItems: 1 + + iommus: + maxItems: 1 + + syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Control Module + - description: register offset to Complex I/O or CSI-PHY register + description: + Phandle and register offset to the Complex I/O or CSI-PHY register + + ti,phy-type: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: + 0 - OMAP3ISP_PHY_TYPE_COMPLEX_IO (e.g. OMAP 3430) + 1 - OMAP3ISP_PHY_TYPE_CSIPHY (e.g. OMAP 3630) + + '#clock-cells': + const: 1 + description: + The ISP provides two external clocks, cam_xclka and cam_xclkb, + at indices 0 and 1 respectively. + + vdd-csiphy1-supply: + description: Voltage supply of the CSI-2 PHY 1 + + vdd-csiphy2-supply: + description: Voltage supply of the CSI-2 PHY 2 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Parallel (CCDC) interface + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: | + CSIPHY1 interface: + OMAP 3630: CSI2C / CCP2B + OMAP 3430: CSI1 (CSIb) + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + lane-polarities: + minItems: 2 + maxItems: 3 + + data-lanes: + minItems: 1 + maxItems: 2 + items: + minimum: 1 + maximum: 3 + + clock-lanes: + minimum: 1 + maximum: 3 + + port@2: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: | + CSIPHY2 interface: + OMAP 3630: CSI2A / CCP2B + OMAP 3430: CSI2 (CSIa) + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + lane-polarities: + minItems: 2 + maxItems: 3 + + data-lanes: + minItems: 1 + maxItems: 2 + items: + minimum: 1 + maximum: 3 + + clock-lanes: + minimum: 1 + maximum: 3 + +required: + - compatible + - reg + - interrupts + - iommus + - syscon + - ti,phy-type + - '#clock-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/omap3-isp.h> + + isp@480bc000 { + compatible = "ti,omap3-isp"; + reg = <0x480bc000 0x12fc>, + <0x480bd800 0x0600>; + interrupts = <24>; + iommus = <&mmu_isp>; + syscon = <&scm_conf 0x2f0>; + ti,phy-type = <OMAP3ISP_PHY_TYPE_CSIPHY>; + #clock-cells = <1>; + vdd-csiphy1-supply = <&vaux2>; + vdd-csiphy2-supply = <&vaux2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + parallel_ep: endpoint { + remote-endpoint = <¶llel>; + }; + }; + + port@1 { + reg = <1>; + csi1_ep: endpoint { + remote-endpoint = <&smia_1>; + clock-lanes = <1>; + data-lanes = <2>; + lane-polarities = <0 0>; + }; + }; + + port@2 { + reg = <2>; + csi2a_ep: endpoint { + remote-endpoint = <&smia_2>; + clock-lanes = <2>; + data-lanes = <1 3>; + lane-polarities = <1 1 1>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/ti,vip.yaml b/Documentation/devicetree/bindings/media/ti,vip.yaml new file mode 100644 index 000000000000..e30cc461542b --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti,vip.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com/ +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/ti,vip.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments DRA7x Video Input Port (VIP) + +maintainers: + - Yemike Abhilash Chandra <y-abhilashchandra@ti.com> + +description: |- + Video Input Port (VIP) can be found on devices such as DRA7xx and + provides the system interface and the processing capability to + connect parallel image-sensor as well as BT.656/1120 capable encoder + chip to DRA7x device. + + Each VIP instance supports 2 independently configurable external + video input capture slices (Slice 0 and Slice 1) each providing + up to two video input ports (Port A and Port B). + +properties: + compatible: + enum: + - ti,dra7-vip + + reg: + maxItems: 1 + + interrupts: + items: + - description: IRQ index 0 is used for Slice0 interrupts + - description: IRQ index 1 is used for Slice1 interrupts + + ti,ctrl-module: + description: + Reference to the device control module that provides clock-edge + inversion control for VIP ports. These controls allow the + VIP to sample pixel data on the correct clock edge. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to device control module + - description: offset to the CTRL_CORE_SMA_SW_1 register + - description: Bit field to slice 0 port A + - description: Bit field to slice 0 port B + - description: Bit field to slice 1 port A + - description: Bit field to slice 1 port B + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@[0-3]$': + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: | + Each VIP instance supports 2 independently configurable external video + input capture slices (Slice 0 and Slice 1) each providing up to two video + input ports (Port A and Port B). These ports represent the following + port@0 -> Slice 0 Port A + port@1 -> Slice 0 Port B + port@2 -> Slice 1 Port A + port@3 -> Slice 1 Port B + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-width: + enum: [8, 16, 24] + default: 8 + +required: + - compatible + - reg + - interrupts + - ti,ctrl-module + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + video@48970000 { + compatible = "ti,dra7-vip"; + reg = <0x48970000 0x1000>; + interrupts = <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH>; + ti,ctrl-module = <&scm_conf 0x534 0x0 0x2 0x1 0x3>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + vin1a: port@0 { + reg = <0>; + + vin1a_ep: endpoint { + remote-endpoint = <&camera1>; + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + bus-width = <8>; + }; + }; + + vin1b: port@1 { + reg = <1>; + + vin1b_ep: endpoint { + remote-endpoint = <&camera2>; + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + bus-width = <8>; + }; + }; + + vin2a: port@2 { + reg = <2>; + + vin2a_ep: endpoint { + remote-endpoint = <&camera3>; + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + bus-width = <16>; + }; + }; + + vin2b: port@3 { + reg = <3>; + + vin2b_ep: endpoint { + remote-endpoint = <&camera4>; + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <0>; + bus-width = <8>; + }; + }; + }; + }; +... diff --git a/Documentation/driver-api/media/v4l2-dev.rst b/Documentation/driver-api/media/v4l2-dev.rst index d5cb19b21a9f..dd239ad42051 100644 --- a/Documentation/driver-api/media/v4l2-dev.rst +++ b/Documentation/driver-api/media/v4l2-dev.rst @@ -157,10 +157,10 @@ changing the e.g. exposure of the webcam. Of course, you can always do all the locking yourself by leaving both lock pointers at ``NULL``. -In the case of :ref:`videobuf2 <vb2_framework>` you will need to implement the -``wait_prepare()`` and ``wait_finish()`` callbacks to unlock/lock if applicable. -If you use the ``queue->lock`` pointer, then you can use the helper functions -:c:func:`vb2_ops_wait_prepare` and :c:func:`vb2_ops_wait_finish`. +In the case of :ref:`videobuf2 <vb2_framework>` you must set the ``queue->lock`` +pointer to the lock you use to serialize the queuing ioctls. This ensures that +that lock is released while waiting in ``VIDIOC_DQBUF`` for a buffer to arrive, +and it is retaken afterwards. The implementation of a hotplug disconnect should also take the lock from :c:type:`video_device` before calling v4l2_device_disconnect. If you are also diff --git a/Documentation/userspace-api/media/conf_nitpick.py b/Documentation/userspace-api/media/conf_nitpick.py index 0a8e236d07ab..445a29c01d1b 100644 --- a/Documentation/userspace-api/media/conf_nitpick.py +++ b/Documentation/userspace-api/media/conf_nitpick.py @@ -42,8 +42,6 @@ nitpick_ignore = [ ("c:func", "struct fd_set"), ("c:func", "struct pollfd"), ("c:func", "usb_make_path"), - ("c:func", "wait_finish"), - ("c:func", "wait_prepare"), ("c:func", "write"), ("c:type", "atomic_t"), diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst index eb662ced0ab4..2beb6ba1b3c2 100644 --- a/Documentation/userspace-api/media/v4l/dev-decoder.rst +++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst @@ -933,7 +933,10 @@ reflected by corresponding queries): * the minimum number of buffers needed for decoding, -* bit-depth of the bitstream has been changed. +* bit-depth of the bitstream has been changed, + +* colorspace of the bitstream has been changed, but it doesn't require + buffer reallocation. Whenever that happens, the decoder must proceed as follows: diff --git a/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst b/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst index 2bec20d87928..1f7bb8fd15e7 100644 --- a/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst +++ b/Documentation/userspace-api/media/v4l/dev-raw-vbi.rst @@ -221,7 +221,7 @@ and always returns default parameters as :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` does :alt: vbi_hsync.svg :align: center - **Figure 4.1. Line synchronization** + Line synchronization .. _vbi-525: @@ -229,7 +229,7 @@ and always returns default parameters as :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` does :alt: vbi_525.svg :align: center - **Figure 4.2. ITU-R 525 line numbering (M/NTSC and M/PAL)** + ITU-R 525 line numbering (M/NTSC and M/PAL) .. _vbi-625: @@ -237,7 +237,7 @@ and always returns default parameters as :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` does :alt: vbi_625.svg :align: center - **Figure 4.3. ITU-R 625 line numbering** + ITU-R 625 line numbering Remember the VBI image format depends on the selected video standard, therefore the application must choose a new standard or query the diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst index 2530170a56ae..142e2cd95062 100644 --- a/Documentation/userspace-api/media/v4l/dev-subdev.rst +++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst @@ -460,7 +460,7 @@ selection will refer to the sink pad format dimensions instead. :alt: subdev-image-processing-crop.svg :align: center - **Figure 4.5. Image processing in subdevs: simple crop example** + Image processing in subdevs: simple crop example In the above example, the subdev supports cropping on its sink pad. To configure it, the user sets the media bus format on the subdev's sink @@ -477,7 +477,7 @@ pad. :alt: subdev-image-processing-scaling-multi-source.svg :align: center - **Figure 4.6. Image processing in subdevs: scaling with multiple sources** + Image processing in subdevs: scaling with multiple sources In this example, the subdev is capable of first cropping, then scaling and finally cropping for two source pads individually from the resulting @@ -493,7 +493,7 @@ an area at location specified by the source crop rectangle from it. :alt: subdev-image-processing-full.svg :align: center - **Figure 4.7. Image processing in subdevs: scaling and composition with multiple sinks and sources** + Image processing in subdevs: scaling and composition with multiple sinks and sources The subdev driver supports two sink pads and two source pads. The images from both of the sink pads are individually cropped, then scaled and @@ -578,15 +578,14 @@ Device types and routing setup Different kinds of sub-devices have differing behaviour for route activation, depending on the hardware. In all cases, however, only routes that have the -``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set are active. +``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag set are active. Devices generating the streams may allow enabling and disabling some of the routes or have a fixed routing configuration. If the routes can be disabled, not -declaring the routes (or declaring them without -``V4L2_SUBDEV_STREAM_FL_ACTIVE`` flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will -disable the routes. ``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes -back to the user in the routes array, with the ``V4L2_SUBDEV_STREAM_FL_ACTIVE`` -flag unset. +declaring the routes (or declaring them without ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` +flag set) in ``VIDIOC_SUBDEV_S_ROUTING`` will disable the routes. +``VIDIOC_SUBDEV_S_ROUTING`` will still return such routes back to the user in +the routes array, with the ``V4L2_SUBDEV_ROUTE_FL_ACTIVE`` flag unset. Devices transporting the streams almost always have more configurability with respect to routing. Typically any route between the sub-device's sink and source diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst index 497ae74379f6..3b1e05c6eb13 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -2959,6 +2959,126 @@ This structure contains all loop filter related parameters. See sections - 0x00000004 - +``V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS (struct)`` + Subset of the :c:type:`v4l2_ctrl_hevc_sps` control. + It extends it with the list of Long-term reference sets parameters. + These parameters are defined according to :ref:`hevc`. + They are described in section 7.4.3.2.1 "General sequence parameter set + RBSP semantics" of the specification. + This control is a dynamically sized 1-dimensional array. + The values in the array should be ignored when either + num_long_term_ref_pics_sps is 0 or the + V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT flag is not set in + :c:type:`v4l2_ctrl_hevc_sps`. + +.. c:type:: v4l2_ctrl_hevc_ext_sps_lt_rps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_hevc_ext_sps_lt_rps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u16 + - ``lt_ref_pic_poc_lsb_sps`` + - Long term reference picture order count as described in section 7.4.3.2.1 + "General sequence parameter set RBSP semantics" of the specification. + * - __u16 + - ``flags`` + - See :ref:`Extended Long-Term RPS Flags <hevc_ext_sps_lt_rps_flags>` + +.. _hevc_ext_sps_lt_rps_flags: + +``Extended SPS Long-Term RPS Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT`` + - 0x00000001 + - Specifies if the long-term reference picture is used 7.4.3.2.1 "General sequence parameter + set RBSP semantics" of the specification. + +``V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS (struct)`` + Subset of the :c:type:`v4l2_ctrl_hevc_sps` control. + It extends it with the list of Short-term reference sets parameters. + These parameters are defined according to :ref:`hevc`. + They are described in section 7.4.8 "Short-term reference picture set + semantics" of the specification. + This control is a dynamically sized 1-dimensional array. + The values in the array should be ignored when + num_short_term_ref_pic_sets is 0. + +.. c:type:: v4l2_ctrl_hevc_ext_sps_st_rps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_hevc_ext_sps_st_rps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``delta_idx_minus1`` + - Specifies the delta compare to the index. See details in section 7.4.8 "Short-term + reference picture set semantics" of the specification. + * - __u8 + - ``delta_rps_sign`` + - Sign of the delta as specified in section 7.4.8 "Short-term reference picture set + semantics" of the specification. + * - __u8 + - ``num_negative_pics`` + - Number of short-term RPS entries that have picture order count values less than the + picture order count value of the current picture. + * - __u8 + - ``num_positive_pics`` + - Number of short-term RPS entries that have picture order count values greater than the + picture order count value of the current picture. + * - __u32 + - ``used_by_curr_pic`` + - Bit i specifies if short-term RPS i is used by the current picture. + * - __u32 + - ``use_delta_flag`` + - Bit i specifies if short-term RPS i is included in the short-term RPS entries. + * - __u16 + - ``abs_delta_rps_minus1`` + - Absolute delta RPS as specified in section 7.4.8 "Short-term reference picture set + semantics" of the specification. + * - __u16 + - ``delta_poc_s0_minus1[16]`` + - Specifies the negative picture order count delta for the i-th entry in the short-term RPS. + See details in section 7.4.8 "Short-term reference picture set semantics" of the + specification. + * - __u16 + - ``delta_poc_s1_minus1[16]`` + - Specifies the positive picture order count delta for the i-th entry in the short-term RPS. + See details in section 7.4.8 "Short-term reference picture set semantics" of the + specification. + * - __u16 + - ``flags`` + - See :ref:`Extended Short-Term RPS Flags <hevc_ext_sps_st_rps_flags>` + +.. _hevc_ext_sps_st_rps_flags: + +``Extended SPS Short-Term RPS Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED`` + - 0x00000001 + - Specifies if the short-term RPS is predicted from another short term RPS. See details in + section 7.4.8 "Short-term reference picture set semantics" of the specification. + .. _v4l2-codec-stateless-av1: ``V4L2_CID_STATELESS_AV1_SEQUENCE (struct)`` diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst index bd024ab461a4..b7f45fc0a797 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst @@ -58,6 +58,8 @@ Flash Control IDs ``V4L2_CID_FLASH_CLASS (class)`` The FLASH class descriptor. +.. _v4l2-cid-flash-led-mode: + ``V4L2_CID_FLASH_LED_MODE (menu)`` Defines the mode of the flash LED, the high-power white LED attached to the flash controller. Setting this control may not be possible in @@ -81,6 +83,8 @@ Flash Control IDs +.. _v4l2-cid-flash-strobe-source: + ``V4L2_CID_FLASH_STROBE_SOURCE (menu)`` Defines the source of the flash LED strobe. @@ -97,6 +101,12 @@ Flash Control IDs - The flash strobe is triggered by an external source. Typically this is a sensor, which makes it possible to synchronise the flash strobe start to exposure start. + This method of controlling flash LED strobe has two additional + prerequisites: the strobe source's :ref:`strobe output + <v4l2-cid-flash-strobe-oe>` must be enabled (if available) + and the flash controller's :ref:`flash LED mode + <v4l2-cid-flash-led-mode>` must be set to + ``V4L2_FLASH_LED_MODE_FLASH``. @@ -187,3 +197,35 @@ Flash Control IDs charged before strobing. LED flashes often require a cooldown period after strobe during which another strobe will not be possible. This is a read-only control. + +.. _v4l2-cid-flash-duration: + +``V4L2_CID_FLASH_DURATION (integer)`` + Duration of the flash strobe pulse generated by the strobe source, when + using external strobe. This control shall be implemented by the device + generating the hardware flash strobe signal, typically a camera sensor, + connected to a flash controller. + + The flash controllers :ref:`strobe source <v4l2-cid-flash-strobe-source>` + must be configured to ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` for this + mode of operation. For more details please also take a look at the + documentation there. + + The unit should be microseconds (µs) if possible. + +.. _v4l2-cid-flash-strobe-oe: + +``V4L2_CID_FLASH_STROBE_OE (boolean)`` + Enables the output of a hardware strobe signal from the strobe source, + when using external strobe. This control shall be implemented by the device + generating the hardware flash strobe signal, typically a camera sensor, + connected to a flash controller. + + Provided the signal generating device driver supports it, the length of the + strobe signal can be configured by adjusting its + :ref:`flash duration <v4l2-cid-flash-duration>`. + + The flash controllers :ref:`strobe source <v4l2-cid-flash-strobe-source>` + must be configured to ``V4L2_FLASH_STROBE_SOURCE_EXTERNAL`` for this + mode of operation. For more details please also take a look at the + documentation there. diff --git a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst index c7efb0465db6..235f955d3cd5 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst @@ -275,6 +275,14 @@ Compressed Formats of macroblocks to decode a full corresponding frame to the matching capture buffer. + * .. _V4L2-PIX-FMT-AV1: + + - ``V4L2_PIX_FMT_AV1`` + - 'AV01' + - AV1 compressed video frame. This format is adapted for implementing AV1 + pipeline. The decoder implements stateful video decoder and expects one + temporal unit per buffer in OBU stream format. + The encoder generates one Temporal Unit per buffer. .. raw:: latex \normalsize diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index cf970750dd4c..896177c5334f 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -2800,7 +2800,7 @@ be named ``MEDIA_BUS_FMT_SRGGB10_2X8_PADHI_LE``. :alt: bayer.svg :align: center - **Figure 4.8 Bayer Patterns** + Bayer Patterns The following table lists existing packed Bayer formats. The data organization is given as an example for the first pixel only. diff --git a/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions index c41693115db6..6182b4e2d2ee 100644 --- a/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions +++ b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions @@ -150,6 +150,8 @@ replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`V4L.v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS :c:type:`V4L.v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`V4L.v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`V4L.v4l2_ctrl_type` diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst index c8baa9430c14..82c8b52e771c 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst @@ -531,6 +531,18 @@ See also the examples in :ref:`control`. - n/a - A struct :c:type:`v4l2_ctrl_hevc_decode_params`, containing HEVC decoding parameters for stateless video decoders. + * - ``V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_hevc_ext_sps_lt_rps`, containing HEVC + extended Long-Term RPS for stateless video decoders. + * - ``V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_hevc_ext_sps_st_rps`, containing HEVC + extended Short-Term RPS for stateless video decoders. * - ``V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR`` - n/a - n/a diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 1cf795480602..6f66ca38589e 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -157,7 +157,14 @@ appropriately. The generic error codes are described at the EINVAL The sink or source pad identifiers reference a non-existing pad or reference pads of different types (ie. the sink_pad identifiers refers to a source - pad), or the ``which`` field has an unsupported value. + pad), the ``which`` field has an unsupported value, or, for + ``VIDIOC_SUBDEV_S_ROUTING``, the num_routes field set by the application is + larger than the len_routes field value. + +ENXIO + The application requested routes cannot be created or the state of + the specified routes cannot be modified. Only returned for + ``VIDIOC_SUBDEV_S_ROUTING``. E2BIG The application provided ``num_routes`` for ``VIDIOC_SUBDEV_S_ROUTING`` is diff --git a/MAINTAINERS b/MAINTAINERS index 0a16baff1936..38072599951a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15832,11 +15832,11 @@ F: include/linux/imx-media.h F: include/media/imx.h MEDIA DRIVERS FOR FREESCALE IMX7/8 -M: Rui Miguel Silva <rmfrfs@gmail.com> M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> +M: Frank Li <Frank.Li@nxp.com> M: Martin Kepplinger-Novakovic <martink@posteo.de> +R: Rui Miguel Silva <rmfrfs@gmail.com> R: Purism Kernel Team <kernel@puri.sm> -R: Frank Li <Frank.Li@nxp.com> L: imx@lists.linux.dev L: linux-media@vger.kernel.org S: Maintained @@ -16113,7 +16113,7 @@ M: Minghsiu Tsai <minghsiu.tsai@mediatek.com> M: Houlong Wei <houlong.wei@mediatek.com> M: Andrew-CT Chen <andrew-ct.chen@mediatek.com> S: Supported -F: Documentation/devicetree/bindings/media/mediatek-mdp.txt +F: Documentation/devicetree/bindings/media/mediatek,mt8173-mdp.yaml F: drivers/media/platform/mediatek/mdp/ F: drivers/media/platform/mediatek/vpu/ @@ -18672,6 +18672,7 @@ M: Sakari Ailus <sakari.ailus@iki.fi> L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/adi,ad5820.yaml +F: Documentation/devicetree/bindings/media/i2c/toshiba,et8ek8.yaml F: drivers/media/i2c/ad5820.c F: drivers/media/i2c/et8ek8 @@ -19329,6 +19330,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml F: drivers/media/i2c/og0ve1b.c +OMNIVISION OS05B10 SENSOR DRIVER +M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io> +M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml +F: drivers/media/i2c/os05b10.c + OMNIVISION OV01A10 SENSOR DRIVER M: Bingbu Cao <bingbu.cao@intel.com> L: linux-media@vger.kernel.org @@ -23393,6 +23402,14 @@ S: Supported F: Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml F: drivers/media/i2c/s5c73m3/* +SAMSUNG S5K3M5 CAMERA DRIVER +M: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/samsung,s5k3m5.yaml +F: drivers/media/i2c/s5k3m5.c + SAMSUNG S5K5BAF CAMERA DRIVER M: Sylwester Nawrocki <s.nawrocki@samsung.com> M: Andrzej Hajda <andrzej.hajda@intel.com> @@ -23400,6 +23417,14 @@ L: linux-media@vger.kernel.org S: Supported F: drivers/media/i2c/s5k5baf.c +SAMSUNG S5KJN1 CAMERA DRIVER +M: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/samsung,s5kjn1.yaml +F: drivers/media/i2c/s5kjn1.c + SAMSUNG S5P Security SubSystem (SSS) DRIVER M: Krzysztof Kozlowski <krzk@kernel.org> M: Vladimir Zapolskiy <vz@mleia.com> @@ -25477,6 +25502,13 @@ S: Maintained F: drivers/i2c/busses/i2c-designware-amdisp.c F: include/linux/soc/amd/isp4_misc.h +SYNOPSYS DESIGNWARE MIPI CSI-2 RECEIVER DRIVER +M: Michael Riesch <michael.riesch@collabora.com> +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml +F: drivers/media/platform/synopsys/dw-mipi-csi2rx.c + SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER M: Jaehoon Chung <jh80.chung@samsung.com> M: Shawn Lin <shawn.lin@rock-chips.com> @@ -26390,6 +26422,7 @@ S: Maintained W: http://linuxtv.org/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ F: Documentation/devicetree/bindings/media/ti,cal.yaml +F: Documentation/devicetree/bindings/media/ti,vip.yaml F: Documentation/devicetree/bindings/media/ti,vpe.yaml F: drivers/media/platform/ti/cal/ F: drivers/media/platform/ti/vpe/ diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 2df566f409b6..2d1f253b4929 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -605,8 +605,7 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned i */ if (vb2_get_num_buffers(q)) { bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming || - q->cnt_prepare_streaming != q->cnt_unprepare_streaming || - q->cnt_wait_prepare != q->cnt_wait_finish; + q->cnt_prepare_streaming != q->cnt_unprepare_streaming; if (unbalanced) { pr_info("unbalanced counters for queue %p:\n", q); @@ -617,13 +616,8 @@ static void __vb2_queue_free(struct vb2_queue *q, unsigned int start, unsigned i if (q->cnt_prepare_streaming != q->cnt_unprepare_streaming) pr_info(" prepare_streaming: %u unprepare_streaming: %u\n", q->cnt_prepare_streaming, q->cnt_unprepare_streaming); - if (q->cnt_wait_prepare != q->cnt_wait_finish) - pr_info(" wait_prepare: %u wait_finish: %u\n", - q->cnt_wait_prepare, q->cnt_wait_finish); } q->cnt_queue_setup = 0; - q->cnt_wait_prepare = 0; - q->cnt_wait_finish = 0; q->cnt_prepare_streaming = 0; q->cnt_start_streaming = 0; q->cnt_stop_streaming = 0; @@ -2037,10 +2031,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * become ready or for streamoff. Driver's lock is released to * allow streamoff or qbuf to be called while waiting. */ - if (q->ops->wait_prepare) - call_void_qop(q, wait_prepare, q); - else if (q->lock) - mutex_unlock(q->lock); + mutex_unlock(q->lock); /* * All locks have been released, it is safe to sleep now. @@ -2050,10 +2041,7 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) !list_empty(&q->done_list) || !q->streaming || q->error); - if (q->ops->wait_finish) - call_void_qop(q, wait_finish, q); - else if (q->lock) - mutex_lock(q->lock); + mutex_lock(q->lock); q->waiting_in_dqbuf = 0; /* @@ -2653,12 +2641,8 @@ int vb2_core_queue_init(struct vb2_queue *q) if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers)) return -EINVAL; - /* Either both or none are set */ - if (WARN_ON(!q->ops->wait_prepare ^ !q->ops->wait_finish)) - return -EINVAL; - - /* Warn if q->lock is NULL and no custom wait_prepare is provided */ - if (WARN_ON(!q->lock && !q->ops->wait_prepare)) + /* Warn if q->lock is NULL */ + if (WARN_ON(!q->lock)) return -EINVAL; INIT_LIST_HEAD(&q->queued_list); @@ -3220,17 +3204,10 @@ static int vb2_thread(void *data) continue; prequeue--; } else { - if (!threadio->stop) { - if (q->ops->wait_finish) - call_void_qop(q, wait_finish, q); - else if (q->lock) - mutex_lock(q->lock); + mutex_lock(q->lock); + if (!threadio->stop) ret = vb2_core_dqbuf(q, &index, NULL, 0); - if (q->ops->wait_prepare) - call_void_qop(q, wait_prepare, q); - else if (q->lock) - mutex_unlock(q->lock); - } + mutex_unlock(q->lock); dprintk(q, 5, "file io: vb2_dqbuf result: %d\n", ret); if (!ret) vb = vb2_get_buffer(q, index); @@ -3245,15 +3222,9 @@ static int vb2_thread(void *data) if (copy_timestamp) vb->timestamp = ktime_get_ns(); if (!threadio->stop) { - if (q->ops->wait_finish) - call_void_qop(q, wait_finish, q); - else if (q->lock) - mutex_lock(q->lock); + mutex_lock(q->lock); ret = vb2_core_qbuf(q, vb, NULL, NULL); - if (q->ops->wait_prepare) - call_void_qop(q, wait_prepare, q); - else if (q->lock) - mutex_unlock(q->lock); + mutex_unlock(q->lock); } if (ret || threadio->stop) break; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 83862d57b126..4baded4fd3b8 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -1302,20 +1302,6 @@ void vb2_video_unregister_device(struct video_device *vdev) } EXPORT_SYMBOL_GPL(vb2_video_unregister_device); -/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ - -void vb2_ops_wait_prepare(struct vb2_queue *vq) -{ - mutex_unlock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); - -void vb2_ops_wait_finish(struct vb2_queue *vq) -{ - mutex_lock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); - /* * Note that this function is called during validation time and * thus the req_queue_mutex is held to ensure no request objects diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 8c6f5aafda1d..c946c8ea6e39 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -171,6 +171,7 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); if (dmxdev->may_do_mmap) dvb_vb2_init(&dmxdev->dvr_vb2_ctx, "dvr", + &dmxdev->mutex, file->f_flags & O_NONBLOCK); dvbdev->readers--; } @@ -397,11 +398,11 @@ static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, if (dvb_vb2_is_streaming(&dmxdevfilter->vb2_ctx)) { ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx, buffer1, buffer1_len, - buffer_flags); + buffer_flags, true); if (ret == buffer1_len) ret = dvb_vb2_fill_buffer(&dmxdevfilter->vb2_ctx, buffer2, buffer2_len, - buffer_flags); + buffer_flags, true); } else { ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len); @@ -452,10 +453,10 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, if (dvb_vb2_is_streaming(ctx)) { ret = dvb_vb2_fill_buffer(ctx, buffer1, buffer1_len, - buffer_flags); + buffer_flags, false); if (ret == buffer1_len) ret = dvb_vb2_fill_buffer(ctx, buffer2, buffer2_len, - buffer_flags); + buffer_flags, false); } else { if (buffer->error) { spin_unlock(&dmxdevfilter->dev->lock); @@ -815,9 +816,21 @@ static int dvb_demux_open(struct inode *inode, struct file *file) dmxdev->may_do_mmap = 0; #endif + /* + * The mutex passed to dvb_vb2_init is unlocked when a buffer + * is in a blocking wait. However, dmxdevfilter has two mutexes: + * dmxdevfilter->mutex and dmxdev->mutex. So this will not work. + * The solution would be to support unlocking two mutexes in vb2, + * but since this problem has been here since the beginning and + * nobody ever complained, we leave it as-is rather than adding + * that second mutex pointer to vb2. + * + * In the unlikely event that someone complains about this, then + * this comment will hopefully help. + */ dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); dvb_vb2_init(&dmxdevfilter->vb2_ctx, "demux_filter", - file->f_flags & O_NONBLOCK); + &dmxdevfilter->mutex, file->f_flags & O_NONBLOCK); dmxdevfilter->type = DMXDEV_TYPE_NONE; dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); timer_setup(&dmxdevfilter->timer, dvb_dmxdev_filter_timeout, 0); @@ -1217,24 +1230,11 @@ static int dvb_demux_mmap(struct file *file, struct vm_area_struct *vma) { struct dmxdev_filter *dmxdevfilter = file->private_data; struct dmxdev *dmxdev = dmxdevfilter->dev; - int ret; if (!dmxdev->may_do_mmap) return -ENOTTY; - if (mutex_lock_interruptible(&dmxdev->mutex)) - return -ERESTARTSYS; - - if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { - mutex_unlock(&dmxdev->mutex); - return -ERESTARTSYS; - } - ret = dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma); - - mutex_unlock(&dmxdevfilter->mutex); - mutex_unlock(&dmxdev->mutex); - - return ret; + return dvb_vb2_mmap(&dmxdevfilter->vb2_ctx, vma); } #endif @@ -1367,7 +1367,6 @@ static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma) { struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; - int ret; if (!dmxdev->may_do_mmap) return -ENOTTY; @@ -1375,12 +1374,7 @@ static int dvb_dvr_mmap(struct file *file, struct vm_area_struct *vma) if (dmxdev->exit) return -ENODEV; - if (mutex_lock_interruptible(&dmxdev->mutex)) - return -ERESTARTSYS; - - ret = dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma); - mutex_unlock(&dmxdev->mutex); - return ret; + return dvb_vb2_mmap(&dmxdev->dvr_vb2_ctx, vma); } #endif diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 29edaaff7a5c..672b0efdca21 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -103,31 +103,12 @@ static void _stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&ctx->slock, flags); } -static void _dmxdev_lock(struct vb2_queue *vq) -{ - struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_lock(&ctx->mutex); - dprintk(3, "[%s]\n", ctx->name); -} - -static void _dmxdev_unlock(struct vb2_queue *vq) -{ - struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq); - - if (mutex_is_locked(&ctx->mutex)) - mutex_unlock(&ctx->mutex); - dprintk(3, "[%s]\n", ctx->name); -} - static const struct vb2_ops dvb_vb2_qops = { .queue_setup = _queue_setup, .buf_prepare = _buffer_prepare, .buf_queue = _buffer_queue, .start_streaming = _start_streaming, .stop_streaming = _stop_streaming, - .wait_prepare = _dmxdev_unlock, - .wait_finish = _dmxdev_lock, }; static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb) @@ -158,9 +139,10 @@ static const struct vb2_buf_ops dvb_vb2_buf_ops = { }; /* - * Videobuf operations + * vb2 operations */ -int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking) +int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, + struct mutex *mutex, int nonblocking) { struct vb2_queue *q = &ctx->vb_q; int ret; @@ -175,14 +157,7 @@ int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking) q->ops = &dvb_vb2_qops; q->mem_ops = &vb2_vmalloc_memops; q->buf_ops = &dvb_vb2_buf_ops; - ret = vb2_core_queue_init(q); - if (ret) { - ctx->state = DVB_VB2_STATE_NONE; - dprintk(1, "[%s] errno=%d\n", ctx->name, ret); - return ret; - } - - mutex_init(&ctx->mutex); + q->lock = mutex; spin_lock_init(&ctx->slock); INIT_LIST_HEAD(&ctx->dvb_q); @@ -190,6 +165,13 @@ int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking) ctx->nonblocking = nonblocking; ctx->state = DVB_VB2_STATE_INIT; + ret = vb2_core_queue_init(q); + if (ret) { + ctx->state = DVB_VB2_STATE_NONE; + dprintk(1, "[%s] errno=%d\n", ctx->name, ret); + return ret; + } + dprintk(3, "[%s]\n", ctx->name); return 0; @@ -249,7 +231,8 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx) int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx, const unsigned char *src, int len, - enum dmx_buffer_flags *buffer_flags) + enum dmx_buffer_flags *buffer_flags, + bool flush) { unsigned long flags = 0; void *vbuf = NULL; @@ -306,7 +289,7 @@ int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx, } } - if (ctx->nonblocking && ctx->buf) { + if (flush && ctx->buf) { vb2_set_plane_payload(&ctx->buf->vb, 0, ll); vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE); list_del(&ctx->buf->list); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 4b4db8c4f496..5eb1e0e0a87a 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -305,6 +305,7 @@ config VIDEO_MT9M111 config VIDEO_MT9M114 tristate "onsemi MT9M114 sensor support" select V4L2_CCI_I2C + select VIDEO_APTINA_PLL help This is a Video4Linux2 sensor-level driver for the onsemi MT9M114 camera. @@ -371,8 +372,19 @@ config VIDEO_OG0VE1B To compile this driver as a module, choose M here: the module will be called og0ve1b. +config VIDEO_OS05B10 + tristate "OmniVision OS05B10 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for Omnivision + OS05B10 camera sensor. + + To compile this driver as a module, choose M here: the + module will be called os05b10. + config VIDEO_OV01A10 tristate "OmniVision OV01A10 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV01A10 camera. @@ -529,6 +541,7 @@ config VIDEO_OV5645 config VIDEO_OV5647 tristate "OmniVision OV5647 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV5647 camera. @@ -743,6 +756,16 @@ config VIDEO_S5C73M3 This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. +config VIDEO_S5K3M5 + tristate "Samsung S5K3M5 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for Samsung S5K3M5 13MP raw + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called s5k3m5. + config VIDEO_S5K5BAF tristate "Samsung S5K5BAF sensor support" help @@ -755,6 +778,16 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. +config VIDEO_S5KJN1 + tristate "Samsung S5KJN1 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for Samsung S5KJN1 50MP raw + camera sensor. + + To compile this driver as a module, choose M here: the + module will be called s5kjn1. + config VIDEO_VD55G1 tristate "ST VD55G1 sensor support" select V4L2_CCI_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c5f17602454f..a3a6396df3c4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o +obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o @@ -125,8 +126,10 @@ obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o obj-$(CONFIG_VIDEO_RDACM21) += rdacm21.o obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ +obj-$(CONFIG_VIDEO_S5K3M5) += s5k3m5.o obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o +obj-$(CONFIG_VIDEO_S5KJN1) += s5kjn1.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 378f4e6af12c..669b0b3165b1 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -507,6 +507,13 @@ static int adv7180_get_frame_interval(struct v4l2_subdev *sd, fi->interval.denominator = 25; } + /* + * If the de-interlacer is active, the chip produces full video frames + * at the field rate. + */ + if (state->field == V4L2_FIELD_NONE) + fi->interval.denominator *= 2; + return 0; } @@ -969,6 +976,32 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd, } } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int adv7180_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct adv7180_state *state = to_state(sd); + int ret; + + ret = adv7180_read(state, reg->reg); + if (ret < 0) + return ret; + + reg->val = ret; + reg->size = 1; + + return 0; +} + +static int adv7180_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct adv7180_state *state = to_state(sd); + + return adv7180_write(state, reg->reg, reg->val); +} +#endif + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, @@ -982,6 +1015,10 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { static const struct v4l2_subdev_core_ops adv7180_core_ops = { .subscribe_event = adv7180_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7180_g_register, + .s_register = adv7180_s_register, +#endif }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { @@ -1066,13 +1103,13 @@ static int adv7180_select_input(struct adv7180_state *state, unsigned int input) static int adv7182_init(struct adv7180_state *state) { - if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) + if (state->csi_client) adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR, - ADV7180_DEFAULT_CSI_I2C_ADDR << 1); + state->csi_client->addr << 1); - if (state->chip_info->flags & ADV7180_FLAG_I2P) + if (state->vpp_client) adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR, - ADV7180_DEFAULT_VPP_I2C_ADDR << 1); + state->vpp_client->addr << 1); if (state->chip_info->flags & ADV7180_FLAG_V2) { /* ADI recommended writes for improved video quality */ @@ -1443,15 +1480,17 @@ static int adv7180_probe(struct i2c_client *client) state->force_bt656_4 = true; if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { - state->csi_client = i2c_new_dummy_device(client->adapter, - ADV7180_DEFAULT_CSI_I2C_ADDR); + state->csi_client = + i2c_new_ancillary_device(client, "csi", + ADV7180_DEFAULT_CSI_I2C_ADDR); if (IS_ERR(state->csi_client)) return PTR_ERR(state->csi_client); } if (state->chip_info->flags & ADV7180_FLAG_I2P) { - state->vpp_client = i2c_new_dummy_device(client->adapter, - ADV7180_DEFAULT_VPP_I2C_ADDR); + state->vpp_client = + i2c_new_ancillary_device(client, "vpp", + ADV7180_DEFAULT_VPP_I2C_ADDR); if (IS_ERR(state->vpp_client)) { ret = PTR_ERR(state->vpp_client); goto err_unregister_csi_client; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 516553fb17e9..67116a4ef134 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3453,7 +3453,13 @@ static int configure_regmaps(struct adv76xx_state *state) static void adv76xx_reset(struct adv76xx_state *state) { if (state->reset_gpio) { - /* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */ + /* + * Note: Misinterpretation of reset assertion - do not re-use + * this code. The reset pin is using incorrect (for a reset + * signal) logical level. + * + * ADV76XX can be reset by a low reset pulse of minimum 5 ms. + */ gpiod_set_value_cansleep(state->reset_gpio, 0); usleep_range(5000, 10000); gpiod_set_value_cansleep(state->reset_gpio, 1); diff --git a/drivers/media/i2c/aptina-pll.c b/drivers/media/i2c/aptina-pll.c index b1f89bbf9d47..cd2ed4583c97 100644 --- a/drivers/media/i2c/aptina-pll.c +++ b/drivers/media/i2c/aptina-pll.c @@ -129,6 +129,8 @@ int aptina_pll_calculate(struct device *dev, p1_max = min(limits->p1_max, limits->out_clock_max * div / (pll->ext_clock * pll->m)); + dev_dbg(dev, "pll: p1 min %u max %u\n", p1_min, p1_max); + for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { unsigned int mf_inc = p1 / gcd(div, p1); unsigned int mf_high; diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 0d7b922fd4c4..aa4dd7e7cf5a 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -13,6 +13,7 @@ * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.com> */ +#include <linux/bits.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> @@ -25,6 +26,7 @@ #include <linux/slab.h> #include <linux/smiapp.h> #include <linux/v4l2-mediabus.h> +#include <media/mipi-csi2.h> #include <media/v4l2-cci.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -245,6 +247,33 @@ out_err: return ret; } +static u8 ccs_mipi_csi2_data_type(unsigned int bpp) +{ + switch (bpp) { + case 6: + return MIPI_CSI2_DT_RAW6; + case 7: + return MIPI_CSI2_DT_RAW7; + case 8: + return MIPI_CSI2_DT_RAW8; + case 10: + return MIPI_CSI2_DT_RAW10; + case 12: + return MIPI_CSI2_DT_RAW12; + case 14: + return MIPI_CSI2_DT_RAW14; + case 16: + return MIPI_CSI2_DT_RAW16; + case 20: + return MIPI_CSI2_DT_RAW20; + case 24: + return MIPI_CSI2_DT_RAW24; + default: + WARN_ON(1); + return 0; + } +} + static int ccs_read_frame_fmt(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -483,16 +512,71 @@ static int ccs_pll_try(struct ccs_sensor *sensor, struct ccs_pll *pll) return ccs_pll_calculate(&client->dev, &lim, pll); } +static void +ccs_get_binning(struct ccs_sensor *sensor, u8 *binning_mode, u8 *binh, u8 *binv) +{ + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->binner->sd); + const struct v4l2_rect *sink_crop = + v4l2_subdev_state_get_crop(state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + const struct v4l2_rect *sink_comp = + v4l2_subdev_state_get_compose(state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + + if (binning_mode) + *binning_mode = sink_crop->width == sink_comp->width && + sink_crop->height == sink_comp->height ? 0 : 1; + + *binh = sink_crop->width / sink_comp->width; + *binv = sink_crop->height / sink_comp->height; +} + +static void ccs_get_scaling(struct ccs_sensor *sensor, u8 *scaling_mode, + u8 *scale_m) +{ + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->scaler->sd); + const struct v4l2_rect *sink_crop = + v4l2_subdev_state_get_crop(state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + const struct v4l2_rect *sink_comp = + v4l2_subdev_state_get_compose(state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + + *scale_m = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) / + sink_comp->width; + + if (!scaling_mode) + return; + + if (sink_crop->width == sink_comp->width) + *scaling_mode = CCS_SCALING_MODE_NO_SCALING; + else if (sink_crop->height == sink_comp->height) + *scaling_mode = CCS_SCALING_MODE_HORIZONTAL; + else + *scaling_mode = SMIAPP_SCALING_MODE_BOTH; +} + static int ccs_pll_update(struct ccs_sensor *sensor) { struct ccs_pll *pll = &sensor->pll; + u8 binh, binv; + u8 scale_m; int rval; - pll->binning_horizontal = sensor->binning_horizontal; - pll->binning_vertical = sensor->binning_vertical; + ccs_get_binning(sensor, NULL, &binh, &binv); + + if (sensor->scaler) + ccs_get_scaling(sensor, NULL, &scale_m); + else + scale_m = CCS_LIM(sensor, SCALER_N_MIN); + + pll->binning_horizontal = binh; + pll->binning_vertical = binv; pll->link_freq = sensor->link_freq->qmenu_int[sensor->link_freq->val]; - pll->scale_m = sensor->scale_m; + pll->scale_m = scale_m; pll->bits_per_pixel = sensor->csi_format->compressed; rval = ccs_pll_try(sensor, pll); @@ -513,12 +597,13 @@ static int ccs_pll_update(struct ccs_sensor *sensor) * */ -static void __ccs_update_exposure_limits(struct ccs_sensor *sensor) +static void __ccs_update_exposure_limits(struct ccs_sensor *sensor, + const struct v4l2_rect *pa_src) { struct v4l2_ctrl *ctrl = sensor->exposure; int max; - max = sensor->pa_src.height + sensor->vblank->val - + max = pa_src->height + sensor->vblank->val - CCS_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); @@ -621,12 +706,20 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct ccs_subdev, ctrl_handler) ->sensor; struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_subdev_state *state; + const struct v4l2_rect *pa_src = NULL; int pm_status; u32 orient = 0; unsigned int i; int exposure; int rval; + if (ctrl->id == V4L2_CID_VBLANK || ctrl->id == V4L2_CID_HBLANK) { + state = v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd); + pa_src = v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC, + CCS_STREAM_PIXEL); + } + switch (ctrl->id) { case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: @@ -645,7 +738,7 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: exposure = sensor->exposure->val; - __ccs_update_exposure_limits(sensor); + __ccs_update_exposure_limits(sensor, pa_src); if (exposure > sensor->exposure->maximum) { sensor->exposure->val = sensor->exposure->maximum; @@ -737,12 +830,12 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_VBLANK: rval = ccs_write(sensor, FRAME_LENGTH_LINES, - sensor->pa_src.height + ctrl->val); + pa_src->height + ctrl->val); break; case V4L2_CID_HBLANK: rval = ccs_write(sensor, LINE_LENGTH_PCK, - sensor->pa_src.width + ctrl->val); + pa_src->width + ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -1117,14 +1210,14 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor) dev_dbg(&client->dev, "jolly good! %u\n", j); - sensor->default_mbus_frame_fmts |= 1 << j; + sensor->default_mbus_frame_fmts |= BIT_U64(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; + pll->scale_m = CCS_LIM(sensor, SCALER_N_MIN); for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { sensor->compressed_min_bpp = @@ -1150,7 +1243,7 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor) f->compressed - sensor->compressed_min_bpp]; unsigned int j; - if (!(sensor->default_mbus_frame_fmts & 1 << i)) + if (!(sensor->default_mbus_frame_fmts & BIT_U64(i))) continue; pll->bits_per_pixel = f->compressed; @@ -1195,14 +1288,18 @@ static int ccs_get_mbus_formats(struct ccs_sensor *sensor) return 0; } -static void ccs_update_blanking(struct ccs_sensor *sensor) +static void ccs_update_blanking(struct ccs_sensor *sensor, + const struct v4l2_rect *pa_src) { struct v4l2_ctrl *vblank = sensor->vblank; struct v4l2_ctrl *hblank = sensor->hblank; u16 min_fll, max_fll, min_llp, max_llp, min_lbp; int min, max; + u8 binh, binv; + + ccs_get_binning(sensor, NULL, &binh, &binv); - if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { + if (binv > 1 || binh > 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); @@ -1218,21 +1315,26 @@ static void ccs_update_blanking(struct ccs_sensor *sensor) min = max_t(int, CCS_LIM(sensor, MIN_FRAME_BLANKING_LINES), - min_fll - sensor->pa_src.height); - max = max_fll - sensor->pa_src.height; + min_fll - pa_src->height); + max = max_fll - pa_src->height; __v4l2_ctrl_modify_range(vblank, min, max, vblank->step, min); - min = max_t(int, min_llp - sensor->pa_src.width, min_lbp); - max = max_llp - sensor->pa_src.width; + min = max_t(int, min_llp - pa_src->width, min_lbp); + max = max_llp - pa_src->width; __v4l2_ctrl_modify_range(hblank, min, max, hblank->step, min); - __ccs_update_exposure_limits(sensor); + __ccs_update_exposure_limits(sensor, pa_src); } static int ccs_pll_blanking_update(struct ccs_sensor *sensor) { + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd); + const struct v4l2_rect *pa_src = + v4l2_subdev_state_get_crop(state, CCS_PA_PAD_SRC, + CCS_STREAM_PIXEL); struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; @@ -1241,15 +1343,15 @@ static int ccs_pll_blanking_update(struct ccs_sensor *sensor) return rval; /* Output from pixel array, including blanking */ - ccs_update_blanking(sensor); + ccs_update_blanking(sensor, pa_src); 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->pa_src.width + sensor->hblank->val) * - (sensor->pa_src.height + sensor->vblank->val) / 100)); + ((pa_src->width + sensor->hblank->val) * + (pa_src->height + sensor->vblank->val) / 100)); return 0; } @@ -1704,72 +1806,122 @@ static int ccs_power_off(struct device *dev) usleep_range(5000, 5000); regulator_bulk_disable(ARRAY_SIZE(ccs_regulators), sensor->regulators); - sensor->streaming = false; return 0; } /* ----------------------------------------------------------------------------- - * Video stream management + * V4L2 subdev video operations */ -static int ccs_start_streaming(struct ccs_sensor *sensor) +static int ccs_pm_get_init(struct ccs_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + /* + * It can't use pm_runtime_resume_and_get() here, as the driver + * relies at the returned value to detect if the device was already + * active or not. + */ + rval = pm_runtime_get_sync(&client->dev); + if (rval < 0) + goto error; + + /* Device was already active, so don't set controls */ + if (rval == 1 && !sensor->handler_setup_needed) + return 0; + + sensor->handler_setup_needed = false; + + /* Restore V4L2 controls to the previously suspended device */ + rval = __v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler); + if (rval) + goto error; + + rval = __v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto error; + + /* Keep PM runtime usage_count incremented on success */ + return 0; + +error: + pm_runtime_put(&client->dev); + return rval; +} + +static int ccs_enable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct v4l2_subdev_state *pa_state = + v4l2_subdev_get_locked_active_state(&sensor->pixel_array->sd); + struct v4l2_subdev_state *src_state = + v4l2_subdev_get_locked_active_state(&sensor->src->sd); + const struct v4l2_rect *pa_src = + v4l2_subdev_state_get_crop(pa_state, CCS_PA_PAD_SRC, + CCS_STREAM_PIXEL); + const struct v4l2_rect *src_src = + v4l2_subdev_state_get_crop(src_state, CCS_PAD_SRC, + CCS_STREAM_PIXEL); struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int binning_mode; + u8 binning_mode, binh, binv; int rval; - mutex_lock(&sensor->mutex); + if (pad != CCS_PAD_SRC) + return -EINVAL; + + if (sensor->streaming) { + sensor->streaming |= streams_mask; + return 0; + } + + rval = ccs_pm_get_init(sensor); + if (rval) + return rval; rval = ccs_write(sensor, CSI_DATA_FORMAT, (sensor->csi_format->width << 8) | sensor->csi_format->compressed); if (rval) - goto out; + goto err_pm_put; /* 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; + ccs_get_binning(sensor, &binning_mode, &binh, &binv); - rval = ccs_write(sensor, BINNING_TYPE, binning_type); + if (binning_mode) { + rval = ccs_write(sensor, BINNING_TYPE, (binh << 4) | binv); if (rval < 0) - goto out; - - binning_mode = 1; + goto err_pm_put; } rval = ccs_write(sensor, BINNING_MODE, binning_mode); if (rval < 0) - goto out; + goto err_pm_put; /* Set up PLL */ rval = ccs_pll_configure(sensor); if (rval) - goto out; + goto err_pm_put; /* Analog crop start coordinates */ - rval = ccs_write(sensor, X_ADDR_START, sensor->pa_src.left); + rval = ccs_write(sensor, X_ADDR_START, pa_src->left); if (rval < 0) - goto out; + goto err_pm_put; - rval = ccs_write(sensor, Y_ADDR_START, sensor->pa_src.top); + rval = ccs_write(sensor, Y_ADDR_START, pa_src->top); if (rval < 0) - goto out; + goto err_pm_put; /* Analog crop end coordinates */ - rval = ccs_write(sensor, X_ADDR_END, - sensor->pa_src.left + sensor->pa_src.width - 1); + rval = ccs_write(sensor, X_ADDR_END, pa_src->left + pa_src->width - 1); if (rval < 0) - goto out; + goto err_pm_put; - rval = ccs_write(sensor, Y_ADDR_END, - sensor->pa_src.top + sensor->pa_src.height - 1); + rval = ccs_write(sensor, Y_ADDR_END, pa_src->top + pa_src->height - 1); if (rval < 0) - goto out; + goto err_pm_put; /* * Output from pixel array, including blanking, is set using @@ -1779,46 +1931,57 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) /* Digital crop */ if (CCS_LIM(sensor, DIGITAL_CROP_CAPABILITY) == CCS_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + struct v4l2_subdev_state *scaler_state = + v4l2_subdev_get_locked_active_state(&sensor->scaler->sd); + const struct v4l2_rect *scaler_sink = + v4l2_subdev_state_get_crop(scaler_state, + sensor->scaler->sink_pad, + CCS_STREAM_PIXEL); + rval = ccs_write(sensor, DIGITAL_CROP_X_OFFSET, - sensor->scaler_sink.left); + scaler_sink->left); if (rval < 0) - goto out; + goto err_pm_put; rval = ccs_write(sensor, DIGITAL_CROP_Y_OFFSET, - sensor->scaler_sink.top); + scaler_sink->top); if (rval < 0) - goto out; + goto err_pm_put; rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_WIDTH, - sensor->scaler_sink.width); + scaler_sink->width); if (rval < 0) - goto out; + goto err_pm_put; rval = ccs_write(sensor, DIGITAL_CROP_IMAGE_HEIGHT, - sensor->scaler_sink.height); + scaler_sink->height); if (rval < 0) - goto out; + goto err_pm_put; } /* Scaling */ if (CCS_LIM(sensor, SCALING_CAPABILITY) != CCS_SCALING_CAPABILITY_NONE) { - rval = ccs_write(sensor, SCALING_MODE, sensor->scaling_mode); + u8 scaling_mode, scale_m; + + ccs_get_scaling(sensor, &scaling_mode, &scale_m); + + rval = ccs_write(sensor, SCALING_MODE, scaling_mode); if (rval < 0) - goto out; + goto err_pm_put; - rval = ccs_write(sensor, SCALE_M, sensor->scale_m); + rval = ccs_write(sensor, SCALE_M, scale_m); if (rval < 0) - goto out; + goto err_pm_put; } /* Output size from sensor */ - rval = ccs_write(sensor, X_OUTPUT_SIZE, sensor->src_src.width); + rval = ccs_write(sensor, X_OUTPUT_SIZE, src_src->width); if (rval < 0) - goto out; - rval = ccs_write(sensor, Y_OUTPUT_SIZE, sensor->src_src.height); + goto err_pm_put; + rval = ccs_write(sensor, Y_OUTPUT_SIZE, src_src->height); if (rval < 0) - goto out; + goto err_pm_put; if (CCS_LIM(sensor, FLASH_MODE_CAPABILITY) & (CCS_FLASH_MODE_CAPABILITY_SINGLE_STROBE | @@ -1827,109 +1990,53 @@ static int ccs_start_streaming(struct ccs_sensor *sensor) sensor->hwcfg.strobe_setup->trigger != 0) { rval = ccs_setup_flash_strobe(sensor); if (rval) - goto out; + goto err_pm_put; } rval = ccs_call_quirk(sensor, pre_streamon); if (rval) { dev_err(&client->dev, "pre_streamon quirks failed\n"); - goto out; + goto err_pm_put; } 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; -} + sensor->streaming |= streams_mask; -/* ----------------------------------------------------------------------------- - * 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; - - /* - * It can't use pm_runtime_resume_and_get() here, as the driver - * relies at the returned value to detect if the device was already - * active or not. - */ - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) - goto error; - - /* Device was already active, so don't set controls */ - if (rval == 1 && !sensor->handler_setup_needed) - return 0; - - sensor->handler_setup_needed = false; - - /* Restore V4L2 controls to the previously suspended device */ - rval = v4l2_ctrl_handler_setup(&sensor->pixel_array->ctrl_handler); - if (rval) - goto error; + return 0; - rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); - if (rval) - goto error; +err_pm_put: + pm_runtime_put_autosuspend(&client->dev); - /* Keep PM runtime usage_count incremented on success */ - return 0; -error: - pm_runtime_put(&client->dev); return rval; } -static int ccs_set_stream(struct v4l2_subdev *subdev, int enable) +static int ccs_disable_streams(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); int rval; - if (!enable) { - ccs_stop_streaming(sensor); - sensor->streaming = false; - pm_runtime_put_autosuspend(&client->dev); + if (pad != CCS_PAD_SRC) + return -EINVAL; + sensor->streaming &= ~streams_mask; + if (sensor->streaming) return 0; - } - rval = ccs_pm_get_init(sensor); + rval = ccs_write(sensor, MODE_SELECT, CCS_MODE_SELECT_SOFTWARE_STANDBY); if (rval) return rval; - sensor->streaming = true; + rval = ccs_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); - rval = ccs_start_streaming(sensor); - if (rval < 0) { - sensor->streaming = false; - pm_runtime_put_autosuspend(&client->dev); - } + pm_runtime_put_autosuspend(&client->dev); - return rval; + return 0; } static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags) @@ -1955,7 +2062,8 @@ static int ccs_pre_streamon(struct v4l2_subdev *subdev, u32 flags) } } - rval = ccs_pm_get_init(sensor); + scoped_guard(mutex, &sensor->mutex) + rval = ccs_pm_get_init(sensor); if (rval) return rval; @@ -1979,6 +2087,20 @@ static int ccs_post_streamoff(struct v4l2_subdev *subdev) return 0; } +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 & BIT_U64(i) && + ccs_csi_data_formats[i].code == code) + return &ccs_csi_data_formats[i]; + } + + return sensor->csi_format; +} + static int ccs_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -1987,42 +2109,35 @@ static int ccs_enum_mbus_code(struct v4l2_subdev *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 %u, index %u\n", subdev->name, code->pad, code->index); if (subdev != &sensor->src->sd || code->pad != CCS_PAD_SRC) { if (code->index) - goto out; + return -EINVAL; code->code = sensor->internal_csi_format->code; - rval = 0; - goto out; + + return 0; } for (i = 0; i < ARRAY_SIZE(ccs_csi_data_formats); i++) { - if (sensor->mbus_frame_fmts & (1 << i)) + if (sensor->mbus_frame_fmts & BIT_U64(i)) idx++; if (idx == code->index) { code->code = ccs_csi_data_formats[i].code; dev_err(&client->dev, "found index %u, i %u, code %x\n", code->index, i, code->code); - rval = 0; - break; + return 0; } } -out: - mutex_unlock(&sensor->mutex); - - return rval; + return -EINVAL; } -static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad) +static u32 ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); @@ -2032,102 +2147,47 @@ static u32 __ccs_get_mbus_code(struct v4l2_subdev *subdev, unsigned int pad) return sensor->internal_csi_format->code; } -static int __ccs_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); - fmt->format.code = __ccs_get_mbus_code(subdev, fmt->pad); - - return 0; -} - static int ccs_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct ccs_sensor *sensor = to_ccs_sensor(subdev); - int rval; - - mutex_lock(&sensor->mutex); - rval = __ccs_get_format(subdev, sd_state, fmt); - mutex_unlock(&sensor->mutex); - - return rval; -} - -static void ccs_get_crop_compose(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_rect **crops, - struct v4l2_rect **comps) -{ - struct ccs_subdev *ssd = to_ccs_subdev(subdev); - unsigned int i; + fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); + fmt->format.code = ccs_get_mbus_code(subdev, fmt->pad); - if (crops) - for (i = 0; i < subdev->entity.num_pads; i++) - crops[i] = - v4l2_subdev_state_get_crop(sd_state, i); - if (comps) - *comps = v4l2_subdev_state_get_compose(sd_state, - ssd->sink_pad); + return 0; } /* Changes require propagation only on sink pad. */ static void ccs_propagate(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, int which, - int target) + struct v4l2_subdev_state *sd_state, 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]; + struct v4l2_rect *comp, *crop; struct v4l2_mbus_framefmt *fmt; - ccs_get_crop_compose(subdev, sd_state, crops, &comp); - + comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad, + CCS_STREAM_PIXEL); 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; - sensor->scaler_sink = *comp; - } else if (ssd == sensor->binner) { - sensor->binning_horizontal = 1; - sensor->binning_vertical = 1; - } - } + crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + comp->width = crop->width; + comp->height = crop->height; fallthrough; case V4L2_SEL_TGT_COMPOSE: - *crops[CCS_PAD_SRC] = *comp; - fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); + crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SRC, + CCS_STREAM_PIXEL); + *crop = *comp; + fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC, + CCS_STREAM_PIXEL); fmt->width = comp->width; fmt->height = comp->height; - if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ssd == sensor->src) - sensor->src_src = *crops[CCS_PAD_SRC]; 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_state *sd_state, struct v4l2_subdev_format *fmt) @@ -2140,7 +2200,7 @@ static int ccs_set_format_source(struct v4l2_subdev *subdev, unsigned int i; int rval; - rval = __ccs_get_format(subdev, sd_state, fmt); + rval = ccs_get_format(subdev, sd_state, fmt); if (rval) return rval; @@ -2187,22 +2247,19 @@ static int ccs_set_format(struct v4l2_subdev *subdev, { 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); + struct v4l2_rect *crop; if (fmt->pad == ssd->source_pad) { int rval; rval = ccs_set_format_source(subdev, sd_state, 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.code = ccs_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; fmt->format.height &= ~1; fmt->format.field = V4L2_FIELD_NONE; @@ -2216,15 +2273,14 @@ static int ccs_set_format(struct v4l2_subdev *subdev, CCS_LIM(sensor, MIN_Y_OUTPUT_SIZE), CCS_LIM(sensor, MAX_Y_OUTPUT_SIZE)); - ccs_get_crop_compose(subdev, sd_state, crops, NULL); + crop = v4l2_subdev_state_get_crop(sd_state, ssd->sink_pad, + CCS_STREAM_PIXEL); - 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; - ccs_propagate(subdev, sd_state, fmt->which, V4L2_SEL_TGT_CROP); - - mutex_unlock(&sensor->mutex); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP); return 0; } @@ -2276,26 +2332,23 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, static void ccs_set_compose_binner(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) + const struct v4l2_rect *sink_crop) { 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); + int best = scaling_goodness(subdev, sink_crop->width, sel->r.width, + sink_crop->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); + int this = scaling_goodness(subdev, + sink_crop->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + sink_crop->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); if (this > best) { binh = sensor->binning_subtypes[i].horizontal; @@ -2303,13 +2356,9 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev, 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; + sel->r.width = (sink_crop->width / binh) & ~1; + sel->r.height = (sink_crop->height / binv) & ~1; } /* @@ -2324,8 +2373,7 @@ static void ccs_set_compose_binner(struct v4l2_subdev *subdev, static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel, - struct v4l2_rect **crops, - struct v4l2_rect *comp) + const struct v4l2_rect *sink_crop) { struct i2c_client *client = v4l2_get_subdevdata(subdev); struct ccs_sensor *sensor = to_ccs_sensor(subdev); @@ -2337,18 +2385,14 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, 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); + sel->r.width = min_t(unsigned int, sel->r.width, sink_crop->width); + sel->r.height = min_t(unsigned int, sel->r.height, sink_crop->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 + a = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.width; + b = sink_crop->height * CCS_LIM(sensor, SCALER_N_MIN) / sel->r.height; + max_m = sink_crop->width * CCS_LIM(sensor, SCALER_N_MIN) - / CCS_LIM(sensor, MIN_X_OUTPUT_SIZE); + / (CCS_LIM(sensor, MIN_X_OUTPUT_SIZE) ?: 1); a = clamp(a, CCS_LIM(sensor, SCALER_M_MIN), CCS_LIM(sensor, SCALER_M_MAX)); @@ -2378,14 +2422,12 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, } 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); + int this = scaling_goodness(subdev, + sink_crop->width + / try[i] + * CCS_LIM(sensor, SCALER_N_MIN), + sel->r.width, sink_crop->height, + sel->r.height, sel->flags); dev_dbg(&client->dev, "trying factor %u (%u)\n", try[i], i); @@ -2400,12 +2442,10 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, continue; this = scaling_goodness( - subdev, crops[CCS_PAD_SINK]->width - / try[i] + subdev, sink_crop->width / try[i] * CCS_LIM(sensor, SCALER_N_MIN), sel->r.width, - crops[CCS_PAD_SINK]->height - / try[i] + sink_crop->height / try[i] * CCS_LIM(sensor, SCALER_N_MIN), sel->r.height, sel->flags); @@ -2417,23 +2457,13 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev, } } - sel->r.width = - (crops[CCS_PAD_SINK]->width - / scale_m - * CCS_LIM(sensor, SCALER_N_MIN)) & ~1; + sel->r.width = (sink_crop->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; + sel->r.height = (sink_crop->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; - } + sel->r.height = sink_crop->height; } /* We're only called on source pads. This function sets scaling. */ static int ccs_set_compose(struct v4l2_subdev *subdev, @@ -2442,20 +2472,24 @@ static int ccs_set_compose(struct v4l2_subdev *subdev, { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); - struct v4l2_rect *comp, *crops[CCS_PADS]; + const struct v4l2_rect *sink_crop; + struct v4l2_rect *comp; - ccs_get_crop_compose(subdev, sd_state, crops, &comp); + sink_crop = v4l2_subdev_state_get_crop(sd_state, CCS_PAD_SINK, + CCS_STREAM_PIXEL); + comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad, + CCS_STREAM_PIXEL); sel->r.top = 0; sel->r.left = 0; if (ssd == sensor->binner) - ccs_set_compose_binner(subdev, sd_state, sel, crops, comp); + ccs_set_compose_binner(subdev, sd_state, sel, sink_crop); else - ccs_set_compose_scaler(subdev, sd_state, sel, crops, comp); + ccs_set_compose_scaler(subdev, sd_state, sel, sink_crop); *comp = sel->r; - ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_COMPOSE); + ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_COMPOSE); if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) return ccs_pll_blanking_update(sensor); @@ -2507,9 +2541,13 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); - struct v4l2_rect src_size = { 0 }, *crops[CCS_PADS], *comp; + struct v4l2_rect src_size = { 0 }, *crop; + const struct v4l2_rect *comp; - ccs_get_crop_compose(subdev, sd_state, crops, &comp); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad, + CCS_STREAM_PIXEL); + comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad, + CCS_STREAM_PIXEL); if (sel->pad == ssd->sink_pad) { struct v4l2_mbus_framefmt *mfmt = @@ -2532,13 +2570,10 @@ static int ccs_set_crop(struct v4l2_subdev *subdev, 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; + *crop = sel->r; if (ssd != sensor->pixel_array && sel->pad == CCS_PAD_SINK) - ccs_propagate(subdev, sd_state, sel->which, V4L2_SEL_TGT_CROP); - else if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && - ssd == sensor->pixel_array) - sensor->pa_src = sel->r; + ccs_propagate(subdev, sd_state, V4L2_SEL_TGT_CROP); return 0; } @@ -2557,14 +2592,17 @@ static int ccs_get_selection(struct v4l2_subdev *subdev, { struct ccs_sensor *sensor = to_ccs_sensor(subdev); struct ccs_subdev *ssd = to_ccs_subdev(subdev); - struct v4l2_rect *comp, *crops[CCS_PADS]; + const struct v4l2_rect *crop, *comp; int ret; ret = ccs_sel_supported(subdev, sel); if (ret) return ret; - ccs_get_crop_compose(subdev, sd_state, crops, &comp); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad, + CCS_STREAM_PIXEL); + comp = v4l2_subdev_state_get_compose(sd_state, ssd->sink_pad, + CCS_STREAM_PIXEL); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: @@ -2584,7 +2622,7 @@ static int ccs_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: case V4L2_SEL_TGT_COMPOSE_BOUNDS: - sel->r = *crops[sel->pad]; + sel->r = *crop; break; case V4L2_SEL_TGT_COMPOSE: sel->r = *comp; @@ -2605,8 +2643,6 @@ static int ccs_set_selection(struct v4l2_subdev *subdev, 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); @@ -2628,10 +2664,44 @@ static int ccs_set_selection(struct v4l2_subdev *subdev, ret = -EINVAL; } - mutex_unlock(&sensor->mutex); return ret; } +static int ccs_get_frame_desc(struct v4l2_subdev *subdev, unsigned int pad, + struct v4l2_mbus_frame_desc *desc) +{ + struct ccs_sensor *sensor = to_ccs_sensor(subdev); + struct v4l2_mbus_frame_desc_entry *entry = desc->entry; + struct v4l2_subdev_state *sd_state; + + switch (sensor->hwcfg.csi_signalling_mode) { + case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY: + case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY: + desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + break; + default: + /* FIXME: CCP2 support */ + return -EINVAL; + } + + sd_state = v4l2_subdev_lock_and_get_active_state(subdev); + if (!sd_state) + return -EINVAL; + + entry->pixelcode = sensor->csi_format->code; + entry->stream = CCS_STREAM_PIXEL; + entry->bus.csi2.dt = + sensor->csi_format->width == sensor->csi_format->compressed ? + ccs_mipi_csi2_data_type(sensor->csi_format->width) : + CCS_DEFAULT_COMPRESSED_DT; + entry++; + desc->num_entries++; + + v4l2_subdev_unlock_state(sd_state); + + return 0; +} + static int ccs_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) { struct ccs_sensor *sensor = to_ccs_sensor(subdev); @@ -2942,6 +3012,8 @@ static void ccs_cleanup(struct ccs_sensor *sensor) ccs_free_controls(sensor); } +static const struct v4l2_subdev_internal_ops ccs_internal_ops; + static int ccs_init_subdev(struct ccs_sensor *sensor, struct ccs_subdev *ssd, const char *name, unsigned short num_pads, u32 function, @@ -2954,11 +3026,14 @@ static int ccs_init_subdev(struct ccs_sensor *sensor, if (!ssd) return 0; - if (ssd != sensor->src) + if (ssd != sensor->src) { v4l2_subdev_init(&ssd->sd, &ccs_ops); + ssd->sd.internal_ops = &ccs_internal_ops; + } ssd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ssd->sd.entity.function = function; + ssd->sd.state_lock = &sensor->mutex; ssd->sensor = sensor; ssd->npads = num_pads; @@ -3004,9 +3079,8 @@ static int ccs_init_state(struct v4l2_subdev *sd, v4l2_subdev_state_get_format(sd_state, pad); struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, pad); - bool is_active = !sd->active_state || sd->active_state == sd_state; - mutex_lock(&sensor->mutex); + guard(mutex)(&sensor->mutex); ccs_get_native_size(ssd, crop); @@ -3015,28 +3089,21 @@ static int ccs_init_state(struct v4l2_subdev *sd, fmt->code = sensor->internal_csi_format->code; fmt->field = V4L2_FIELD_NONE; - if (ssd == sensor->pixel_array) { - if (is_active) - sensor->pa_src = *crop; - - mutex_unlock(&sensor->mutex); + if (ssd == sensor->pixel_array) return 0; - } fmt = v4l2_subdev_state_get_format(sd_state, CCS_PAD_SRC); fmt->code = ssd == sensor->src ? sensor->csi_format->code : sensor->internal_csi_format->code; fmt->field = V4L2_FIELD_NONE; - ccs_propagate(sd, sd_state, is_active, V4L2_SEL_TGT_CROP); - - mutex_unlock(&sensor->mutex); + ccs_propagate(sd, sd_state, V4L2_SEL_TGT_CROP); return 0; } static const struct v4l2_subdev_video_ops ccs_video_ops = { - .s_stream = ccs_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, .pre_streamon = ccs_pre_streamon, .post_streamoff = ccs_post_streamoff, }; @@ -3047,6 +3114,9 @@ static const struct v4l2_subdev_pad_ops ccs_pad_ops = { .set_fmt = ccs_set_format, .get_selection = ccs_get_selection, .set_selection = ccs_set_selection, + .enable_streams = ccs_enable_streams, + .disable_streams = ccs_disable_streams, + .get_frame_desc = ccs_get_frame_desc, }; static const struct v4l2_subdev_sensor_ops ccs_sensor_ops = { @@ -3064,6 +3134,10 @@ static const struct media_entity_operations ccs_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +static const struct v4l2_subdev_internal_ops ccs_internal_ops = { + .init_state = ccs_init_state, +}; + static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { .init_state = ccs_init_state, .registered = ccs_registered, @@ -3076,11 +3150,9 @@ static const struct v4l2_subdev_internal_ops ccs_internal_src_ops = { 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); - unsigned int i; + struct fwnode_handle *fwnode = dev_fwnode(dev), *ep; + struct ccs_hwconfig *hwcfg = &sensor->hwcfg; int rval; ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, @@ -3107,9 +3179,9 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) 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->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: @@ -3118,11 +3190,7 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) goto out_err; } - rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &hwcfg->ext_clk); - - dev_dbg(dev, "clk %u, mode %u\n", hwcfg->ext_clk, - hwcfg->csi_signalling_mode); + dev_dbg(dev, "signalling mode: %u\n", hwcfg->csi_signalling_mode); if (!bus_cfg.nr_of_link_frequencies) { dev_warn(dev, "no link frequencies defined\n"); @@ -3130,23 +3198,22 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) 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); + 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++) { + for (unsigned int i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i]; dev_dbg(dev, "freq %u: %lld\n", i, hwcfg->op_sys_clock[i]); } - v4l2_fwnode_endpoint_free(&bus_cfg); - fwnode_handle_put(ep); - - return 0; + fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &hwcfg->ext_clk); out_err: v4l2_fwnode_endpoint_free(&bus_cfg); @@ -3234,47 +3301,30 @@ static int ccs_probe(struct i2c_client *client) 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 (%pe)\n", - sensor->ext_clk); - return -EPROBE_DEFER; - } - - if (sensor->ext_clk) { - if (sensor->hwcfg.ext_clk) { - unsigned long rate; + sensor->ext_clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); + if (IS_ERR(sensor->ext_clk)) + return dev_err_probe(&client->dev, PTR_ERR(sensor->ext_clk), + "could not get clock\n"); - 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; - } + if (sensor->hwcfg.ext_clk) { + unsigned long rate; - 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); + rval = clk_set_rate(sensor->ext_clk, sensor->hwcfg.ext_clk); + if (rval < 0) + return dev_err_probe(&client->dev, rval, + "unable to set clock freq to %u\n", + sensor->hwcfg.ext_clk); + + rate = clk_get_rate(sensor->ext_clk); + if (rate != sensor->hwcfg.ext_clk) { + return dev_err_probe(&client->dev, -EINVAL, + "can't set clock freq, asked for %u but got %lu\n", + sensor->hwcfg.ext_clk, rate); } - } 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->hwcfg.ext_clk = clk_get_rate(sensor->ext_clk); + dev_dbg(&client->dev, "obtained clock freq %u\n", + sensor->hwcfg.ext_clk); } if (!sensor->hwcfg.ext_clk) { @@ -3386,8 +3436,6 @@ static int ccs_probe(struct i2c_client *client) 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"); @@ -3424,10 +3472,22 @@ static int ccs_probe(struct i2c_client *client) 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; + switch (sensor->hwcfg.csi_signalling_mode) { + case CCS_CSI_SIGNALING_MODE_CSI_2_CPHY: + sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_CPHY; + break; + case CCS_CSI_SIGNALING_MODE_CSI_2_DPHY: + case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK: + case SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE: + sensor->pll.bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY; + break; + default: + dev_err(&client->dev, "unsupported signalling mode %u\n", + sensor->hwcfg.csi_signalling_mode); + rval = -EINVAL; + goto out_cleanup; + } sensor->pll.csi2.lanes = sensor->hwcfg.lanes; if (CCS_LIM(sensor, CLOCK_CALCULATION) & CCS_CLOCK_CALCULATION_LANE_SPEED) { @@ -3519,15 +3579,13 @@ static int ccs_probe(struct i2c_client *client) goto out_cleanup; } - mutex_lock(&sensor->mutex); - rval = ccs_pll_blanking_update(sensor); - mutex_unlock(&sensor->mutex); + scoped_guard(mutex, &sensor->mutex) + rval = ccs_pll_blanking_update(sensor); if (rval) { dev_err(&client->dev, "update mode failed\n"); goto out_cleanup; } - sensor->streaming = false; sensor->dev_init_done = true; sensor->handler_setup_needed = true; diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 0726c4687f0f..58952c5bf06b 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -48,6 +48,8 @@ #define CCS_COLOUR_COMPONENTS 4 +#define CCS_DEFAULT_COMPRESSED_DT MIPI_CSI2_DT_USER_DEFINED(0) + #define SMIAPP_NAME "smiapp" #define CCS_NAME "ccs" @@ -177,6 +179,8 @@ struct ccs_csi_data_format { #define CCS_PAD_SRC 1 #define CCS_PADS 2 +#define CCS_STREAM_PIXEL 0 + struct ccs_binning_subtype { u8 horizontal:4; u8 vertical:4; @@ -218,27 +222,20 @@ struct ccs_sensor { void *ccs_limits; u8 nbinning_subtypes; struct ccs_binning_subtype binning_subtypes[CCS_LIM_BINNING_SUB_TYPE_MAX_N + 1]; - u32 mbus_frame_fmts; + u64 mbus_frame_fmts; const struct ccs_csi_data_format *csi_format; const struct ccs_csi_data_format *internal_csi_format; - struct v4l2_rect pa_src, scaler_sink, src_src; - u32 default_mbus_frame_fmts; + u64 default_mbus_frame_fmts; int default_pixel_order; struct ccs_data_container sdata, mdata; - u8 binning_horizontal; - u8 binning_vertical; - - u8 scale_m; - u8 scaling_mode; - u8 frame_skip; u16 embedded_start; /* embedded data start line */ u16 embedded_end; u16 image_start; /* image data start line */ u16 visible_pixel_start; /* start pixel of the visible image */ - bool streaming; + u8 streaming; bool dev_init_done; bool handler_setup_needed; u8 compressed_min_bpp; diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 1e7ad355a388..3288de539452 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -149,7 +149,7 @@ static int dw9714_power_up(struct dw9714_device *dw9714_dev) gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 0); - usleep_range(1000, 2000); + usleep_range(12000, 14000); return 0; } diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 2cb7b718782b..50121c3e5b48 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -835,6 +835,10 @@ static int et8ek8_power_on(struct et8ek8_sensor *sensor) udelay(10); /* I wish this is a good value */ + /* + * Note: Misinterpretation of reset assertion - do not re-use this code. + * The reset pin is using incorrect (for a reset signal) logical level. + */ gpiod_set_value(sensor->reset, 1); msleep(5000 * 1000 / sensor->xclk_freq + 1); /* Wait 5000 cycles */ diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index bc55fe2a93b4..fee63bc106d9 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -792,21 +792,6 @@ static int imx219_disable_streams(struct v4l2_subdev *sd, return ret; } -static void imx219_update_pad_format(struct imx219 *imx219, - const struct imx219_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) -{ - /* Bayer order varies with flips */ - fmt->code = imx219_get_format_code(imx219, code); - fmt->width = mode->width; - fmt->height = mode->height; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_XFER_FUNC_NONE; -} - static int imx219_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_mbus_code_enum *code) @@ -858,12 +843,24 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, format = v4l2_subdev_state_get_format(state, 0); prev_line_len = format->width + imx219->hblank->val; + /* + * Adjust the requested format to match the closest mode. The Bayer + * order varies with flips. + */ mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + fmt->format.code = imx219_get_format_code(imx219, fmt->format.code); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + *format = fmt->format; /* diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c index 51ebbe7ae996..16b0ace15813 100644 --- a/drivers/media/i2c/mt9m114.c +++ b/drivers/media/i2c/mt9m114.c @@ -32,6 +32,8 @@ #include <media/v4l2-mediabus.h> #include <media/v4l2-subdev.h> +#include "aptina-pll.h" + /* Sysctl registers */ #define MT9M114_CHIP_ID CCI_REG16(0x0000) #define MT9M114_COMMAND_REGISTER CCI_REG16(0x0080) @@ -267,9 +269,9 @@ #define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0) #define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00 #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980) -#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m)) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) ((((n) - 1) << 8) | (m)) #define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982) -#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) ((p) << 8) +#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(p) (((p) - 1) << 8) #define MT9M114_CAM_PORT_OUTPUT_CONTROL CCI_REG16(0xc984) #define MT9M114_CAM_PORT_PORT_SELECT_PARALLEL (0 << 0) #define MT9M114_CAM_PORT_PORT_SELECT_MIPI (1 << 0) @@ -327,19 +329,22 @@ /* * The minimum amount of horizontal and vertical blanking is undocumented. The - * minimum values that have been seen in register lists are 303 and 38, use + * minimum values that have been seen in register lists are 303 and 21, use * them. * - * Set the default to achieve 1280x960 at 30fps. + * Set the default to achieve full resolution (1296x976 analog crop + * rectangle, 1280x960 output size) at 30fps with a 48 MHz pixclock. */ #define MT9M114_MIN_HBLANK 303 -#define MT9M114_MIN_VBLANK 38 -#define MT9M114_DEF_HBLANK 323 -#define MT9M114_DEF_VBLANK 39 +#define MT9M114_MIN_VBLANK 21 +#define MT9M114_DEF_HBLANK 308 +#define MT9M114_DEF_VBLANK 21 #define MT9M114_DEF_FRAME_RATE 30 #define MT9M114_MAX_FRAME_RATE 120 +#define MT9M114_DEF_PIXCLOCK 48000000 + #define MT9M114_PIXEL_ARRAY_WIDTH 1296U #define MT9M114_PIXEL_ARRAY_HEIGHT 976U @@ -384,11 +389,7 @@ struct mt9m114 { struct v4l2_fwnode_endpoint bus_cfg; bool bypass_pll; - struct { - unsigned int m; - unsigned int n; - unsigned int p; - } pll; + struct aptina_pll pll; unsigned int pixrate; bool streaming; @@ -758,7 +759,7 @@ static int mt9m114_initialize(struct mt9m114 *sensor) sensor->pll.n), &ret); cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P, - MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), + MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p1), &ret); } @@ -788,14 +789,6 @@ static int mt9m114_initialize(struct mt9m114 *sensor) if (ret < 0) return ret; - ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); - if (ret < 0) - return ret; - - ret = mt9m114_set_state(sensor, MT9M114_SYS_STATE_ENTER_SUSPEND); - if (ret < 0) - return ret; - return 0; } @@ -850,6 +843,18 @@ static int mt9m114_configure_pa(struct mt9m114 *sensor, return ret; } +/* + * For source pad formats other then RAW10 the IFP removes a 4 pixel border from + * its sink pad format size for demosaicing. + */ +static int mt9m114_ifp_get_border(struct v4l2_subdev_state *state) +{ + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 1); + + return format->code == MEDIA_BUS_FMT_SGRBG10_1X10 ? 0 : 4; +} + static int mt9m114_configure_ifp(struct mt9m114 *sensor, struct v4l2_subdev_state *state) { @@ -857,6 +862,7 @@ static int mt9m114_configure_ifp(struct mt9m114 *sensor, const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; const struct v4l2_rect *compose; + unsigned int border; u64 output_format; int ret = 0; @@ -871,15 +877,18 @@ static int mt9m114_configure_ifp(struct mt9m114 *sensor, return ret; /* - * Color pipeline (IFP) cropping and scaling. Subtract 4 from the left - * and top coordinates to compensate for the lines and columns removed - * by demosaicing that are taken into account in the crop rectangle but - * not in the hardware. + * Color pipeline (IFP) cropping and scaling. The crop window registers + * apply cropping after demosaicing, which itself consumes 4 pixels on + * each side of the image. The crop rectangle exposed to userspace + * includes that demosaicing border, subtract it from the left and top + * coordinates to configure the crop window. */ + border = mt9m114_ifp_get_border(state); + cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET, - crop->left - 4, &ret); + crop->left - border, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET, - crop->top - 4, &ret); + crop->top - border, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH, crop->width, &ret); cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT, @@ -950,6 +959,10 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor, if (ret) return ret; + ret = mt9m114_initialize(sensor); + if (ret) + goto error; + ret = mt9m114_configure_ifp(sensor, ifp_state); if (ret) goto error; @@ -1823,6 +1836,41 @@ static int mt9m114_ifp_enum_frameintervals(struct v4l2_subdev *sd, return 0; } +/* + * Helper function to update IFP crop, compose rectangles and source format + * when the pixel border size changes, which requires resetting these. + */ +static void mt9m114_ifp_update_sel_and_src_fmt(struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *src_format, *sink_format; + struct v4l2_rect *crop; + unsigned int border; + + sink_format = v4l2_subdev_state_get_format(state, 0); + src_format = v4l2_subdev_state_get_format(state, 1); + crop = v4l2_subdev_state_get_crop(state, 0); + border = mt9m114_ifp_get_border(state); + + crop->left = border; + crop->top = border; + crop->width = sink_format->width - 2 * border; + crop->height = sink_format->height - 2 * border; + *v4l2_subdev_state_get_compose(state, 0) = *crop; + + src_format->width = crop->width; + src_format->height = crop->height; + + if (src_format->code == MEDIA_BUS_FMT_SGRBG10_1X10) { + src_format->colorspace = V4L2_COLORSPACE_RAW; + src_format->ycbcr_enc = V4L2_YCBCR_ENC_601; + src_format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + } else { + src_format->colorspace = V4L2_COLORSPACE_SRGB; + src_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + src_format->quantization = V4L2_QUANTIZATION_DEFAULT; + } +} + static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) @@ -1840,17 +1888,28 @@ static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd, format->height = clamp(ALIGN(fmt->format.height, 8), MT9M114_PIXEL_ARRAY_MIN_OUTPUT_HEIGHT, MT9M114_PIXEL_ARRAY_HEIGHT); + + /* Propagate changes downstream. */ + mt9m114_ifp_update_sel_and_src_fmt(state); } else { const struct mt9m114_format_info *info; /* Only the media bus code can be changed on the source pad. */ info = mt9m114_format_info(sensor, 1, fmt->format.code); - format->code = info->code; - - /* If the output format is RAW10, bypass the scaler. */ - if (format->code == MEDIA_BUS_FMT_SGRBG10_1X10) - *format = *v4l2_subdev_state_get_format(state, 0); + /* + * If the output format changes from/to RAW10 then the crop + * rectangle needs to be adjusted to add / remove the 4 pixel + * border used for demosaicing. And these changes then need to + * be propagated to the compose rectangle and source format. + */ + if ((format->code == MEDIA_BUS_FMT_SGRBG10_1X10) != + (info->code == MEDIA_BUS_FMT_SGRBG10_1X10)) { + format->code = info->code; + mt9m114_ifp_update_sel_and_src_fmt(state); + } else { + format->code = info->code; + } } fmt->format = *format; @@ -1864,6 +1923,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; + unsigned int border; int ret = 0; /* Crop and compose are only supported on the sink pad. */ @@ -1878,15 +1938,17 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: /* - * The crop default and bounds are equal to the sink - * format size minus 4 pixels on each side for demosaicing. + * Crop defaults and bounds are equal to the sink format size. + * For source pad formats other then RAW10 this gets reduced + * by 4 pixels on each side for demosaicing. */ format = v4l2_subdev_state_get_format(state, 0); + border = mt9m114_ifp_get_border(state); - sel->r.left = 4; - sel->r.top = 4; - sel->r.width = format->width - 8; - sel->r.height = format->height - 8; + sel->r.left = border; + sel->r.top = border; + sel->r.width = format->width - 2 * border; + sel->r.height = format->height - 2 * border; break; case V4L2_SEL_TGT_COMPOSE: @@ -1918,9 +1980,10 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { - struct v4l2_mbus_framefmt *format; + struct v4l2_mbus_framefmt *format, *src_format; struct v4l2_rect *crop; struct v4l2_rect *compose; + unsigned int border; if (sel->target != V4L2_SEL_TGT_CROP && sel->target != V4L2_SEL_TGT_COMPOSE) @@ -1930,27 +1993,37 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, if (sel->pad != 0) return -EINVAL; - format = v4l2_subdev_state_get_format(state, 0); crop = v4l2_subdev_state_get_crop(state, 0); + + /* Crop and compose cannot be changed when bypassing the scaler. */ + src_format = v4l2_subdev_state_get_format(state, 1); + if (src_format->code == MEDIA_BUS_FMT_SGRBG10_1X10) { + sel->r = *crop; + return 0; + } + + format = v4l2_subdev_state_get_format(state, 0); compose = v4l2_subdev_state_get_compose(state, 0); if (sel->target == V4L2_SEL_TGT_CROP) { /* - * Clamp the crop rectangle. Demosaicing removes 4 pixels on - * each side of the image. + * Clamp the crop rectangle. For source pad formats other then + * RAW10 demosaicing removes 4 pixels on each side of the image. */ - crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), 4, - format->width - 4 - + border = mt9m114_ifp_get_border(state); + + crop->left = clamp_t(unsigned int, ALIGN(sel->r.left, 2), border, + format->width - border - MT9M114_SCALER_CROPPED_INPUT_WIDTH); - crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), 4, - format->height - 4 - + crop->top = clamp_t(unsigned int, ALIGN(sel->r.top, 2), border, + format->height - border - MT9M114_SCALER_CROPPED_INPUT_HEIGHT); crop->width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9M114_SCALER_CROPPED_INPUT_WIDTH, - format->width - 4 - crop->left); + format->width - border - crop->left); crop->height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9M114_SCALER_CROPPED_INPUT_HEIGHT, - format->height - 4 - crop->top); + format->height - border - crop->top); sel->r = *crop; @@ -1974,9 +2047,8 @@ static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd, } /* Propagate the compose rectangle to the source format. */ - format = v4l2_subdev_state_get_format(state, 1); - format->width = compose->width; - format->height = compose->height; + src_format->width = compose->width; + src_format->height = compose->height; return 0; } @@ -2227,6 +2299,13 @@ error_regulator: static void mt9m114_power_off(struct mt9m114 *sensor) { + unsigned int duration; + + gpiod_set_value(sensor->reset, 1); + /* Power off takes 10 clock cycles. Double it to be safe. */ + duration = DIV_ROUND_UP(2 * 10 * 1000000, clk_get_rate(sensor->clk)); + fsleep(duration); + clk_disable_unprepare(sensor->clk); regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies); } @@ -2235,19 +2314,8 @@ static int __maybe_unused mt9m114_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct mt9m114 *sensor = ifp_to_mt9m114(sd); - int ret; - ret = mt9m114_power_on(sensor); - if (ret) - return ret; - - ret = mt9m114_initialize(sensor); - if (ret) { - mt9m114_power_off(sensor); - return ret; - } - - return 0; + return mt9m114_power_on(sensor); } static int __maybe_unused mt9m114_runtime_suspend(struct device *dev) @@ -2281,14 +2349,39 @@ static int mt9m114_verify_link_frequency(struct mt9m114 *sensor, return 0; } +/* + * Based on the docs the PLL is believed to have the following setup: + * + * +-----+ +-----+ +-----+ +-----+ +-----+ + * Fin --> | / N | --> | x M | --> | x 2 | --> | / P | --> | / 2 | --> + * +-----+ +-----+ +-----+ +-----+ +-----+ + * fBit fWord fSensor + * ext_clock int_clock out_clock pix_clock + * + * The MT9M114 docs give a max fBit rate of 768 MHz which translates to + * an out_clock_max of 384 MHz. + */ static int mt9m114_clk_init(struct mt9m114 *sensor) { + static const struct aptina_pll_limits limits = { + .ext_clock_min = 6000000, + .ext_clock_max = 54000000, + /* int_clock_* limits are not documented taken from mt9p031.c */ + .int_clock_min = 2000000, + .int_clock_max = 13500000, + /* out_clock_min is not documented, taken from mt9p031.c */ + .out_clock_min = 180000000, + .out_clock_max = 384000000, + .pix_clock_max = 48000000, + .n_min = 1, + .n_max = 64, + .m_min = 16, + .m_max = 192, + .p1_min = 8, + .p1_max = 8, + }; unsigned int pixrate; - - /* Hardcode the PLL multiplier and dividers to default settings. */ - sensor->pll.m = 32; - sensor->pll.n = 1; - sensor->pll.p = 7; + int ret; /* * Calculate the pixel rate and link frequency. The CSI-2 bus is clocked @@ -2308,8 +2401,15 @@ static int mt9m114_clk_init(struct mt9m114 *sensor) } /* Check if the PLL configuration fits the configured link frequency. */ - pixrate = clk_get_rate(sensor->clk) * sensor->pll.m - / ((sensor->pll.n + 1) * (sensor->pll.p + 1)); + sensor->pll.ext_clock = clk_get_rate(sensor->clk); + sensor->pll.pix_clock = MT9M114_DEF_PIXCLOCK; + + ret = aptina_pll_calculate(&sensor->client->dev, &limits, &sensor->pll); + if (ret) + return ret; + + pixrate = sensor->pll.ext_clock * sensor->pll.m + / (sensor->pll.n * sensor->pll.p1); if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) { sensor->pixrate = pixrate; sensor->bypass_pll = false; @@ -2360,11 +2460,17 @@ static int mt9m114_parse_dt(struct mt9m114 *sensor) struct fwnode_handle *ep; int ret; + /* + * On ACPI systems the fwnode graph can be initialized by a bridge + * driver, which may not have probed yet. Wait for this. + * + * TODO: Return an error once bridge driver code will have moved + * to the ACPI core. + */ ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) { - dev_err(&sensor->client->dev, "No endpoint found\n"); - return -EINVAL; - } + if (!ep) + return dev_err_probe(&sensor->client->dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); sensor->bus_cfg.bus_type = V4L2_MBUS_UNKNOWN; ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg); @@ -2434,7 +2540,7 @@ static int mt9m114_probe(struct i2c_client *client) goto error_ep_free; } - sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset)) { ret = PTR_ERR(sensor->reset); dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); @@ -2459,8 +2565,8 @@ static int mt9m114_probe(struct i2c_client *client) /* * Identify the sensor. The driver supports runtime PM, but needs to * work when runtime PM is disabled in the kernel. To that end, power - * the sensor on manually here, and initialize it after identification - * to reach the same state as if resumed through runtime PM. + * the sensor on manually here to reach the same state as if resumed + * through runtime PM. */ ret = mt9m114_power_on(sensor); if (ret < 0) { @@ -2472,10 +2578,6 @@ static int mt9m114_probe(struct i2c_client *client) if (ret < 0) goto error_power_off; - ret = mt9m114_initialize(sensor); - if (ret < 0) - goto error_power_off; - /* * Enable runtime PM with autosuspend. As the device has been powered * manually, mark it as active, and increase the usage count without @@ -2550,11 +2652,18 @@ static const struct of_device_id mt9m114_of_ids[] = { }; MODULE_DEVICE_TABLE(of, mt9m114_of_ids); +static const struct acpi_device_id mt9m114_acpi_ids[] = { + { "INT33F0" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, mt9m114_acpi_ids); + static struct i2c_driver mt9m114_driver = { .driver = { .name = "mt9m114", .pm = &mt9m114_pm_ops, .of_match_table = mt9m114_of_ids, + .acpi_match_table = mt9m114_acpi_ids, }, .probe = mt9m114_probe, .remove = mt9m114_remove, diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c index 262d9df766fe..84a28cdcade1 100644 --- a/drivers/media/i2c/og0ve1b.c +++ b/drivers/media/i2c/og0ve1b.c @@ -41,6 +41,10 @@ #define OG0VE1B_ANALOGUE_GAIN_STEP 1 #define OG0VE1B_ANALOGUE_GAIN_DEFAULT 16 +/* Vertical timing size */ +#define OG0VE1B_REG_VTS CCI_REG16(0x380e) +#define OG0VE1B_VTS_MAX 0xffff + /* Test pattern */ #define OG0VE1B_REG_PRE_ISP CCI_REG8(0x5e00) #define OG0VE1B_TEST_PATTERN_ENABLE BIT(7) @@ -89,6 +93,8 @@ struct og0ve1b { struct v4l2_subdev sd; struct media_pad pad; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; struct v4l2_ctrl_handler ctrl_handler; /* Saved register value */ @@ -140,8 +146,6 @@ static const struct cci_reg_sequence og0ve1b_640x480_120fps_mode[] = { { CCI_REG8(0x380b), 0xe0 }, { CCI_REG8(0x380c), 0x03 }, /* horizontal timing size */ { CCI_REG8(0x380d), 0x18 }, - { CCI_REG8(0x380e), 0x02 }, /* vertical timing size */ - { CCI_REG8(0x380f), 0x38 }, { CCI_REG8(0x3811), 0x04 }, { CCI_REG8(0x3813), 0x04 }, { CCI_REG8(0x3814), 0x11 }, @@ -273,8 +277,25 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl) { struct og0ve1b *og0ve1b = container_of(ctrl->handler, struct og0ve1b, ctrl_handler); + const struct og0ve1b_mode *mode = &supported_modes[0]; + s64 exposure_max; int ret; + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + exposure_max = ctrl->val + mode->height - + OG0VE1B_EXPOSURE_MAX_MARGIN; + ret = __v4l2_ctrl_modify_range(og0ve1b->exposure, + og0ve1b->exposure->minimum, + exposure_max, + og0ve1b->exposure->step, + og0ve1b->exposure->default_value); + if (ret) + return ret; + } + /* V4L2 controls are applied, when sensor is powered up for streaming */ if (!pm_runtime_get_if_active(og0ve1b->dev)) return 0; @@ -288,6 +309,10 @@ static int og0ve1b_set_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_EXPOSURE, ctrl->val << 4, NULL); break; + case V4L2_CID_VBLANK: + ret = cci_write(og0ve1b->regmap, OG0VE1B_REG_VTS, + ctrl->val + mode->height, NULL); + break; case V4L2_CID_TEST_PATTERN: ret = og0ve1b_enable_test_pattern(og0ve1b, ctrl->val); break; @@ -309,8 +334,8 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b) { struct v4l2_ctrl_handler *ctrl_hdlr = &og0ve1b->ctrl_handler; const struct og0ve1b_mode *mode = &supported_modes[0]; + s64 exposure_max, pixel_rate, h_blank, v_blank; struct v4l2_fwnode_device_properties props; - s64 exposure_max, pixel_rate, h_blank; struct v4l2_ctrl *ctrl; int ret; @@ -333,24 +358,24 @@ static int og0ve1b_init_controls(struct og0ve1b *og0ve1b) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_VBLANK, - mode->vts - mode->height, - mode->vts - mode->height, 1, - mode->vts - mode->height); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v_blank = mode->vts - mode->height; + og0ve1b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, + V4L2_CID_VBLANK, v_blank, + OG0VE1B_VTS_MAX - mode->height, 1, + v_blank); v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, OG0VE1B_ANALOGUE_GAIN_MIN, OG0VE1B_ANALOGUE_GAIN_MAX, OG0VE1B_ANALOGUE_GAIN_STEP, OG0VE1B_ANALOGUE_GAIN_DEFAULT); - exposure_max = (mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN); - v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, - V4L2_CID_EXPOSURE, - OG0VE1B_EXPOSURE_MIN, exposure_max, - OG0VE1B_EXPOSURE_STEP, - OG0VE1B_EXPOSURE_DEFAULT); + exposure_max = mode->vts - OG0VE1B_EXPOSURE_MAX_MARGIN; + og0ve1b->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &og0ve1b_ctrl_ops, + V4L2_CID_EXPOSURE, + OG0VE1B_EXPOSURE_MIN, + exposure_max, + OG0VE1B_EXPOSURE_STEP, + OG0VE1B_EXPOSURE_DEFAULT); v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &og0ve1b_ctrl_ops, V4L2_CID_TEST_PATTERN, diff --git a/drivers/media/i2c/os05b10.c b/drivers/media/i2c/os05b10.c new file mode 100644 index 000000000000..e0453c988e4a --- /dev/null +++ b/drivers/media/i2c/os05b10.c @@ -0,0 +1,1130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Support for the os05b10 + * + * Copyright (C) 2025 Silicon Signals Pvt. Ltd. + * + * Inspired from imx219, ov2735 camera drivers. + */ + +#include <linux/array_size.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/device/devres.h> +#include <linux/err.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 <linux/types.h> +#include <linux/time.h> +#include <linux/units.h> + +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> + +#define OS05B10_XCLK_FREQ (24 * HZ_PER_MHZ) + +#define OS05B10_REG_CHIP_ID CCI_REG24(0x300a) +#define OS05B10_CHIP_ID 0x530641 + +#define OS05B10_REG_CTRL_MODE CCI_REG8(0x0100) +#define OS05B10_MODE_STANDBY 0x00 +#define OS05B10_MODE_STREAMING 0x01 + +#define OS05B10_REG_EXPOSURE CCI_REG24(0x3500) +#define OS05B10_EXPOSURE_MIN 2 +#define OS05B10_EXPOSURE_STEP 1 +#define OS05B10_EXPOSURE_MARGIN 8 + +#define OS05B10_REG_ANALOG_GAIN CCI_REG16(0x3508) +#define OS05B10_ANALOG_GAIN_MIN 0x80 +#define OS05B10_ANALOG_GAIN_MAX 0x7C0 +#define OS05B10_ANALOG_GAIN_STEP 1 +#define OS05B10_ANALOG_GAIN_DEFAULT 0x80 + +#define OS05B10_REG_HTS CCI_REG16(0x380c) + +#define OS05B10_REG_VTS CCI_REG16(0x380e) +#define OS05B10_VTS_MAX 0x7fff + +#define OS05B10_LINK_FREQ_600MHZ (600 * HZ_PER_MHZ) + +static const struct v4l2_rect os05b10_native_area = { + .top = 0, + .left = 0, + .width = 2608, + .height = 1960, +}; + +static const struct v4l2_rect os05b10_active_area = { + .top = 8, + .left = 8, + .width = 2592, + .height = 1944, +}; + +static const char * const os05b10_supply_name[] = { + "avdd", /* Analog supply */ + "dovdd", /* Digital IO */ + "dvdd", /* Digital core */ +}; + +static const struct cci_reg_sequence os05b10_common_regs[] = { + { CCI_REG8(0x0301), 0x44 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0305), 0x32 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x0325), 0x3b }, + { CCI_REG8(0x3002), 0x21 }, + { CCI_REG8(0x3016), 0x72 }, + { CCI_REG8(0x301e), 0xb4 }, + { CCI_REG8(0x301f), 0xd0 }, + { CCI_REG8(0x3021), 0x03 }, + { CCI_REG8(0x3022), 0x01 }, + { CCI_REG8(0x3107), 0xa1 }, + { CCI_REG8(0x3108), 0x7d }, + { CCI_REG8(0x3109), 0xfc }, + { CCI_REG8(0x3503), 0x88 }, + { CCI_REG8(0x350a), 0x04 }, + { CCI_REG8(0x350b), 0x00 }, + { CCI_REG8(0x350c), 0x00 }, + { CCI_REG8(0x350d), 0x80 }, + { CCI_REG8(0x350e), 0x04 }, + { CCI_REG8(0x350f), 0x00 }, + { CCI_REG8(0x3510), 0x00 }, + { CCI_REG8(0x3511), 0x00 }, + { CCI_REG8(0x3512), 0x20 }, + { CCI_REG8(0x3600), 0x4d }, + { CCI_REG8(0x3601), 0x08 }, + { CCI_REG8(0x3610), 0x87 }, + { CCI_REG8(0x3611), 0x24 }, + { CCI_REG8(0x3614), 0x4c }, + { CCI_REG8(0x3620), 0x0c }, + { CCI_REG8(0x3632), 0x80 }, + { CCI_REG8(0x3633), 0x00 }, + { CCI_REG8(0x3636), 0xcc }, + { CCI_REG8(0x3637), 0x27 }, + { CCI_REG8(0x3660), 0x00 }, + { CCI_REG8(0x3662), 0x10 }, + { CCI_REG8(0x3665), 0x00 }, + { CCI_REG8(0x3666), 0x00 }, + { CCI_REG8(0x366a), 0x14 }, + { CCI_REG8(0x3670), 0x0b }, + { CCI_REG8(0x3671), 0x0b }, + { CCI_REG8(0x3672), 0x0b }, + { CCI_REG8(0x3673), 0x0b }, + { CCI_REG8(0x3678), 0x2b }, + { CCI_REG8(0x367a), 0x11 }, + { CCI_REG8(0x367b), 0x11 }, + { CCI_REG8(0x367c), 0x11 }, + { CCI_REG8(0x367d), 0x11 }, + { CCI_REG8(0x3681), 0xff }, + { CCI_REG8(0x3682), 0x86 }, + { CCI_REG8(0x3683), 0x44 }, + { CCI_REG8(0x3684), 0x24 }, + { CCI_REG8(0x3685), 0x00 }, + { CCI_REG8(0x368a), 0x00 }, + { CCI_REG8(0x368d), 0x2b }, + { CCI_REG8(0x368e), 0x2b }, + { CCI_REG8(0x3690), 0x00 }, + { CCI_REG8(0x3691), 0x0b }, + { CCI_REG8(0x3692), 0x0b }, + { CCI_REG8(0x3693), 0x0b }, + { CCI_REG8(0x3694), 0x0b }, + { CCI_REG8(0x369d), 0x68 }, + { CCI_REG8(0x369e), 0x34 }, + { CCI_REG8(0x369f), 0x1b }, + { CCI_REG8(0x36a0), 0x0f }, + { CCI_REG8(0x36a1), 0x77 }, + { CCI_REG8(0x36b0), 0x30 }, + { CCI_REG8(0x36b2), 0x00 }, + { CCI_REG8(0x36b3), 0x00 }, + { CCI_REG8(0x36b4), 0x00 }, + { CCI_REG8(0x36b5), 0x00 }, + { CCI_REG8(0x36b6), 0x00 }, + { CCI_REG8(0x36b7), 0x00 }, + { CCI_REG8(0x36b8), 0x00 }, + { CCI_REG8(0x36b9), 0x00 }, + { CCI_REG8(0x36ba), 0x00 }, + { CCI_REG8(0x36bb), 0x00 }, + { CCI_REG8(0x36bc), 0x00 }, + { CCI_REG8(0x36bd), 0x00 }, + { CCI_REG8(0x36be), 0x00 }, + { CCI_REG8(0x36bf), 0x00 }, + { CCI_REG8(0x36c0), 0x01 }, + { CCI_REG8(0x36c1), 0x00 }, + { CCI_REG8(0x36c2), 0x00 }, + { CCI_REG8(0x36c3), 0x00 }, + { CCI_REG8(0x36c4), 0x00 }, + { CCI_REG8(0x36c5), 0x00 }, + { CCI_REG8(0x36c6), 0x00 }, + { CCI_REG8(0x36c7), 0x00 }, + { CCI_REG8(0x36c8), 0x00 }, + { CCI_REG8(0x36c9), 0x00 }, + { CCI_REG8(0x36ca), 0x0e }, + { CCI_REG8(0x36cb), 0x0e }, + { CCI_REG8(0x36cc), 0x0e }, + { CCI_REG8(0x36cd), 0x0e }, + { CCI_REG8(0x36ce), 0x0c }, + { CCI_REG8(0x36cf), 0x0c }, + { CCI_REG8(0x36d0), 0x0c }, + { CCI_REG8(0x36d1), 0x0c }, + { CCI_REG8(0x36d2), 0x00 }, + { CCI_REG8(0x36d3), 0x08 }, + { CCI_REG8(0x36d4), 0x10 }, + { CCI_REG8(0x36d5), 0x10 }, + { CCI_REG8(0x36d6), 0x00 }, + { CCI_REG8(0x36d7), 0x08 }, + { CCI_REG8(0x36d8), 0x10 }, + { CCI_REG8(0x36d9), 0x10 }, + { CCI_REG8(0x3701), 0x1d }, + { CCI_REG8(0x3703), 0x2a }, + { CCI_REG8(0x3704), 0x05 }, + { CCI_REG8(0x3709), 0x57 }, + { CCI_REG8(0x370b), 0x63 }, + { CCI_REG8(0x3706), 0x28 }, + { CCI_REG8(0x370a), 0x00 }, + { CCI_REG8(0x370b), 0x63 }, + { CCI_REG8(0x370e), 0x0c }, + { CCI_REG8(0x370f), 0x1c }, + { CCI_REG8(0x3710), 0x00 }, + { CCI_REG8(0x3713), 0x00 }, + { CCI_REG8(0x3714), 0x24 }, + { CCI_REG8(0x3716), 0x24 }, + { CCI_REG8(0x371a), 0x1e }, + { CCI_REG8(0x3724), 0x09 }, + { CCI_REG8(0x3725), 0xb2 }, + { CCI_REG8(0x372b), 0x54 }, + { CCI_REG8(0x3730), 0xe1 }, + { CCI_REG8(0x3735), 0x80 }, + { CCI_REG8(0x3739), 0x10 }, + { CCI_REG8(0x373f), 0xb0 }, + { CCI_REG8(0x3740), 0x28 }, + { CCI_REG8(0x3741), 0x21 }, + { CCI_REG8(0x3742), 0x21 }, + { CCI_REG8(0x3743), 0x21 }, + { CCI_REG8(0x3744), 0x63 }, + { CCI_REG8(0x3745), 0x5a }, + { CCI_REG8(0x3746), 0x5a }, + { CCI_REG8(0x3747), 0x5a }, + { CCI_REG8(0x3748), 0x00 }, + { CCI_REG8(0x3749), 0x00 }, + { CCI_REG8(0x374a), 0x00 }, + { CCI_REG8(0x374b), 0x00 }, + { CCI_REG8(0x3756), 0x00 }, + { CCI_REG8(0x3757), 0x0e }, + { CCI_REG8(0x375d), 0x84 }, + { CCI_REG8(0x3760), 0x11 }, + { CCI_REG8(0x3767), 0x08 }, + { CCI_REG8(0x376f), 0x42 }, + { CCI_REG8(0x3771), 0x00 }, + { CCI_REG8(0x3773), 0x01 }, + { CCI_REG8(0x3774), 0x02 }, + { CCI_REG8(0x3775), 0x12 }, + { CCI_REG8(0x3776), 0x02 }, + { CCI_REG8(0x377b), 0x40 }, + { CCI_REG8(0x377c), 0x00 }, + { CCI_REG8(0x377d), 0x0c }, + { CCI_REG8(0x3782), 0x02 }, + { CCI_REG8(0x3787), 0x24 }, + { CCI_REG8(0x378a), 0x01 }, + { CCI_REG8(0x378d), 0x00 }, + { CCI_REG8(0x3790), 0x1f }, + { CCI_REG8(0x3791), 0x58 }, + { CCI_REG8(0x3795), 0x24 }, + { CCI_REG8(0x3796), 0x01 }, + { CCI_REG8(0x3798), 0x40 }, + { CCI_REG8(0x379c), 0x00 }, + { CCI_REG8(0x379d), 0x00 }, + { CCI_REG8(0x379e), 0x00 }, + { CCI_REG8(0x379f), 0x01 }, + { CCI_REG8(0x37a1), 0x10 }, + { CCI_REG8(0x37a6), 0x00 }, + { CCI_REG8(0x37ab), 0x0e }, + { CCI_REG8(0x37ac), 0xa0 }, + { CCI_REG8(0x37be), 0x0a }, + { CCI_REG8(0x37bf), 0x05 }, + { CCI_REG8(0x37bb), 0x02 }, + { CCI_REG8(0x37bf), 0x05 }, + { CCI_REG8(0x37c2), 0x04 }, + { CCI_REG8(0x37c4), 0x11 }, + { CCI_REG8(0x37c5), 0x80 }, + { CCI_REG8(0x37c6), 0x14 }, + { CCI_REG8(0x37c7), 0x08 }, + { CCI_REG8(0x37c8), 0x42 }, + { CCI_REG8(0x37cd), 0x17 }, + { CCI_REG8(0x37ce), 0x01 }, + { CCI_REG8(0x37d8), 0x02 }, + { CCI_REG8(0x37d9), 0x08 }, + { CCI_REG8(0x37dc), 0x01 }, + { CCI_REG8(0x37e0), 0x0c }, + { CCI_REG8(0x37e1), 0x20 }, + { CCI_REG8(0x37e2), 0x10 }, + { CCI_REG8(0x37e3), 0x04 }, + { CCI_REG8(0x37e4), 0x28 }, + { CCI_REG8(0x37e5), 0x02 }, + { CCI_REG8(0x37ef), 0x00 }, + { CCI_REG8(0x37f4), 0x00 }, + { CCI_REG8(0x37f5), 0x00 }, + { CCI_REG8(0x37f6), 0x00 }, + { CCI_REG8(0x37f7), 0x00 }, + { CCI_REG8(0x3800), 0x01 }, + { CCI_REG8(0x3801), 0x30 }, + { CCI_REG8(0x3802), 0x00 }, + { CCI_REG8(0x3803), 0x00 }, + { CCI_REG8(0x3804), 0x0b }, + { CCI_REG8(0x3805), 0x5f }, + { CCI_REG8(0x3806), 0x07 }, + { CCI_REG8(0x3807), 0xa7 }, + { CCI_REG8(0x3808), 0x0a }, + { CCI_REG8(0x3809), 0x20 }, + { CCI_REG8(0x380a), 0x07 }, + { CCI_REG8(0x380b), 0x98 }, + { CCI_REG8(0x380c), 0x06 }, + { CCI_REG8(0x380d), 0xd0 }, + { CCI_REG8(0x3810), 0x00 }, + { CCI_REG8(0x3811), 0x08 }, + { CCI_REG8(0x3812), 0x00 }, + { CCI_REG8(0x3813), 0x08 }, + { CCI_REG8(0x3814), 0x01 }, + { CCI_REG8(0x3815), 0x01 }, + { CCI_REG8(0x3816), 0x01 }, + { CCI_REG8(0x3817), 0x01 }, + { CCI_REG8(0x3818), 0x00 }, + { CCI_REG8(0x3819), 0x00 }, + { CCI_REG8(0x381a), 0x00 }, + { CCI_REG8(0x381b), 0x01 }, + { CCI_REG8(0x3820), 0x88 }, + { CCI_REG8(0x3821), 0x00 }, + { CCI_REG8(0x3822), 0x12 }, + { CCI_REG8(0x3823), 0x08 }, + { CCI_REG8(0x3824), 0x00 }, + { CCI_REG8(0x3825), 0x20 }, + { CCI_REG8(0x3826), 0x00 }, + { CCI_REG8(0x3827), 0x08 }, + { CCI_REG8(0x3829), 0x03 }, + { CCI_REG8(0x382a), 0x00 }, + { CCI_REG8(0x382b), 0x00 }, + { CCI_REG8(0x3832), 0x08 }, + { CCI_REG8(0x3838), 0x00 }, + { CCI_REG8(0x3839), 0x00 }, + { CCI_REG8(0x383a), 0x00 }, + { CCI_REG8(0x383b), 0x00 }, + { CCI_REG8(0x383d), 0x01 }, + { CCI_REG8(0x383e), 0x00 }, + { CCI_REG8(0x383f), 0x00 }, + { CCI_REG8(0x3843), 0x00 }, + { CCI_REG8(0x3880), 0x16 }, + { CCI_REG8(0x3881), 0x00 }, + { CCI_REG8(0x3882), 0x08 }, + { CCI_REG8(0x389a), 0x00 }, + { CCI_REG8(0x389b), 0x00 }, + { CCI_REG8(0x38a2), 0x02 }, + { CCI_REG8(0x38a3), 0x02 }, + { CCI_REG8(0x38a4), 0x02 }, + { CCI_REG8(0x38a5), 0x02 }, + { CCI_REG8(0x38a7), 0x04 }, + { CCI_REG8(0x38b8), 0x02 }, + { CCI_REG8(0x3c80), 0x3e }, + { CCI_REG8(0x3c86), 0x01 }, + { CCI_REG8(0x3c87), 0x02 }, + { CCI_REG8(0x389c), 0x00 }, + { CCI_REG8(0x3ca2), 0x0c }, + { CCI_REG8(0x3d85), 0x1b }, + { CCI_REG8(0x3d8c), 0x01 }, + { CCI_REG8(0x3d8d), 0xe2 }, + { CCI_REG8(0x3f00), 0xcb }, + { CCI_REG8(0x3f03), 0x08 }, + { CCI_REG8(0x3f9e), 0x07 }, + { CCI_REG8(0x3f9f), 0x04 }, + { CCI_REG8(0x4000), 0xf3 }, + { CCI_REG8(0x4002), 0x00 }, + { CCI_REG8(0x4003), 0x40 }, + { CCI_REG8(0x4008), 0x02 }, + { CCI_REG8(0x4009), 0x0d }, + { CCI_REG8(0x400a), 0x01 }, + { CCI_REG8(0x400b), 0x00 }, + { CCI_REG8(0x4040), 0x00 }, + { CCI_REG8(0x4041), 0x07 }, + { CCI_REG8(0x4090), 0x14 }, + { CCI_REG8(0x40b0), 0x01 }, + { CCI_REG8(0x40b1), 0x01 }, + { CCI_REG8(0x40b2), 0x30 }, + { CCI_REG8(0x40b3), 0x04 }, + { CCI_REG8(0x40b4), 0xe8 }, + { CCI_REG8(0x40b5), 0x01 }, + { CCI_REG8(0x40b7), 0x07 }, + { CCI_REG8(0x40b8), 0xff }, + { CCI_REG8(0x40b9), 0x00 }, + { CCI_REG8(0x40ba), 0x00 }, + { CCI_REG8(0x4300), 0xff }, + { CCI_REG8(0x4301), 0x00 }, + { CCI_REG8(0x4302), 0x0f }, + { CCI_REG8(0x4303), 0x20 }, + { CCI_REG8(0x4304), 0x20 }, + { CCI_REG8(0x4305), 0x83 }, + { CCI_REG8(0x4306), 0x21 }, + { CCI_REG8(0x430d), 0x00 }, + { CCI_REG8(0x4505), 0xc4 }, + { CCI_REG8(0x4506), 0x00 }, + { CCI_REG8(0x4507), 0x60 }, + { CCI_REG8(0x4803), 0x00 }, + { CCI_REG8(0x4809), 0x8e }, + { CCI_REG8(0x480e), 0x00 }, + { CCI_REG8(0x4813), 0x00 }, + { CCI_REG8(0x4814), 0x2a }, + { CCI_REG8(0x481b), 0x40 }, + { CCI_REG8(0x481f), 0x30 }, + { CCI_REG8(0x4825), 0x34 }, + { CCI_REG8(0x4829), 0x64 }, + { CCI_REG8(0x4837), 0x12 }, + { CCI_REG8(0x484b), 0x07 }, + { CCI_REG8(0x4883), 0x36 }, + { CCI_REG8(0x4885), 0x03 }, + { CCI_REG8(0x488b), 0x00 }, + { CCI_REG8(0x4d06), 0x01 }, + { CCI_REG8(0x4e00), 0x2a }, + { CCI_REG8(0x4e0d), 0x00 }, + { CCI_REG8(0x5000), 0xf9 }, + { CCI_REG8(0x5001), 0x09 }, + { CCI_REG8(0x5004), 0x00 }, + { CCI_REG8(0x5005), 0x0e }, + { CCI_REG8(0x5036), 0x00 }, + { CCI_REG8(0x5080), 0x04 }, + { CCI_REG8(0x5082), 0x00 }, + { CCI_REG8(0x5180), 0x00 }, + { CCI_REG8(0x5181), 0x10 }, + { CCI_REG8(0x5182), 0x01 }, + { CCI_REG8(0x5183), 0xdf }, + { CCI_REG8(0x5184), 0x02 }, + { CCI_REG8(0x5185), 0x6c }, + { CCI_REG8(0x5189), 0x48 }, + { CCI_REG8(0x520a), 0x03 }, + { CCI_REG8(0x520b), 0x0f }, + { CCI_REG8(0x520c), 0x3f }, + { CCI_REG8(0x580b), 0x03 }, + { CCI_REG8(0x580d), 0x00 }, + { CCI_REG8(0x580f), 0x00 }, + { CCI_REG8(0x5820), 0x00 }, + { CCI_REG8(0x5821), 0x00 }, + { CCI_REG8(0x3222), 0x03 }, + { CCI_REG8(0x3208), 0x06 }, + { CCI_REG8(0x3701), 0x1d }, + { CCI_REG8(0x37ab), 0x01 }, + { CCI_REG8(0x3790), 0x21 }, + { CCI_REG8(0x38be), 0x00 }, + { CCI_REG8(0x3791), 0x5a }, + { CCI_REG8(0x37bf), 0x1c }, + { CCI_REG8(0x3610), 0x37 }, + { CCI_REG8(0x3208), 0x16 }, + { CCI_REG8(0x3208), 0x07 }, + { CCI_REG8(0x3701), 0x1d }, + { CCI_REG8(0x37ab), 0x0e }, + { CCI_REG8(0x3790), 0x21 }, + { CCI_REG8(0x38be), 0x00 }, + { CCI_REG8(0x3791), 0x5a }, + { CCI_REG8(0x37bf), 0x0a }, + { CCI_REG8(0x3610), 0x87 }, + { CCI_REG8(0x3208), 0x17 }, + { CCI_REG8(0x3208), 0x08 }, + { CCI_REG8(0x3701), 0x1d }, + { CCI_REG8(0x37ab), 0x0e }, + { CCI_REG8(0x3790), 0x21 }, + { CCI_REG8(0x38be), 0x00 }, + { CCI_REG8(0x3791), 0x5a }, + { CCI_REG8(0x37bf), 0x0a }, + { CCI_REG8(0x3610), 0x87 }, + { CCI_REG8(0x3208), 0x18 }, + { CCI_REG8(0x3208), 0x09 }, + { CCI_REG8(0x3701), 0x1d }, + { CCI_REG8(0x37ab), 0x0e }, + { CCI_REG8(0x3790), 0x28 }, + { CCI_REG8(0x38be), 0x00 }, + { CCI_REG8(0x3791), 0x63 }, + { CCI_REG8(0x37bf), 0x0a }, + { CCI_REG8(0x3610), 0x87 }, + { CCI_REG8(0x3208), 0x19 }, +}; + +struct os05b10 { + struct device *dev; + struct regmap *cci; + struct v4l2_subdev sd; + struct media_pad pad; + struct clk *xclk; + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(os05b10_supply_name)]; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler handler; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + + u32 link_freq_index; + u32 data_lanes; +}; + +struct os05b10_mode { + u32 width; + u32 height; + u32 vts; + u32 hts; + u32 exp; + u8 bpp; +}; + +static const struct os05b10_mode supported_modes_10bit[] = { + { + .width = 2592, + .height = 1944, + .vts = 2006, + .hts = 1744, + .exp = 1944, + .bpp = 10, + }, +}; + +static const s64 link_frequencies[] = { + OS05B10_LINK_FREQ_600MHZ, +}; + +static const u32 os05b10_mbus_codes[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static inline struct os05b10 *to_os05b10(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct os05b10, sd); +}; + +static int os05b10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct os05b10 *os05b10 = container_of_const(ctrl->handler, + struct os05b10, handler); + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + int vmax, ret; + + state = v4l2_subdev_get_locked_active_state(&os05b10->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Honour the VBLANK limits when setting exposure. */ + s64 max = fmt->height + ctrl->val - OS05B10_EXPOSURE_MARGIN; + + ret = __v4l2_ctrl_modify_range(os05b10->exposure, + os05b10->exposure->minimum, max, + os05b10->exposure->step, + os05b10->exposure->default_value); + if (ret) + return ret; + } + + if (pm_runtime_get_if_in_use(os05b10->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + vmax = fmt->height + ctrl->val; + ret = cci_write(os05b10->cci, OS05B10_REG_VTS, vmax, NULL); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(os05b10->cci, OS05B10_REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(os05b10->cci, OS05B10_REG_EXPOSURE, + ctrl->val, NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(os05b10->dev); + + return ret; +} + +static int os05b10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(os05b10_mbus_codes)) + return -EINVAL; + + code->code = os05b10_mbus_codes[code->index]; + + return 0; +} + +static int os05b10_set_framing_limits(struct os05b10 *os05b10, + const struct os05b10_mode *mode) +{ + u32 hblank, vblank, vblank_max, max_exp; + int ret; + + hblank = mode->hts - mode->width; + ret = __v4l2_ctrl_modify_range(os05b10->hblank, hblank, hblank, 1, + hblank); + if (ret) + return ret; + + vblank = mode->vts - mode->height; + vblank_max = OS05B10_VTS_MAX - mode->height; + ret = __v4l2_ctrl_modify_range(os05b10->vblank, 0, vblank_max, 1, + vblank); + if (ret) + return ret; + + max_exp = mode->vts - OS05B10_EXPOSURE_MARGIN; + return __v4l2_ctrl_modify_range(os05b10->exposure, + OS05B10_EXPOSURE_MIN, max_exp, + OS05B10_EXPOSURE_STEP, mode->exp); +} + +static int os05b10_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + const struct os05b10_mode *mode = &supported_modes_10bit[0]; + struct os05b10 *os05b10 = to_os05b10(sd); + struct v4l2_mbus_framefmt *format; + int ret; + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + format = v4l2_subdev_state_get_format(sd_state, 0); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = os05b10_set_framing_limits(os05b10, mode); + if (ret) + return ret; + } + + *format = fmt->format; + + return 0; +} + +static int os05b10_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = os05b10_native_area; + return 0; + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = os05b10_active_area; + return 0; + default: + return -EINVAL; + } +} + +static int os05b10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes_10bit)) + return -EINVAL; + + fse->min_width = supported_modes_10bit[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes_10bit[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int os05b10_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct os05b10 *os05b10 = to_os05b10(sd); + int ret; + + ret = pm_runtime_resume_and_get(os05b10->dev); + if (ret < 0) + return ret; + + /* Write common registers */ + ret = cci_multi_reg_write(os05b10->cci, os05b10_common_regs, + ARRAY_SIZE(os05b10_common_regs), NULL); + if (ret) { + dev_err(os05b10->dev, "failed to write common registers\n"); + goto err_rpm_put; + } + + /* Apply customized user controls */ + ret = __v4l2_ctrl_handler_setup(os05b10->sd.ctrl_handler); + if (ret) + goto err_rpm_put; + + /* Stream ON */ + ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE, + OS05B10_MODE_STREAMING, NULL); + if (ret) + goto err_rpm_put; + + return 0; + +err_rpm_put: + pm_runtime_put(os05b10->dev); + + return ret; +} + +static int os05b10_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct os05b10 *os05b10 = to_os05b10(sd); + int ret; + + ret = cci_write(os05b10->cci, OS05B10_REG_CTRL_MODE, + OS05B10_MODE_STANDBY, NULL); + if (ret) + dev_err(os05b10->dev, "failed to set stream off\n"); + + pm_runtime_put(os05b10->dev); + + return 0; +} + +static int os05b10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *format; + const struct os05b10_mode *mode; + + /* Initialize try_fmt */ + format = v4l2_subdev_state_get_format(state, 0); + + mode = &supported_modes_10bit[0]; + format->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + /* Update image pad formate */ + format->width = mode->width; + format->height = mode->height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static const struct v4l2_subdev_video_ops os05b10_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops os05b10_pad_ops = { + .enum_mbus_code = os05b10_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = os05b10_set_pad_format, + .get_selection = os05b10_get_selection, + .enum_frame_size = os05b10_enum_frame_size, + .enable_streams = os05b10_enable_streams, + .disable_streams = os05b10_disable_streams, +}; + +static const struct v4l2_subdev_internal_ops os05b10_internal_ops = { + .init_state = os05b10_init_state, +}; + +static const struct v4l2_subdev_ops os05b10_subdev_ops = { + .video = &os05b10_video_ops, + .pad = &os05b10_pad_ops, +}; + +static const struct v4l2_ctrl_ops os05b10_ctrl_ops = { + .s_ctrl = os05b10_set_ctrl, +}; + +static int os05b10_identify_module(struct os05b10 *os05b10) +{ + int ret; + u64 val; + + ret = cci_read(os05b10->cci, OS05B10_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(os05b10->dev, ret, + "failed to read chip id %x\n", + OS05B10_CHIP_ID); + + if (val != OS05B10_CHIP_ID) + return dev_err_probe(os05b10->dev, -ENODEV, + "chip id mismatch: %x!=%llx\n", + OS05B10_CHIP_ID, val); + + return 0; +} + +static int os05b10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct os05b10 *os05b10 = to_os05b10(sd); + unsigned long delay_us; + int ret; + + /* Enable power rails */ + ret = regulator_bulk_enable(ARRAY_SIZE(os05b10_supply_name), + os05b10->supplies); + if (ret) { + dev_err(os05b10->dev, "failed to enable regulators\n"); + return ret; + } + + /* Enable xclk */ + ret = clk_prepare_enable(os05b10->xclk); + if (ret) { + dev_err(os05b10->dev, "failed to enable clock\n"); + goto err_regulator_off; + } + + gpiod_set_value_cansleep(os05b10->reset_gpio, 0); + + /* Delay T1 */ + fsleep(5 * USEC_PER_MSEC); + + /* Delay T2 (8192 cycles before SCCB/I2C access) */ + delay_us = DIV_ROUND_UP(8192, OS05B10_XCLK_FREQ / 1000 / 1000); + usleep_range(delay_us, delay_us * 2); + + return 0; + +err_regulator_off: + regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name), + os05b10->supplies); + + return ret; +} + +static int os05b10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct os05b10 *os05b10 = to_os05b10(sd); + + gpiod_set_value_cansleep(os05b10->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(os05b10_supply_name), + os05b10->supplies); + clk_disable_unprepare(os05b10->xclk); + + return 0; +} + +static int os05b10_parse_endpoint(struct os05b10 *os05b10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + unsigned long link_freq_bitmap; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(os05b10->dev), 0, 0, 0); + if (!ep) { + dev_err(os05b10->dev, "Failed to get next endpoint\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(os05b10->dev, -EINVAL, + "only 4 data lanes are supported\n"); + goto error_out; + } + + os05b10->data_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + + ret = v4l2_link_freq_to_bitmap(os05b10->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_frequencies, + ARRAY_SIZE(link_frequencies), + &link_freq_bitmap); + if (ret) { + dev_err(os05b10->dev, "only 600MHz frequency is available\n"); + goto error_out; + } + + os05b10->link_freq_index = __ffs(link_freq_bitmap); + +error_out: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static u64 os05b10_pixel_rate(struct os05b10 *os05b10, + const struct os05b10_mode *mode) +{ + u64 link_freq = link_frequencies[os05b10->link_freq_index]; + u64 pixel_rate = div_u64(link_freq * 2 * os05b10->data_lanes, mode->bpp); + + dev_dbg(os05b10->dev, + "link_freq=%llu bpp=%u lanes=%u pixel_rate=%llu\n", + link_freq, mode->bpp, os05b10->data_lanes, pixel_rate); + + return pixel_rate; +} + +static int os05b10_init_controls(struct os05b10 *os05b10) +{ + const struct os05b10_mode *mode = &supported_modes_10bit[0]; + u64 hblank_def, vblank_def, exp_max, pixel_rate; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + int ret; + + ctrl_hdlr = &os05b10->handler; + v4l2_ctrl_handler_init(ctrl_hdlr, 8); + + pixel_rate = os05b10_pixel_rate(os05b10, mode); + v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, pixel_rate); + + os05b10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os05b10_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_frequencies) - 1, + os05b10->link_freq_index, + link_frequencies); + + if (os05b10->link_freq) + os05b10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + hblank_def = mode->hts - mode->width; + os05b10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, + hblank_def, hblank_def, + 1, hblank_def); + if (os05b10->hblank) + os05b10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts - mode->height; + os05b10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OS05B10_VTS_MAX - mode->height, + 1, vblank_def); + + exp_max = mode->vts - OS05B10_EXPOSURE_MARGIN; + os05b10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, + V4L2_CID_EXPOSURE, + OS05B10_EXPOSURE_MIN, + exp_max, OS05B10_EXPOSURE_STEP, + mode->exp); + + os05b10->gain = v4l2_ctrl_new_std(ctrl_hdlr, &os05b10_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + OS05B10_ANALOG_GAIN_MIN, + OS05B10_ANALOG_GAIN_MAX, + OS05B10_ANALOG_GAIN_STEP, + OS05B10_ANALOG_GAIN_DEFAULT); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(os05b10->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(os05b10->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &os05b10_ctrl_ops, + &props); + if (ret) + goto error; + + os05b10->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int os05b10_probe(struct i2c_client *client) +{ + struct os05b10 *os05b10; + unsigned int xclk_freq; + int ret; + + os05b10 = devm_kzalloc(&client->dev, sizeof(*os05b10), GFP_KERNEL); + if (!os05b10) + return -ENOMEM; + + os05b10->client = client; + os05b10->dev = &client->dev; + + v4l2_i2c_subdev_init(&os05b10->sd, client, &os05b10_subdev_ops); + + os05b10->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(os05b10->cci)) + return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->cci), + "failed to initialize CCI\n"); + + os05b10->xclk = devm_v4l2_sensor_clk_get(os05b10->dev, NULL); + if (IS_ERR(os05b10->xclk)) + return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->xclk), + "failed to get xclk\n"); + + xclk_freq = clk_get_rate(os05b10->xclk); + if (xclk_freq != OS05B10_XCLK_FREQ) + return dev_err_probe(os05b10->dev, -EINVAL, + "xclk frequency not supported: %d Hz\n", + xclk_freq); + + for (unsigned int i = 0; i < ARRAY_SIZE(os05b10_supply_name); i++) + os05b10->supplies[i].supply = os05b10_supply_name[i]; + + ret = devm_regulator_bulk_get(os05b10->dev, + ARRAY_SIZE(os05b10_supply_name), + os05b10->supplies); + if (ret) + return dev_err_probe(os05b10->dev, ret, + "failed to get regulators\n"); + + ret = os05b10_parse_endpoint(os05b10); + if (ret) + return dev_err_probe(os05b10->dev, ret, + "failed to parse endpoint configuration\n"); + + os05b10->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(os05b10->reset_gpio)) + return dev_err_probe(os05b10->dev, PTR_ERR(os05b10->reset_gpio), + "failed to get reset GPIO\n"); + + ret = os05b10_power_on(os05b10->dev); + if (ret) + return ret; + + ret = os05b10_identify_module(os05b10); + if (ret) + goto error_power_off; + + /* This needs the pm runtime to be registered. */ + ret = os05b10_init_controls(os05b10); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + os05b10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + os05b10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + os05b10->sd.internal_ops = &os05b10_internal_ops; + os05b10->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&os05b10->sd.entity, 1, &os05b10->pad); + if (ret) { + dev_err_probe(os05b10->dev, ret, + "failed to init entity pads\n"); + goto error_handler_free; + } + + os05b10->sd.state_lock = os05b10->handler.lock; + ret = v4l2_subdev_init_finalize(&os05b10->sd); + if (ret < 0) { + dev_err_probe(os05b10->dev, ret, "subdev init error\n"); + goto error_media_entity; + } + + pm_runtime_set_active(os05b10->dev); + pm_runtime_enable(os05b10->dev); + + ret = v4l2_async_register_subdev_sensor(&os05b10->sd); + if (ret < 0) { + dev_err_probe(os05b10->dev, ret, + "failed to register os05b10 sub-device\n"); + goto error_subdev_cleanup; + } + + pm_runtime_idle(os05b10->dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&os05b10->sd); + pm_runtime_disable(os05b10->dev); + pm_runtime_set_suspended(os05b10->dev); + +error_media_entity: + media_entity_cleanup(&os05b10->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler); + +error_power_off: + os05b10_power_off(os05b10->dev); + + return ret; +} + +static void os05b10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct os05b10 *os05b10 = to_os05b10(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&os05b10->sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(os05b10->sd.ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + os05b10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static DEFINE_RUNTIME_DEV_PM_OPS(os05b10_pm_ops, os05b10_power_off, + os05b10_power_on, NULL); + +static const struct of_device_id os05b10_id[] = { + { .compatible = "ovti,os05b10" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, os05b10_id); + +static struct i2c_driver os05b10_driver = { + .driver = { + .name = "os05b10", + .pm = pm_ptr(&os05b10_pm_ops), + .of_match_table = os05b10_id, + }, + .probe = os05b10_probe, + .remove = os05b10_remove, +}; + +module_i2c_driver(os05b10_driver); + +MODULE_DESCRIPTION("OS05B10 Camera Sensor Driver"); +MODULE_AUTHOR("Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>"); +MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c index 141cb6f75b55..8a29e5b4b6ba 100644 --- a/drivers/media/i2c/ov01a10.c +++ b/drivers/media/i2c/ov01a10.c @@ -7,105 +7,98 @@ #include <linux/acpi.h> #include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> #define OV01A10_LINK_FREQ_400MHZ 400000000ULL -#define OV01A10_SCLK 40000000LL +#define OV01A10_SCLK 80000000LL #define OV01A10_DATA_LANES 1 +#define OV01A10_MCLK 19200000 -#define OV01A10_REG_CHIP_ID 0x300a +#define OV01A10_REG_CHIP_ID CCI_REG24(0x300a) #define OV01A10_CHIP_ID 0x560141 -#define OV01A10_REG_MODE_SELECT 0x0100 +#define OV01A10_REG_MODE_SELECT CCI_REG8(0x0100) #define OV01A10_MODE_STANDBY 0x00 #define OV01A10_MODE_STREAMING 0x01 /* pixel array */ -#define OV01A10_PIXEL_ARRAY_WIDTH 1296 -#define OV01A10_PIXEL_ARRAY_HEIGHT 816 -#define OV01A10_ACITVE_WIDTH 1280 -#define OV01A10_ACITVE_HEIGHT 800 +#define OV01A10_NATIVE_WIDTH 1296 +#define OV01A10_NATIVE_HEIGHT 816 +#define OV01A10_DEFAULT_WIDTH 1280 +#define OV01A10_DEFAULT_HEIGHT 800 /* vertical and horizontal timings */ -#define OV01A10_REG_VTS 0x380e -#define OV01A10_VTS_DEF 0x0380 +#define OV01A10_VTS_DEF 0x0700 #define OV01A10_VTS_MIN 0x0380 #define OV01A10_VTS_MAX 0xffff #define OV01A10_HTS_DEF 1488 /* exposure controls */ -#define OV01A10_REG_EXPOSURE 0x3501 +#define OV01A10_REG_EXPOSURE CCI_REG16(0x3501) #define OV01A10_EXPOSURE_MIN 4 #define OV01A10_EXPOSURE_MAX_MARGIN 8 #define OV01A10_EXPOSURE_STEP 1 /* analog gain controls */ -#define OV01A10_REG_ANALOG_GAIN 0x3508 +#define OV01A10_REG_ANALOG_GAIN CCI_REG16(0x3508) #define OV01A10_ANAL_GAIN_MIN 0x100 -#define OV01A10_ANAL_GAIN_MAX 0xffff +#define OV01A10_ANAL_GAIN_MAX 0x3fff #define OV01A10_ANAL_GAIN_STEP 1 /* digital gain controls */ -#define OV01A10_REG_DIGITAL_GAIN_B 0x350a -#define OV01A10_REG_DIGITAL_GAIN_GB 0x3510 -#define OV01A10_REG_DIGITAL_GAIN_GR 0x3513 -#define OV01A10_REG_DIGITAL_GAIN_R 0x3516 +#define OV01A10_REG_DIGITAL_GAIN_B CCI_REG24(0x350a) +#define OV01A10_REG_DIGITAL_GAIN_GB CCI_REG24(0x3510) +#define OV01A10_REG_DIGITAL_GAIN_GR CCI_REG24(0x3513) +#define OV01A10_REG_DIGITAL_GAIN_R CCI_REG24(0x3516) #define OV01A10_DGTL_GAIN_MIN 0 -#define OV01A10_DGTL_GAIN_MAX 0x3ffff +#define OV01A10_DGTL_GAIN_MAX 0x3fff #define OV01A10_DGTL_GAIN_STEP 1 #define OV01A10_DGTL_GAIN_DEFAULT 1024 -/* test pattern control */ -#define OV01A10_REG_TEST_PATTERN 0x4503 -#define OV01A10_TEST_PATTERN_ENABLE BIT(7) -#define OV01A10_LINK_FREQ_400MHZ_INDEX 0 +/* timing control */ +#define OV01A10_REG_X_ADDR_START CCI_REG16(0x3800) +#define OV01A10_REG_Y_ADDR_START CCI_REG16(0x3802) +#define OV01A10_REG_X_ADDR_END CCI_REG16(0x3804) +#define OV01A10_REG_Y_ADDR_END CCI_REG16(0x3806) +#define OV01A10_REG_X_OUTPUT_SIZE CCI_REG16(0x3808) +#define OV01A10_REG_Y_OUTPUT_SIZE CCI_REG16(0x380a) +#define OV01A10_REG_HTS CCI_REG16(0x380c) /* in units of 2 pixels */ +#define OV01A10_REG_VTS CCI_REG16(0x380e) +#define OV01A10_REG_X_WIN CCI_REG16(0x3810) +#define OV01A10_REG_Y_WIN CCI_REG16(0x3812) /* flip and mirror control */ -#define OV01A10_REG_FORMAT1 0x3820 +#define OV01A10_REG_FORMAT1 CCI_REG8(0x3820) #define OV01A10_VFLIP_MASK BIT(4) #define OV01A10_HFLIP_MASK BIT(3) -/* window offset */ -#define OV01A10_REG_X_WIN 0x3811 -#define OV01A10_REG_Y_WIN 0x3813 - -struct ov01a10_reg { - u16 address; - u8 val; -}; - -struct ov01a10_reg_list { - u32 num_of_regs; - const struct ov01a10_reg *regs; -}; +/* test pattern control */ +#define OV01A10_REG_TEST_PATTERN CCI_REG8(0x4503) +#define OV01A10_TEST_PATTERN_ENABLE BIT(7) struct ov01a10_link_freq_config { - const struct ov01a10_reg_list reg_list; + const struct reg_sequence *regs; + int regs_len; }; -struct ov01a10_mode { - u32 width; - u32 height; - u32 hts; - u32 vts_def; - u32 vts_min; - u32 link_freq_index; - - const struct ov01a10_reg_list reg_list; -}; - -static const struct ov01a10_reg mipi_data_rate_720mbps[] = { +static const struct reg_sequence mipi_data_rate_720mbps[] = { {0x0103, 0x01}, {0x0302, 0x00}, {0x0303, 0x06}, {0x0304, 0x01}, - {0x0305, 0xe0}, + {0x0305, 0xf4}, {0x0306, 0x00}, {0x0308, 0x01}, {0x0309, 0x00}, @@ -116,15 +109,11 @@ static const struct ov01a10_reg mipi_data_rate_720mbps[] = { {0x0325, 0x68}, }; -static const struct ov01a10_reg sensor_1280x800_setting[] = { +static const struct reg_sequence ov01a10_global_setting[] = { {0x3002, 0xa1}, {0x301e, 0xf0}, {0x3022, 0x01}, - {0x3501, 0x03}, - {0x3502, 0x78}, {0x3504, 0x0c}, - {0x3508, 0x01}, - {0x3509, 0x00}, {0x3601, 0xc0}, {0x3603, 0x71}, {0x3610, 0x68}, @@ -168,31 +157,10 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = { {0x37e4, 0x04}, {0x37e5, 0x03}, {0x37e6, 0x04}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x05}, - {0x3805, 0x0f}, - {0x3806, 0x03}, - {0x3807, 0x2f}, - {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x03}, - {0x380b, 0x20}, - {0x380c, 0x02}, - {0x380d, 0xe8}, - {0x380e, 0x03}, - {0x380f, 0x80}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x08}, {0x3814, 0x01}, {0x3815, 0x01}, {0x3816, 0x01}, {0x3817, 0x01}, - {0x3820, 0xa0}, {0x3822, 0x13}, {0x3832, 0x28}, {0x3833, 0x10}, @@ -214,7 +182,6 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = { {0x4300, 0xff}, {0x4301, 0x00}, {0x4302, 0x0f}, - {0x4503, 0x00}, {0x4601, 0x50}, {0x4800, 0x64}, {0x481f, 0x34}, @@ -233,16 +200,14 @@ static const struct ov01a10_reg sensor_1280x800_setting[] = { {0x5200, 0x18}, {0x5004, 0x00}, {0x5080, 0x40}, - {0x0305, 0xf4}, {0x0325, 0xc2}, }; static const char * const ov01a10_test_pattern_menu[] = { "Disabled", "Color Bar", - "Top-Bottom Darker Color Bar", - "Right-Left Darker Color Bar", - "Color Bar type 4", + "Left-Right Darker Color Bar", + "Bottom-Top Darker Color Bar", }; static const s64 link_freq_menu_items[] = { @@ -250,30 +215,39 @@ static const s64 link_freq_menu_items[] = { }; static const struct ov01a10_link_freq_config link_freq_configs[] = { - [OV01A10_LINK_FREQ_400MHZ_INDEX] = { - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps), - .regs = mipi_data_rate_720mbps, - } + { + .regs = mipi_data_rate_720mbps, + .regs_len = ARRAY_SIZE(mipi_data_rate_720mbps), }, }; -static const struct ov01a10_mode supported_modes[] = { - { - .width = OV01A10_ACITVE_WIDTH, - .height = OV01A10_ACITVE_HEIGHT, - .hts = OV01A10_HTS_DEF, - .vts_def = OV01A10_VTS_DEF, - .vts_min = OV01A10_VTS_MIN, - .reg_list = { - .num_of_regs = ARRAY_SIZE(sensor_1280x800_setting), - .regs = sensor_1280x800_setting, - }, - .link_freq_index = OV01A10_LINK_FREQ_400MHZ_INDEX, - }, +static const struct v4l2_rect ov01a10_default_crop = { + .left = (OV01A10_NATIVE_WIDTH - OV01A10_DEFAULT_WIDTH) / 2, + .top = (OV01A10_NATIVE_HEIGHT - OV01A10_DEFAULT_HEIGHT) / 2, + .width = OV01A10_DEFAULT_WIDTH, + .height = OV01A10_DEFAULT_HEIGHT, +}; + +static const char * const ov01a10_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +struct ov01a10_sensor_cfg { + const char *model; + u32 bus_fmt; + int pattern_size; + int border_size; + u8 format1_base_val; + bool invert_hflip_shift; + bool invert_vflip_shift; }; struct ov01a10 { + struct device *dev; + struct regmap *regmap; + const struct ov01a10_sensor_cfg *cfg; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -284,8 +258,15 @@ struct ov01a10 { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; - const struct ov01a10_mode *cur_mode; + u32 link_freq_index; + + struct clk *clk; + struct gpio_desc *reset; + struct gpio_desc *powerdown; + struct regulator_bulk_data supplies[ARRAY_SIZE(ov01a10_supply_names)]; }; static inline struct ov01a10 *to_ov01a10(struct v4l2_subdev *subdev) @@ -293,183 +274,115 @@ static inline struct ov01a10 *to_ov01a10(struct v4l2_subdev *subdev) return container_of(subdev, struct ov01a10, sd); } -static int ov01a10_read_reg(struct ov01a10 *ov01a10, u16 reg, u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; - int ret = 0; - - 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 ov01a10_write_reg(struct ov01a10 *ov01a10, u16 reg, u16 len, u32 val) +static struct v4l2_mbus_framefmt *ov01a10_get_active_format(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); - u8 buf[6]; - int ret = 0; + struct v4l2_subdev_state *active_state = + v4l2_subdev_get_locked_active_state(&ov01a10->sd); - 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; + return v4l2_subdev_state_get_format(active_state, 0); } -static int ov01a10_write_reg_list(struct ov01a10 *ov01a10, - const struct ov01a10_reg_list *r_list) +static struct v4l2_rect *ov01a10_get_active_crop(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); - unsigned int i; - int ret = 0; + struct v4l2_subdev_state *active_state = + v4l2_subdev_get_locked_active_state(&ov01a10->sd); - for (i = 0; i < r_list->num_of_regs; i++) { - ret = ov01a10_write_reg(ov01a10, r_list->regs[i].address, 1, - r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "write reg 0x%4.4x err = %d\n", - r_list->regs[i].address, ret); - return ret; - } - } - - return 0; + return v4l2_subdev_state_get_crop(active_state, 0); } static int ov01a10_update_digital_gain(struct ov01a10 *ov01a10, u32 d_gain) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); u32 real = d_gain << 6; int ret = 0; - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_B, 3, real); - if (ret) { - dev_err(&client->dev, "failed to set DIGITAL_GAIN_B\n"); - return ret; - } - - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_GB, 3, real); - if (ret) { - dev_err(&client->dev, "failed to set DIGITAL_GAIN_GB\n"); - return ret; - } - - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_GR, 3, real); - if (ret) { - dev_err(&client->dev, "failed to set DIGITAL_GAIN_GR\n"); - return ret; - } - - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_DIGITAL_GAIN_R, 3, real); - if (ret) - dev_err(&client->dev, "failed to set DIGITAL_GAIN_R\n"); + cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_B, real, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_GB, real, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_GR, real, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_DIGITAL_GAIN_R, real, &ret); return ret; } static int ov01a10_test_pattern(struct ov01a10 *ov01a10, u32 pattern) { - if (!pattern) - return 0; + if (pattern) + pattern |= OV01A10_TEST_PATTERN_ENABLE; - pattern = (pattern - 1) | OV01A10_TEST_PATTERN_ENABLE; - - return ov01a10_write_reg(ov01a10, OV01A10_REG_TEST_PATTERN, 1, pattern); + return cci_write(ov01a10->regmap, OV01A10_REG_TEST_PATTERN, pattern, + NULL); } -/* for vflip and hflip, use 0x9 as window offset to keep the bayer */ -static int ov01a10_set_hflip(struct ov01a10 *ov01a10, u32 hflip) +static void ov01a10_set_format1(struct ov01a10 *ov01a10, int *ret) { - int ret; - u32 val, offset; + u8 val = ov01a10->cfg->format1_base_val; - offset = hflip ? 0x9 : 0x8; - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_X_WIN, 1, offset); - if (ret) - return ret; + /* hflip register bit is inverted */ + if (!ov01a10->hflip->val) + val |= FIELD_PREP(OV01A10_HFLIP_MASK, 0x1); - ret = ov01a10_read_reg(ov01a10, OV01A10_REG_FORMAT1, 1, &val); - if (ret) - return ret; + if (ov01a10->vflip->val) + val |= FIELD_PREP(OV01A10_VFLIP_MASK, 0x1); - val = hflip ? val | FIELD_PREP(OV01A10_HFLIP_MASK, 0x1) : - val & ~OV01A10_HFLIP_MASK; - - return ov01a10_write_reg(ov01a10, OV01A10_REG_FORMAT1, 1, val); + cci_write(ov01a10->regmap, OV01A10_REG_FORMAT1, val, ret); } -static int ov01a10_set_vflip(struct ov01a10 *ov01a10, u32 vflip) +static int ov01a10_set_hflip(struct ov01a10 *ov01a10, bool hflip) { - int ret; - u32 val, offset; + struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10); + const struct ov01a10_sensor_cfg *cfg = ov01a10->cfg; + u32 offset; + int ret = 0; - offset = vflip ? 0x9 : 0x8; - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_Y_WIN, 1, offset); - if (ret) - return ret; + offset = crop->left; + if ((hflip ^ cfg->invert_hflip_shift) && cfg->border_size) + offset++; - ret = ov01a10_read_reg(ov01a10, OV01A10_REG_FORMAT1, 1, &val); - if (ret) - return ret; + cci_write(ov01a10->regmap, OV01A10_REG_X_WIN, offset, &ret); + ov01a10_set_format1(ov01a10, &ret); + + return ret; +} + +static int ov01a10_set_vflip(struct ov01a10 *ov01a10, bool vflip) +{ + struct v4l2_rect *crop = ov01a10_get_active_crop(ov01a10); + const struct ov01a10_sensor_cfg *cfg = ov01a10->cfg; + u32 offset; + int ret = 0; + + offset = crop->top; + if ((vflip ^ cfg->invert_vflip_shift) && cfg->border_size) + offset++; - val = vflip ? val | FIELD_PREP(OV01A10_VFLIP_MASK, 0x1) : - val & ~OV01A10_VFLIP_MASK; + cci_write(ov01a10->regmap, OV01A10_REG_Y_WIN, offset, &ret); + ov01a10_set_format1(ov01a10, &ret); - return ov01a10_write_reg(ov01a10, OV01A10_REG_FORMAT1, 1, val); + return ret; } static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov01a10 *ov01a10 = container_of(ctrl->handler, struct ov01a10, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); + struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10); s64 exposure_max; int ret = 0; if (ctrl->id == V4L2_CID_VBLANK) { - exposure_max = ov01a10->cur_mode->height + ctrl->val - - OV01A10_EXPOSURE_MAX_MARGIN; + exposure_max = fmt->height + ctrl->val - + OV01A10_EXPOSURE_MAX_MARGIN; __v4l2_ctrl_modify_range(ov01a10->exposure, - ov01a10->exposure->minimum, - exposure_max, ov01a10->exposure->step, - exposure_max); + OV01A10_EXPOSURE_MIN, exposure_max, + OV01A10_EXPOSURE_STEP, exposure_max); } - if (!pm_runtime_get_if_in_use(&client->dev)) + if (!pm_runtime_get_if_in_use(ov01a10->dev)) return 0; switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_ANALOG_GAIN, 2, - ctrl->val); + ret = cci_write(ov01a10->regmap, OV01A10_REG_ANALOG_GAIN, + ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: @@ -477,13 +390,13 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_EXPOSURE, 2, - ctrl->val); + ret = cci_write(ov01a10->regmap, OV01A10_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_VBLANK: - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_VTS, 2, - ov01a10->cur_mode->height + ctrl->val); + ret = cci_write(ov01a10->regmap, OV01A10_REG_VTS, + fmt->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: @@ -503,7 +416,7 @@ static int ov01a10_set_ctrl(struct v4l2_ctrl *ctrl) break; } - pm_runtime_put(&client->dev); + pm_runtime_put(ov01a10->dev); return ret; } @@ -514,16 +427,13 @@ static const struct v4l2_ctrl_ops ov01a10_ctrl_ops = { static int ov01a10_init_controls(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); struct v4l2_fwnode_device_properties props; u32 vblank_min, vblank_max, vblank_default; struct v4l2_ctrl_handler *ctrl_hdlr; - const struct ov01a10_mode *cur_mode; s64 exposure_max, h_blank; int ret = 0; - int size; - ret = v4l2_fwnode_device_parse(&client->dev, &props); + ret = v4l2_fwnode_device_parse(ov01a10->dev, &props); if (ret) return ret; @@ -532,34 +442,27 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10) if (ret) return ret; - cur_mode = ov01a10->cur_mode; - size = ARRAY_SIZE(link_freq_menu_items); - ov01a10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_LINK_FREQ, - size - 1, 0, + ov01a10->link_freq_index, 0, link_freq_menu_items); - if (ov01a10->link_freq) - ov01a10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; ov01a10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0, OV01A10_SCLK, 1, OV01A10_SCLK); - vblank_min = cur_mode->vts_min - cur_mode->height; - vblank_max = OV01A10_VTS_MAX - cur_mode->height; - vblank_default = cur_mode->vts_def - cur_mode->height; + vblank_min = OV01A10_VTS_MIN - OV01A10_DEFAULT_HEIGHT; + vblank_max = OV01A10_VTS_MAX - OV01A10_DEFAULT_HEIGHT; + vblank_default = OV01A10_VTS_DEF - OV01A10_DEFAULT_HEIGHT; ov01a10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_VBLANK, vblank_min, vblank_max, 1, vblank_default); - h_blank = cur_mode->hts - cur_mode->width; + h_blank = OV01A10_HTS_DEF - OV01A10_DEFAULT_WIDTH; ov01a10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_HBLANK, h_blank, h_blank, 1, h_blank); - if (ov01a10->hblank) - ov01a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, OV01A10_ANAL_GAIN_MIN, OV01A10_ANAL_GAIN_MAX, @@ -568,7 +471,7 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10) OV01A10_DGTL_GAIN_MIN, OV01A10_DGTL_GAIN_MAX, OV01A10_DGTL_GAIN_STEP, OV01A10_DGTL_GAIN_DEFAULT); - exposure_max = cur_mode->vts_def - OV01A10_EXPOSURE_MAX_MARGIN; + exposure_max = OV01A10_VTS_DEF - OV01A10_EXPOSURE_MAX_MARGIN; ov01a10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_EXPOSURE, OV01A10_EXPOSURE_MIN, @@ -581,10 +484,10 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10) ARRAY_SIZE(ov01a10_test_pattern_menu) - 1, 0, 0, ov01a10_test_pattern_menu); - v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_HFLIP, - 0, 1, 1, 0); - v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, V4L2_CID_VFLIP, - 0, 1, 1, 0); + ov01a10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + ov01a10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov01a10_ctrl_ops, &props); @@ -596,6 +499,9 @@ static int ov01a10_init_controls(struct ov01a10 *ov01a10) goto fail; } + ov01a10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ov01a10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ov01a10->sd.ctrl_handler = ctrl_hdlr; return 0; @@ -605,83 +511,104 @@ fail: return ret; } -static void ov01a10_update_pad_format(const struct ov01a10_mode *mode, - struct v4l2_mbus_framefmt *fmt) +static void ov01a10_fill_format(struct ov01a10 *ov01a10, + struct v4l2_mbus_framefmt *fmt, + unsigned int width, unsigned int height) { - fmt->width = mode->width; - fmt->height = mode->height; - fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + memset(fmt, 0, sizeof(*fmt)); + fmt->width = width; + fmt->height = height; + fmt->code = ov01a10->cfg->bus_fmt; fmt->field = V4L2_FIELD_NONE; fmt->colorspace = V4L2_COLORSPACE_RAW; } -static int ov01a10_start_streaming(struct ov01a10 *ov01a10) +static int ov01a10_set_mode(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); - const struct ov01a10_reg_list *reg_list; - int link_freq_index; + struct v4l2_mbus_framefmt *fmt = ov01a10_get_active_format(ov01a10); int ret = 0; - link_freq_index = ov01a10->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = ov01a10_write_reg_list(ov01a10, reg_list); + cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_START, 0, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_START, 0, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_X_ADDR_END, + OV01A10_NATIVE_WIDTH - 1, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_Y_ADDR_END, + OV01A10_NATIVE_HEIGHT - 1, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_X_OUTPUT_SIZE, + fmt->width, &ret); + cci_write(ov01a10->regmap, OV01A10_REG_Y_OUTPUT_SIZE, + fmt->height, &ret); + /* HTS register is in units of 2 pixels */ + cci_write(ov01a10->regmap, OV01A10_REG_HTS, + OV01A10_HTS_DEF / 2, &ret); + /* OV01A10_REG_VTS is set by vblank control */ + /* OV01A10_REG_X_WIN is set by hlip control */ + /* OV01A10_REG_Y_WIN is set by vflip control */ + + return ret; +} + +static int ov01a10_start_streaming(struct ov01a10 *ov01a10) +{ + const struct ov01a10_link_freq_config *freq_cfg; + int ret; + + freq_cfg = &link_freq_configs[ov01a10->link_freq_index]; + ret = regmap_multi_reg_write(ov01a10->regmap, freq_cfg->regs, + freq_cfg->regs_len); if (ret) { - dev_err(&client->dev, "failed to set plls\n"); + dev_err(ov01a10->dev, "failed to set plls\n"); return ret; } - reg_list = &ov01a10->cur_mode->reg_list; - ret = ov01a10_write_reg_list(ov01a10, reg_list); + ret = regmap_multi_reg_write(ov01a10->regmap, ov01a10_global_setting, + ARRAY_SIZE(ov01a10_global_setting)); if (ret) { - dev_err(&client->dev, "failed to set mode\n"); + dev_err(ov01a10->dev, "failed to initialize sensor\n"); return ret; } - ret = __v4l2_ctrl_handler_setup(ov01a10->sd.ctrl_handler); - if (ret) + ret = ov01a10_set_mode(ov01a10); + if (ret) { + dev_err(ov01a10->dev, "failed to set mode\n"); return ret; + } - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_MODE_SELECT, 1, - OV01A10_MODE_STREAMING); + ret = __v4l2_ctrl_handler_setup(ov01a10->sd.ctrl_handler); if (ret) - dev_err(&client->dev, "failed to start streaming\n"); + return ret; - return ret; + return cci_write(ov01a10->regmap, OV01A10_REG_MODE_SELECT, + OV01A10_MODE_STREAMING, NULL); } static void ov01a10_stop_streaming(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); - int ret = 0; - - ret = ov01a10_write_reg(ov01a10, OV01A10_REG_MODE_SELECT, 1, - OV01A10_MODE_STANDBY); - if (ret) - dev_err(&client->dev, "failed to stop streaming\n"); + cci_write(ov01a10->regmap, OV01A10_REG_MODE_SELECT, + OV01A10_MODE_STANDBY, NULL); } static int ov01a10_set_stream(struct v4l2_subdev *sd, int enable) { struct ov01a10 *ov01a10 = to_ov01a10(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_subdev_state *state; int ret = 0; state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); + ret = pm_runtime_resume_and_get(ov01a10->dev); if (ret < 0) goto unlock; ret = ov01a10_start_streaming(ov01a10); if (ret) { - pm_runtime_put(&client->dev); + pm_runtime_put(ov01a10->dev); goto unlock; } } else { ov01a10_stop_streaming(ov01a10); - pm_runtime_put(&client->dev); + pm_runtime_put(ov01a10->dev); } unlock: @@ -690,56 +617,66 @@ unlock: return ret; } +static void ov01a10_update_blank_ctrls(struct ov01a10 *ov01a10, + unsigned int width, unsigned int height) +{ + s32 hblank, vblank_def; + + vblank_def = OV01A10_VTS_DEF - height; + __v4l2_ctrl_modify_range(ov01a10->vblank, + OV01A10_VTS_MIN - height, + OV01A10_VTS_MAX - height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def); + + hblank = OV01A10_HTS_DEF - width; + __v4l2_ctrl_modify_range(ov01a10->hblank, hblank, hblank, 1, hblank); +} + static int ov01a10_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad); struct ov01a10 *ov01a10 = to_ov01a10(sd); - const struct ov01a10_mode *mode; - struct v4l2_mbus_framefmt *format; - s32 vblank_def, h_blank; - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), width, - height, fmt->format.width, - fmt->format.height); - - ov01a10_update_pad_format(mode, &fmt->format); - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ov01a10->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov01a10->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(ov01a10->pixel_rate, OV01A10_SCLK); - - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(ov01a10->vblank, - mode->vts_min - mode->height, - OV01A10_VTS_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov01a10->vblank, vblank_def); - h_blank = mode->hts - mode->width; - __v4l2_ctrl_modify_range(ov01a10->hblank, h_blank, h_blank, 1, - h_blank); + const int pattern_size = ov01a10->cfg->pattern_size; + const int border_size = ov01a10->cfg->border_size; + unsigned int width, height; + + width = clamp_val(ALIGN(fmt->format.width, pattern_size), + pattern_size, + OV01A10_NATIVE_WIDTH - 2 * border_size); + height = clamp_val(ALIGN(fmt->format.height, pattern_size), + pattern_size, + OV01A10_NATIVE_HEIGHT - 2 * border_size); + + /* Center image for userspace which does not set the crop first */ + if (width != crop->width || height != crop->height) { + crop->left = ALIGN((OV01A10_NATIVE_WIDTH - width) / 2, + pattern_size); + crop->top = ALIGN((OV01A10_NATIVE_HEIGHT - height) / 2, + pattern_size); + crop->width = width; + crop->height = height; } - format = v4l2_subdev_state_get_format(sd_state, fmt->stream); - *format = fmt->format; + ov01a10_fill_format(ov01a10, &fmt->format, width, height); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ov01a10_update_blank_ctrls(ov01a10, width, height); return 0; } static int ov01a10_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) + struct v4l2_subdev_state *sd_state) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_TRY, - .format = { - .width = OV01A10_ACITVE_WIDTH, - .height = OV01A10_ACITVE_HEIGHT, - }, - }; + struct ov01a10 *ov01a10 = to_ov01a10(sd); - ov01a10_set_format(sd, state, &fmt); + *v4l2_subdev_state_get_crop(sd_state, 0) = ov01a10_default_crop; + ov01a10_fill_format(ov01a10, v4l2_subdev_state_get_format(sd_state, 0), + OV01A10_DEFAULT_WIDTH, OV01A10_DEFAULT_HEIGHT); return 0; } @@ -748,10 +685,12 @@ static int ov01a10_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { + struct ov01a10 *ov01a10 = to_ov01a10(sd); + if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = ov01a10->cfg->bus_fmt; return 0; } @@ -760,14 +699,17 @@ static int ov01a10_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { - if (fse->index >= ARRAY_SIZE(supported_modes) || - fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + struct ov01a10 *ov01a10 = to_ov01a10(sd); + const int pattern_size = ov01a10->cfg->pattern_size; + const int border_size = ov01a10->cfg->border_size; + + if (fse->index) 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; + fse->min_width = pattern_size; + fse->max_width = OV01A10_NATIVE_WIDTH - 2 * border_size; + fse->min_height = pattern_size; + fse->max_height = OV01A10_NATIVE_HEIGHT - 2 * border_size; return 0; } @@ -776,31 +718,80 @@ static int ov01a10_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_selection *sel) { - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; + struct ov01a10 *ov01a10 = to_ov01a10(sd); + const int border_size = ov01a10->cfg->border_size; switch (sel->target) { - case V4L2_SEL_TGT_NATIVE_SIZE: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = OV01A10_PIXEL_ARRAY_WIDTH; - sel->r.height = OV01A10_PIXEL_ARRAY_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); + return 0; case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = (OV01A10_PIXEL_ARRAY_HEIGHT - - OV01A10_ACITVE_HEIGHT) / 2; - sel->r.left = (OV01A10_PIXEL_ARRAY_WIDTH - - OV01A10_ACITVE_WIDTH) / 2; - sel->r.width = OV01A10_ACITVE_WIDTH; - sel->r.height = OV01A10_ACITVE_HEIGHT; + sel->r = ov01a10_default_crop; + return 0; + case V4L2_SEL_TGT_CROP_BOUNDS: + /* Keep a border for hvflip shift to preserve bayer-pattern */ + sel->r.left = border_size; + sel->r.top = border_size; + sel->r.width = OV01A10_NATIVE_WIDTH - 2 * border_size; + sel->r.height = OV01A10_NATIVE_HEIGHT - 2 * border_size; + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = OV01A10_NATIVE_WIDTH; + sel->r.height = OV01A10_NATIVE_HEIGHT; return 0; } return -EINVAL; } +static int ov01a10_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov01a10 *ov01a10 = to_ov01a10(sd); + const int pattern_size = ov01a10->cfg->pattern_size; + const int border_size = ov01a10->cfg->border_size; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + struct v4l2_rect rect; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* + * Clamp the boundaries of the crop rectangle to the size of the sensor + * pixel array. Align to pattern-size to ensure pattern isn't disrupted. + */ + rect.left = clamp_val(ALIGN(sel->r.left, pattern_size), border_size, + OV01A10_NATIVE_WIDTH - 2 * border_size); + rect.top = clamp_val(ALIGN(sel->r.top, pattern_size), border_size, + OV01A10_NATIVE_HEIGHT - 2 * border_size); + rect.width = clamp_val(ALIGN(sel->r.width, pattern_size), pattern_size, + OV01A10_NATIVE_WIDTH - rect.left - border_size); + rect.height = clamp_val(ALIGN(sel->r.height, pattern_size), pattern_size, + OV01A10_NATIVE_HEIGHT - rect.top - border_size); + + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); + + /* Reset the output size if the crop rectangle size has changed */ + if (rect.width != crop->width || rect.height != crop->height) { + format = v4l2_subdev_state_get_format(sd_state, sel->pad); + format->width = rect.width; + format->height = rect.height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ov01a10_update_blank_ctrls(ov01a10, rect.width, + rect.height); + } + + *crop = rect; + sel->r = rect; + + return 0; +} + static const struct v4l2_subdev_core_ops ov01a10_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, }; @@ -813,6 +804,7 @@ static const struct v4l2_subdev_pad_ops ov01a10_pad_ops = { .set_fmt = ov01a10_set_format, .get_fmt = v4l2_subdev_get_fmt, .get_selection = ov01a10_get_selection, + .set_selection = ov01a10_set_selection, .enum_mbus_code = ov01a10_enum_mbus_code, .enum_frame_size = ov01a10_enum_frame_size, }; @@ -831,18 +823,103 @@ static const struct media_entity_operations ov01a10_subdev_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +static int ov01a10_get_pm_resources(struct ov01a10 *ov01a10) +{ + unsigned long freq; + int i, ret; + + ov01a10->clk = devm_v4l2_sensor_clk_get(ov01a10->dev, NULL); + if (IS_ERR(ov01a10->clk)) + return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->clk), + "getting clock\n"); + + freq = clk_get_rate(ov01a10->clk); + if (freq != OV01A10_MCLK) + return dev_err_probe(ov01a10->dev, -EINVAL, + "external clock %lu is not supported", + freq); + + ov01a10->reset = devm_gpiod_get_optional(ov01a10->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov01a10->reset)) + return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->reset), + "getting reset gpio\n"); + + ov01a10->powerdown = devm_gpiod_get_optional(ov01a10->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ov01a10->powerdown)) + return dev_err_probe(ov01a10->dev, PTR_ERR(ov01a10->powerdown), + "getting powerdown gpio\n"); + + for (i = 0; i < ARRAY_SIZE(ov01a10_supply_names); i++) + ov01a10->supplies[i].supply = ov01a10_supply_names[i]; + + ret = devm_regulator_bulk_get(ov01a10->dev, + ARRAY_SIZE(ov01a10_supply_names), + ov01a10->supplies); + if (ret) + return dev_err_probe(ov01a10->dev, ret, "getting regulators\n"); + + return 0; +} + +static int ov01a10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a10 *ov01a10 = to_ov01a10(sd); + int ret; + + ret = clk_prepare_enable(ov01a10->clk); + if (ret) { + dev_err(dev, "Error enabling clk: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ov01a10_supply_names), + ov01a10->supplies); + if (ret) { + dev_err(dev, "Error enabling regulators: %d\n", ret); + clk_disable_unprepare(ov01a10->clk); + return ret; + } + + if (ov01a10->reset || ov01a10->powerdown) { + /* Assert reset/powerdown for at least 2ms on back to back off-on */ + fsleep(2000); + gpiod_set_value_cansleep(ov01a10->powerdown, 0); + gpiod_set_value_cansleep(ov01a10->reset, 0); + fsleep(20000); + } + + return 0; +} + +static int ov01a10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov01a10 *ov01a10 = to_ov01a10(sd); + + gpiod_set_value_cansleep(ov01a10->reset, 1); + gpiod_set_value_cansleep(ov01a10->powerdown, 1); + + regulator_bulk_disable(ARRAY_SIZE(ov01a10_supply_names), + ov01a10->supplies); + + clk_disable_unprepare(ov01a10->clk); + return 0; +} + static int ov01a10_identify_module(struct ov01a10 *ov01a10) { - struct i2c_client *client = v4l2_get_subdevdata(&ov01a10->sd); int ret; - u32 val; + u64 val; - ret = ov01a10_read_reg(ov01a10, OV01A10_REG_CHIP_ID, 3, &val); + ret = cci_read(ov01a10->regmap, OV01A10_REG_CHIP_ID, &val, NULL); if (ret) return ret; if (val != OV01A10_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(ov01a10->dev, "chip id mismatch: %x!=%llx\n", OV01A10_CHIP_ID, val); return -EIO; } @@ -850,43 +927,114 @@ static int ov01a10_identify_module(struct ov01a10 *ov01a10) return 0; } +static int ov01a10_check_hwcfg(struct ov01a10 *ov01a10) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep, *fwnode = dev_fwnode(ov01a10->dev); + unsigned long link_freq_bitmap; + int ret; + + /* + * Sometimes the fwnode graph is initialized by the bridge driver, + * wait for this. + */ + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + if (!ep) + return dev_err_probe(ov01a10->dev, -EPROBE_DEFER, + "waiting for fwnode graph endpoint\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(ov01a10->dev, ret, "parsing endpoint\n"); + + ret = v4l2_link_freq_to_bitmap(ov01a10->dev, + bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + if (ret) + goto check_hwcfg_error; + + /* v4l2_link_freq_to_bitmap() guarantees at least 1 bit is set */ + ov01a10->link_freq_index = ffs(link_freq_bitmap) - 1; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV01A10_DATA_LANES) { + ret = dev_err_probe(ov01a10->dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto check_hwcfg_error; + } + +check_hwcfg_error: + v4l2_fwnode_endpoint_free(&bus_cfg); + return ret; +} + static void ov01a10_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) { + ov01a10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } } static int ov01a10_probe(struct i2c_client *client) { - struct device *dev = &client->dev; + const struct ov01a10_sensor_cfg *cfg; struct ov01a10 *ov01a10; - int ret = 0; + int ret; + + cfg = device_get_match_data(&client->dev); + if (!cfg) + return -EINVAL; - ov01a10 = devm_kzalloc(dev, sizeof(*ov01a10), GFP_KERNEL); + ov01a10 = devm_kzalloc(&client->dev, sizeof(*ov01a10), GFP_KERNEL); if (!ov01a10) return -ENOMEM; + ov01a10->dev = &client->dev; + ov01a10->cfg = cfg; + + ov01a10->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(ov01a10->regmap)) + return PTR_ERR(ov01a10->regmap); + v4l2_i2c_subdev_init(&ov01a10->sd, client, &ov01a10_subdev_ops); + /* Override driver->name with actual sensor model */ + v4l2_i2c_subdev_set_name(&ov01a10->sd, client, cfg->model, NULL); ov01a10->sd.internal_ops = &ov01a10_internal_ops; - ret = ov01a10_identify_module(ov01a10); + ret = ov01a10_check_hwcfg(ov01a10); + if (ret) + return ret; + + ret = ov01a10_get_pm_resources(ov01a10); + if (ret) + return ret; + + ret = ov01a10_power_on(&client->dev); if (ret) - return dev_err_probe(dev, ret, - "failed to find sensor\n"); + return ret; - ov01a10->cur_mode = &supported_modes[0]; + ret = ov01a10_identify_module(ov01a10); + if (ret) + goto err_power_off; ret = ov01a10_init_controls(ov01a10); - if (ret) { - dev_err(dev, "failed to init controls: %d\n", ret); - return ret; - } + if (ret) + goto err_power_off; ov01a10->sd.state_lock = ov01a10->ctrl_handler.lock; ov01a10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -895,36 +1043,31 @@ static int ov01a10_probe(struct i2c_client *client) ov01a10->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov01a10->sd.entity, 1, &ov01a10->pad); - if (ret) { - dev_err(dev, "Failed to init entity pads: %d\n", ret); + if (ret) goto err_handler_free; - } ret = v4l2_subdev_init_finalize(&ov01a10->sd); - if (ret) { - dev_err(dev, "Failed to allocate subdev state: %d\n", ret); + if (ret) goto err_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(dev); - pm_runtime_idle(dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); ret = v4l2_async_register_subdev_sensor(&ov01a10->sd); - if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); + if (ret) goto err_pm_disable; - } return 0; err_pm_disable: - pm_runtime_disable(dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); + v4l2_subdev_cleanup(&ov01a10->sd); err_media_entity_cleanup: media_entity_cleanup(&ov01a10->sd.entity); @@ -932,12 +1075,44 @@ err_media_entity_cleanup: err_handler_free: v4l2_ctrl_handler_free(ov01a10->sd.ctrl_handler); +err_power_off: + ov01a10_power_off(&client->dev); + return ret; } +static DEFINE_RUNTIME_DEV_PM_OPS(ov01a10_pm_ops, ov01a10_power_off, + ov01a10_power_on, NULL); + #ifdef CONFIG_ACPI +/* + * The native ov01a10 bayer-pattern is GBRG, but there was a driver bug enabling + * hflip/mirroring by default resulting in BGGR. Because of this bug Intel's + * proprietary IPU6 userspace stack expects BGGR. So we report BGGR to not break + * userspace and fix things up by shifting the crop window-x coordinate by 1 + * when hflip is *disabled*. + */ +static const struct ov01a10_sensor_cfg ov01a10_cfg = { + .model = "ov01a10", + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .pattern_size = 2, /* 2x2 */ + .border_size = 2, + .format1_base_val = 0xa0, + .invert_hflip_shift = true, + .invert_vflip_shift = false, +}; + +static const struct ov01a10_sensor_cfg ov01a1b_cfg = { + .model = "ov01a1b", + .bus_fmt = MEDIA_BUS_FMT_Y10_1X10, + .pattern_size = 2, /* Keep coordinates aligned to a multiple of 2 */ + .border_size = 0, + .format1_base_val = 0xa0, +}; + static const struct acpi_device_id ov01a10_acpi_ids[] = { - { "OVTI01A0" }, + { "OVTI01A0", (uintptr_t)&ov01a10_cfg }, + { "OVTI01AB", (uintptr_t)&ov01a1b_cfg }, { } }; @@ -947,6 +1122,7 @@ MODULE_DEVICE_TABLE(acpi, ov01a10_acpi_ids); static struct i2c_driver ov01a10_i2c_driver = { .driver = { .name = "ov01a10", + .pm = pm_sleep_ptr(&ov01a10_pm_ops), .acpi_match_table = ACPI_PTR(ov01a10_acpi_ids), }, .probe = ov01a10_probe, diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c index b96600204141..dcb1add1fd9f 100644 --- a/drivers/media/i2c/ov2735.c +++ b/drivers/media/i2c/ov2735.c @@ -993,7 +993,7 @@ static int ov2735_probe(struct i2c_client *client) "failed to parse endpoint configuration\n"); ov2735->reset_gpio = devm_gpiod_get_optional(ov2735->dev, - "reset", GPIOD_OUT_LOW); + "reset", GPIOD_OUT_HIGH); if (IS_ERR(ov2735->reset_gpio)) return dev_err_probe(ov2735->dev, PTR_ERR(ov2735->reset_gpio), "failed to get reset GPIO\n"); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index e193fef4fced..6a46ef7233ac 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -20,8 +20,11 @@ #include <linux/module.h> #include <linux/of_graph.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/videodev2.h> +#include <media/v4l2-cci.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -41,25 +44,26 @@ #define MIPI_CTRL00_BUS_IDLE BIT(2) #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) -#define OV5647_SW_STANDBY 0x0100 -#define OV5647_SW_RESET 0x0103 -#define OV5647_REG_CHIPID_H 0x300a -#define OV5647_REG_CHIPID_L 0x300b -#define OV5640_REG_PAD_OUT 0x300d -#define OV5647_REG_EXP_HI 0x3500 -#define OV5647_REG_EXP_MID 0x3501 -#define OV5647_REG_EXP_LO 0x3502 -#define OV5647_REG_AEC_AGC 0x3503 -#define OV5647_REG_GAIN_HI 0x350a -#define OV5647_REG_GAIN_LO 0x350b -#define OV5647_REG_VTS_HI 0x380e -#define OV5647_REG_VTS_LO 0x380f -#define OV5647_REG_FRAME_OFF_NUMBER 0x4202 -#define OV5647_REG_MIPI_CTRL00 0x4800 -#define OV5647_REG_MIPI_CTRL14 0x4814 -#define OV5647_REG_AWB 0x5001 -#define OV5647_REG_ISPCTRL3D 0x503d - +#define OV5647_SW_STANDBY CCI_REG8(0x0100) +#define OV5647_SW_RESET CCI_REG8(0x0103) +#define OV5647_REG_CHIPID CCI_REG16(0x300a) +#define OV5640_REG_PAD_OUT CCI_REG8(0x300d) +#define OV5647_REG_EXPOSURE CCI_REG24(0x3500) +#define OV5647_REG_AEC_AGC CCI_REG8(0x3503) +#define OV5647_REG_GAIN CCI_REG16(0x350a) +#define OV5647_REG_HTS CCI_REG16(0x380c) +#define OV5647_REG_VTS CCI_REG16(0x380e) +#define OV5647_REG_TIMING_TC_V CCI_REG8(0x3820) +#define OV5647_REG_TIMING_TC_H CCI_REG8(0x3821) +#define OV5647_REG_FRAME_OFF_NUMBER CCI_REG8(0x4202) +#define OV5647_REG_MIPI_CTRL00 CCI_REG8(0x4800) +#define OV5647_REG_MIPI_CTRL14 CCI_REG8(0x4814) +#define OV5647_REG_MIPI_CTRL14_CHANNEL_MASK GENMASK(7, 6) +#define OV5647_REG_MIPI_CTRL14_CHANNEL_SHIFT 6 +#define OV5647_REG_AWB CCI_REG8(0x5001) +#define OV5647_REG_ISPCTRL3D CCI_REG8(0x503d) + +#define OV5647_CHIP_ID 0x5647 #define REG_TERM 0xfffe #define VAL_TERM 0xfe #define REG_DLY 0xffff @@ -69,39 +73,54 @@ #define OV5647_NATIVE_HEIGHT 1956U #define OV5647_PIXEL_ARRAY_LEFT 16U -#define OV5647_PIXEL_ARRAY_TOP 16U +#define OV5647_PIXEL_ARRAY_TOP 6U #define OV5647_PIXEL_ARRAY_WIDTH 2592U #define OV5647_PIXEL_ARRAY_HEIGHT 1944U -#define OV5647_VBLANK_MIN 4 +#define OV5647_VBLANK_MIN 24 #define OV5647_VTS_MAX 32767 +#define OV5647_HTS_MAX 0x1fff + #define OV5647_EXPOSURE_MIN 4 #define OV5647_EXPOSURE_STEP 1 #define OV5647_EXPOSURE_DEFAULT 1000 #define OV5647_EXPOSURE_MAX 65535 -struct regval_list { - u16 addr; - u8 data; +/* regulator supplies */ +static const char * const ov5647_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV5647_NUM_SUPPLIES ARRAY_SIZE(ov5647_supply_names) + +#define FREQ_INDEX_FULL 0 +#define FREQ_INDEX_VGA 1 +static const s64 ov5647_link_freqs[] = { + [FREQ_INDEX_FULL] = 218750000, + [FREQ_INDEX_VGA] = 145833300, }; struct ov5647_mode { struct v4l2_mbus_framefmt format; struct v4l2_rect crop; u64 pixel_rate; + unsigned int link_freq_index; int hts; int vts; - const struct regval_list *reg_list; + const struct reg_sequence *reg_list; unsigned int num_regs; }; struct ov5647 { struct v4l2_subdev sd; + struct regmap *regmap; struct media_pad pad; - struct mutex lock; struct clk *xclk; struct gpio_desc *pwdn; + struct regulator_bulk_data supplies[OV5647_NUM_SUPPLIES]; bool clock_ncont; struct v4l2_ctrl_handler ctrls; const struct ov5647_mode *mode; @@ -109,6 +128,9 @@ struct ov5647 { struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *link_freq; }; static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) @@ -130,34 +152,28 @@ static const u8 ov5647_test_pattern_val[] = { 0x81, /* Random Data */ }; -static const struct regval_list sensor_oe_disable_regs[] = { +static const struct reg_sequence sensor_oe_disable_regs[] = { {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, }; -static const struct regval_list sensor_oe_enable_regs[] = { +static const struct reg_sequence sensor_oe_enable_regs[] = { {0x3000, 0x0f}, {0x3001, 0xff}, {0x3002, 0xe4}, }; -static struct regval_list ov5647_2592x1944_10bpp[] = { +static const struct reg_sequence ov5647_common_regs[] = { {0x0100, 0x00}, {0x0103, 0x01}, {0x3034, 0x1a}, {0x3035, 0x21}, - {0x3036, 0x69}, {0x303c, 0x11}, {0x3106, 0xf5}, - {0x3821, 0x06}, - {0x3820, 0x00}, {0x3827, 0xec}, {0x370c, 0x03}, - {0x3612, 0x5b}, - {0x3618, 0x04}, {0x5000, 0x06}, - {0x5002, 0x41}, {0x5003, 0x08}, {0x5a00, 0x08}, {0x3000, 0x00}, @@ -172,26 +188,6 @@ static struct regval_list ov5647_2592x1944_10bpp[] = { {0x3a19, 0xf8}, {0x3c01, 0x80}, {0x3b07, 0x0c}, - {0x380c, 0x0b}, - {0x380d, 0x1c}, - {0x3814, 0x11}, - {0x3815, 0x11}, - {0x3708, 0x64}, - {0x3709, 0x12}, - {0x3808, 0x0a}, - {0x3809, 0x20}, - {0x380a, 0x07}, - {0x380b, 0x98}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0xa3}, - {0x3811, 0x10}, - {0x3813, 0x06}, {0x3630, 0x2e}, {0x3632, 0xe2}, {0x3633, 0x23}, @@ -211,11 +207,6 @@ static struct regval_list ov5647_2592x1944_10bpp[] = { {0x3f06, 0x10}, {0x3f01, 0x0a}, {0x3a08, 0x01}, - {0x3a09, 0x28}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0d, 0x08}, - {0x3a0e, 0x06}, {0x3a0f, 0x58}, {0x3a10, 0x50}, {0x3a1b, 0x58}, @@ -223,54 +214,57 @@ static struct regval_list ov5647_2592x1944_10bpp[] = { {0x3a11, 0x60}, {0x3a1f, 0x28}, {0x4001, 0x02}, - {0x4004, 0x04}, {0x4000, 0x09}, + {0x3503, 0x03}, +}; + +static const struct reg_sequence ov5647_2592x1944_10bpp[] = { + {0x3036, 0x69}, + {0x3821, 0x02}, + {0x3820, 0x00}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x5002, 0x41}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x3811, 0x10}, + {0x3813, 0x06}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x4004, 0x04}, {0x4837, 0x19}, {0x4800, 0x24}, - {0x3503, 0x03}, {0x0100, 0x01}, }; -static struct regval_list ov5647_1080p30_10bpp[] = { - {0x0100, 0x00}, - {0x0103, 0x01}, - {0x3034, 0x1a}, - {0x3035, 0x21}, - {0x3036, 0x62}, - {0x303c, 0x11}, - {0x3106, 0xf5}, - {0x3821, 0x06}, +static const struct reg_sequence ov5647_1080p30_10bpp[] = { + {0x3036, 0x69}, + {0x3821, 0x02}, {0x3820, 0x00}, - {0x3827, 0xec}, - {0x370c, 0x03}, {0x3612, 0x5b}, {0x3618, 0x04}, - {0x5000, 0x06}, {0x5002, 0x41}, - {0x5003, 0x08}, - {0x5a00, 0x08}, - {0x3000, 0x00}, - {0x3001, 0x00}, - {0x3002, 0x00}, - {0x3016, 0x08}, - {0x3017, 0xe0}, - {0x3018, 0x44}, - {0x301c, 0xf8}, - {0x301d, 0xf0}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3c01, 0x80}, - {0x3b07, 0x0c}, - {0x380c, 0x09}, - {0x380d, 0x70}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3708, 0x64}, {0x3709, 0x12}, - {0x3808, 0x07}, - {0x3809, 0x80}, - {0x380a, 0x04}, - {0x380b, 0x38}, {0x3800, 0x01}, {0x3801, 0x5c}, {0x3802, 0x01}, @@ -279,75 +273,30 @@ static struct regval_list ov5647_1080p30_10bpp[] = { {0x3805, 0xe3}, {0x3806, 0x05}, {0x3807, 0xf1}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, {0x3811, 0x04}, {0x3813, 0x02}, - {0x3630, 0x2e}, - {0x3632, 0xe2}, - {0x3633, 0x23}, - {0x3634, 0x44}, - {0x3636, 0x06}, - {0x3620, 0x64}, - {0x3621, 0xe0}, - {0x3600, 0x37}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x3731, 0x02}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3f05, 0x02}, - {0x3f06, 0x10}, - {0x3f01, 0x0a}, - {0x3a08, 0x01}, {0x3a09, 0x4b}, {0x3a0a, 0x01}, {0x3a0b, 0x13}, {0x3a0d, 0x04}, {0x3a0e, 0x03}, - {0x3a0f, 0x58}, - {0x3a10, 0x50}, - {0x3a1b, 0x58}, - {0x3a1e, 0x50}, - {0x3a11, 0x60}, - {0x3a1f, 0x28}, - {0x4001, 0x02}, {0x4004, 0x04}, - {0x4000, 0x09}, {0x4837, 0x19}, {0x4800, 0x34}, - {0x3503, 0x03}, {0x0100, 0x01}, }; -static struct regval_list ov5647_2x2binned_10bpp[] = { - {0x0100, 0x00}, - {0x0103, 0x01}, - {0x3034, 0x1a}, - {0x3035, 0x21}, - {0x3036, 0x62}, - {0x303c, 0x11}, - {0x3106, 0xf5}, - {0x3827, 0xec}, - {0x370c, 0x03}, +static const struct reg_sequence ov5647_2x2binned_10bpp[] = { + {0x3036, 0x69}, + {0x3821, 0x03}, + {0x3820, 0x41}, {0x3612, 0x59}, {0x3618, 0x00}, - {0x5000, 0x06}, {0x5002, 0x41}, - {0x5003, 0x08}, - {0x5a00, 0x08}, - {0x3000, 0x00}, - {0x3001, 0x00}, - {0x3002, 0x00}, - {0x3016, 0x08}, - {0x3017, 0xe0}, - {0x3018, 0x44}, - {0x301c, 0xf8}, - {0x301d, 0xf0}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3c01, 0x80}, - {0x3b07, 0x0c}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, @@ -360,50 +309,18 @@ static struct regval_list ov5647_2x2binned_10bpp[] = { {0x3809, 0x10}, {0x380a, 0x03}, {0x380b, 0xcc}, - {0x380c, 0x07}, - {0x380d, 0x68}, {0x3811, 0x0c}, {0x3813, 0x06}, {0x3814, 0x31}, {0x3815, 0x31}, - {0x3630, 0x2e}, - {0x3632, 0xe2}, - {0x3633, 0x23}, - {0x3634, 0x44}, - {0x3636, 0x06}, - {0x3620, 0x64}, - {0x3621, 0xe0}, - {0x3600, 0x37}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x3731, 0x02}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3f05, 0x02}, - {0x3f06, 0x10}, - {0x3f01, 0x0a}, - {0x3a08, 0x01}, {0x3a09, 0x28}, {0x3a0a, 0x00}, {0x3a0b, 0xf6}, {0x3a0d, 0x08}, {0x3a0e, 0x06}, - {0x3a0f, 0x58}, - {0x3a10, 0x50}, - {0x3a1b, 0x58}, - {0x3a1e, 0x50}, - {0x3a11, 0x60}, - {0x3a1f, 0x28}, - {0x4001, 0x02}, {0x4004, 0x04}, - {0x4000, 0x09}, {0x4837, 0x16}, {0x4800, 0x24}, - {0x3503, 0x03}, - {0x3820, 0x41}, - {0x3821, 0x07}, {0x350a, 0x00}, {0x350b, 0x10}, {0x3500, 0x00}, @@ -413,38 +330,16 @@ static struct regval_list ov5647_2x2binned_10bpp[] = { {0x0100, 0x01}, }; -static struct regval_list ov5647_640x480_10bpp[] = { - {0x0100, 0x00}, - {0x0103, 0x01}, - {0x3035, 0x11}, +static const struct reg_sequence ov5647_640x480_10bpp[] = { {0x3036, 0x46}, - {0x303c, 0x11}, - {0x3821, 0x07}, + {0x3821, 0x03}, {0x3820, 0x41}, - {0x370c, 0x03}, {0x3612, 0x59}, {0x3618, 0x00}, - {0x5000, 0x06}, - {0x5003, 0x08}, - {0x5a00, 0x08}, - {0x3000, 0xff}, - {0x3001, 0xff}, - {0x3002, 0xff}, - {0x301d, 0xf0}, - {0x3a18, 0x00}, - {0x3a19, 0xf8}, - {0x3c01, 0x80}, - {0x3b07, 0x0c}, - {0x380c, 0x07}, - {0x380d, 0x3c}, {0x3814, 0x35}, {0x3815, 0x35}, {0x3708, 0x64}, {0x3709, 0x52}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0xe0}, {0x3800, 0x00}, {0x3801, 0x10}, {0x3802, 0x00}, @@ -453,53 +348,17 @@ static struct regval_list ov5647_640x480_10bpp[] = { {0x3805, 0x2f}, {0x3806, 0x07}, {0x3807, 0x9f}, - {0x3630, 0x2e}, - {0x3632, 0xe2}, - {0x3633, 0x23}, - {0x3634, 0x44}, - {0x3620, 0x64}, - {0x3621, 0xe0}, - {0x3600, 0x37}, - {0x3704, 0xa0}, - {0x3703, 0x5a}, - {0x3715, 0x78}, - {0x3717, 0x01}, - {0x3731, 0x02}, - {0x370b, 0x60}, - {0x3705, 0x1a}, - {0x3f05, 0x02}, - {0x3f06, 0x10}, - {0x3f01, 0x0a}, - {0x3a08, 0x01}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xe0}, {0x3a09, 0x2e}, {0x3a0a, 0x00}, {0x3a0b, 0xfb}, {0x3a0d, 0x02}, {0x3a0e, 0x01}, - {0x3a0f, 0x58}, - {0x3a10, 0x50}, - {0x3a1b, 0x58}, - {0x3a1e, 0x50}, - {0x3a11, 0x60}, - {0x3a1f, 0x28}, - {0x4001, 0x02}, {0x4004, 0x02}, - {0x4000, 0x09}, - {0x3000, 0x00}, - {0x3001, 0x00}, - {0x3002, 0x00}, - {0x3017, 0xe0}, - {0x301c, 0xfc}, - {0x3636, 0x06}, - {0x3016, 0x08}, - {0x3827, 0xec}, - {0x3018, 0x44}, - {0x3035, 0x21}, - {0x3106, 0xf5}, - {0x3034, 0x1a}, - {0x301c, 0xf8}, {0x4800, 0x34}, - {0x3503, 0x03}, {0x0100, 0x01}, }; @@ -508,7 +367,7 @@ static const struct ov5647_mode ov5647_modes[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 2592, .height = 1944 @@ -520,6 +379,7 @@ static const struct ov5647_mode ov5647_modes[] = { .height = 1944 }, .pixel_rate = 87500000, + .link_freq_index = FREQ_INDEX_FULL, .hts = 2844, .vts = 0x7b0, .reg_list = ov5647_2592x1944_10bpp, @@ -529,7 +389,7 @@ static const struct ov5647_mode ov5647_modes[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 1920, .height = 1080 @@ -540,7 +400,8 @@ static const struct ov5647_mode ov5647_modes[] = { .width = 1928, .height = 1080, }, - .pixel_rate = 81666700, + .pixel_rate = 87500000, + .link_freq_index = FREQ_INDEX_FULL, .hts = 2416, .vts = 0x450, .reg_list = ov5647_1080p30_10bpp, @@ -550,7 +411,7 @@ static const struct ov5647_mode ov5647_modes[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 1296, .height = 972 @@ -561,7 +422,8 @@ static const struct ov5647_mode ov5647_modes[] = { .width = 2592, .height = 1944, }, - .pixel_rate = 81666700, + .pixel_rate = 87500000, + .link_freq_index = FREQ_INDEX_FULL, .hts = 1896, .vts = 0x59b, .reg_list = ov5647_2x2binned_10bpp, @@ -571,7 +433,7 @@ static const struct ov5647_mode ov5647_modes[] = { { .format = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .colorspace = V4L2_COLORSPACE_SRGB, + .colorspace = V4L2_COLORSPACE_RAW, .field = V4L2_FIELD_NONE, .width = 640, .height = 480 @@ -582,7 +444,8 @@ static const struct ov5647_mode ov5647_modes[] = { .width = 2560, .height = 1920, }, - .pixel_rate = 55000000, + .pixel_rate = 58333000, + .link_freq_index = FREQ_INDEX_VGA, .hts = 1852, .vts = 0x1f8, .reg_list = ov5647_640x480_10bpp, @@ -594,109 +457,36 @@ static const struct ov5647_mode ov5647_modes[] = { #define OV5647_DEFAULT_MODE (&ov5647_modes[3]) #define OV5647_DEFAULT_FORMAT (ov5647_modes[3].format) -static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) -{ - unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff}; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_master_send(client, data, 4); - if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret; - } - - return 0; -} - -static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) -{ - unsigned char data[3] = { reg >> 8, reg & 0xff, val}; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_master_send(client, data, 3); - if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret; - } - - return 0; -} - -static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2] = { reg >> 8, reg & 0xff }; - struct i2c_msg msg[2]; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = client->flags; - msg[0].buf = buf; - msg[0].len = sizeof(buf); - - msg[1].addr = client->addr; - msg[1].flags = client->flags | I2C_M_RD; - msg[1].buf = buf; - msg[1].len = 1; - - ret = i2c_transfer(client->adapter, msg, 2); - if (ret != 2) { - dev_err(&client->dev, "%s: i2c read error, reg: %x = %d\n", - __func__, reg, ret); - return ret >= 0 ? -EINVAL : ret; - } - - *val = buf[0]; - - return 0; -} - -static int ov5647_write_array(struct v4l2_subdev *sd, - const struct regval_list *regs, int array_size) -{ - int i, ret; - - for (i = 0; i < array_size; i++) { - ret = ov5647_write(sd, regs[i].addr, regs[i].data); - if (ret < 0) - return ret; - } - - return 0; -} - static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) { - u8 channel_id; - int ret; - - ret = ov5647_read(sd, OV5647_REG_MIPI_CTRL14, &channel_id); - if (ret < 0) - return ret; - - channel_id &= ~(3 << 6); + struct ov5647 *sensor = to_sensor(sd); - return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, - channel_id | (channel << 6)); + return cci_update_bits(sensor->regmap, OV5647_REG_MIPI_CTRL14, + OV5647_REG_MIPI_CTRL14_CHANNEL_MASK, + channel << OV5647_REG_MIPI_CTRL14_CHANNEL_SHIFT, + NULL); } static int ov5647_set_mode(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *sensor = to_sensor(sd); - u8 resetval, rdval; + u64 resetval, rdval; int ret; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + ret = cci_read(sensor->regmap, OV5647_SW_STANDBY, &rdval, NULL); if (ret < 0) return ret; - ret = ov5647_write_array(sd, sensor->mode->reg_list, - sensor->mode->num_regs); + ret = regmap_multi_reg_write(sensor->regmap, ov5647_common_regs, + ARRAY_SIZE(ov5647_common_regs)); + if (ret < 0) { + dev_err(&client->dev, "write sensor common regs error\n"); + return ret; + } + + ret = regmap_multi_reg_write(sensor->regmap, sensor->mode->reg_list, + sensor->mode->num_regs); if (ret < 0) { dev_err(&client->dev, "write sensor default regs error\n"); return ret; @@ -706,13 +496,13 @@ static int ov5647_set_mode(struct v4l2_subdev *sd) if (ret < 0) return ret; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); + ret = cci_read(sensor->regmap, OV5647_SW_STANDBY, &resetval, NULL); if (ret < 0) return ret; if (!(resetval & 0x01)) { dev_err(&client->dev, "Device was in SW standby"); - ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); + ret = cci_write(sensor->regmap, OV5647_SW_STANDBY, 0x01, NULL); if (ret < 0) return ret; } @@ -720,54 +510,71 @@ static int ov5647_set_mode(struct v4l2_subdev *sd) return 0; } -static int ov5647_stream_on(struct v4l2_subdev *sd) +static int ov5647_stream_stop(struct ov5647 *sensor) +{ + int ret = 0; + + cci_write(sensor->regmap, OV5647_REG_MIPI_CTRL00, + MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | + MIPI_CTRL00_CLOCK_LANE_DISABLE, &ret); + cci_write(sensor->regmap, OV5647_REG_FRAME_OFF_NUMBER, 0x0f, &ret); + cci_write(sensor->regmap, OV5640_REG_PAD_OUT, 0x01, &ret); + + return ret; +} + +static int ov5647_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *sensor = to_sensor(sd); u8 val = MIPI_CTRL00_BUS_IDLE; int ret; + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + ret = ov5647_set_mode(sd); if (ret) { dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret); - return ret; + goto done; } /* Apply customized values from user when stream starts. */ ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler); if (ret) - return ret; + goto done; if (sensor->clock_ncont) val |= MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_LINE_SYNC_ENABLE; - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, val); - if (ret < 0) - return ret; + cci_write(sensor->regmap, OV5647_REG_MIPI_CTRL00, val, &ret); + cci_write(sensor->regmap, OV5647_REG_FRAME_OFF_NUMBER, 0x00, &ret); + cci_write(sensor->regmap, OV5640_REG_PAD_OUT, 0x00, &ret); - ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x00); - if (ret < 0) - return ret; +done: + if (ret) + pm_runtime_put(&client->dev); - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); + return ret; } -static int ov5647_stream_off(struct v4l2_subdev *sd) +static int ov5647_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_sensor(sd); int ret; - ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, - MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | - MIPI_CTRL00_CLOCK_LANE_DISABLE); - if (ret < 0) - return ret; + ret = ov5647_stream_stop(sensor); - ret = ov5647_write(sd, OV5647_REG_FRAME_OFF_NUMBER, 0x0f); - if (ret < 0) - return ret; + pm_runtime_put(&client->dev); - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); + return ret; } static int ov5647_power_on(struct device *dev) @@ -777,26 +584,35 @@ static int ov5647_power_on(struct device *dev) dev_dbg(dev, "OV5647 power on\n"); - if (sensor->pwdn) { - gpiod_set_value_cansleep(sensor->pwdn, 0); - msleep(PWDN_ACTIVE_DELAY_MS); + ret = regulator_bulk_enable(OV5647_NUM_SUPPLIES, sensor->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; } + ret = gpiod_set_value_cansleep(sensor->pwdn, 0); + if (ret < 0) { + dev_err(dev, "pwdn gpio set value failed: %d\n", ret); + goto error_reg_disable; + } + + msleep(PWDN_ACTIVE_DELAY_MS); + ret = clk_prepare_enable(sensor->xclk); if (ret < 0) { dev_err(dev, "clk prepare enable failed\n"); goto error_pwdn; } - ret = ov5647_write_array(&sensor->sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); + ret = regmap_multi_reg_write(sensor->regmap, sensor_oe_enable_regs, + ARRAY_SIZE(sensor_oe_enable_regs)); if (ret < 0) { dev_err(dev, "write sensor_oe_enable_regs error\n"); goto error_clk_disable; } /* Stream off to coax lanes into LP-11 state. */ - ret = ov5647_stream_off(&sensor->sd); + ret = ov5647_stream_stop(sensor); if (ret < 0) { dev_err(dev, "camera not available, check power\n"); goto error_clk_disable; @@ -808,6 +624,8 @@ error_clk_disable: clk_disable_unprepare(sensor->xclk); error_pwdn: gpiod_set_value_cansleep(sensor->pwdn, 1); +error_reg_disable: + regulator_bulk_disable(OV5647_NUM_SUPPLIES, sensor->supplies); return ret; } @@ -815,28 +633,23 @@ error_pwdn: static int ov5647_power_off(struct device *dev) { struct ov5647 *sensor = dev_get_drvdata(dev); - u8 rdval; int ret; dev_dbg(dev, "OV5647 power off\n"); - ret = ov5647_write_array(&sensor->sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); + ret = regmap_multi_reg_write(sensor->regmap, sensor_oe_disable_regs, + ARRAY_SIZE(sensor_oe_disable_regs)); if (ret < 0) dev_dbg(dev, "disable oe failed\n"); /* Enter software standby */ - ret = ov5647_read(&sensor->sd, OV5647_SW_STANDBY, &rdval); - if (ret < 0) - dev_dbg(dev, "software standby failed\n"); - - rdval &= ~0x01; - ret = ov5647_write(&sensor->sd, OV5647_SW_STANDBY, rdval); + ret = cci_update_bits(sensor->regmap, OV5647_SW_STANDBY, 0x01, 0x00, NULL); if (ret < 0) dev_dbg(dev, "software standby failed\n"); clk_disable_unprepare(sensor->xclk); gpiod_set_value_cansleep(sensor->pwdn, 1); + regulator_bulk_disable(OV5647_NUM_SUPPLIES, sensor->supplies); return 0; } @@ -845,10 +658,11 @@ static int ov5647_power_off(struct device *dev) static int ov5647_sensor_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { + struct ov5647 *sensor = to_sensor(sd); int ret; - u8 val; + u64 val; - ret = ov5647_read(sd, reg->reg & 0xff, &val); + ret = cci_read(sensor->regmap, reg->reg & 0xff, &val, NULL); if (ret < 0) return ret; @@ -861,7 +675,9 @@ static int ov5647_sensor_get_register(struct v4l2_subdev *sd, static int ov5647_sensor_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); + struct ov5647 *sensor = to_sensor(sd); + + return cci_write(sensor->regmap, reg->reg & 0xff, reg->val & 0xff, NULL); } #endif @@ -890,49 +706,30 @@ __ov5647_get_pad_crop(struct ov5647 *ov5647, return NULL; } -static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) +static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +/* + * This function returns the mbus code for the current settings of the HFLIP + * and VFLIP controls. + */ +static u32 ov5647_get_mbus_code(struct v4l2_subdev *sd) { - struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *sensor = to_sensor(sd); - int ret; - - mutex_lock(&sensor->lock); - - if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) - goto error_unlock; - - ret = ov5647_stream_on(sd); - if (ret < 0) { - dev_err(&client->dev, "stream start failed: %d\n", ret); - goto error_pm; - } - } else { - ret = ov5647_stream_off(sd); - if (ret < 0) { - dev_err(&client->dev, "stream stop failed: %d\n", ret); - goto error_pm; - } - pm_runtime_put(&client->dev); - } - - mutex_unlock(&sensor->lock); - - return 0; - -error_pm: - pm_runtime_put(&client->dev); -error_unlock: - mutex_unlock(&sensor->lock); + /* The control values are only 0 or 1. */ + int index = sensor->hflip->val | (sensor->vflip->val << 1); + + static const u32 codes[4] = { + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10 + }; - return ret; + return codes[index]; } -static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { - .s_stream = ov5647_s_stream, -}; - static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) @@ -940,7 +737,7 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = ov5647_get_mbus_code(sd); return 0; } @@ -951,7 +748,7 @@ static int ov5647_enum_frame_size(struct v4l2_subdev *sd, { const struct v4l2_mbus_framefmt *fmt; - if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10 || + if (fse->code != ov5647_get_mbus_code(sd) || fse->index >= ARRAY_SIZE(ov5647_modes)) return -EINVAL; @@ -972,7 +769,6 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, const struct v4l2_mbus_framefmt *sensor_format; struct ov5647 *sensor = to_sensor(sd); - mutex_lock(&sensor->lock); switch (format->which) { case V4L2_SUBDEV_FORMAT_TRY: sensor_format = v4l2_subdev_state_get_format(sd_state, @@ -984,7 +780,8 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd, } *fmt = *sensor_format; - mutex_unlock(&sensor->lock); + /* The code we pass back must reflect the current h/vflips. */ + fmt->code = ov5647_get_mbus_code(sd); return 0; } @@ -1002,7 +799,6 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, fmt->width, fmt->height); /* Update the sensor mode and apply at it at streamon time. */ - mutex_lock(&sensor->lock); if (format->which == V4L2_SUBDEV_FORMAT_TRY) { *v4l2_subdev_state_get_format(sd_state, format->pad) = mode->format; } else { @@ -1014,7 +810,8 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, mode->pixel_rate, 1, mode->pixel_rate); hblank = mode->hts - mode->format.width; - __v4l2_ctrl_modify_range(sensor->hblank, hblank, hblank, 1, + __v4l2_ctrl_modify_range(sensor->hblank, hblank, + OV5647_HTS_MAX - mode->format.width, 1, hblank); vblank = mode->vts - mode->format.height; @@ -1029,9 +826,12 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd, sensor->exposure->minimum, exposure_max, sensor->exposure->step, exposure_def); + + __v4l2_ctrl_s_ctrl(sensor->link_freq, mode->link_freq_index); } *fmt = mode->format; - mutex_unlock(&sensor->lock); + /* The code we pass back must reflect the current h/vflips. */ + fmt->code = ov5647_get_mbus_code(sd); return 0; } @@ -1044,10 +844,8 @@ static int ov5647_get_selection(struct v4l2_subdev *sd, case V4L2_SEL_TGT_CROP: { struct ov5647 *sensor = to_sensor(sd); - mutex_lock(&sensor->lock); sel->r = *__ov5647_get_pad_crop(sensor, sd_state, sel->pad, sel->which); - mutex_unlock(&sensor->lock); return 0; } @@ -1079,6 +877,8 @@ static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .set_fmt = ov5647_set_pad_fmt, .get_fmt = ov5647_get_pad_fmt, .get_selection = ov5647_get_selection, + .enable_streams = ov5647_enable_streams, + .disable_streams = ov5647_disable_streams, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { @@ -1089,33 +889,27 @@ static const struct v4l2_subdev_ops ov5647_subdev_ops = { static int ov5647_detect(struct v4l2_subdev *sd) { + struct ov5647 *sensor = to_sensor(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 read; + u64 read; int ret; - ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); + ret = cci_write(sensor->regmap, OV5647_SW_RESET, 0x01, NULL); if (ret < 0) return ret; - ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read); + ret = cci_read(sensor->regmap, OV5647_REG_CHIPID, &read, NULL); if (ret < 0) - return ret; - - if (read != 0x56) { - dev_err(&client->dev, "ID High expected 0x56 got %x", read); - return -ENODEV; - } - - ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read); - if (ret < 0) - return ret; + return dev_err_probe(&client->dev, ret, + "failed to read chip id %x\n", + OV5647_REG_CHIPID); - if (read != 0x47) { - dev_err(&client->dev, "ID Low expected 0x47 got %x", read); + if (read != OV5647_CHIP_ID) { + dev_err(&client->dev, "Chip ID expected 0x5647 got 0x%llx", read); return -ENODEV; } - return ov5647_write(sd, OV5647_SW_RESET, 0x00); + return cci_write(sensor->regmap, OV5647_SW_RESET, 0x00, NULL); } static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) @@ -1138,74 +932,6 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { .open = ov5647_open, }; -static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val) -{ - return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0); -} - -static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val) -{ - int ret; - u8 reg; - - /* Non-zero turns on AGC by clearing bit 1.*/ - ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); - if (ret) - return ret; - - return ov5647_write(sd, OV5647_REG_AEC_AGC, val ? reg & ~BIT(1) - : reg | BIT(1)); -} - -static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val) -{ - int ret; - u8 reg; - - /* - * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by - * clearing bit 0. - */ - ret = ov5647_read(sd, OV5647_REG_AEC_AGC, ®); - if (ret) - return ret; - - return ov5647_write(sd, OV5647_REG_AEC_AGC, - val == V4L2_EXPOSURE_MANUAL ? reg | BIT(0) - : reg & ~BIT(0)); -} - -static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val) -{ - int ret; - - /* 10 bits of gain, 2 in the high register. */ - ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3); - if (ret) - return ret; - - return ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff); -} - -static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val) -{ - int ret; - - /* - * Sensor has 20 bits, but the bottom 4 bits are fractions of a line - * which we leave as zero (and don't receive in "val"). - */ - ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf); - if (ret) - return ret; - - ret = ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff); - if (ret) - return ret; - - return ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4); -} - static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov5647 *sensor = container_of(ctrl->handler, @@ -1214,9 +940,6 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; - - /* v4l2_ctrl_lock() locks our own mutex */ - if (ctrl->id == V4L2_CID_VBLANK) { int exposure_max, exposure_def; @@ -1239,33 +962,55 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: - ret = ov5647_s_auto_white_balance(sd, ctrl->val); + ret = cci_write(sensor->regmap, OV5647_REG_AWB, + ctrl->val ? 1 : 0, NULL); break; case V4L2_CID_AUTOGAIN: - ret = ov5647_s_autogain(sd, ctrl->val); + /* Non-zero turns on AGC by clearing bit 1.*/ + return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); break; case V4L2_CID_EXPOSURE_AUTO: - ret = ov5647_s_exposure_auto(sd, ctrl->val); + /* + * Everything except V4L2_EXPOSURE_MANUAL turns on AEC by + * clearing bit 0. + */ + return cci_update_bits(sensor->regmap, OV5647_REG_AEC_AGC, BIT(0), + ctrl->val == V4L2_EXPOSURE_MANUAL ? BIT(0) : 0, NULL); break; case V4L2_CID_ANALOGUE_GAIN: - ret = ov5647_s_analogue_gain(sd, ctrl->val); + /* 10 bits of gain, 2 in the high register. */ + return cci_write(sensor->regmap, OV5647_REG_GAIN, + ctrl->val & 0x3ff, NULL); break; case V4L2_CID_EXPOSURE: - ret = ov5647_s_exposure(sd, ctrl->val); + /* + * Sensor has 20 bits, but the bottom 4 bits are fractions of a line + * which we leave as zero (and don't receive in "val"). + */ + ret = cci_write(sensor->regmap, OV5647_REG_EXPOSURE, + ctrl->val << 4, NULL); break; case V4L2_CID_VBLANK: - ret = ov5647_write16(sd, OV5647_REG_VTS_HI, - sensor->mode->format.height + ctrl->val); + ret = cci_write(sensor->regmap, OV5647_REG_VTS, + sensor->mode->format.height + ctrl->val, NULL); + break; + case V4L2_CID_HBLANK: + ret = cci_write(sensor->regmap, OV5647_REG_HTS, + sensor->mode->format.width + ctrl->val, &ret); break; case V4L2_CID_TEST_PATTERN: - ret = ov5647_write(sd, OV5647_REG_ISPCTRL3D, - ov5647_test_pattern_val[ctrl->val]); + ret = cci_write(sensor->regmap, OV5647_REG_ISPCTRL3D, + ov5647_test_pattern_val[ctrl->val], NULL); break; - - /* Read-only, but we adjust it based on mode. */ - case V4L2_CID_PIXEL_RATE: - case V4L2_CID_HBLANK: - /* Read-only, but we adjust it based on mode. */ + case V4L2_CID_HFLIP: + /* There's an in-built hflip in the sensor, so account for that here. */ + ret = cci_update_bits(sensor->regmap, OV5647_REG_TIMING_TC_H, BIT(1), + ctrl->val ? 0 : BIT(1), NULL); + break; + case V4L2_CID_VFLIP: + ret = cci_update_bits(sensor->regmap, OV5647_REG_TIMING_TC_V, BIT(1), + ctrl->val ? BIT(1) : 0, NULL); break; default: @@ -1284,12 +1029,24 @@ static const struct v4l2_ctrl_ops ov5647_ctrl_ops = { .s_ctrl = ov5647_s_ctrl, }; +static int ov5647_configure_regulators(struct device *dev, + struct ov5647 *sensor) +{ + for (unsigned int i = 0; i < OV5647_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = ov5647_supply_names[i]; + + return devm_regulator_bulk_get(dev, OV5647_NUM_SUPPLIES, + sensor->supplies); +} + static int ov5647_init_controls(struct ov5647 *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + struct v4l2_fwnode_device_properties props; int hblank, exposure_max, exposure_def; + struct device *dev = &client->dev; - v4l2_ctrl_handler_init(&sensor->ctrls, 9); + v4l2_ctrl_handler_init(&sensor->ctrls, 14); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1314,16 +1071,17 @@ static int ov5647_init_controls(struct ov5647 *sensor) V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32); /* By default, PIXEL_RATE is read only, but it does change per mode */ - sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + sensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, NULL, V4L2_CID_PIXEL_RATE, sensor->mode->pixel_rate, sensor->mode->pixel_rate, 1, sensor->mode->pixel_rate); - /* By default, HBLANK is read only, but it does change per mode. */ hblank = sensor->mode->hts - sensor->mode->format.width; sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, 1, + V4L2_CID_HBLANK, hblank, + OV5647_HTS_MAX - + sensor->mode->format.width, 1, hblank); sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, @@ -1338,11 +1096,28 @@ static int ov5647_init_controls(struct ov5647 *sensor) ARRAY_SIZE(ov5647_test_pattern_menu) - 1, 0, 0, ov5647_test_pattern_menu); + sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->link_freq = + v4l2_ctrl_new_int_menu(&sensor->ctrls, NULL, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov5647_link_freqs) - 1, + sensor->mode->link_freq_index, + ov5647_link_freqs); + if (sensor->link_freq) + sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_fwnode_device_parse(dev, &props); + + v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &ov5647_ctrl_ops, + &props); + if (sensor->ctrls.error) goto handler_free; - sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; - sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->sd.ctrl_handler = &sensor->ctrls; return 0; @@ -1416,25 +1191,34 @@ static int ov5647_probe(struct i2c_client *client) return -EINVAL; } - mutex_init(&sensor->lock); + ret = ov5647_configure_regulators(dev, sensor); + if (ret) + dev_err_probe(dev, ret, "Failed to get power regulators\n"); sensor->mode = OV5647_DEFAULT_MODE; - ret = ov5647_init_controls(sensor); - if (ret) - goto mutex_destroy; - sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); sd->internal_ops = &ov5647_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + ret = ov5647_init_controls(sensor); + if (ret) + return ret; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); if (ret < 0) goto ctrl_handler_free; + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) { + ret = dev_err_probe(dev, PTR_ERR(sensor->regmap), + "Failed to init CCI\n"); + goto entity_cleanup; + } + ret = ov5647_power_on(dev); if (ret) goto entity_cleanup; @@ -1443,27 +1227,35 @@ static int ov5647_probe(struct i2c_client *client) if (ret < 0) goto power_off; - ret = v4l2_async_register_subdev(sd); - if (ret < 0) + sd->state_lock = sensor->ctrls.lock; + ret = v4l2_subdev_init_finalize(sd); + if (ret < 0) { + ret = dev_err_probe(dev, ret, "failed to init subdev\n"); goto power_off; + } /* Enable runtime PM and turn off the device */ pm_runtime_set_active(dev); pm_runtime_enable(dev); + + ret = v4l2_async_register_subdev_sensor(sd); + if (ret < 0) + goto v4l2_subdev_cleanup; + pm_runtime_idle(dev); dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); return 0; +v4l2_subdev_cleanup: + v4l2_subdev_cleanup(sd); power_off: ov5647_power_off(dev); entity_cleanup: media_entity_cleanup(&sd->entity); ctrl_handler_free: v4l2_ctrl_handler_free(&sensor->ctrls); -mutex_destroy: - mutex_destroy(&sensor->lock); return ret; } @@ -1474,11 +1266,11 @@ static void ov5647_remove(struct i2c_client *client) struct ov5647 *sensor = to_sensor(sd); v4l2_async_unregister_subdev(&sensor->sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sensor->sd.entity); v4l2_ctrl_handler_free(&sensor->ctrls); v4l2_device_unregister_subdev(sd); pm_runtime_disable(&client->dev); - mutex_destroy(&sensor->lock); } static const struct dev_pm_ops ov5647_pm_ops = { diff --git a/drivers/media/i2c/ov6211.c b/drivers/media/i2c/ov6211.c index e3ac5ecf27d1..034d5d57d67e 100644 --- a/drivers/media/i2c/ov6211.c +++ b/drivers/media/i2c/ov6211.c @@ -41,6 +41,10 @@ #define OV6211_ANALOGUE_GAIN_STEP 1 #define OV6211_ANALOGUE_GAIN_DEFAULT 160 +/* Vertical timing size */ +#define OV6211_REG_VTS CCI_REG16(0x380e) +#define OV6211_VTS_MAX 0xffff + /* Test pattern */ #define OV6211_REG_PRE_ISP CCI_REG8(0x5e00) #define OV6211_TEST_PATTERN_ENABLE BIT(7) @@ -89,6 +93,8 @@ struct ov6211 { struct v4l2_subdev sd; struct media_pad pad; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; struct v4l2_ctrl_handler ctrl_handler; /* Saved register values */ @@ -167,8 +173,6 @@ static const struct cci_reg_sequence ov6211_400x400_120fps_mode[] = { { CCI_REG8(0x380b), 0x90 }, { CCI_REG8(0x380c), 0x05 }, /* horizontal timing size */ { CCI_REG8(0x380d), 0xf2 }, - { CCI_REG8(0x380e), 0x01 }, /* vertical timing size */ - { CCI_REG8(0x380f), 0xb6 }, { CCI_REG8(0x3810), 0x00 }, { CCI_REG8(0x3811), 0x04 }, { CCI_REG8(0x3812), 0x00 }, @@ -251,8 +255,25 @@ static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov6211 *ov6211 = container_of(ctrl->handler, struct ov6211, ctrl_handler); + const struct ov6211_mode *mode = &supported_modes[0]; + s64 exposure_max; int ret; + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + exposure_max = ctrl->val + mode->height - + OV6211_EXPOSURE_MAX_MARGIN; + ret = __v4l2_ctrl_modify_range(ov6211->exposure, + ov6211->exposure->minimum, + exposure_max, + ov6211->exposure->step, + ov6211->exposure->default_value); + if (ret) + return ret; + } + /* V4L2 controls are applied, when sensor is powered up for streaming */ if (!pm_runtime_get_if_active(ov6211->dev)) return 0; @@ -266,6 +287,10 @@ static int ov6211_set_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(ov6211->regmap, OV6211_REG_EXPOSURE, ctrl->val << 4, NULL); break; + case V4L2_CID_VBLANK: + ret = cci_write(ov6211->regmap, OV6211_REG_VTS, + ctrl->val + mode->height, NULL); + break; case V4L2_CID_TEST_PATTERN: ret = ov6211_set_test_pattern(ov6211, ctrl->val); break; @@ -287,8 +312,8 @@ static int ov6211_init_controls(struct ov6211 *ov6211) { struct v4l2_ctrl_handler *ctrl_hdlr = &ov6211->ctrl_handler; const struct ov6211_mode *mode = &supported_modes[0]; + s64 exposure_max, pixel_rate, h_blank, v_blank; struct v4l2_fwnode_device_properties props; - s64 exposure_max, pixel_rate, h_blank; struct v4l2_ctrl *ctrl; int ret; @@ -311,24 +336,24 @@ static int ov6211_init_controls(struct ov6211 *ov6211) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_VBLANK, - mode->vts - mode->height, - mode->vts - mode->height, 1, - mode->vts - mode->height); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v_blank = mode->vts - mode->height; + ov6211->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, + V4L2_CID_VBLANK, v_blank, + OV6211_VTS_MAX - mode->height, 1, + v_blank); v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, OV6211_ANALOGUE_GAIN_MIN, OV6211_ANALOGUE_GAIN_MAX, OV6211_ANALOGUE_GAIN_STEP, OV6211_ANALOGUE_GAIN_DEFAULT); - exposure_max = (mode->vts - OV6211_EXPOSURE_MAX_MARGIN); - v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, - V4L2_CID_EXPOSURE, - OV6211_EXPOSURE_MIN, exposure_max, - OV6211_EXPOSURE_STEP, - OV6211_EXPOSURE_DEFAULT); + exposure_max = mode->vts - OV6211_EXPOSURE_MAX_MARGIN; + ov6211->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov6211_ctrl_ops, + V4L2_CID_EXPOSURE, + OV6211_EXPOSURE_MIN, + exposure_max, + OV6211_EXPOSURE_STEP, + OV6211_EXPOSURE_DEFAULT); v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov6211_ctrl_ops, V4L2_CID_TEST_PATTERN, diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 3e24d88f603c..ded9b2044ff8 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -37,6 +38,29 @@ #define OV9282_REG_ID 0x300a #define OV9282_ID 0x9281 +/* Output enable registers */ +#define OV9282_REG_OUTPUT_ENABLE4 0x3004 +#define OV9282_OUTPUT_ENABLE4_GPIO2 BIT(1) +#define OV9282_OUTPUT_ENABLE4_D9 BIT(0) + +#define OV9282_REG_OUTPUT_ENABLE5 0x3005 +#define OV9282_OUTPUT_ENABLE5_D8 BIT(7) +#define OV9282_OUTPUT_ENABLE5_D7 BIT(6) +#define OV9282_OUTPUT_ENABLE5_D6 BIT(5) +#define OV9282_OUTPUT_ENABLE5_D5 BIT(4) +#define OV9282_OUTPUT_ENABLE5_D4 BIT(3) +#define OV9282_OUTPUT_ENABLE5_D3 BIT(2) +#define OV9282_OUTPUT_ENABLE5_D2 BIT(1) +#define OV9282_OUTPUT_ENABLE5_D1 BIT(0) + +#define OV9282_REG_OUTPUT_ENABLE6 0x3006 +#define OV9282_OUTPUT_ENABLE6_D0 BIT(7) +#define OV9282_OUTPUT_ENABLE6_PCLK BIT(6) +#define OV9282_OUTPUT_ENABLE6_HREF BIT(5) +#define OV9282_OUTPUT_ENABLE6_STROBE BIT(3) +#define OV9282_OUTPUT_ENABLE6_ILPWM BIT(2) +#define OV9282_OUTPUT_ENABLE6_VSYNC BIT(1) + /* Exposure control */ #define OV9282_REG_EXPOSURE 0x3500 #define OV9282_EXPOSURE_MIN 1 @@ -74,6 +98,10 @@ #define OV9282_REG_MIPI_CTRL00 0x4800 #define OV9282_GATED_CLOCK BIT(5) +/* Flash/Strobe control registers */ +#define OV9282_REG_STROBE_FRAME_SPAN 0x3925 +#define OV9282_STROBE_FRAME_SPAN_DEFAULT 0x0000001a + /* Input clock rate */ #define OV9282_INCLK_RATE 24000000 @@ -101,6 +129,8 @@ #define OV9282_REG_MIN 0x00 #define OV9282_REG_MAX 0xfffff +#define OV9282_STROBE_SPAN_FACTOR 192 + static const char * const ov9282_supply_names[] = { "avdd", /* Analog power */ "dovdd", /* Digital I/O power */ @@ -169,6 +199,7 @@ struct ov9282_mode { * @exp_ctrl: Pointer to exposure control * @again_ctrl: Pointer to analog gain control * @pixel_rate: Pointer to pixel rate control + * @flash_duration: Pointer to flash duration control * @vblank: Vertical blanking in lines * @noncontinuous_clock: Selection of CSI2 noncontinuous clock mode * @cur_mode: Pointer to current selected sensor mode @@ -191,6 +222,7 @@ struct ov9282 { struct v4l2_ctrl *again_ctrl; }; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *flash_duration; u32 vblank; bool noncontinuous_clock; const struct ov9282_mode *cur_mode; @@ -213,9 +245,9 @@ static const struct ov9282_reg common_regs[] = { {0x0302, 0x32}, {0x030e, 0x02}, {0x3001, 0x00}, - {0x3004, 0x00}, - {0x3005, 0x00}, - {0x3006, 0x04}, + {OV9282_REG_OUTPUT_ENABLE4, 0x00}, + {OV9282_REG_OUTPUT_ENABLE5, 0x00}, + {OV9282_REG_OUTPUT_ENABLE6, OV9282_OUTPUT_ENABLE6_ILPWM}, {0x3011, 0x0a}, {0x3013, 0x18}, {0x301c, 0xf0}, @@ -582,6 +614,15 @@ static int ov9282_update_controls(struct ov9282 *ov9282, mode->vblank_max, 1, mode->vblank); } +static u32 ov9282_exposure_to_us(struct ov9282 *ov9282, u32 exposure) +{ + /* calculate exposure time in µs */ + u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val; + u32 trow_us = frame_width / (ov9282->pixel_rate->val / 1000000UL); + + return exposure * trow_us; +} + /** * ov9282_update_exp_gain() - Set updated exposure and gain * @ov9282: pointer to ov9282 device @@ -593,9 +634,10 @@ static int ov9282_update_controls(struct ov9282 *ov9282, static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) { int ret; + u32 exposure_us = ov9282_exposure_to_us(ov9282, exposure); - dev_dbg(ov9282->dev, "Set exp %u, analog gain %u", - exposure, gain); + dev_dbg(ov9282->dev, "Set exp %u (~%u us), analog gain %u", + exposure, exposure_us, gain); ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1); if (ret) @@ -606,6 +648,12 @@ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) goto error_release_group_hold; ret = ov9282_write_reg(ov9282, OV9282_REG_AGAIN, 1, gain); + if (ret) + goto error_release_group_hold; + + ret = __v4l2_ctrl_modify_range(ov9282->flash_duration, + 0, exposure_us, 1, + OV9282_STROBE_FRAME_SPAN_DEFAULT); error_release_group_hold: ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 0); @@ -647,6 +695,75 @@ static int ov9282_set_ctrl_vflip(struct ov9282 *ov9282, int value) current_val); } +static int ov9282_set_ctrl_flash_strobe_oe(struct ov9282 *ov9282, bool enable) +{ + u32 current_val; + int ret; + + ret = ov9282_read_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, ¤t_val); + if (ret) + return ret; + + if (enable) + current_val |= OV9282_OUTPUT_ENABLE6_STROBE; + else + current_val &= ~OV9282_OUTPUT_ENABLE6_STROBE; + + return ov9282_write_reg(ov9282, OV9282_REG_OUTPUT_ENABLE6, 1, current_val); +} + +static u32 ov9282_us_to_flash_duration(struct ov9282 *ov9282, u32 value) +{ + /* + * Calculate "strobe_frame_span" increments from a given value (µs). + * This is quite tricky as "The step width of shift and span is + * programmable under system clock domain.", but it's not documented + * how to program this step width (at least in the datasheet available + * to the author at time of writing). + * The formula below is interpolated from different modes/framerates + * and should work quite well for most settings. + */ + u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val; + + return value * OV9282_STROBE_SPAN_FACTOR / frame_width; +} + +static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value) +{ + /* + * Calculate back to microseconds from "strobe_frame_span" increments. + * As the calculation in ov9282_us_to_flash_duration uses an integer + * divison round up here. + */ + u32 frame_width = ov9282->cur_mode->width + ov9282->hblank_ctrl->val; + + return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR); +} + +static int ov9282_set_ctrl_flash_duration(struct ov9282 *ov9282, u32 value) +{ + u32 val = ov9282_us_to_flash_duration(ov9282, value); + int ret; + + ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN, 1, + (val >> 24) & 0xff); + if (ret) + return ret; + + ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 1, 1, + (val >> 16) & 0xff); + if (ret) + return ret; + + ret = ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 2, 1, + (val >> 8) & 0xff); + if (ret) + return ret; + + return ov9282_write_reg(ov9282, OV9282_REG_STROBE_FRAME_SPAN + 3, 1, + val & 0xff); +} + /** * ov9282_set_ctrl() - Set subdevice control * @ctrl: pointer to v4l2_ctrl structure @@ -713,6 +830,12 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_HTS, 2, (ctrl->val + ov9282->cur_mode->width) >> 1); break; + case V4L2_CID_FLASH_STROBE_OE: + ret = ov9282_set_ctrl_flash_strobe_oe(ov9282, ctrl->val); + break; + case V4L2_CID_FLASH_DURATION: + ret = ov9282_set_ctrl_flash_duration(ov9282, ctrl->val); + break; default: dev_err(ov9282->dev, "Invalid control %d", ctrl->id); ret = -EINVAL; @@ -723,9 +846,36 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) return ret; } +static int ov9282_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov9282 *ov9282 = + container_of_const(ctrl->handler, struct ov9282, ctrl_handler); + + if (ctrl->id == V4L2_CID_FLASH_DURATION) { + u32 us = ctrl->val; + u32 fd = ov9282_us_to_flash_duration(ov9282, us); + + /* get nearest strobe_duration value */ + u32 us0 = ov9282_flash_duration_to_us(ov9282, fd); + u32 us1 = ov9282_flash_duration_to_us(ov9282, fd + 1); + + if (abs(us1 - us) < abs(us - us0)) + ctrl->val = us1; + else + ctrl->val = us0; + + if (us != ctrl->val) + dev_dbg(ov9282->dev, "using next valid strobe_duration %u instead of %u\n", + ctrl->val, us); + } + + return 0; +} + /* V4l2 subdevice control ops*/ static const struct v4l2_ctrl_ops ov9282_ctrl_ops = { .s_ctrl = ov9282_set_ctrl, + .try_ctrl = ov9282_try_ctrl, }; /** @@ -1299,10 +1449,11 @@ static int ov9282_init_controls(struct ov9282 *ov9282) const struct ov9282_mode *mode = ov9282->cur_mode; struct v4l2_fwnode_device_properties props; u32 hblank_min; + u32 exposure_us; u32 lpfr; int ret; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); if (ret) return ret; @@ -1367,6 +1518,16 @@ static int ov9282_init_controls(struct ov9282 *ov9282) OV9282_TIMING_HTS_MAX - mode->width, 1, hblank_min); + /* Flash/Strobe controls */ + v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, + V4L2_CID_FLASH_STROBE_OE, 0, 1, 1, 0); + + exposure_us = ov9282_exposure_to_us(ov9282, OV9282_EXPOSURE_DEFAULT); + ov9282->flash_duration = + v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, + V4L2_CID_FLASH_DURATION, 0, exposure_us, 1, + OV9282_STROBE_FRAME_SPAN_DEFAULT); + ret = v4l2_fwnode_device_parse(ov9282->dev, &props); if (!ret) { /* Failure sets ctrl_hdlr->error, which we check afterwards anyway */ diff --git a/drivers/media/i2c/s5k3m5.c b/drivers/media/i2c/s5k3m5.c new file mode 100644 index 000000000000..c591b580d2e7 --- /dev/null +++ b/drivers/media/i2c/s5k3m5.c @@ -0,0 +1,1377 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Linaro Ltd + +#include <linux/clk.h> +#include <linux/delay.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 <linux/units.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define S5K3M5_LINK_FREQ_602P5MHZ (602500ULL * HZ_PER_KHZ) +#define S5K3M5_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) +#define S5K3M5_DATA_LANES 4 + +/* Register map is similar to MIPI CCS compliant camera sensors */ +#define S5K3M5_REG_CHIP_ID CCI_REG16(0x0000) +#define S5K3M5_CHIP_ID 0x30d5 + +/* Both streaming and flipping settings are controlled at the same time */ +#define S5K3M5_REG_CTRL_MODE CCI_REG16(0x0100) +#define S5K3M5_MODE_STREAMING BIT(8) +#define S5K3M5_VFLIP BIT(1) +#define S5K3M5_HFLIP BIT(0) + +#define S5K3M5_REG_EXPOSURE CCI_REG16(0x0202) +#define S5K3M5_EXPOSURE_MIN 8 +#define S5K3M5_EXPOSURE_STEP 1 +#define S5K3M5_EXPOSURE_MARGIN 4 + +#define S5K3M5_REG_AGAIN CCI_REG16(0x0204) +#define S5K3M5_AGAIN_MIN 1 +#define S5K3M5_AGAIN_MAX 16 +#define S5K3M5_AGAIN_STEP 1 +#define S5K3M5_AGAIN_DEFAULT 1 +#define S5K3M5_AGAIN_SHIFT 5 + +#define S5K3M5_REG_VTS CCI_REG16(0x0340) +#define S5K3M5_VTS_MAX 0xffff + +#define S5K3M5_REG_HTS CCI_REG16(0x0342) +#define S5K3M5_REG_X_ADDR_START CCI_REG16(0x0344) +#define S5K3M5_REG_Y_ADDR_START CCI_REG16(0x0346) +#define S5K3M5_REG_X_ADDR_END CCI_REG16(0x0348) +#define S5K3M5_REG_Y_ADDR_END CCI_REG16(0x034a) +#define S5K3M5_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define S5K3M5_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) + +#define S5K3M5_REG_TEST_PATTERN CCI_REG16(0x0600) + +#define to_s5k3m5(_sd) container_of(_sd, struct s5k3m5, sd) + +static const s64 s5k3m5_link_freq_menu[] = { + S5K3M5_LINK_FREQ_602P5MHZ, +}; + +/* List of supported formats to cover horizontal and vertical flip controls */ +static const u32 s5k3m5_mbus_formats[] = { + MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, +}; + +struct s5k3m5_reg_list { + const struct cci_reg_sequence *regs; + unsigned int num_regs; +}; + +struct s5k3m5_mode { + u32 width; /* Frame width in pixels */ + u32 height; /* Frame height in pixels */ + u32 hts; /* Horizontal timing size */ + u32 vts; /* Default vertical timing size */ + u32 exposure; /* Default exposure value */ + + const struct s5k3m5_reg_list reg_list; /* Sensor register setting */ +}; + +static const char * const s5k3m5_test_pattern_menu[] = { + "Disabled", + "Solid colour", + "Colour bars", + "Fade to grey colour bars", + "PN9", +}; + +static const char * const s5k3m5_supply_names[] = { + "afvdd", /* Autofocus power */ + "vdda", /* Analog power */ + "vddd", /* Digital core power */ + "vddio", /* Digital I/O power */ +}; + +#define S5K3M5_NUM_SUPPLIES ARRAY_SIZE(s5k3m5_supply_names) + +struct s5k3m5 { + struct device *dev; + struct regmap *regmap; + struct clk *mclk; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[S5K3M5_NUM_SUPPLIES]; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + const struct s5k3m5_mode *mode; +}; + +static const struct cci_reg_sequence burst_array_setting[] = { + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0549 }, + { CCI_REG16(0x6f12), 0x0448 }, + { CCI_REG16(0x6f12), 0x054a }, + { CCI_REG16(0x6f12), 0xc1f8 }, + { CCI_REG16(0x6f12), 0xc804 }, + { CCI_REG16(0x6f12), 0x101a }, + { CCI_REG16(0x6f12), 0xa1f8 }, + { CCI_REG16(0x6f12), 0xcc04 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x1bb9 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x4210 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x2e50 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x7000 }, + { CCI_REG16(0x6f12), 0x10b5 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x4ff9 }, + { CCI_REG16(0x6f12), 0x9949 }, + { CCI_REG16(0x6f12), 0x0120 }, + { CCI_REG16(0x6f12), 0x0880 }, + { CCI_REG16(0x6f12), 0x10bd }, + { CCI_REG16(0x6f12), 0x2de9 }, + { CCI_REG16(0x6f12), 0xf041 }, + { CCI_REG16(0x6f12), 0x974c }, + { CCI_REG16(0x6f12), 0x954f }, + { CCI_REG16(0x6f12), 0x0026 }, + { CCI_REG16(0x6f12), 0xb4f8 }, + { CCI_REG16(0x6f12), 0x6a52 }, + { CCI_REG16(0x6f12), 0x3888 }, + { CCI_REG16(0x6f12), 0x08b1 }, + { CCI_REG16(0x6f12), 0xa4f8 }, + { CCI_REG16(0x6f12), 0x6a62 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x43f9 }, + { CCI_REG16(0x6f12), 0x3e80 }, + { CCI_REG16(0x6f12), 0xa4f8 }, + { CCI_REG16(0x6f12), 0x6a52 }, + { CCI_REG16(0x6f12), 0xbde8 }, + { CCI_REG16(0x6f12), 0xf081 }, + { CCI_REG16(0x6f12), 0x2de9 }, + { CCI_REG16(0x6f12), 0xf041 }, + { CCI_REG16(0x6f12), 0x0746 }, + { CCI_REG16(0x6f12), 0x8c48 }, + { CCI_REG16(0x6f12), 0x0e46 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0x4068 }, + { CCI_REG16(0x6f12), 0x84b2 }, + { CCI_REG16(0x6f12), 0x050c }, + { CCI_REG16(0x6f12), 0x2146 }, + { CCI_REG16(0x6f12), 0x2846 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x36f9 }, + { CCI_REG16(0x6f12), 0x3146 }, + { CCI_REG16(0x6f12), 0x3846 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x37f9 }, + { CCI_REG16(0x6f12), 0x874f }, + { CCI_REG16(0x6f12), 0x4df2 }, + { CCI_REG16(0x6f12), 0x0c26 }, + { CCI_REG16(0x6f12), 0x4ff4 }, + { CCI_REG16(0x6f12), 0x8061 }, + { CCI_REG16(0x6f12), 0x3a78 }, + { CCI_REG16(0x6f12), 0x3046 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x29f9 }, + { CCI_REG16(0x6f12), 0x7878 }, + { CCI_REG16(0x6f12), 0xb8b3 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0x8021 }, + { CCI_REG16(0x6f12), 0x3046 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x22f9 }, + { CCI_REG16(0x6f12), 0x8048 }, + { CCI_REG16(0x6f12), 0x0088 }, + { CCI_REG16(0x6f12), 0x804b }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x5c02 }, + { CCI_REG16(0x6f12), 0x7e48 }, + { CCI_REG16(0x6f12), 0x001d }, + { CCI_REG16(0x6f12), 0x0088 }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x5e02 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0x5c02 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0x5e12 }, + { CCI_REG16(0x6f12), 0x4218 }, + { CCI_REG16(0x6f12), 0x02d0 }, + { CCI_REG16(0x6f12), 0x8002 }, + { CCI_REG16(0x6f12), 0xb0fb }, + { CCI_REG16(0x6f12), 0xf2f2 }, + { CCI_REG16(0x6f12), 0x91b2 }, + { CCI_REG16(0x6f12), 0x784a }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x6012 }, + { CCI_REG16(0x6f12), 0xb2f8 }, + { CCI_REG16(0x6f12), 0x1602 }, + { CCI_REG16(0x6f12), 0xb2f8 }, + { CCI_REG16(0x6f12), 0x1422 }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x9805 }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x9a25 }, + { CCI_REG16(0x6f12), 0x8018 }, + { CCI_REG16(0x6f12), 0x04d0 }, + { CCI_REG16(0x6f12), 0x9202 }, + { CCI_REG16(0x6f12), 0xb2fb }, + { CCI_REG16(0x6f12), 0xf0f0 }, + { CCI_REG16(0x6f12), 0xa3f8 }, + { CCI_REG16(0x6f12), 0x9c05 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0x9c05 }, + { CCI_REG16(0x6f12), 0x0a18 }, + { CCI_REG16(0x6f12), 0x01fb }, + { CCI_REG16(0x6f12), 0x1020 }, + { CCI_REG16(0x6f12), 0x40f3 }, + { CCI_REG16(0x6f12), 0x9510 }, + { CCI_REG16(0x6f12), 0x1028 }, + { CCI_REG16(0x6f12), 0x06dc }, + { CCI_REG16(0x6f12), 0x0028 }, + { CCI_REG16(0x6f12), 0x05da }, + { CCI_REG16(0x6f12), 0x0020 }, + { CCI_REG16(0x6f12), 0x03e0 }, + { CCI_REG16(0x6f12), 0xffe7 }, + { CCI_REG16(0x6f12), 0x0122 }, + { CCI_REG16(0x6f12), 0xc5e7 }, + { CCI_REG16(0x6f12), 0x1020 }, + { CCI_REG16(0x6f12), 0x6849 }, + { CCI_REG16(0x6f12), 0x0880 }, + { CCI_REG16(0x6f12), 0x2146 }, + { CCI_REG16(0x6f12), 0x2846 }, + { CCI_REG16(0x6f12), 0xbde8 }, + { CCI_REG16(0x6f12), 0xf041 }, + { CCI_REG16(0x6f12), 0x0122 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0xe2b8 }, + { CCI_REG16(0x6f12), 0xf0b5 }, + { CCI_REG16(0x6f12), 0x644c }, + { CCI_REG16(0x6f12), 0xdde9 }, + { CCI_REG16(0x6f12), 0x0565 }, + { CCI_REG16(0x6f12), 0x08b1 }, + { CCI_REG16(0x6f12), 0x2788 }, + { CCI_REG16(0x6f12), 0x0760 }, + { CCI_REG16(0x6f12), 0x09b1 }, + { CCI_REG16(0x6f12), 0x6088 }, + { CCI_REG16(0x6f12), 0x0860 }, + { CCI_REG16(0x6f12), 0x12b1 }, + { CCI_REG16(0x6f12), 0xa088 }, + { CCI_REG16(0x6f12), 0x401c }, + { CCI_REG16(0x6f12), 0x1060 }, + { CCI_REG16(0x6f12), 0x0bb1 }, + { CCI_REG16(0x6f12), 0xe088 }, + { CCI_REG16(0x6f12), 0x1860 }, + { CCI_REG16(0x6f12), 0x0eb1 }, + { CCI_REG16(0x6f12), 0xa07b }, + { CCI_REG16(0x6f12), 0x3060 }, + { CCI_REG16(0x6f12), 0x002d }, + { CCI_REG16(0x6f12), 0x01d0 }, + { CCI_REG16(0x6f12), 0xe07b }, + { CCI_REG16(0x6f12), 0x2860 }, + { CCI_REG16(0x6f12), 0xf0bd }, + { CCI_REG16(0x6f12), 0x70b5 }, + { CCI_REG16(0x6f12), 0x0646 }, + { CCI_REG16(0x6f12), 0x5048 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0x8068 }, + { CCI_REG16(0x6f12), 0x84b2 }, + { CCI_REG16(0x6f12), 0x050c }, + { CCI_REG16(0x6f12), 0x2146 }, + { CCI_REG16(0x6f12), 0x2846 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0xbef8 }, + { CCI_REG16(0x6f12), 0x3046 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0xc5f8 }, + { CCI_REG16(0x6f12), 0x5248 }, + { CCI_REG16(0x6f12), 0x0368 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0x7401 }, + { CCI_REG16(0x6f12), 0x010a }, + { CCI_REG16(0x6f12), 0x5048 }, + { CCI_REG16(0x6f12), 0x4268 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x5010 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x7511 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x5210 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0x7811 }, + { CCI_REG16(0x6f12), 0x090a }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x5810 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x7911 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x5a10 }, + { CCI_REG16(0x6f12), 0x33f8 }, + { CCI_REG16(0x6f12), 0xf01f }, + { CCI_REG16(0x6f12), 0x0068 }, + { CCI_REG16(0x6f12), 0x090a }, + { CCI_REG16(0x6f12), 0x00f8 }, + { CCI_REG16(0x6f12), 0xce1f }, + { CCI_REG16(0x6f12), 0x5978 }, + { CCI_REG16(0x6f12), 0x8170 }, + { CCI_REG16(0x6f12), 0x5988 }, + { CCI_REG16(0x6f12), 0x090a }, + { CCI_REG16(0x6f12), 0x0171 }, + { CCI_REG16(0x6f12), 0xd978 }, + { CCI_REG16(0x6f12), 0x8171 }, + { CCI_REG16(0x6f12), 0x988c }, + { CCI_REG16(0x6f12), 0x000a }, + { CCI_REG16(0x6f12), 0x9074 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x2500 }, + { CCI_REG16(0x6f12), 0x1075 }, + { CCI_REG16(0x6f12), 0xd88c }, + { CCI_REG16(0x6f12), 0x000a }, + { CCI_REG16(0x6f12), 0x9075 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x2700 }, + { CCI_REG16(0x6f12), 0x1076 }, + { CCI_REG16(0x6f12), 0xb3f8 }, + { CCI_REG16(0x6f12), 0xb000 }, + { CCI_REG16(0x6f12), 0x000a }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x7e00 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0xb100 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x8000 }, + { CCI_REG16(0x6f12), 0x2f48 }, + { CCI_REG16(0x6f12), 0x90f8 }, + { CCI_REG16(0x6f12), 0xb313 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x8210 }, + { CCI_REG16(0x6f12), 0x90f8 }, + { CCI_REG16(0x6f12), 0xb103 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x8400 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0xb400 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x8600 }, + { CCI_REG16(0x6f12), 0x0020 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x8800 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x6211 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x9610 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0112 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0x9e10 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0212 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xa010 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xa200 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xa400 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0512 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xa610 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0612 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xa810 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0712 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xaa10 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xac00 }, + { CCI_REG16(0x6f12), 0x5a20 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xad00 }, + { CCI_REG16(0x6f12), 0x93f8 }, + { CCI_REG16(0x6f12), 0x0902 }, + { CCI_REG16(0x6f12), 0x82f8 }, + { CCI_REG16(0x6f12), 0xae00 }, + { CCI_REG16(0x6f12), 0x2146 }, + { CCI_REG16(0x6f12), 0x2846 }, + { CCI_REG16(0x6f12), 0xbde8 }, + { CCI_REG16(0x6f12), 0x7040 }, + { CCI_REG16(0x6f12), 0x0122 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x47b8 }, + { CCI_REG16(0x6f12), 0x10b5 }, + { CCI_REG16(0x6f12), 0x0f4c }, + { CCI_REG16(0x6f12), 0x0020 }, + { CCI_REG16(0x6f12), 0x2080 }, + { CCI_REG16(0x6f12), 0xaff2 }, + { CCI_REG16(0x6f12), 0x3321 }, + { CCI_REG16(0x6f12), 0x1748 }, + { CCI_REG16(0x6f12), 0x0161 }, + { CCI_REG16(0x6f12), 0xaff2 }, + { CCI_REG16(0x6f12), 0x2d21 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0x8161 }, + { CCI_REG16(0x6f12), 0xaff2 }, + { CCI_REG16(0x6f12), 0x1121 }, + { CCI_REG16(0x6f12), 0x1448 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x45f8 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0xaff2 }, + { CCI_REG16(0x6f12), 0x2911 }, + { CCI_REG16(0x6f12), 0x6060 }, + { CCI_REG16(0x6f12), 0x1148 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x3ef8 }, + { CCI_REG16(0x6f12), 0x0022 }, + { CCI_REG16(0x6f12), 0xaff2 }, + { CCI_REG16(0x6f12), 0x6b11 }, + { CCI_REG16(0x6f12), 0xa060 }, + { CCI_REG16(0x6f12), 0x0f48 }, + { CCI_REG16(0x6f12), 0x00f0 }, + { CCI_REG16(0x6f12), 0x37f8 }, + { CCI_REG16(0x6f12), 0xe060 }, + { CCI_REG16(0x6f12), 0x10bd }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x4200 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x2e50 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x41d0 }, + { CCI_REG16(0x6f12), 0x4000 }, + { CCI_REG16(0x6f12), 0x9404 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x38e0 }, + { CCI_REG16(0x6f12), 0x4000 }, + { CCI_REG16(0x6f12), 0xd000 }, + { CCI_REG16(0x6f12), 0x4000 }, + { CCI_REG16(0x6f12), 0xa410 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x2c66 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x0890 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x3620 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x0840 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x6f12), 0x020d }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x67cd }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x3ae1 }, + { CCI_REG16(0x6f12), 0x45f6 }, + { CCI_REG16(0x6f12), 0x250c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x45f6 }, + { CCI_REG16(0x6f12), 0xf31c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x4af2 }, + { CCI_REG16(0x6f12), 0xd74c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x40f2 }, + { CCI_REG16(0x6f12), 0x0d2c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x010c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x46f2 }, + { CCI_REG16(0x6f12), 0xcd7c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x4af6 }, + { CCI_REG16(0x6f12), 0xe75c }, + { CCI_REG16(0x6f12), 0xc0f2 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x6f12), 0x6047 }, + { CCI_REG16(0x6f12), 0x30d5 }, + { CCI_REG16(0x6f12), 0x00fc }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x000e }, +}; + +static const struct cci_reg_sequence init_array_setting[] = { + { CCI_REG16(0x602a), 0x41d0 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x1662 }, + { CCI_REG16(0x6f12), 0x1e00 }, + { CCI_REG16(0x602a), 0x1c9a }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0ff2 }, + { CCI_REG16(0x6f12), 0x0020 }, + { CCI_REG16(0x602a), 0x0ef6 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x23b2 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x602a), 0x0fe4 }, + { CCI_REG16(0x6f12), 0x0107 }, + { CCI_REG16(0x6f12), 0x07d0 }, + { CCI_REG16(0x602a), 0x12f8 }, + { CCI_REG16(0x6f12), 0x3d09 }, + { CCI_REG16(0x602a), 0x0e18 }, + { CCI_REG16(0x6f12), 0x0040 }, + { CCI_REG16(0x602a), 0x1066 }, + { CCI_REG16(0x6f12), 0x000c }, + { CCI_REG16(0x602a), 0x13de }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x12f2 }, + { CCI_REG16(0x6f12), 0x0f0f }, + { CCI_REG16(0x602a), 0x13dc }, + { CCI_REG16(0x6f12), 0x806f }, + { CCI_REG16(0xf46e), 0x00c3 }, + { CCI_REG16(0xf46c), 0xbfa0 }, + { CCI_REG16(0xf44a), 0x0007 }, + { CCI_REG16(0xf456), 0x000a }, + { CCI_REG16(0x6028), 0x2000 }, + { CCI_REG16(0x602a), 0x12f6 }, + { CCI_REG16(0x6f12), 0x7008 }, + { CCI_REG16(0x0bc6), 0x0000 }, + { CCI_REG16(0x0b36), 0x0001 }, +}; + +static const struct cci_reg_sequence s5k3m5_4208x3120_30fps_mode[] = { + { CCI_REG16(0x6214), 0x7971 }, + { CCI_REG16(0x6218), 0x7150 }, + { S5K3M5_REG_X_ADDR_START, 0x0000 }, + { S5K3M5_REG_Y_ADDR_START, 0x0008 }, + { S5K3M5_REG_X_ADDR_END, 0x1077 }, + { S5K3M5_REG_Y_ADDR_END, 0x0c37 }, + { S5K3M5_REG_X_OUTPUT_SIZE, 0x1070 }, + { S5K3M5_REG_Y_OUTPUT_SIZE, 0x0c30 }, + { S5K3M5_REG_VTS, 0x0cf2 }, + { S5K3M5_REG_HTS, 0x12f0 }, + + { CCI_REG16(0x0900), 0x0011 }, + { CCI_REG16(0x0380), 0x0001 }, + { CCI_REG16(0x0382), 0x0001 }, + { CCI_REG16(0x0384), 0x0001 }, + { CCI_REG16(0x0386), 0x0001 }, + { CCI_REG16(0x0402), 0x1010 }, + { CCI_REG16(0x0404), 0x1000 }, + { CCI_REG16(0x0350), 0x0008 }, + { CCI_REG16(0x0352), 0x0000 }, + + /* Clock settings */ + { CCI_REG16(0x0136), 0x1800 }, + { CCI_REG16(0x013e), 0x0000 }, + { CCI_REG16(0x0300), 0x0008 }, + { CCI_REG16(0x0302), 0x0001 }, + { CCI_REG16(0x0304), 0x0006 }, + { CCI_REG16(0x0306), 0x00f1 }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0003 }, + { CCI_REG16(0x0310), 0x005a }, + { CCI_REG16(0x0312), 0x0000 }, + + { CCI_REG16(0x0b06), 0x0101 }, + { CCI_REG16(0x6028), 0x2000 }, + { CCI_REG16(0x602a), 0x1ff6 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x021e), 0x0000 }, + { CCI_REG16(0x0202), 0x0100 }, + { CCI_REG16(0x0204), 0x0020 }, + { CCI_REG16(0x0d00), 0x0100 }, + { CCI_REG16(0x0d02), 0x0001 }, + { CCI_REG16(0x0114), 0x0301 }, + { CCI_REG16(0x0d06), 0x0208 }, + { CCI_REG16(0x0d08), 0x0300 }, + { CCI_REG16(0x6028), 0x2000 }, + { CCI_REG16(0x602a), 0x0f10 }, + { CCI_REG16(0x6f12), 0x0003 }, + { CCI_REG16(0x602a), 0x0f12 }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x2bc0 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x0b30), 0x0000 }, + { CCI_REG16(0x0b32), 0x0000 }, + { CCI_REG16(0x0b34), 0x0001 }, + { CCI_REG16(0x0804), 0x0200 }, + { CCI_REG16(0x0810), 0x0020 }, +}; + +static const struct cci_reg_sequence s5k3m5_2104x1184_60fps_mode[] = { + { CCI_REG16(0x6214), 0x7971 }, + { CCI_REG16(0x6218), 0x7150 }, + { S5K3M5_REG_X_ADDR_START, 0x0000 }, + { S5K3M5_REG_Y_ADDR_START, 0x0180 }, + { S5K3M5_REG_X_ADDR_END, 0x1077 }, + { S5K3M5_REG_Y_ADDR_END, 0x0abf }, + { S5K3M5_REG_X_OUTPUT_SIZE, 0x0838 }, + { S5K3M5_REG_Y_OUTPUT_SIZE, 0x04a0 }, + { S5K3M5_REG_VTS, 0x0658 }, + { S5K3M5_REG_HTS, 0x1340 }, + + { CCI_REG16(0x0900), 0x0112 }, + { CCI_REG16(0x0380), 0x0001 }, + { CCI_REG16(0x0382), 0x0001 }, + { CCI_REG16(0x0384), 0x0001 }, + { CCI_REG16(0x0386), 0x0003 }, + { CCI_REG16(0x0402), 0x1010 }, + { CCI_REG16(0x0404), 0x2000 }, + { CCI_REG16(0x0350), 0x0004 }, + { CCI_REG16(0x0352), 0x0000 }, + + /* Clock settings */ + { CCI_REG16(0x0136), 0x1800 }, + { CCI_REG16(0x013e), 0x0000 }, + { CCI_REG16(0x0300), 0x0008 }, + { CCI_REG16(0x0302), 0x0001 }, + { CCI_REG16(0x0304), 0x0006 }, + { CCI_REG16(0x0306), 0x00f1 }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0003 }, + { CCI_REG16(0x0310), 0x0063 }, + { CCI_REG16(0x0312), 0x0001 }, + + { CCI_REG16(0x0b06), 0x0101 }, + { CCI_REG16(0x6028), 0x2000 }, + { CCI_REG16(0x602a), 0x1ff6 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x021e), 0x0000 }, + { CCI_REG16(0x0202), 0x0100 }, + { CCI_REG16(0x0204), 0x0020 }, + { CCI_REG16(0x0d00), 0x0101 }, + { CCI_REG16(0x0d02), 0x0101 }, + { CCI_REG16(0x0114), 0x0301 }, + { CCI_REG16(0x0d06), 0x0208 }, + { CCI_REG16(0x0d08), 0x0300 }, + { CCI_REG16(0x6028), 0x2000 }, + { CCI_REG16(0x602a), 0x0f10 }, + { CCI_REG16(0x6f12), 0x0003 }, + { CCI_REG16(0x602a), 0x0f12 }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x2bc0 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x0b30), 0x0000 }, + { CCI_REG16(0x0b32), 0x0000 }, + { CCI_REG16(0x0b34), 0x0001 }, + { CCI_REG16(0x0804), 0x0200 }, + { CCI_REG16(0x0810), 0x0020 }, +}; + +static const struct s5k3m5_mode s5k3m5_supported_modes[] = { + { + .width = 4208, + .height = 3120, + .hts = 4848, + .vts = 3314, + .exposure = 256, + .reg_list = { + .regs = s5k3m5_4208x3120_30fps_mode, + .num_regs = ARRAY_SIZE(s5k3m5_4208x3120_30fps_mode), + }, + }, + { + .width = 2104, + .height = 1184, + .hts = 4928, + .vts = 1624, + .exposure = 256, + .reg_list = { + .regs = s5k3m5_2104x1184_60fps_mode, + .num_regs = ARRAY_SIZE(s5k3m5_2104x1184_60fps_mode), + }, + }, +}; + +static int s5k3m5_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5k3m5 *s5k3m5 = container_of(ctrl->handler, struct s5k3m5, + ctrl_handler); + const struct s5k3m5_mode *mode = s5k3m5->mode; + s64 exposure_max; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + /* The orientation settings are applied along with streaming */ + return 0; + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->height + ctrl->val - S5K3M5_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(s5k3m5->exposure, + s5k3m5->exposure->minimum, + exposure_max, + s5k3m5->exposure->step, + s5k3m5->exposure->default_value); + break; + } + + /* V4L2 controls are applied, when sensor is powered up for streaming */ + if (!pm_runtime_get_if_active(s5k3m5->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(s5k3m5->regmap, S5K3M5_REG_AGAIN, + ctrl->val << S5K3M5_AGAIN_SHIFT, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(s5k3m5->regmap, S5K3M5_REG_EXPOSURE, + ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(s5k3m5->regmap, S5K3M5_REG_VTS, + ctrl->val + mode->height, NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(s5k3m5->regmap, S5K3M5_REG_TEST_PATTERN, + ctrl->val, NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(s5k3m5->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops s5k3m5_ctrl_ops = { + .s_ctrl = s5k3m5_set_ctrl, +}; + +static inline u64 s5k3m5_freq_to_pixel_rate(const u64 freq) +{ + return div_u64(freq * 2 * S5K3M5_DATA_LANES, 10); +} + +static int s5k3m5_init_controls(struct s5k3m5 *s5k3m5) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &s5k3m5->ctrl_handler; + const struct s5k3m5_mode *mode = s5k3m5->mode; + s64 pixel_rate, hblank, vblank, exposure_max; + struct v4l2_fwnode_device_properties props; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 9); + + s5k3m5->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(s5k3m5_link_freq_menu) - 1, + 0, s5k3m5_link_freq_menu); + if (s5k3m5->link_freq) + s5k3m5->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = s5k3m5_freq_to_pixel_rate(s5k3m5_link_freq_menu[0]); + s5k3m5->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 0, pixel_rate, 1, pixel_rate); + + hblank = mode->hts - mode->width; + s5k3m5->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_HBLANK, hblank, + hblank, 1, hblank); + if (s5k3m5->hblank) + s5k3m5->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank = mode->vts - mode->height; + s5k3m5->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_VBLANK, vblank, + S5K3M5_VTS_MAX - mode->height, 1, + vblank); + + v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + S5K3M5_AGAIN_MIN, S5K3M5_AGAIN_MAX, + S5K3M5_AGAIN_STEP, S5K3M5_AGAIN_DEFAULT); + + exposure_max = mode->vts - S5K3M5_EXPOSURE_MARGIN; + s5k3m5->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_EXPOSURE, + S5K3M5_EXPOSURE_MIN, + exposure_max, + S5K3M5_EXPOSURE_STEP, + mode->exposure); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(s5k3m5_test_pattern_menu) - 1, + 0, 0, s5k3m5_test_pattern_menu); + + s5k3m5->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (s5k3m5->hflip) + s5k3m5->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + s5k3m5->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5k3m5_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (s5k3m5->vflip) + s5k3m5->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ret = v4l2_fwnode_device_parse(s5k3m5->dev, &props); + if (ret) + goto error_free_hdlr; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &s5k3m5_ctrl_ops, + &props); + if (ret) + goto error_free_hdlr; + + s5k3m5->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_free_hdlr: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int s5k3m5_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + const struct s5k3m5_reg_list *reg_list = &s5k3m5->mode->reg_list; + int ret; + + ret = pm_runtime_resume_and_get(s5k3m5->dev); + if (ret) + return ret; + + /* Page pointer */ + cci_write(s5k3m5->regmap, CCI_REG16(0x6028), 0x4000, &ret); + + /* Set version */ + cci_write(s5k3m5->regmap, CCI_REG16(0x0000), 0x0006, &ret); + cci_write(s5k3m5->regmap, CCI_REG16(0x0000), S5K3M5_CHIP_ID, &ret); + cci_write(s5k3m5->regmap, CCI_REG16(0x6214), 0x7971, &ret); + cci_write(s5k3m5->regmap, CCI_REG16(0x6218), 0x7150, &ret); + if (ret) + goto error; + + usleep_range(4 * USEC_PER_MSEC, 5 * USEC_PER_MSEC); + + cci_write(s5k3m5->regmap, CCI_REG16(0x0a02), 0x7800, &ret); + cci_write(s5k3m5->regmap, CCI_REG16(0x6028), 0x2000, &ret); + cci_write(s5k3m5->regmap, CCI_REG16(0x602a), 0x3eac, &ret); + + /* Sensor init settings */ + cci_multi_reg_write(s5k3m5->regmap, burst_array_setting, + ARRAY_SIZE(burst_array_setting), &ret); + cci_multi_reg_write(s5k3m5->regmap, init_array_setting, + ARRAY_SIZE(init_array_setting), &ret); + cci_multi_reg_write(s5k3m5->regmap, reg_list->regs, + reg_list->num_regs, &ret); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(s5k3m5->sd.ctrl_handler); + + cci_write(s5k3m5->regmap, S5K3M5_REG_CTRL_MODE, + S5K3M5_MODE_STREAMING | + (s5k3m5->vflip->val ? S5K3M5_VFLIP : 0) | + (s5k3m5->hflip->val ? S5K3M5_HFLIP : 0), &ret); + if (ret) + goto error; + + return 0; + +error: + dev_err(s5k3m5->dev, "failed to start streaming: %d\n", ret); + pm_runtime_put_autosuspend(s5k3m5->dev); + + return ret; +} + +static int s5k3m5_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + int ret; + + ret = cci_write(s5k3m5->regmap, S5K3M5_REG_CTRL_MODE, 0, NULL); + if (ret) + dev_err(s5k3m5->dev, "failed to stop streaming: %d\n", ret); + + pm_runtime_put_autosuspend(s5k3m5->dev); + + return ret; +} + +static u32 s5k3m5_get_format_code(struct s5k3m5 *s5k3m5) +{ + unsigned int i; + + i = (s5k3m5->vflip->val ? 2 : 0) | (s5k3m5->hflip->val ? 1 : 0); + + return s5k3m5_mbus_formats[i]; +} + +static void s5k3m5_update_pad_format(struct s5k3m5 *s5k3m5, + const struct s5k3m5_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = s5k3m5_get_format_code(s5k3m5); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int s5k3m5_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + s64 hblank, vblank, exposure_max; + const struct s5k3m5_mode *mode; + + mode = v4l2_find_nearest_size(s5k3m5_supported_modes, + ARRAY_SIZE(s5k3m5_supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + s5k3m5_update_pad_format(s5k3m5, mode, &fmt->format); + + /* Format code could be updated with respect to flip controls */ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY || s5k3m5->mode == mode) + goto set_format; + + /* Update limits and set FPS and exposure to default values */ + hblank = mode->hts - mode->width; + __v4l2_ctrl_modify_range(s5k3m5->hblank, hblank, hblank, 1, hblank); + + vblank = mode->vts - mode->height; + __v4l2_ctrl_modify_range(s5k3m5->vblank, vblank, + S5K3M5_VTS_MAX - mode->height, 1, vblank); + __v4l2_ctrl_s_ctrl(s5k3m5->vblank, vblank); + + exposure_max = mode->vts - S5K3M5_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(s5k3m5->exposure, S5K3M5_EXPOSURE_MIN, + exposure_max, S5K3M5_EXPOSURE_STEP, + mode->exposure); + __v4l2_ctrl_s_ctrl(s5k3m5->exposure, mode->exposure); + + if (s5k3m5->sd.ctrl_handler->error) + return s5k3m5->sd.ctrl_handler->error; + + s5k3m5->mode = mode; + +set_format: + *v4l2_subdev_state_get_format(state, 0) = fmt->format; + + return 0; +} + +static int s5k3m5_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + + /* Media bus code index is constant, but code formats are not */ + if (code->index > 0) + return -EINVAL; + + code->code = s5k3m5_get_format_code(s5k3m5); + + return 0; +} + +static int s5k3m5_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + + if (fse->index >= ARRAY_SIZE(s5k3m5_supported_modes)) + return -EINVAL; + + if (fse->code != s5k3m5_get_format_code(s5k3m5)) + return -EINVAL; + + fse->min_width = s5k3m5_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = s5k3m5_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int s5k3m5_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = s5k3m5->mode->width; + sel->r.height = s5k3m5->mode->width; + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int s5k3m5_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + /* Media bus code depends on current flip controls */ + .width = s5k3m5->mode->width, + .height = s5k3m5->mode->height, + }, + }; + + s5k3m5_set_pad_format(sd, state, &fmt); + + return 0; +} + +static const struct v4l2_subdev_video_ops s5k3m5_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops s5k3m5_pad_ops = { + .set_fmt = s5k3m5_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .get_selection = s5k3m5_get_selection, + .enum_mbus_code = s5k3m5_enum_mbus_code, + .enum_frame_size = s5k3m5_enum_frame_size, + .enable_streams = s5k3m5_enable_streams, + .disable_streams = s5k3m5_disable_streams, +}; + +static const struct v4l2_subdev_ops s5k3m5_subdev_ops = { + .video = &s5k3m5_video_ops, + .pad = &s5k3m5_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops s5k3m5_internal_ops = { + .init_state = s5k3m5_init_state, +}; + +static const struct media_entity_operations s5k3m5_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int s5k3m5_identify_sensor(struct s5k3m5 *s5k3m5) +{ + u64 val; + int ret; + + ret = cci_read(s5k3m5->regmap, S5K3M5_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(s5k3m5->dev, "failed to read chip id: %d\n", ret); + return ret; + } + + if (val != S5K3M5_CHIP_ID) { + dev_err(s5k3m5->dev, "chip id mismatch: %x!=%llx\n", + S5K3M5_CHIP_ID, val); + return -ENODEV; + } + + return 0; +} + +static int s5k3m5_check_hwcfg(struct s5k3m5 *s5k3m5) +{ + struct fwnode_handle *fwnode = dev_fwnode(s5k3m5->dev), *ep; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus = { + .mipi_csi2 = { + .num_data_lanes = S5K3M5_DATA_LANES, + }, + }, + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned long freq_bitmap; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != S5K3M5_DATA_LANES) { + dev_err(s5k3m5->dev, "Invalid number of data lanes: %u\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(s5k3m5->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + s5k3m5_link_freq_menu, + ARRAY_SIZE(s5k3m5_link_freq_menu), + &freq_bitmap); + +endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int s5k3m5_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + int ret; + + ret = regulator_bulk_enable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies); + if (ret) + return ret; + + ret = clk_prepare_enable(s5k3m5->mclk); + if (ret) + goto disable_regulators; + + gpiod_set_value_cansleep(s5k3m5->reset_gpio, 0); + usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC); + + return 0; + +disable_regulators: + regulator_bulk_disable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies); + + return ret; +} + +static int s5k3m5_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + + gpiod_set_value_cansleep(s5k3m5->reset_gpio, 1); + + clk_disable_unprepare(s5k3m5->mclk); + + regulator_bulk_disable(S5K3M5_NUM_SUPPLIES, s5k3m5->supplies); + + return 0; +} + +static int s5k3m5_probe(struct i2c_client *client) +{ + struct s5k3m5 *s5k3m5; + unsigned long freq; + unsigned int i; + int ret; + + s5k3m5 = devm_kzalloc(&client->dev, sizeof(*s5k3m5), GFP_KERNEL); + if (!s5k3m5) + return -ENOMEM; + + s5k3m5->dev = &client->dev; + v4l2_i2c_subdev_init(&s5k3m5->sd, client, &s5k3m5_subdev_ops); + + s5k3m5->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(s5k3m5->regmap)) + return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->regmap), + "failed to init CCI\n"); + + s5k3m5->mclk = devm_v4l2_sensor_clk_get(s5k3m5->dev, NULL); + if (IS_ERR(s5k3m5->mclk)) + return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->mclk), + "failed to get MCLK clock\n"); + + freq = clk_get_rate(s5k3m5->mclk); + if (freq != S5K3M5_MCLK_FREQ_24MHZ) + return dev_err_probe(s5k3m5->dev, -EINVAL, + "MCLK clock frequency %lu is not supported\n", + freq); + + ret = s5k3m5_check_hwcfg(s5k3m5); + if (ret) + return dev_err_probe(s5k3m5->dev, ret, + "failed to check HW configuration\n"); + + s5k3m5->reset_gpio = devm_gpiod_get_optional(s5k3m5->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(s5k3m5->reset_gpio)) + return dev_err_probe(s5k3m5->dev, PTR_ERR(s5k3m5->reset_gpio), + "cannot get reset GPIO\n"); + + for (i = 0; i < S5K3M5_NUM_SUPPLIES; i++) + s5k3m5->supplies[i].supply = s5k3m5_supply_names[i]; + + ret = devm_regulator_bulk_get(s5k3m5->dev, S5K3M5_NUM_SUPPLIES, + s5k3m5->supplies); + if (ret) + return dev_err_probe(s5k3m5->dev, ret, + "failed to get supply regulators\n"); + + /* The sensor must be powered on to read the CHIP_ID register */ + ret = s5k3m5_power_on(s5k3m5->dev); + if (ret) + return ret; + + ret = s5k3m5_identify_sensor(s5k3m5); + if (ret) { + dev_err_probe(s5k3m5->dev, ret, "failed to find sensor\n"); + goto power_off; + } + + s5k3m5->mode = &s5k3m5_supported_modes[0]; + ret = s5k3m5_init_controls(s5k3m5); + if (ret) { + dev_err_probe(s5k3m5->dev, ret, "failed to init controls\n"); + goto power_off; + } + + s5k3m5->sd.state_lock = s5k3m5->ctrl_handler.lock; + s5k3m5->sd.internal_ops = &s5k3m5_internal_ops; + s5k3m5->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + s5k3m5->sd.entity.ops = &s5k3m5_subdev_entity_ops; + s5k3m5->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + s5k3m5->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&s5k3m5->sd.entity, 1, &s5k3m5->pad); + if (ret) { + dev_err_probe(s5k3m5->dev, ret, + "failed to init media entity pads\n"); + goto v4l2_ctrl_handler_free; + } + + ret = v4l2_subdev_init_finalize(&s5k3m5->sd); + if (ret < 0) { + dev_err_probe(s5k3m5->dev, ret, + "failed to init media entity pads\n"); + goto media_entity_cleanup; + } + + pm_runtime_set_active(s5k3m5->dev); + pm_runtime_enable(s5k3m5->dev); + + ret = v4l2_async_register_subdev_sensor(&s5k3m5->sd); + if (ret < 0) { + dev_err_probe(s5k3m5->dev, ret, + "failed to register V4L2 subdev\n"); + goto subdev_cleanup; + } + + pm_runtime_set_autosuspend_delay(s5k3m5->dev, 1000); + pm_runtime_use_autosuspend(s5k3m5->dev); + pm_runtime_idle(s5k3m5->dev); + + return 0; + +subdev_cleanup: + v4l2_subdev_cleanup(&s5k3m5->sd); + pm_runtime_disable(s5k3m5->dev); + pm_runtime_set_suspended(s5k3m5->dev); + +media_entity_cleanup: + media_entity_cleanup(&s5k3m5->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(s5k3m5->sd.ctrl_handler); + +power_off: + s5k3m5_power_off(s5k3m5->dev); + + return ret; +} + +static void s5k3m5_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k3m5 *s5k3m5 = to_s5k3m5(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(s5k3m5->dev); + + if (!pm_runtime_status_suspended(s5k3m5->dev)) { + s5k3m5_power_off(s5k3m5->dev); + pm_runtime_set_suspended(s5k3m5->dev); + } +} + +static const struct dev_pm_ops s5k3m5_pm_ops = { + SET_RUNTIME_PM_OPS(s5k3m5_power_off, s5k3m5_power_on, NULL) +}; + +static const struct of_device_id s5k3m5_of_match[] = { + { .compatible = "samsung,s5k3m5" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s5k3m5_of_match); + +static struct i2c_driver s5k3m5_i2c_driver = { + .driver = { + .name = "s5k3m5", + .pm = &s5k3m5_pm_ops, + .of_match_table = s5k3m5_of_match, + }, + .probe = s5k3m5_probe, + .remove = s5k3m5_remove, +}; + +module_i2c_driver(s5k3m5_i2c_driver); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>"); +MODULE_DESCRIPTION("Samsung S5K3M5 image sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/s5kjn1.c b/drivers/media/i2c/s5kjn1.c new file mode 100644 index 000000000000..a707cb740556 --- /dev/null +++ b/drivers/media/i2c/s5kjn1.c @@ -0,0 +1,1487 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Linaro Ltd + +#include <linux/clk.h> +#include <linux/delay.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 <linux/units.h> +#include <media/v4l2-cci.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define S5KJN1_LINK_FREQ_700MHZ (700ULL * HZ_PER_MHZ) +#define S5KJN1_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) +#define S5KJN1_DATA_LANES 4 + +/* Register map is similar to MIPI CCS compliant camera sensors */ +#define S5KJN1_REG_CHIP_ID CCI_REG16(0x0000) +#define S5KJN1_CHIP_ID 0x38e1 + +#define S5KJN1_REG_CTRL_MODE CCI_REG8(0x0100) +#define S5KJN1_MODE_STREAMING BIT(0) + +#define S5KJN1_REG_ORIENTATION CCI_REG8(0x0101) +#define S5KJN1_VFLIP BIT(1) +#define S5KJN1_HFLIP BIT(0) + +#define S5KJN1_REG_EXPOSURE CCI_REG16(0x0202) +#define S5KJN1_EXPOSURE_MIN 8 +#define S5KJN1_EXPOSURE_STEP 1 + +#define S5KJN1_REG_AGAIN CCI_REG16(0x0204) +#define S5KJN1_AGAIN_MIN 1 +#define S5KJN1_AGAIN_MAX 64 +#define S5KJN1_AGAIN_STEP 1 +#define S5KJN1_AGAIN_DEFAULT 6 +#define S5KJN1_AGAIN_SHIFT 5 + +#define S5KJN1_REG_VTS CCI_REG16(0x0340) +#define S5KJN1_VTS_MAX 0xffff + +#define S5KJN1_REG_HTS CCI_REG16(0x0342) +#define S5KJN1_REG_X_ADDR_START CCI_REG16(0x0344) +#define S5KJN1_REG_Y_ADDR_START CCI_REG16(0x0346) +#define S5KJN1_REG_X_ADDR_END CCI_REG16(0x0348) +#define S5KJN1_REG_Y_ADDR_END CCI_REG16(0x034a) +#define S5KJN1_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define S5KJN1_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) + +#define S5KJN1_REG_TEST_PATTERN CCI_REG16(0x0600) + +#define to_s5kjn1(_sd) container_of(_sd, struct s5kjn1, sd) + +static const s64 s5kjn1_link_freq_menu[] = { + S5KJN1_LINK_FREQ_700MHZ, +}; + +/* List of supported formats to cover horizontal and vertical flip controls */ +static const u32 s5kjn1_mbus_formats[] = { + MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, +}; + +struct s5kjn1_reg_list { + const struct cci_reg_sequence *regs; + unsigned int num_regs; +}; + +struct s5kjn1_mode { + u32 width; /* Frame width in pixels */ + u32 height; /* Frame height in pixels */ + u32 hts; /* Horizontal timing size */ + u32 vts; /* Default vertical timing size */ + u32 exposure; /* Default exposure value */ + u32 exposure_margin; /* Exposure margin */ + + const struct s5kjn1_reg_list reg_list; /* Sensor register setting */ +}; + +static const char * const s5kjn1_test_pattern_menu[] = { + "Disabled", + "Solid color", + "Color bars", + "Fade to grey color bars", + "PN9", +}; + +struct s5kjn1 { + struct device *dev; + struct regmap *regmap; + struct clk *mclk; + struct gpio_desc *reset_gpio; + struct regulator *afvdd; /* Autofocus actuator power */ + struct regulator *vdda; /* Analog power */ + struct regulator *vddd; /* Digital core power */ + struct regulator *vddio; /* Digital I/O power */ + + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + const struct s5kjn1_mode *mode; +}; + +static const struct cci_reg_sequence init_array_setting[] = { + { CCI_REG16(0x6028), 0x2400 }, + { CCI_REG16(0x602a), 0x1354 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x6f12), 0x7017 }, + { CCI_REG16(0x602a), 0x13b2 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1236 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a0a }, + { CCI_REG16(0x6f12), 0x4c0a }, + { CCI_REG16(0x602a), 0x2210 }, + { CCI_REG16(0x6f12), 0x3401 }, + { CCI_REG16(0x602a), 0x2176 }, + { CCI_REG16(0x6f12), 0x6400 }, + { CCI_REG16(0x602a), 0x222e }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x602a), 0x06b6 }, + { CCI_REG16(0x6f12), 0x0a00 }, + { CCI_REG16(0x602a), 0x06bc }, + { CCI_REG16(0x6f12), 0x1001 }, + { CCI_REG16(0x602a), 0x2140 }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x602a), 0x1a0e }, + { CCI_REG16(0x6f12), 0x9600 }, + { CCI_REG16(0x6028), 0x4000 }, + { CCI_REG16(0xf44e), 0x0011 }, + { CCI_REG16(0xf44c), 0x0b0b }, + { CCI_REG16(0xf44a), 0x0006 }, + { CCI_REG16(0x0118), 0x0002 }, + { CCI_REG16(0x011a), 0x0001 }, +}; + +static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode[] = { + { CCI_REG16(0x6028), 0x2400 }, + { CCI_REG16(0x602a), 0x1a28 }, + { CCI_REG16(0x6f12), 0x4c00 }, + { CCI_REG16(0x602a), 0x065a }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x139e }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x139c }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x13a0 }, + { CCI_REG16(0x6f12), 0x0a00 }, + { CCI_REG16(0x6f12), 0x0120 }, + { CCI_REG16(0x602a), 0x2072 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a64 }, + { CCI_REG16(0x6f12), 0x0301 }, + { CCI_REG16(0x6f12), 0xff00 }, + { CCI_REG16(0x602a), 0x19e6 }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x1a30 }, + { CCI_REG16(0x6f12), 0x3401 }, + { CCI_REG16(0x602a), 0x19fc }, + { CCI_REG16(0x6f12), 0x0b00 }, + { CCI_REG16(0x602a), 0x19f4 }, + { CCI_REG16(0x6f12), 0x0606 }, + { CCI_REG16(0x602a), 0x19f8 }, + { CCI_REG16(0x6f12), 0x1010 }, + { CCI_REG16(0x602a), 0x1b26 }, + { CCI_REG16(0x6f12), 0x6f80 }, + { CCI_REG16(0x6f12), 0xa060 }, + { CCI_REG16(0x602a), 0x1a3c }, + { CCI_REG16(0x6f12), 0x6207 }, + { CCI_REG16(0x602a), 0x1a48 }, + { CCI_REG16(0x6f12), 0x6207 }, + { CCI_REG16(0x602a), 0x1444 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x602a), 0x144c }, + { CCI_REG16(0x6f12), 0x3f00 }, + { CCI_REG16(0x6f12), 0x3f00 }, + { CCI_REG16(0x602a), 0x7f6c }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x6f12), 0x2f00 }, + { CCI_REG16(0x6f12), 0xfa00 }, + { CCI_REG16(0x6f12), 0x2400 }, + { CCI_REG16(0x6f12), 0xe500 }, + { CCI_REG16(0x602a), 0x0650 }, + { CCI_REG16(0x6f12), 0x0600 }, + { CCI_REG16(0x602a), 0x0654 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a46 }, + { CCI_REG16(0x6f12), 0x8a00 }, + { CCI_REG16(0x602a), 0x1a52 }, + { CCI_REG16(0x6f12), 0xbf00 }, + { CCI_REG16(0x602a), 0x0674 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x602a), 0x0668 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x602a), 0x0684 }, + { CCI_REG16(0x6f12), 0x4001 }, + { CCI_REG16(0x602a), 0x0688 }, + { CCI_REG16(0x6f12), 0x4001 }, + { CCI_REG16(0x602a), 0x147c }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x1480 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x19f6 }, + { CCI_REG16(0x6f12), 0x0904 }, + { CCI_REG16(0x602a), 0x0812 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a02 }, + { CCI_REG16(0x6f12), 0x1800 }, + { CCI_REG16(0x602a), 0x2148 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x2042 }, + { CCI_REG16(0x6f12), 0x1a00 }, + { CCI_REG16(0x602a), 0x0874 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x09c0 }, + { CCI_REG16(0x6f12), 0x2008 }, + { CCI_REG16(0x602a), 0x09c4 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x602a), 0x19fe }, + { CCI_REG16(0x6f12), 0x0e1c }, + { CCI_REG16(0x602a), 0x4d92 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x84c8 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x4d94 }, + { CCI_REG16(0x6f12), 0x0005 }, + { CCI_REG16(0x6f12), 0x000a }, + { CCI_REG16(0x6f12), 0x0010 }, + { CCI_REG16(0x6f12), 0x0810 }, + { CCI_REG16(0x6f12), 0x000a }, + { CCI_REG16(0x6f12), 0x0040 }, + { CCI_REG16(0x6f12), 0x0810 }, + { CCI_REG16(0x6f12), 0x0810 }, + { CCI_REG16(0x6f12), 0x8002 }, + { CCI_REG16(0x6f12), 0xfd03 }, + { CCI_REG16(0x6f12), 0x0010 }, + { CCI_REG16(0x6f12), 0x1510 }, + { CCI_REG16(0x602a), 0x3570 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x3574 }, + { CCI_REG16(0x6f12), 0x1201 }, + { CCI_REG16(0x602a), 0x21e4 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x602a), 0x21ec }, + { CCI_REG16(0x6f12), 0x1f04 }, + { CCI_REG16(0x602a), 0x2080 }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x6f12), 0xff00 }, + { CCI_REG16(0x6f12), 0x7f01 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x6f12), 0x8001 }, + { CCI_REG16(0x6f12), 0xd244 }, + { CCI_REG16(0x6f12), 0xd244 }, + { CCI_REG16(0x6f12), 0x14f4 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x20ba }, + { CCI_REG16(0x6f12), 0x141c }, + { CCI_REG16(0x6f12), 0x111c }, + { CCI_REG16(0x6f12), 0x54f4 }, + { CCI_REG16(0x602a), 0x120e }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x212e }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x13ae }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x602a), 0x0718 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x602a), 0x0710 }, + { CCI_REG16(0x6f12), 0x0002 }, + { CCI_REG16(0x6f12), 0x0804 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x1b5c }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0786 }, + { CCI_REG16(0x6f12), 0x7701 }, + { CCI_REG16(0x602a), 0x2022 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x602a), 0x1360 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x1376 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x6f12), 0x6038 }, + { CCI_REG16(0x6f12), 0x7038 }, + { CCI_REG16(0x6f12), 0x8038 }, + { CCI_REG16(0x602a), 0x1386 }, + { CCI_REG16(0x6f12), 0x0b00 }, + { CCI_REG16(0x602a), 0x06fa }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x4a94 }, + { CCI_REG16(0x6f12), 0x0900 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0300 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0300 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0900 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0a76 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0aee }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0b66 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0bde }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0be8 }, + { CCI_REG16(0x6f12), 0x3000 }, + { CCI_REG16(0x6f12), 0x3000 }, + { CCI_REG16(0x602a), 0x0c56 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0c60 }, + { CCI_REG16(0x6f12), 0x3000 }, + { CCI_REG16(0x6f12), 0x3000 }, + { CCI_REG16(0x602a), 0x0cb6 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x0cf2 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x602a), 0x0cf0 }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x602a), 0x11b8 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x11f6 }, + { CCI_REG16(0x6f12), 0x0020 }, + { CCI_REG16(0x602a), 0x4a74 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0xd8ff }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0xd8ff }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x218e }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x2268 }, + { CCI_REG16(0x6f12), 0xf279 }, + { CCI_REG16(0x602a), 0x5006 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x500e }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x4e70 }, + { CCI_REG16(0x6f12), 0x2062 }, + { CCI_REG16(0x6f12), 0x5501 }, + { CCI_REG16(0x602a), 0x06dc }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6028), 0x4000 }, + { CCI_REG16(0xf46a), 0xae80 }, + { S5KJN1_REG_X_ADDR_START, 0x0000 }, + { S5KJN1_REG_Y_ADDR_START, 0x0000 }, + { S5KJN1_REG_X_ADDR_END, 0x1fff }, + { S5KJN1_REG_Y_ADDR_END, 0x181f }, + { S5KJN1_REG_X_OUTPUT_SIZE, 0x0ff0 }, + { S5KJN1_REG_Y_OUTPUT_SIZE, 0x0c00 }, + { CCI_REG16(0x0350), 0x0008 }, + { CCI_REG16(0x0352), 0x0008 }, + { CCI_REG16(0x0900), 0x0122 }, + { CCI_REG16(0x0380), 0x0002 }, + { CCI_REG16(0x0382), 0x0002 }, + { CCI_REG16(0x0384), 0x0002 }, + { CCI_REG16(0x0386), 0x0002 }, + { CCI_REG16(0x0110), 0x1002 }, + { CCI_REG16(0x0114), 0x0301 }, + { CCI_REG16(0x0116), 0x3000 }, + + /* Clock settings */ + { CCI_REG16(0x0136), 0x1800 }, + { CCI_REG16(0x013e), 0x0000 }, + { CCI_REG16(0x0300), 0x0006 }, + { CCI_REG16(0x0302), 0x0001 }, + { CCI_REG16(0x0304), 0x0004 }, + { CCI_REG16(0x0306), 0x008c }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0004 }, + { CCI_REG16(0x0310), 0x0092 }, + { CCI_REG16(0x0312), 0x0000 }, + + { CCI_REG16(0x080e), 0x0000 }, + { S5KJN1_REG_VTS, 0x10c0 }, + { S5KJN1_REG_HTS, 0x1100 }, + { CCI_REG16(0x0702), 0x0000 }, + { S5KJN1_REG_EXPOSURE, 0x0100 }, + { CCI_REG16(0x0200), 0x0100 }, + { CCI_REG16(0x0d00), 0x0101 }, + { CCI_REG16(0x0d02), 0x0101 }, + { CCI_REG16(0x0d04), 0x0102 }, + { CCI_REG16(0x6226), 0x0000 }, + { CCI_REG16(0x0816), 0x1c00 }, +}; + +static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_mode[] = { + { CCI_REG16(0x6028), 0x2400 }, + { CCI_REG16(0x602a), 0x1a28 }, + { CCI_REG16(0x6f12), 0x4c00 }, + { CCI_REG16(0x602a), 0x065a }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x139e }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x602a), 0x139c }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x13a0 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0120 }, + { CCI_REG16(0x602a), 0x2072 }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x602a), 0x1a64 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x19e6 }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x1a30 }, + { CCI_REG16(0x6f12), 0x3403 }, + { CCI_REG16(0x602a), 0x19fc }, + { CCI_REG16(0x6f12), 0x0700 }, + { CCI_REG16(0x602a), 0x19f4 }, + { CCI_REG16(0x6f12), 0x0707 }, + { CCI_REG16(0x602a), 0x19f8 }, + { CCI_REG16(0x6f12), 0x0b0b }, + { CCI_REG16(0x602a), 0x1b26 }, + { CCI_REG16(0x6f12), 0x6f80 }, + { CCI_REG16(0x6f12), 0xa060 }, + { CCI_REG16(0x602a), 0x1a3c }, + { CCI_REG16(0x6f12), 0x8207 }, + { CCI_REG16(0x602a), 0x1a48 }, + { CCI_REG16(0x6f12), 0x8207 }, + { CCI_REG16(0x602a), 0x1444 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x6f12), 0x2000 }, + { CCI_REG16(0x602a), 0x144c }, + { CCI_REG16(0x6f12), 0x3f00 }, + { CCI_REG16(0x6f12), 0x3f00 }, + { CCI_REG16(0x602a), 0x7f6c }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x6f12), 0x2f00 }, + { CCI_REG16(0x6f12), 0xfa00 }, + { CCI_REG16(0x6f12), 0x2400 }, + { CCI_REG16(0x6f12), 0xe500 }, + { CCI_REG16(0x602a), 0x0650 }, + { CCI_REG16(0x6f12), 0x0600 }, + { CCI_REG16(0x602a), 0x0654 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a46 }, + { CCI_REG16(0x6f12), 0x8500 }, + { CCI_REG16(0x602a), 0x1a52 }, + { CCI_REG16(0x6f12), 0x9800 }, + { CCI_REG16(0x602a), 0x0674 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x602a), 0x0668 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x602a), 0x0684 }, + { CCI_REG16(0x6f12), 0x4001 }, + { CCI_REG16(0x602a), 0x0688 }, + { CCI_REG16(0x6f12), 0x4001 }, + { CCI_REG16(0x602a), 0x147c }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x602a), 0x1480 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x602a), 0x19f6 }, + { CCI_REG16(0x6f12), 0x0404 }, + { CCI_REG16(0x602a), 0x0812 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1a02 }, + { CCI_REG16(0x6f12), 0x1800 }, + { CCI_REG16(0x602a), 0x2148 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x2042 }, + { CCI_REG16(0x6f12), 0x1a00 }, + { CCI_REG16(0x602a), 0x0874 }, + { CCI_REG16(0x6f12), 0x0106 }, + { CCI_REG16(0x602a), 0x09c0 }, + { CCI_REG16(0x6f12), 0x4000 }, + { CCI_REG16(0x602a), 0x09c4 }, + { CCI_REG16(0x6f12), 0x4000 }, + { CCI_REG16(0x602a), 0x19fe }, + { CCI_REG16(0x6f12), 0x0c1c }, + { CCI_REG16(0x602a), 0x4d92 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x84c8 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x4d94 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x3570 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x3574 }, + { CCI_REG16(0x6f12), 0x7306 }, + { CCI_REG16(0x602a), 0x21e4 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x602a), 0x21ec }, + { CCI_REG16(0x6f12), 0x6902 }, + { CCI_REG16(0x602a), 0x2080 }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x6f12), 0xff00 }, + { CCI_REG16(0x6f12), 0x0002 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x6f12), 0x0002 }, + { CCI_REG16(0x6f12), 0xd244 }, + { CCI_REG16(0x6f12), 0xd244 }, + { CCI_REG16(0x6f12), 0x14f4 }, + { CCI_REG16(0x6f12), 0x101c }, + { CCI_REG16(0x6f12), 0x0d1c }, + { CCI_REG16(0x6f12), 0x54f4 }, + { CCI_REG16(0x602a), 0x20ba }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x120e }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x212e }, + { CCI_REG16(0x6f12), 0x0200 }, + { CCI_REG16(0x602a), 0x13ae }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x0718 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0710 }, + { CCI_REG16(0x6f12), 0x0010 }, + { CCI_REG16(0x6f12), 0x0201 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x602a), 0x1b5c }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0786 }, + { CCI_REG16(0x6f12), 0x1401 }, + { CCI_REG16(0x602a), 0x2022 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x6f12), 0x0500 }, + { CCI_REG16(0x602a), 0x1360 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x1376 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x6038 }, + { CCI_REG16(0x6f12), 0x7038 }, + { CCI_REG16(0x6f12), 0x8038 }, + { CCI_REG16(0x602a), 0x1386 }, + { CCI_REG16(0x6f12), 0x0b00 }, + { CCI_REG16(0x602a), 0x06fa }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x4a94 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0400 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x6f12), 0x0800 }, + { CCI_REG16(0x602a), 0x0a76 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0aee }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0b66 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0bde }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0be8 }, + { CCI_REG16(0x6f12), 0x5000 }, + { CCI_REG16(0x6f12), 0x5000 }, + { CCI_REG16(0x602a), 0x0c56 }, + { CCI_REG16(0x6f12), 0x1000 }, + { CCI_REG16(0x602a), 0x0c60 }, + { CCI_REG16(0x6f12), 0x5000 }, + { CCI_REG16(0x6f12), 0x5000 }, + { CCI_REG16(0x602a), 0x0cb6 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x0cf2 }, + { CCI_REG16(0x6f12), 0x0001 }, + { CCI_REG16(0x602a), 0x0cf0 }, + { CCI_REG16(0x6f12), 0x0101 }, + { CCI_REG16(0x602a), 0x11b8 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x11f6 }, + { CCI_REG16(0x6f12), 0x0010 }, + { CCI_REG16(0x602a), 0x4a74 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x218e }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x2268 }, + { CCI_REG16(0x6f12), 0xf279 }, + { CCI_REG16(0x602a), 0x5006 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x602a), 0x500e }, + { CCI_REG16(0x6f12), 0x0100 }, + { CCI_REG16(0x602a), 0x4e70 }, + { CCI_REG16(0x6f12), 0x2062 }, + { CCI_REG16(0x6f12), 0x5501 }, + { CCI_REG16(0x602a), 0x06dc }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6f12), 0x0000 }, + { CCI_REG16(0x6028), 0x4000 }, + { CCI_REG16(0xf46a), 0xae80 }, + { S5KJN1_REG_X_ADDR_START, 0x0000 }, + { S5KJN1_REG_Y_ADDR_START, 0x0000 }, + { S5KJN1_REG_X_ADDR_END, 0x1fff }, + { S5KJN1_REG_Y_ADDR_END, 0x181f }, + { S5KJN1_REG_X_OUTPUT_SIZE, 0x1fe0 }, + { S5KJN1_REG_Y_OUTPUT_SIZE, 0x1800 }, + { CCI_REG16(0x0350), 0x0010 }, + { CCI_REG16(0x0352), 0x0010 }, + { CCI_REG16(0x0900), 0x0111 }, + { CCI_REG16(0x0380), 0x0001 }, + { CCI_REG16(0x0382), 0x0001 }, + { CCI_REG16(0x0384), 0x0001 }, + { CCI_REG16(0x0386), 0x0001 }, + { CCI_REG16(0x0110), 0x1002 }, + { CCI_REG16(0x0114), 0x0300 }, + { CCI_REG16(0x0116), 0x3000 }, + + /* Clock settings */ + { CCI_REG16(0x0136), 0x1800 }, + { CCI_REG16(0x013e), 0x0000 }, + { CCI_REG16(0x0300), 0x0006 }, + { CCI_REG16(0x0302), 0x0001 }, + { CCI_REG16(0x0304), 0x0004 }, + { CCI_REG16(0x0306), 0x008c }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0004 }, + { CCI_REG16(0x0310), 0x0074 }, + { CCI_REG16(0x0312), 0x0000 }, + + { CCI_REG16(0x080e), 0x0000 }, + { S5KJN1_REG_VTS, 0x1900 }, + { S5KJN1_REG_HTS, 0x21f0 }, + { CCI_REG16(0x0702), 0x0000 }, + { S5KJN1_REG_EXPOSURE, 0x0100 }, + { CCI_REG16(0x0200), 0x0100 }, + { CCI_REG16(0x0d00), 0x0100 }, + { CCI_REG16(0x0d02), 0x0001 }, + { CCI_REG16(0x0d04), 0x0002 }, + { CCI_REG16(0x6226), 0x0000 }, +}; + +static const struct s5kjn1_mode s5kjn1_supported_modes[] = { + { + .width = 4080, + .height = 3072, + .hts = 4352, + .vts = 4288, + .exposure = 3840, + .exposure_margin = 22, + .reg_list = { + .regs = s5kjn1_4080x3072_30fps_mode, + .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_mode), + }, + }, + { + .width = 8160, + .height = 6144, + .hts = 8688, + .vts = 6400, + .exposure = 6144, + .exposure_margin = 44, + .reg_list = { + .regs = s5kjn1_8160x6144_10fps_mode, + .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_mode), + }, + }, +}; + +static int s5kjn1_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5kjn1 *s5kjn1 = container_of(ctrl->handler, struct s5kjn1, + ctrl_handler); + const struct s5kjn1_mode *mode = s5kjn1->mode; + s64 exposure_max; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->height + ctrl->val - mode->exposure_margin; + __v4l2_ctrl_modify_range(s5kjn1->exposure, + s5kjn1->exposure->minimum, + exposure_max, + s5kjn1->exposure->step, + s5kjn1->exposure->default_value); + break; + } + + /* V4L2 controls are applied, when sensor is powered up for streaming */ + if (!pm_runtime_get_if_active(s5kjn1->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_AGAIN, + ctrl->val << S5KJN1_AGAIN_SHIFT, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_EXPOSURE, + ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_VTS, + ctrl->val + mode->height, NULL); + break; + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_ORIENTATION, + (s5kjn1->vflip->val ? S5KJN1_VFLIP : 0) | + (s5kjn1->hflip->val ? S5KJN1_HFLIP : 0), NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_TEST_PATTERN, + ctrl->val, NULL); + break; + default: + ret = -EINVAL; + break; + } + + pm_runtime_put(s5kjn1->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops s5kjn1_ctrl_ops = { + .s_ctrl = s5kjn1_set_ctrl, +}; + +static inline u64 s5kjn1_freq_to_pixel_rate(const u64 freq) +{ + return div_u64(freq * 2 * S5KJN1_DATA_LANES, 10); +} + +static int s5kjn1_init_controls(struct s5kjn1 *s5kjn1) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &s5kjn1->ctrl_handler; + const struct s5kjn1_mode *mode = s5kjn1->mode; + s64 pixel_rate, hblank, vblank, exposure_max; + struct v4l2_fwnode_device_properties props; + int ret; + + v4l2_ctrl_handler_init(ctrl_hdlr, 9); + + s5kjn1->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(s5kjn1_link_freq_menu) - 1, + 0, s5kjn1_link_freq_menu); + if (s5kjn1->link_freq) + s5kjn1->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate = s5kjn1_freq_to_pixel_rate(s5kjn1_link_freq_menu[0]); + s5kjn1->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 0, pixel_rate, 1, pixel_rate); + + hblank = mode->hts - mode->width; + s5kjn1->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_HBLANK, hblank, + hblank, 1, hblank); + if (s5kjn1->hblank) + s5kjn1->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank = mode->vts - mode->height; + s5kjn1->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_VBLANK, vblank, + S5KJN1_VTS_MAX - mode->height, 1, + vblank); + + v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + S5KJN1_AGAIN_MIN, S5KJN1_AGAIN_MAX, + S5KJN1_AGAIN_STEP, S5KJN1_AGAIN_DEFAULT); + + exposure_max = mode->vts - mode->exposure_margin; + s5kjn1->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_EXPOSURE, + S5KJN1_EXPOSURE_MIN, + exposure_max, + S5KJN1_EXPOSURE_STEP, + mode->exposure); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(s5kjn1_test_pattern_menu) - 1, + 0, 0, s5kjn1_test_pattern_menu); + + s5kjn1->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (s5kjn1->hflip) + s5kjn1->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + s5kjn1->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &s5kjn1_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (s5kjn1->vflip) + s5kjn1->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ret = v4l2_fwnode_device_parse(s5kjn1->dev, &props); + if (ret) + goto error_free_hdlr; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &s5kjn1_ctrl_ops, + &props); + if (ret) + goto error_free_hdlr; + + s5kjn1->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_free_hdlr: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int s5kjn1_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + const struct s5kjn1_reg_list *reg_list = &s5kjn1->mode->reg_list; + int ret; + + ret = pm_runtime_resume_and_get(s5kjn1->dev); + if (ret) + return ret; + + /* Page pointer */ + cci_write(s5kjn1->regmap, CCI_REG16(0x6028), 0x4000, &ret); + + /* Set version */ + cci_write(s5kjn1->regmap, CCI_REG16(0x0000), 0x0003, &ret); + cci_write(s5kjn1->regmap, CCI_REG16(0x0000), S5KJN1_CHIP_ID, &ret); + cci_write(s5kjn1->regmap, CCI_REG16(0x001e), 0x0007, &ret); + cci_write(s5kjn1->regmap, CCI_REG16(0x6028), 0x4000, &ret); + cci_write(s5kjn1->regmap, CCI_REG16(0x6010), 0x0001, &ret); + if (ret) + goto error; + + usleep_range(5 * USEC_PER_MSEC, 6 * USEC_PER_MSEC); + + cci_write(s5kjn1->regmap, CCI_REG16(0x6226), 0x0001, &ret); + if (ret) + goto error; + + usleep_range(10 * USEC_PER_MSEC, 11 * USEC_PER_MSEC); + + /* Sensor init settings */ + cci_multi_reg_write(s5kjn1->regmap, init_array_setting, + ARRAY_SIZE(init_array_setting), &ret); + cci_multi_reg_write(s5kjn1->regmap, reg_list->regs, + reg_list->num_regs, &ret); + if (ret) + goto error; + + ret = __v4l2_ctrl_handler_setup(s5kjn1->sd.ctrl_handler); + + cci_write(s5kjn1->regmap, S5KJN1_REG_CTRL_MODE, + S5KJN1_MODE_STREAMING, &ret); + if (ret) + goto error; + + return 0; + +error: + dev_err(s5kjn1->dev, "failed to start streaming: %d\n", ret); + pm_runtime_put_autosuspend(s5kjn1->dev); + + return ret; +} + +static int s5kjn1_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + int ret; + + ret = cci_write(s5kjn1->regmap, S5KJN1_REG_CTRL_MODE, 0x0, NULL); + if (ret) + dev_err(s5kjn1->dev, "failed to stop streaming: %d\n", ret); + + pm_runtime_put_autosuspend(s5kjn1->dev); + + return ret; +} + +static u32 s5kjn1_get_format_code(struct s5kjn1 *s5kjn1) +{ + unsigned int i; + + i = (s5kjn1->vflip->val ? 2 : 0) | (s5kjn1->hflip->val ? 1 : 0); + + return s5kjn1_mbus_formats[i]; +} + +static void s5kjn1_update_pad_format(struct s5kjn1 *s5kjn1, + const struct s5kjn1_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->code = s5kjn1_get_format_code(s5kjn1); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int s5kjn1_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + s64 hblank, vblank, exposure_max; + const struct s5kjn1_mode *mode; + + mode = v4l2_find_nearest_size(s5kjn1_supported_modes, + ARRAY_SIZE(s5kjn1_supported_modes), + width, height, + fmt->format.width, fmt->format.height); + + s5kjn1_update_pad_format(s5kjn1, mode, &fmt->format); + + /* Format code could be updated with respect to flip controls */ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY || s5kjn1->mode == mode) + goto set_format; + + /* Update limits and set FPS and exposure to default values */ + hblank = mode->hts - mode->width; + __v4l2_ctrl_modify_range(s5kjn1->hblank, hblank, hblank, 1, hblank); + + vblank = mode->vts - mode->height; + __v4l2_ctrl_modify_range(s5kjn1->vblank, vblank, + S5KJN1_VTS_MAX - mode->height, 1, vblank); + __v4l2_ctrl_s_ctrl(s5kjn1->vblank, vblank); + + exposure_max = mode->vts - mode->exposure_margin; + __v4l2_ctrl_modify_range(s5kjn1->exposure, S5KJN1_EXPOSURE_MIN, + exposure_max, S5KJN1_EXPOSURE_STEP, + mode->exposure); + __v4l2_ctrl_s_ctrl(s5kjn1->exposure, mode->exposure); + + if (s5kjn1->sd.ctrl_handler->error) + return s5kjn1->sd.ctrl_handler->error; + + s5kjn1->mode = mode; + +set_format: + *v4l2_subdev_state_get_format(state, 0) = fmt->format; + + return 0; +} + +static int s5kjn1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + + /* Media bus code index is constant, but code formats are not */ + if (code->index > 0) + return -EINVAL; + + code->code = s5kjn1_get_format_code(s5kjn1); + + return 0; +} + +static int s5kjn1_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + + if (fse->index >= ARRAY_SIZE(s5kjn1_supported_modes)) + return -EINVAL; + + if (fse->code != s5kjn1_get_format_code(s5kjn1)) + return -EINVAL; + + fse->min_width = s5kjn1_supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = s5kjn1_supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int s5kjn1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = s5kjn1->mode->width; + sel->r.height = s5kjn1->mode->width; + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int s5kjn1_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + /* Media bus code depends on current flip controls */ + .width = s5kjn1->mode->width, + .height = s5kjn1->mode->height, + }, + }; + + s5kjn1_set_pad_format(sd, state, &fmt); + + return 0; +} + +static const struct v4l2_subdev_video_ops s5kjn1_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops s5kjn1_pad_ops = { + .set_fmt = s5kjn1_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .get_selection = s5kjn1_get_selection, + .enum_mbus_code = s5kjn1_enum_mbus_code, + .enum_frame_size = s5kjn1_enum_frame_size, + .enable_streams = s5kjn1_enable_streams, + .disable_streams = s5kjn1_disable_streams, +}; + +static const struct v4l2_subdev_ops s5kjn1_subdev_ops = { + .video = &s5kjn1_video_ops, + .pad = &s5kjn1_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops s5kjn1_internal_ops = { + .init_state = s5kjn1_init_state, +}; + +static const struct media_entity_operations s5kjn1_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int s5kjn1_identify_sensor(struct s5kjn1 *s5kjn1) +{ + u64 val; + int ret; + + ret = cci_read(s5kjn1->regmap, S5KJN1_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(s5kjn1->dev, "failed to read chip id: %d\n", ret); + return ret; + } + + if (val != S5KJN1_CHIP_ID) { + dev_err(s5kjn1->dev, "chip id mismatch: %x!=%llx\n", + S5KJN1_CHIP_ID, val); + return -ENODEV; + } + + return 0; +} + +static int s5kjn1_check_hwcfg(struct s5kjn1 *s5kjn1) +{ + struct fwnode_handle *fwnode = dev_fwnode(s5kjn1->dev), *ep; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus = { + .mipi_csi2 = { + .num_data_lanes = S5KJN1_DATA_LANES, + }, + }, + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned long freq_bitmap; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != S5KJN1_DATA_LANES) { + dev_err(s5kjn1->dev, "Invalid number of data lanes: %u\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(s5kjn1->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + s5kjn1_link_freq_menu, + ARRAY_SIZE(s5kjn1_link_freq_menu), + &freq_bitmap); + +endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int s5kjn1_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + int ret; + + if (s5kjn1->vddd) { + ret = regulator_enable(s5kjn1->vddd); + if (ret) + return ret; + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + } + + if (s5kjn1->vdda) { + ret = regulator_enable(s5kjn1->vdda); + if (ret) + goto disable_vddd; + } + + if (s5kjn1->vddio) { + ret = regulator_enable(s5kjn1->vddio); + if (ret) + goto disable_vdda; + } + + if (s5kjn1->afvdd) { + ret = regulator_enable(s5kjn1->afvdd); + if (ret) + goto disable_vddio; + } + + ret = clk_prepare_enable(s5kjn1->mclk); + if (ret) + goto disable_regulators; + + gpiod_set_value_cansleep(s5kjn1->reset_gpio, 0); + usleep_range(10 * USEC_PER_MSEC, 15 * USEC_PER_MSEC); + + return 0; + +disable_regulators: + if (s5kjn1->afvdd) + regulator_disable(s5kjn1->afvdd); + +disable_vddio: + if (s5kjn1->vddio) + regulator_disable(s5kjn1->vddio); + +disable_vdda: + if (s5kjn1->vdda) + regulator_disable(s5kjn1->vdda); + +disable_vddd: + if (s5kjn1->vddd) { + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + regulator_disable(s5kjn1->vddd); + } + + return ret; +} + +static int s5kjn1_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + + gpiod_set_value_cansleep(s5kjn1->reset_gpio, 1); + + clk_disable_unprepare(s5kjn1->mclk); + + if (s5kjn1->afvdd) + regulator_disable(s5kjn1->afvdd); + + if (s5kjn1->vddio) + regulator_disable(s5kjn1->vddio); + + if (s5kjn1->vdda) + regulator_disable(s5kjn1->vdda); + + if (s5kjn1->vddd) { + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + regulator_disable(s5kjn1->vddd); + } + + return 0; +} + +static int s5kjn1_probe(struct i2c_client *client) +{ + struct s5kjn1 *s5kjn1; + unsigned long freq; + int ret; + + s5kjn1 = devm_kzalloc(&client->dev, sizeof(*s5kjn1), GFP_KERNEL); + if (!s5kjn1) + return -ENOMEM; + + s5kjn1->dev = &client->dev; + v4l2_i2c_subdev_init(&s5kjn1->sd, client, &s5kjn1_subdev_ops); + + s5kjn1->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(s5kjn1->regmap)) + return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->regmap), + "failed to init CCI\n"); + + s5kjn1->mclk = devm_v4l2_sensor_clk_get(s5kjn1->dev, NULL); + if (IS_ERR(s5kjn1->mclk)) + return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->mclk), + "failed to get MCLK clock\n"); + + freq = clk_get_rate(s5kjn1->mclk); + if (freq != S5KJN1_MCLK_FREQ_24MHZ) + return dev_err_probe(s5kjn1->dev, -EINVAL, + "MCLK clock frequency %lu is not supported\n", + freq); + + ret = s5kjn1_check_hwcfg(s5kjn1); + if (ret) + return dev_err_probe(s5kjn1->dev, ret, + "failed to check HW configuration\n"); + + s5kjn1->reset_gpio = devm_gpiod_get_optional(s5kjn1->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(s5kjn1->reset_gpio)) + return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->reset_gpio), + "cannot get reset GPIO\n"); + + s5kjn1->afvdd = devm_regulator_get_optional(s5kjn1->dev, "afvdd"); + if (IS_ERR(s5kjn1->afvdd)) { + ret = PTR_ERR(s5kjn1->afvdd); + if (ret != -ENODEV) { + return dev_err_probe(s5kjn1->dev, ret, + "Failed to get 'afvdd' regulator\n"); + } + + s5kjn1->afvdd = NULL; + } + + s5kjn1->vdda = devm_regulator_get_optional(s5kjn1->dev, "vdda"); + if (IS_ERR(s5kjn1->vdda)) { + ret = PTR_ERR(s5kjn1->vdda); + if (ret != -ENODEV) { + return dev_err_probe(s5kjn1->dev, ret, + "Failed to get 'vdda' regulator\n"); + } + + s5kjn1->vdda = NULL; + } + + s5kjn1->vddd = devm_regulator_get_optional(s5kjn1->dev, "vddd"); + if (IS_ERR(s5kjn1->vddd)) { + ret = PTR_ERR(s5kjn1->vddd); + if (ret != -ENODEV) { + return dev_err_probe(s5kjn1->dev, ret, + "Failed to get 'vddd' regulator\n"); + } + + s5kjn1->vddd = NULL; + } + + s5kjn1->vddio = devm_regulator_get_optional(s5kjn1->dev, "vddio"); + if (IS_ERR(s5kjn1->vddio)) { + ret = PTR_ERR(s5kjn1->vddio); + if (ret != -ENODEV) { + return dev_err_probe(s5kjn1->dev, ret, + "Failed to get 'vddio' regulator\n"); + } + + s5kjn1->vddio = NULL; + } + + /* The sensor must be powered on to read the CHIP_ID register */ + ret = s5kjn1_power_on(s5kjn1->dev); + if (ret) + return ret; + + ret = s5kjn1_identify_sensor(s5kjn1); + if (ret) { + dev_err_probe(s5kjn1->dev, ret, "failed to find sensor\n"); + goto power_off; + } + + s5kjn1->mode = &s5kjn1_supported_modes[0]; + ret = s5kjn1_init_controls(s5kjn1); + if (ret) { + dev_err_probe(s5kjn1->dev, ret, "failed to init controls\n"); + goto power_off; + } + + s5kjn1->sd.state_lock = s5kjn1->ctrl_handler.lock; + s5kjn1->sd.internal_ops = &s5kjn1_internal_ops; + s5kjn1->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + s5kjn1->sd.entity.ops = &s5kjn1_subdev_entity_ops; + s5kjn1->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + s5kjn1->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&s5kjn1->sd.entity, 1, &s5kjn1->pad); + if (ret) { + dev_err_probe(s5kjn1->dev, ret, + "failed to init media entity pads\n"); + goto v4l2_ctrl_handler_free; + } + + ret = v4l2_subdev_init_finalize(&s5kjn1->sd); + if (ret < 0) { + dev_err_probe(s5kjn1->dev, ret, + "failed to init media entity pads\n"); + goto media_entity_cleanup; + } + + pm_runtime_set_active(s5kjn1->dev); + pm_runtime_enable(s5kjn1->dev); + + ret = v4l2_async_register_subdev_sensor(&s5kjn1->sd); + if (ret < 0) { + dev_err_probe(s5kjn1->dev, ret, + "failed to register V4L2 subdev\n"); + goto subdev_cleanup; + } + + pm_runtime_set_autosuspend_delay(s5kjn1->dev, 1000); + pm_runtime_use_autosuspend(s5kjn1->dev); + pm_runtime_idle(s5kjn1->dev); + + return 0; + +subdev_cleanup: + v4l2_subdev_cleanup(&s5kjn1->sd); + pm_runtime_disable(s5kjn1->dev); + pm_runtime_set_suspended(s5kjn1->dev); + +media_entity_cleanup: + media_entity_cleanup(&s5kjn1->sd.entity); + +v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(s5kjn1->sd.ctrl_handler); + +power_off: + s5kjn1_power_off(s5kjn1->dev); + + return ret; +} + +static void s5kjn1_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + pm_runtime_disable(s5kjn1->dev); + + if (!pm_runtime_status_suspended(s5kjn1->dev)) { + s5kjn1_power_off(s5kjn1->dev); + pm_runtime_set_suspended(s5kjn1->dev); + } +} + +static const struct dev_pm_ops s5kjn1_pm_ops = { + SET_RUNTIME_PM_OPS(s5kjn1_power_off, s5kjn1_power_on, NULL) +}; + +static const struct of_device_id s5kjn1_of_match[] = { + { .compatible = "samsung,s5kjn1" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s5kjn1_of_match); + +static struct i2c_driver s5kjn1_i2c_driver = { + .driver = { + .name = "s5kjn1", + .pm = &s5kjn1_pm_ops, + .of_match_table = s5kjn1_of_match, + }, + .probe = s5kjn1_probe, + .remove = s5kjn1_remove, +}; + +module_i2c_driver(s5kjn1_i2c_driver); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>"); +MODULE_DESCRIPTION("Samsung S5KJN1 image sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index fb09e4560d8a..90ae4121a68a 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -49,8 +49,6 @@ MODULE_LICENSE("GPL"); /* ---------------------------------------------------------------------- */ #define UNSET (-1U) -#define PREFIX "saa6588: " -#define dprintk if (debug) printk struct saa6588 { struct v4l2_subdev sd; @@ -144,14 +142,14 @@ static bool block_from_buf(struct saa6588 *s, unsigned char *buf) if (s->rd_index == s->wr_index) { if (debug > 2) - dprintk(PREFIX "Read: buffer empty.\n"); + v4l2_info(&s->sd, "Read: buffer empty.\n"); return false; } if (debug > 2) { - dprintk(PREFIX "Read: "); + v4l2_info(&s->sd, "Read: "); for (i = s->rd_index; i < s->rd_index + 3; i++) - dprintk("0x%02x ", s->buffer[i]); + v4l2_info(&s->sd, "0x%02x ", s->buffer[i]); } memcpy(buf, &s->buffer[s->rd_index], 3); @@ -162,7 +160,7 @@ static bool block_from_buf(struct saa6588 *s, unsigned char *buf) s->block_count--; if (debug > 2) - dprintk("%d blocks total.\n", s->block_count); + v4l2_info(&s->sd, "%d blocks total.\n", s->block_count); return true; } @@ -222,11 +220,11 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) unsigned int i; if (debug > 3) - dprintk(PREFIX "New block: "); + v4l2_info(&s->sd, "New block: "); for (i = 0; i < 3; ++i) { if (debug > 3) - dprintk("0x%02x ", blockbuf[i]); + v4l2_info(&s->sd, "0x%02x ", blockbuf[i]); s->buffer[s->wr_index] = blockbuf[i]; s->wr_index++; } @@ -242,7 +240,7 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) s->block_count++; if (debug > 3) - dprintk("%d blocks total.\n", s->block_count); + v4l2_info(&s->sd, "%d blocks total.\n", s->block_count); } static void saa6588_i2c_poll(struct saa6588 *s) @@ -257,7 +255,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) SAA6588 returns garbage otherwise. */ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { if (debug > 1) - dprintk(PREFIX "read error!\n"); + v4l2_info(&s->sd, "read error!\n"); return; } @@ -267,7 +265,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) blocknum = tmpbuf[0] >> 5; if (blocknum == s->last_blocknum) { if (debug > 3) - dprintk("Saw block %d again.\n", blocknum); + v4l2_info(&s->sd, "Saw block %d again.\n", blocknum); return; } @@ -370,12 +368,13 @@ static void saa6588_configure(struct saa6588 *s) break; } - dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", - buf[0], buf[1], buf[2]); + if (debug) + v4l2_info(&s->sd, "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", + buf[0], buf[1], buf[2]); rc = i2c_master_send(client, buf, 3); if (rc != 3) - printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); + v4l2_info(&s->sd, "i2c i/o error: rc == %d (should be 3)\n", rc); } /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index b996a05e56f2..c3eafd5d5dc8 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -228,6 +228,7 @@ static int tw9903_probe(struct i2c_client *client) if (write_regs(sd, initial_registers) < 0) { v4l2_err(client, "error initializing TW9903\n"); + v4l2_ctrl_handler_free(hdl); return -EINVAL; } diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 6220f4fddbab..0ab43fe42d7f 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -196,6 +196,7 @@ static int tw9906_probe(struct i2c_client *client) if (write_regs(sd, initial_registers) < 0) { v4l2_err(client, "error initializing TW9906\n"); + v4l2_ctrl_handler_free(hdl); return -EINVAL; } diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index c0dd4ae57227..5a4465b290f0 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -679,6 +679,23 @@ void media_device_unregister_entity(struct media_entity *entity) } EXPORT_SYMBOL_GPL(media_device_unregister_entity); +#ifdef CONFIG_DEBUG_FS +/* + * Log the state of media requests. Very useful for debugging. + */ +static int media_device_requests(struct seq_file *file, void *priv) +{ + struct media_device *dev = dev_get_drvdata(file->private); + + seq_printf(file, "number of requests: %d\n", + atomic_read(&dev->num_requests)); + seq_printf(file, "number of request objects: %d\n", + atomic_read(&dev->num_request_objects)); + + return 0; +} +#endif + void media_device_init(struct media_device *mdev) { INIT_LIST_HEAD(&mdev->entities); @@ -697,6 +714,9 @@ void media_device_init(struct media_device *mdev) media_set_bus_info(mdev->bus_info, sizeof(mdev->bus_info), mdev->dev); + atomic_set(&mdev->num_requests, 0); + atomic_set(&mdev->num_request_objects, 0); + dev_dbg(mdev->dev, "Media device initialized\n"); } EXPORT_SYMBOL_GPL(media_device_init); @@ -748,6 +768,15 @@ int __must_check __media_device_register(struct media_device *mdev, dev_dbg(mdev->dev, "Media device registered\n"); +#ifdef CONFIG_DEBUG_FS + if (!media_debugfs_root) + media_debugfs_root = debugfs_create_dir("media", NULL); + mdev->media_dir = debugfs_create_dir(dev_name(&devnode->dev), + media_debugfs_root); + debugfs_create_devm_seqfile(&devnode->dev, "requests", + mdev->media_dir, media_device_requests); +#endif + return 0; } EXPORT_SYMBOL_GPL(__media_device_register); @@ -824,6 +853,7 @@ void media_device_unregister(struct media_device *mdev) dev_dbg(mdev->dev, "Media device unregistered\n"); + debugfs_remove_recursive(mdev->media_dir); device_remove_file(&mdev->devnode->dev, &dev_attr_model); media_devnode_unregister(mdev->devnode); /* devnode free is handled in media_devnode_*() */ diff --git a/drivers/media/mc/mc-devnode.c b/drivers/media/mc/mc-devnode.c index 6daa7aa99442..51260a7d6921 100644 --- a/drivers/media/mc/mc-devnode.c +++ b/drivers/media/mc/mc-devnode.c @@ -45,6 +45,9 @@ static dev_t media_dev_t; static DEFINE_MUTEX(media_devnode_lock); static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); +/* debugfs */ +struct dentry *media_debugfs_root; + /* Called when the last user of the media device exits. */ static void media_devnode_release(struct device *cd) { @@ -231,6 +234,7 @@ int __must_check media_devnode_register(struct media_device *mdev, if (devnode->parent) devnode->dev.parent = devnode->parent; dev_set_name(&devnode->dev, "media%d", devnode->minor); + dev_set_drvdata(&devnode->dev, mdev); device_initialize(&devnode->dev); /* Part 2: Initialize the character device */ @@ -309,6 +313,7 @@ static int __init media_devnode_init(void) static void __exit media_devnode_exit(void) { + debugfs_remove_recursive(media_debugfs_root); bus_unregister(&media_bus_type); unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); } diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c index 3cca9a0c7c97..8ad10c72f9db 100644 --- a/drivers/media/mc/mc-request.c +++ b/drivers/media/mc/mc-request.c @@ -54,6 +54,7 @@ static void media_request_clean(struct media_request *req) req->access_count = 0; WARN_ON(req->num_incomplete_objects); req->num_incomplete_objects = 0; + req->manual_completion = false; wake_up_interruptible_all(&req->poll_wait); } @@ -74,6 +75,7 @@ static void media_request_release(struct kref *kref) mdev->ops->req_free(req); else kfree(req); + atomic_dec(&mdev->num_requests); } void media_request_put(struct media_request *req) @@ -298,6 +300,7 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) req->mdev = mdev; req->state = MEDIA_REQUEST_STATE_IDLE; req->num_incomplete_objects = 0; + req->manual_completion = false; kref_init(&req->kref); INIT_LIST_HEAD(&req->objects); spin_lock_init(&req->lock); @@ -317,6 +320,7 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d", atomic_inc_return(&mdev->request_id), fd_prepare_fd(fdf)); + atomic_inc(&mdev->num_requests); dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str); *alloc_fd = fd_publish(fdf); @@ -337,10 +341,12 @@ static void media_request_object_release(struct kref *kref) struct media_request_object *obj = container_of(kref, struct media_request_object, kref); struct media_request *req = obj->req; + struct media_device *mdev = obj->mdev; if (WARN_ON(req)) media_request_object_unbind(obj); obj->ops->release(obj); + atomic_dec(&mdev->num_request_objects); } struct media_request_object * @@ -405,6 +411,7 @@ int media_request_object_bind(struct media_request *req, obj->req = req; obj->ops = ops; obj->priv = priv; + obj->mdev = req->mdev; if (is_buffer) list_add_tail(&obj->list, &req->objects); @@ -412,6 +419,7 @@ int media_request_object_bind(struct media_request *req, list_add(&obj->list, &req->objects); req->num_incomplete_objects++; ret = 0; + atomic_inc(&obj->mdev->num_request_objects); unlock: spin_unlock_irqrestore(&req->lock, flags); @@ -449,7 +457,7 @@ void media_request_object_unbind(struct media_request_object *obj) req->num_incomplete_objects--; if (req->state == MEDIA_REQUEST_STATE_QUEUED && - !req->num_incomplete_objects) { + !req->num_incomplete_objects && !req->manual_completion) { req->state = MEDIA_REQUEST_STATE_COMPLETE; completed = true; wake_up_interruptible_all(&req->poll_wait); @@ -478,7 +486,7 @@ void media_request_object_complete(struct media_request_object *obj) WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED)) goto unlock; - if (!--req->num_incomplete_objects) { + if (!--req->num_incomplete_objects && !req->manual_completion) { req->state = MEDIA_REQUEST_STATE_COMPLETE; wake_up_interruptible_all(&req->poll_wait); completed = true; @@ -489,3 +497,38 @@ unlock: media_request_put(req); } EXPORT_SYMBOL_GPL(media_request_object_complete); + +void media_request_manual_complete(struct media_request *req) +{ + bool completed = false; + unsigned long flags; + + if (WARN_ON_ONCE(!req)) + return; + + spin_lock_irqsave(&req->lock, flags); + + if (WARN_ON_ONCE(!req->manual_completion)) + goto unlock; + + if (WARN_ON_ONCE(req->state != MEDIA_REQUEST_STATE_QUEUED)) + goto unlock; + + req->manual_completion = false; + /* + * It is expected that all other objects in this request are + * completed when this function is called. WARN if that is + * not the case. + */ + if (!WARN_ON(req->num_incomplete_objects)) { + req->state = MEDIA_REQUEST_STATE_COMPLETE; + wake_up_interruptible_all(&req->poll_wait); + completed = true; + } + +unlock: + spin_unlock_irqrestore(&req->lock, flags); + if (completed) + media_request_put(req); +} +EXPORT_SYMBOL_GPL(media_request_manual_complete); diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index 25dc8d4dc5b7..717fc6c9ef21 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -392,8 +392,10 @@ static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); - if (ret < 0) + if (ret < 0) { + cx23885_alsa_dma_unmap(chip); goto error; + } /* Loop back to start of program */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index a42f0c03a7ca..f463365163b7 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -535,6 +535,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, chip->period_size, chip->num_periods, 1); if (ret < 0) { pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); + cx25821_alsa_dma_unmap(chip); goto error; } diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 6627fa9166d3..a7336be44474 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -908,6 +908,7 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) if (!dev->lmmio) { CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n"); + release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); cx25821_iounmap(dev); return -ENOMEM; } diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index 29fb1311e443..4e574d8390b4 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -483,8 +483,10 @@ static int snd_cx88_hw_params(struct snd_pcm_substream *substream, ret = cx88_risc_databuffer(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); - if (ret < 0) + if (ret < 0) { + cx88_alsa_dma_unmap(chip); goto error; + } /* Loop back to start of program */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c index 43a2a16a3c2a..7e539a0c6c92 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c @@ -88,7 +88,7 @@ s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2) if (!csi2) return -EINVAL; - src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); + src_pad = media_pad_remote_pad_unique(&csi2->asd.sd.entity.pads[CSI2_PAD_SINK]); if (IS_ERR(src_pad)) { dev_err(&csi2->isys->adev->auxdev.dev, "can't get source pad of %s (%pe)\n", diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index aa2cf7287477..fabaed63df0c 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -3,6 +3,7 @@ * Copyright (C) 2013--2024 Intel Corporation */ #include <linux/atomic.h> +#include <linux/cleanup.h> #include <linux/bug.h> #include <linux/device.h> #include <linux/list.h> @@ -131,9 +132,6 @@ void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl, list_add_tail(&ib->head, &aq->incoming); spin_unlock_irqrestore(&aq->lock, flags); - if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE) - vb2_buffer_done(vb, state); - if (first) { dev_dbg(dev, "queue buf list %p flags %lx, s %d, %d bufs\n", @@ -201,6 +199,8 @@ static int buffer_list_get(struct ipu6_isys_stream *stream, unsigned long flags; unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING; + lockdep_assert_held(&stream->mutex); + bl->nbufs = 0; INIT_LIST_HEAD(&bl->head); @@ -287,16 +287,15 @@ ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set, /* Start streaming for real. The buffer list must be available. */ static int ipu6_isys_stream_start(struct ipu6_isys_video *av, - struct ipu6_isys_buffer_list *bl, bool error) + struct ipu6_isys_buffer_list *bl) { struct ipu6_isys_stream *stream = av->stream; struct device *dev = &stream->isys->adev->auxdev.dev; struct ipu6_isys_buffer_list __bl; int ret; - mutex_lock(&stream->isys->stream_mutex); + guard(mutex)(&stream->isys->stream_mutex); ret = ipu6_isys_video_set_streaming(av, 1, bl); - mutex_unlock(&stream->isys->stream_mutex); if (ret) goto out_requeue; @@ -334,10 +333,7 @@ static int ipu6_isys_stream_start(struct ipu6_isys_video *av, out_requeue: if (bl && bl->nbufs) ipu6_isys_buffer_list_queue(bl, - IPU6_ISYS_BUFFER_LIST_FL_INCOMING | - (error ? - IPU6_ISYS_BUFFER_LIST_FL_SET_STATE : - 0), error ? VB2_BUF_STATE_ERROR : + IPU6_ISYS_BUFFER_LIST_FL_INCOMING, VB2_BUF_STATE_QUEUED); flush_firmware_streamon_fail(stream); @@ -353,8 +349,6 @@ static void buf_queue(struct vb2_buffer *vb) vb2_buffer_to_ipu6_isys_video_buffer(vvb); struct ipu6_isys_buffer *ib = &ivb->ib; struct device *dev = &av->isys->adev->auxdev.dev; - struct media_pipeline *media_pipe = - media_entity_pipeline(&av->vdev.entity); struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL; struct ipu6_isys_stream *stream = av->stream; struct ipu6_isys_buffer_list bl; @@ -372,8 +366,8 @@ static void buf_queue(struct vb2_buffer *vb) list_add(&ib->head, &aq->incoming); spin_unlock_irqrestore(&aq->lock, flags); - if (!media_pipe || !vb->vb2_queue->start_streaming_called) { - dev_dbg(dev, "media pipeline is not ready for %s\n", + if (!vb2_start_streaming_called(vb->vb2_queue)) { + dev_dbg(dev, "start_streaming hasn't been called yet on %s\n", av->vdev.name); return; } @@ -406,13 +400,6 @@ static void buf_queue(struct vb2_buffer *vb) ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl); ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); - if (!stream->streaming) { - ret = ipu6_isys_stream_start(av, &bl, true); - if (ret) - dev_err(dev, "stream start failed.\n"); - goto out; - } - /* * We must queue the buffers in the buffer list to the * appropriate video buffer queues BEFORE passing them to the @@ -433,14 +420,13 @@ out: static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq) { - struct v4l2_mbus_framefmt format; + struct v4l2_mbus_framefmt format, *__format; struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); struct device *dev = &av->isys->adev->auxdev.dev; struct media_pad *remote_pad = media_pad_remote_pad_first(av->vdev.entity.pads); struct v4l2_subdev *sd; u32 r_stream, code; - int ret; if (!remote_pad) return -ENOTCONN; @@ -448,13 +434,20 @@ static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq) sd = media_entity_to_v4l2_subdev(remote_pad->entity); r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index); - ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, - &format); + struct v4l2_subdev_state *state = + v4l2_subdev_lock_and_get_active_state(sd); - if (ret) { + __format = v4l2_subdev_state_get_format(state, remote_pad->index, + r_stream); + if (__format) + format = *__format; + + v4l2_subdev_unlock_state(state); + + if (!__format) { dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", sd->entity.name, remote_pad->index, r_stream); - return ret; + return -EPIPE; } if (format.width != ipu6_isys_get_frame_width(av) || @@ -545,14 +538,28 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); struct ipu6_isys_buffer_list __bl, *bl = NULL; struct ipu6_isys_stream *stream; - struct media_entity *source_entity = NULL; + struct media_pad *source_pad, *remote_pad; int nr_queues, ret; dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", av->vdev.name, ipu6_isys_get_frame_width(av), ipu6_isys_get_frame_height(av), pfmt->css_pixelformat); - ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues); + remote_pad = media_pad_remote_pad_unique(&av->pad); + if (IS_ERR(remote_pad)) { + dev_dbg(dev, "failed to get remote pad\n"); + ret = PTR_ERR(remote_pad); + goto out_return_buffers; + } + + source_pad = media_pad_remote_pad_unique(&remote_pad->entity->pads[0]); + if (IS_ERR(source_pad)) { + dev_dbg(dev, "No external source entity\n"); + ret = PTR_ERR(source_pad); + goto out_return_buffers; + } + + ret = ipu6_isys_setup_video(av, remote_pad, source_pad, &nr_queues); if (ret < 0) { dev_dbg(dev, "failed to setup video\n"); goto out_return_buffers; @@ -573,7 +580,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) stream = av->stream; mutex_lock(&stream->mutex); if (!stream->nr_streaming) { - ret = ipu6_isys_video_prepare_stream(av, source_entity, + ret = ipu6_isys_video_prepare_stream(av, source_pad->entity, nr_queues); if (ret) goto out_fw_close; @@ -584,7 +591,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) stream->nr_queues); list_add(&aq->node, &stream->queues); - ipu6_isys_configure_stream_watermark(av, true); + ipu6_isys_configure_stream_watermark(av, source_pad->entity); ipu6_isys_update_stream_watermark(av, true); if (stream->nr_streaming != stream->nr_queues) @@ -597,7 +604,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) goto out; } - ret = ipu6_isys_stream_start(av, bl, false); + ret = ipu6_isys_stream_start(av, bl); if (ret) goto out_stream_start; @@ -637,10 +644,10 @@ static void stop_streaming(struct vb2_queue *q) mutex_lock(&av->isys->stream_mutex); if (stream->nr_streaming == stream->nr_queues && stream->streaming) ipu6_isys_video_set_streaming(av, 0, NULL); + list_del(&aq->node); mutex_unlock(&av->isys->stream_mutex); stream->nr_streaming--; - list_del(&aq->node); stream->streaming = 0; mutex_unlock(&stream->mutex); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h index 844dfda15ab6..dec1fed44dd2 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h @@ -39,7 +39,6 @@ struct ipu6_isys_video_buffer { #define IPU6_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) #define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) -#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) struct ipu6_isys_buffer_list { struct list_head head; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c index 869e7d4ba572..dbd6f76a066d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c @@ -265,42 +265,6 @@ static int subdev_set_routing(struct v4l2_subdev *sd, return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); } -int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_mbus_framefmt *format) -{ - struct v4l2_mbus_framefmt *fmt; - struct v4l2_subdev_state *state; - - if (!sd || !format) - return -EINVAL; - - state = v4l2_subdev_lock_and_get_active_state(sd); - fmt = v4l2_subdev_state_get_format(state, pad, stream); - if (fmt) - *format = *fmt; - v4l2_subdev_unlock_state(state); - - return fmt ? 0 : -EINVAL; -} - -int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_rect *crop) -{ - struct v4l2_subdev_state *state; - struct v4l2_rect *rect; - - if (!sd || !crop) - return -EINVAL; - - state = v4l2_subdev_lock_and_get_active_state(sd); - rect = v4l2_subdev_state_get_crop(state, pad, stream); - if (rect) - *crop = *rect; - v4l2_subdev_unlock_state(state); - - return rect ? 0 : -EINVAL; -} - u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) { struct v4l2_subdev_state *state; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h index 268dfa01e903..35069099c364 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h @@ -38,10 +38,6 @@ int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code); u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); -int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_mbus_framefmt *format); -int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream, - struct v4l2_rect *crop); int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, enum v4l2_subdev_format_whence which, diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index dec8f5ffcfa5..3ac48d2076da 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -455,6 +455,7 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, { struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); + struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd); struct ipu6_fw_isys_input_pin_info_abi *input_pin; struct ipu6_fw_isys_output_pin_info_abi *output_pin; struct ipu6_isys_stream *stream = av->stream; @@ -464,26 +465,13 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av, ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); struct v4l2_rect v4l2_crop; struct ipu6_isys *isys = av->isys; - struct device *dev = &isys->adev->auxdev.dev; int input_pins = cfg->nof_input_pins++; int output_pins; u32 src_stream; - int ret; src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index); - ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, - &fmt); - if (ret < 0) { - dev_err(dev, "can't get stream format (%d)\n", ret); - return ret; - } - - ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream, - &v4l2_crop); - if (ret < 0) { - dev_err(dev, "can't get stream crop (%d)\n", ret); - return ret; - } + fmt = *v4l2_subdev_state_get_format(state, src_pad->index, src_stream); + v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream); input_pin = &cfg->input_pins[input_pins]; input_pin->input_res.width = fmt.width; @@ -745,17 +733,16 @@ int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av, stream->stream_source = stream->asd->source; csi2 = ipu6_isys_subdev_to_csi2(stream->asd); csi2->receiver_errors = 0; - stream->source_entity = source_entity; dev_dbg(&av->isys->adev->auxdev.dev, "prepare stream: external entity %s\n", - stream->source_entity->name); + source_entity->name); return 0; } void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - bool state) + struct media_entity *source) { struct ipu6_isys *isys = av->isys; struct ipu6_isys_csi2 *csi2 = NULL; @@ -769,10 +756,7 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, u64 pixel_rate = 0; int ret; - if (!state) - return; - - esd = media_entity_to_v4l2_subdev(av->stream->source_entity); + esd = media_entity_to_v4l2_subdev(source); av->watermark.width = ipu6_isys_get_frame_width(av); av->watermark.height = ipu6_isys_get_frame_height(av); @@ -788,13 +772,16 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd); link_freq = ipu6_isys_csi2_get_link_freq(csi2); if (link_freq > 0) { + struct v4l2_subdev_state *state = + v4l2_subdev_lock_and_get_active_state(&csi2->asd.sd); + lanes = csi2->nlanes; - ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0, - av->source_stream, &format); - if (!ret) { - bpp = ipu6_isys_mbus_code_to_bpp(format.code); - pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp); - } + format = *v4l2_subdev_state_get_format(state, 0, + av->source_stream); + bpp = ipu6_isys_mbus_code_to_bpp(format.code); + pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp); + + v4l2_subdev_unlock_state(state); } av->watermark.pixel_rate = pixel_rate; @@ -804,7 +791,7 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, iwake_watermark->force_iwake_disable = true; mutex_unlock(&iwake_watermark->mutex); dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n", - av->stream->source_entity->name); + source->name); } } @@ -1011,9 +998,6 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, dev_dbg(dev, "set stream: %d\n", state); - if (WARN(!stream->source_entity, "No source entity for stream\n")) - return -ENODEV; - sd = &stream->asd->sd; r_pad = media_pad_remote_pad_first(&av->pad); r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); @@ -1036,11 +1020,10 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, sd->name, r_pad->index, stream_mask); ret = v4l2_subdev_disable_streams(sd, r_pad->index, stream_mask); - if (ret) { + if (ret) dev_err(dev, "stream off %s failed with %d\n", sd->name, ret); - return ret; - } + close_streaming_firmware(av); } else { ret = start_stream_firmware(av, bl); @@ -1066,6 +1049,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, out_media_entity_stop_streaming_firmware: stop_streaming_firmware(av); + close_streaming_firmware(av); return ret; } @@ -1179,7 +1163,8 @@ void ipu6_isys_fw_close(struct ipu6_isys *isys) } int ipu6_isys_setup_video(struct ipu6_isys_video *av, - struct media_entity **source_entity, int *nr_queues) + struct media_pad *remote_pad, + struct media_pad *source_pad, int *nr_queues) { const struct ipu6_isys_pixelformat *pfmt = ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0); @@ -1188,30 +1173,13 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av, struct v4l2_subdev_route *route = NULL; struct v4l2_subdev_route *r; struct v4l2_subdev_state *state; - struct ipu6_isys_subdev *asd; - struct v4l2_subdev *remote_sd; - struct media_pipeline *pipeline; - struct media_pad *source_pad, *remote_pad; + struct v4l2_subdev *remote_sd = + media_entity_to_v4l2_subdev(remote_pad->entity); + struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(remote_sd); int ret = -EINVAL; *nr_queues = 0; - remote_pad = media_pad_remote_pad_unique(&av->pad); - if (IS_ERR(remote_pad)) { - dev_dbg(dev, "failed to get remote pad\n"); - return PTR_ERR(remote_pad); - } - - remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - asd = to_ipu6_isys_subdev(remote_sd); - source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); - if (!source_pad) { - dev_dbg(dev, "No external source entity\n"); - return -ENODEV; - } - - *source_entity = source_pad->entity; - /* Find the root */ state = v4l2_subdev_lock_and_get_active_state(remote_sd); for_each_active_route(&state->routing, r) { @@ -1231,7 +1199,7 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av, ret = ipu6_isys_csi2_get_remote_desc(av->source_stream, to_ipu6_isys_csi2(asd), - *source_entity, &entry); + source_pad->entity, &entry); if (ret == -ENOIOCTLCMD) { av->vc = 0; av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code); @@ -1247,11 +1215,7 @@ int ipu6_isys_setup_video(struct ipu6_isys_video *av, return ret; } - pipeline = media_entity_pipeline(&av->vdev.entity); - if (!pipeline) - ret = video_device_pipeline_alloc_start(&av->vdev); - else - ret = video_device_pipeline_start(&av->vdev, pipeline); + ret = video_device_pipeline_alloc_start(&av->vdev); if (ret < 0) { dev_dbg(dev, "media pipeline start failed\n"); return ret; diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h index 1dd36f2a077e..2ff53315d7b9 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h @@ -43,7 +43,6 @@ struct sequence_info { */ struct ipu6_isys_stream { struct mutex mutex; - struct media_entity *source_entity; atomic_t sequence; unsigned int seq_index; struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF]; @@ -113,7 +112,8 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, int ipu6_isys_fw_open(struct ipu6_isys *isys); void ipu6_isys_fw_close(struct ipu6_isys *isys); int ipu6_isys_setup_video(struct ipu6_isys_video *av, - struct media_entity **source_entity, int *nr_queues); + struct media_pad *remote_pad, + struct media_pad *source_pad, int *nr_queues); int ipu6_isys_video_init(struct ipu6_isys_video *av); void ipu6_isys_video_cleanup(struct ipu6_isys_video *av); void ipu6_isys_put_stream(struct ipu6_isys_stream *stream); @@ -123,7 +123,7 @@ struct ipu6_isys_stream * ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc); void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av, - bool state); + struct media_entity *source); void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state); u32 ipu6_isys_get_format(struct ipu6_isys_video *av); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index fc0ec0a4b8f5..c9cdeb7054d7 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -269,7 +269,7 @@ fail: return ret; } -void isys_setup_hw(struct ipu6_isys *isys) +static void isys_setup_hw(struct ipu6_isys *isys) { void __iomem *base = isys->pdata->base; const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold; @@ -341,7 +341,7 @@ static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2) } } -irqreturn_t isys_isr(struct ipu6_bus_device *adev) +static irqreturn_t isys_isr(struct ipu6_bus_device *adev) { struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev); void __iomem *base = isys->pdata->base; @@ -857,9 +857,6 @@ static int isys_runtime_pm_resume(struct device *dev) unsigned long flags; int ret; - if (!isys) - return 0; - ret = ipu6_mmu_hw_init(adev->mmu); if (ret) return ret; @@ -884,13 +881,9 @@ static int isys_runtime_pm_resume(struct device *dev) static int isys_runtime_pm_suspend(struct device *dev) { struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); - struct ipu6_isys *isys; + struct ipu6_isys *isys = dev_get_drvdata(dev); unsigned long flags; - isys = dev_get_drvdata(dev); - if (!isys) - return 0; - spin_lock_irqsave(&isys->power_lock, flags); isys->power = 0; spin_unlock_irqrestore(&isys->power_lock, flags); @@ -1070,10 +1063,6 @@ static int isys_probe(struct auxiliary_device *auxdev, if (!isys->csi2) return -ENOMEM; - ret = ipu6_mmu_hw_init(adev->mmu); - if (ret) - return ret; - /* initial sensor type */ isys->sensor_type = isys->pdata->ipdata->sensor_type_start; @@ -1125,8 +1114,6 @@ static int isys_probe(struct auxiliary_device *auxdev, if (ret) goto free_fw_msg_bufs; - ipu6_mmu_hw_cleanup(adev->mmu); - return 0; free_fw_msg_bufs: @@ -1148,8 +1135,6 @@ release_firmware: mutex_destroy(&isys->mutex); mutex_destroy(&isys->stream_mutex); - ipu6_mmu_hw_cleanup(adev->mmu); - return ret; } @@ -1373,7 +1358,7 @@ MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); -MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>"); +MODULE_AUTHOR("Hongju Wang"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel IPU6 input system driver"); MODULE_IMPORT_NS("INTEL_IPU6"); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h index 0e2c8b71edfc..7fb8cb820912 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h @@ -181,8 +181,6 @@ void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys); extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops; -void isys_setup_hw(struct ipu6_isys *isys); -irqreturn_t isys_isr(struct ipu6_bus_device *adev); void update_watermark_setting(struct ipu6_isys *isys); int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys, diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c index 6d1c0b90169d..85cc6d5b4dd1 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-mmu.c +++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c @@ -102,7 +102,7 @@ static void page_table_dump(struct ipu6_mmu_info *mmu_info) if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) continue; - l2_phys = TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx];) + l2_phys = TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]); dev_dbg(mmu_info->dev, "l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pap\n", l1_idx, iova, iova + ISP_PAGE_SIZE, &l2_phys); @@ -248,7 +248,7 @@ static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info) dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); - for (i = 0; i < ISP_L1PT_PTES; i++) + for (i = 0; i < ISP_L2PT_PTES; i++) pt[i] = mmu_info->dummy_page_pteval; return pt; diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index 1f4f20b9c94d..24238f8311a6 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -630,21 +630,21 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) { dev_err_probe(&isp->pdev->dev, ret, "Failed to set MMU hardware\n"); - goto out_ipu6_bus_del_devices; + goto out_ipu6_rpm_put; } ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw, &isp->psys->fw_sgt); if (ret) { dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n"); - goto out_ipu6_bus_del_devices; + goto out_ipu6_rpm_put; } ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data); if (ret) { dev_err_probe(&isp->pdev->dev, ret, "failed to create pkg dir\n"); - goto out_ipu6_bus_del_devices; + goto out_ipu6_rpm_put; } ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr, @@ -652,7 +652,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) IRQF_SHARED, IPU6_NAME, isp); if (ret) { dev_err_probe(dev, ret, "Requesting irq failed\n"); - goto out_ipu6_bus_del_devices; + goto out_ipu6_rpm_put; } ret = ipu6_buttress_authenticate(isp); @@ -683,6 +683,8 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) out_free_irq: devm_free_irq(dev, pdev->irq, isp); +out_ipu6_rpm_put: + pm_runtime_put_sync(&isp->psys->auxdev.dev); out_ipu6_bus_del_devices: if (isp->psys) { ipu6_cpd_free_pkg_dir(isp->psys); @@ -841,6 +843,6 @@ MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>"); MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>"); -MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>"); +MODULE_AUTHOR("Hongju Wang"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel IPU6 PCI driver"); diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index 3ce6b717ca32..a7cb8dc50b53 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -381,6 +381,18 @@ static int get_serial_number(struct mgb4_dev *mgbdev) return 0; } +static const char *module_type_str(struct mgb4_dev *mgbdev) +{ + if (MGB4_IS_FPDL3(mgbdev)) + return "FPDL3"; + else if (MGB4_IS_GMSL3(mgbdev)) + return "GMSL3"; + else if (MGB4_IS_GMSL1(mgbdev)) + return "GMSL1"; + else + return "UNKNOWN"; +} + static int get_module_version(struct mgb4_dev *mgbdev) { struct device *dev = &mgbdev->pdev->dev; @@ -402,19 +414,21 @@ static int get_module_version(struct mgb4_dev *mgbdev) } mgbdev->module_version = ~((u32)version) & 0xff; - if (!(MGB4_IS_FPDL3(mgbdev) || MGB4_IS_GMSL(mgbdev))) { + if (!(MGB4_IS_FPDL3(mgbdev) || + MGB4_IS_GMSL3(mgbdev) || + MGB4_IS_GMSL1(mgbdev))) { dev_err(dev, "unknown module type\n"); return -EINVAL; } fw_version = mgb4_read_reg(&mgbdev->video, 0xC4) >> 24; if ((MGB4_IS_FPDL3(mgbdev) && fw_version != 1) || - (MGB4_IS_GMSL(mgbdev) && fw_version != 2)) { + (MGB4_IS_GMSL3(mgbdev) && fw_version != 2) || + (MGB4_IS_GMSL1(mgbdev) && fw_version != 3)) { dev_err(dev, "module/firmware type mismatch\n"); return -EINVAL; } - dev_info(dev, "%s module detected\n", - MGB4_IS_FPDL3(mgbdev) ? "FPDL3" : "GMSL"); + dev_info(dev, "%s module detected\n", module_type_str(mgbdev)); return 0; } diff --git a/drivers/media/pci/mgb4/mgb4_core.h b/drivers/media/pci/mgb4/mgb4_core.h index cc24068400a2..bfae75f30c7c 100644 --- a/drivers/media/pci/mgb4/mgb4_core.h +++ b/drivers/media/pci/mgb4/mgb4_core.h @@ -18,14 +18,20 @@ #define MGB4_VIN_DEVICES 2 #define MGB4_VOUT_DEVICES 2 -#define MGB4_IS_GMSL(mgbdev) \ - ((((mgbdev)->module_version >> 4) >= 2) && \ - (((mgbdev)->module_version >> 4) <= 4)) +#define MGB4_IS_GMSL1(mgbdev) \ + (((mgbdev)->module_version >> 4) == 6) +#define MGB4_IS_GMSL3(mgbdev) \ + (((((mgbdev)->module_version >> 4) >= 2) && \ + (((mgbdev)->module_version >> 4) <= 4)) || \ + (((mgbdev)->module_version >> 4) == 8)) +#define MGB4_IS_GMSL3C(mgbdev) \ + (((mgbdev)->module_version >> 4) == 8) #define MGB4_IS_FPDL3(mgbdev) \ (((mgbdev)->module_version >> 4) == 1) #define MGB4_HAS_VOUT(mgbdev) \ - ((((mgbdev)->module_version >> 4) >= 1) && \ - (((mgbdev)->module_version >> 4) <= 3)) + (((((mgbdev)->module_version >> 4) >= 1) && \ + (((mgbdev)->module_version >> 4) <= 3)) || \ + ((((mgbdev)->module_version >> 4) == 6))) struct mgb4_dma_channel { struct dma_chan *chan; diff --git a/drivers/media/pci/mgb4/mgb4_sysfs.h b/drivers/media/pci/mgb4/mgb4_sysfs.h index 017d82c0624e..fa7fd232c2c2 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs.h +++ b/drivers/media/pci/mgb4/mgb4_sysfs.h @@ -11,8 +11,10 @@ extern struct attribute *mgb4_pci_attrs[]; extern struct attribute *mgb4_fpdl3_in_attrs[]; -extern struct attribute *mgb4_gmsl_in_attrs[]; +extern struct attribute *mgb4_gmsl3_in_attrs[]; +extern struct attribute *mgb4_gmsl1_in_attrs[]; extern struct attribute *mgb4_fpdl3_out_attrs[]; -extern struct attribute *mgb4_gmsl_out_attrs[]; +extern struct attribute *mgb4_gmsl3_out_attrs[]; +extern struct attribute *mgb4_gmsl1_out_attrs[]; #endif diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_in.c b/drivers/media/pci/mgb4/mgb4_sysfs_in.c index 9626fa59e3d3..4cd4addcd0a5 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_in.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_in.c @@ -36,10 +36,13 @@ static ssize_t oldi_lane_width_show(struct device *dev, u32 config; int ret; - i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49; - i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03; - i2c_single_val = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02; - i2c_dual_val = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00; + if (MGB4_IS_GMSL1(mgbdev)) + return sprintf(buf, "0\n"); + + i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49; + i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03; + i2c_single_val = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02; + i2c_dual_val = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00; mutex_lock(&mgbdev->i2c_lock); ret = mgb4_i2c_read_byte(&vindev->deser, i2c_reg); @@ -79,21 +82,24 @@ static ssize_t oldi_lane_width_store(struct device *dev, if (ret) return ret; + if (MGB4_IS_GMSL1(mgbdev)) + return val ? -EINVAL : count; + switch (val) { case 0: /* single */ fpga_data = 0; - i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02; + i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02; break; case 1: /* dual */ fpga_data = 1U << 9; - i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00; + i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00; break; default: return -EINVAL; } - i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49; - i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03; + i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49; + i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03; mutex_lock(&mgbdev->i2c_lock); ret = mgb4_i2c_mask_byte(&vindev->deser, i2c_reg, i2c_mask, i2c_data); @@ -102,7 +108,7 @@ static ssize_t oldi_lane_width_store(struct device *dev, return -EIO; mgb4_mask_reg(&mgbdev->video, vindev->config->regs.config, 1U << 9, fpga_data); - if (MGB4_IS_GMSL(mgbdev)) { + if (MGB4_IS_GMSL3(mgbdev)) { /* reset input link */ mutex_lock(&mgbdev->i2c_lock); ret = mgb4_i2c_mask_byte(&vindev->deser, 0x10, 1U << 5, 1U << 5); @@ -745,7 +751,7 @@ struct attribute *mgb4_fpdl3_in_attrs[] = { NULL }; -struct attribute *mgb4_gmsl_in_attrs[] = { +struct attribute *mgb4_gmsl3_in_attrs[] = { &dev_attr_input_id.attr, &dev_attr_link_status.attr, &dev_attr_stream_status.attr, @@ -770,3 +776,26 @@ struct attribute *mgb4_gmsl_in_attrs[] = { &dev_attr_gmsl_fec.attr, NULL }; + +struct attribute *mgb4_gmsl1_in_attrs[] = { + &dev_attr_input_id.attr, + &dev_attr_link_status.attr, + &dev_attr_stream_status.attr, + &dev_attr_video_width.attr, + &dev_attr_video_height.attr, + &dev_attr_hsync_status.attr, + &dev_attr_vsync_status.attr, + &dev_attr_oldi_lane_width.attr, + &dev_attr_color_mapping.attr, + &dev_attr_hsync_gap_length.attr, + &dev_attr_vsync_gap_length.attr, + &dev_attr_pclk_frequency.attr, + &dev_attr_hsync_width.attr, + &dev_attr_vsync_width.attr, + &dev_attr_hback_porch.attr, + &dev_attr_hfront_porch.attr, + &dev_attr_vback_porch.attr, + &dev_attr_vfront_porch.attr, + &dev_attr_frequency_range.attr, + NULL +}; diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c index 573aa61c69d4..5769f3ca6c2f 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c @@ -665,6 +665,7 @@ static ssize_t pclk_frequency_store(struct device *dev, { struct video_device *vdev = to_video_device(dev); struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); + struct mgb4_dev *mgbdev = voutdev->mgbdev; unsigned long val; int ret; unsigned int dp; @@ -679,14 +680,16 @@ static ssize_t pclk_frequency_store(struct device *dev, return -EBUSY; } - dp = (val > 50000) ? 1 : 0; + dp = (MGB4_IS_FPDL3(mgbdev) && val > 50000) ? 1 : 0; voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, val >> dp) << dp; - - mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config, - 0x10, dp << 4); - mutex_lock(&voutdev->mgbdev->i2c_lock); - ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, ((~dp) & 1) << 6); - mutex_unlock(&voutdev->mgbdev->i2c_lock); + mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0x10, + dp << 4); + if (MGB4_IS_FPDL3(mgbdev)) { + mutex_lock(&mgbdev->i2c_lock); + ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, + ((~dp) & 1) << 6); + mutex_unlock(&mgbdev->i2c_lock); + } mutex_unlock(voutdev->vdev.lock); @@ -731,7 +734,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = { NULL }; -struct attribute *mgb4_gmsl_out_attrs[] = { +struct attribute *mgb4_gmsl3_out_attrs[] = { &dev_attr_output_id.attr, &dev_attr_video_source.attr, &dev_attr_display_width.attr, @@ -739,3 +742,22 @@ struct attribute *mgb4_gmsl_out_attrs[] = { &dev_attr_frame_rate.attr, NULL }; + +struct attribute *mgb4_gmsl1_out_attrs[] = { + &dev_attr_output_id.attr, + &dev_attr_video_source.attr, + &dev_attr_display_width.attr, + &dev_attr_display_height.attr, + &dev_attr_frame_rate.attr, + &dev_attr_hsync_polarity.attr, + &dev_attr_vsync_polarity.attr, + &dev_attr_de_polarity.attr, + &dev_attr_pclk_frequency.attr, + &dev_attr_hsync_width.attr, + &dev_attr_vsync_width.attr, + &dev_attr_hback_porch.attr, + &dev_attr_hfront_porch.attr, + &dev_attr_vback_porch.attr, + &dev_attr_vfront_porch.attr, + NULL +}; diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 4b38076486ff..e782db79686f 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -32,7 +32,8 @@ #include "mgb4_vin.h" ATTRIBUTE_GROUPS(mgb4_fpdl3_in); -ATTRIBUTE_GROUPS(mgb4_gmsl_in); +ATTRIBUTE_GROUPS(mgb4_gmsl3_in); +ATTRIBUTE_GROUPS(mgb4_gmsl1_in); static const struct mgb4_vin_config vin_cfg[] = { {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}}, @@ -44,23 +45,43 @@ static const struct i2c_board_info fpdl3_deser_info[] = { {I2C_BOARD_INFO("deserializer2", 0x36)}, }; -static const struct i2c_board_info gmsl_deser_info[] = { +static const struct i2c_board_info gmsl3_deser_info[] = { {I2C_BOARD_INFO("deserializer1", 0x4C)}, {I2C_BOARD_INFO("deserializer2", 0x2A)}, }; +static const struct i2c_board_info gmsl3c_deser_info[] = { + {I2C_BOARD_INFO("deserializer1", 0x6A)}, + {I2C_BOARD_INFO("deserializer2", 0x6C)}, +}; + +static const struct i2c_board_info gmsl1_deser_info[] = { + {I2C_BOARD_INFO("deserializer1", 0x2C)}, + {I2C_BOARD_INFO("deserializer2", 0x6C)}, +}; + static const struct mgb4_i2c_kv fpdl3_i2c[] = { {0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8}, {0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00} }; -static const struct mgb4_i2c_kv gmsl_i2c[] = { +static const struct mgb4_i2c_kv gmsl3_i2c[] = { {0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0}, {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40}, {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07}, {0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40} }; +static const struct mgb4_i2c_kv gmsl3c_i2c[] = { + {0x01, 0x03, 0x02}, {0x300, 0x0C, 0x08}, {0x03, 0xC0, 0x00}, + {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x05}, {0x05, 0xC0, 0x40}, + {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x00}, + {0x308, 0x01, 0x00}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40} +}; + +static const struct mgb4_i2c_kv gmsl1_i2c[] = { +}; + static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { @@ -796,22 +817,36 @@ static irqreturn_t err_handler(int irq, void *ctx) static int deser_init(struct mgb4_vin_dev *vindev, int id) { - int rv, addr_size; - size_t values_count; - const struct mgb4_i2c_kv *values; - const struct i2c_board_info *info; + int rv, addr_size = 0; + size_t count = 0; + const struct mgb4_i2c_kv *values = NULL; + const struct i2c_board_info *info = NULL; struct device *dev = &vindev->mgbdev->pdev->dev; - if (MGB4_IS_GMSL(vindev->mgbdev)) { - info = &gmsl_deser_info[id]; - addr_size = 16; - values = gmsl_i2c; - values_count = ARRAY_SIZE(gmsl_i2c); - } else { + if (MGB4_IS_GMSL3(vindev->mgbdev)) { + if (MGB4_IS_GMSL3C(vindev->mgbdev)) { + info = &gmsl3c_deser_info[id]; + addr_size = 16; + values = gmsl3c_i2c; + count = ARRAY_SIZE(gmsl3c_i2c); + } else { + info = &gmsl3_deser_info[id]; + addr_size = 16; + values = gmsl3_i2c; + count = ARRAY_SIZE(gmsl3_i2c); + } + } else if (MGB4_IS_FPDL3(vindev->mgbdev)) { info = &fpdl3_deser_info[id]; addr_size = 8; values = fpdl3_i2c; - values_count = ARRAY_SIZE(fpdl3_i2c); + count = ARRAY_SIZE(fpdl3_i2c); + } else if (MGB4_IS_GMSL1(vindev->mgbdev)) { + info = &gmsl1_deser_info[id]; + addr_size = 8; + values = gmsl1_i2c; + count = ARRAY_SIZE(gmsl1_i2c); + } else { + return -EINVAL; } rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info, @@ -820,7 +855,7 @@ static int deser_init(struct mgb4_vin_dev *vindev, int id) dev_err(dev, "failed to create deserializer\n"); return rv; } - rv = mgb4_i2c_configure(&vindev->deser, values, values_count); + rv = mgb4_i2c_configure(&vindev->deser, values, count); if (rv < 0) { dev_err(dev, "failed to configure deserializer\n"); goto err_i2c_dev; @@ -838,11 +873,12 @@ static void fpga_init(struct mgb4_vin_dev *vindev) { struct mgb4_regs *video = &vindev->mgbdev->video; const struct mgb4_vin_regs *regs = &vindev->config->regs; + int dp = MGB4_IS_GMSL1(vindev->mgbdev) ? 0 : 1; mgb4_write_reg(video, regs->config, 0x00000001); mgb4_write_reg(video, regs->sync, 0x03E80002); mgb4_write_reg(video, regs->padding, 0x00000000); - mgb4_write_reg(video, regs->config, 1U << 9); + mgb4_write_reg(video, regs->config, dp << 9); } static void create_debugfs(struct mgb4_vin_dev *vindev) @@ -890,10 +926,21 @@ static void create_debugfs(struct mgb4_vin_dev *vindev) #endif } +static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev) +{ + if (MGB4_IS_FPDL3(mgbdev)) + return mgb4_fpdl3_in_groups; + else if (MGB4_IS_GMSL3(mgbdev)) + return mgb4_gmsl3_in_groups; + else if (MGB4_IS_GMSL1(mgbdev)) + return mgb4_gmsl1_in_groups; + else + return NULL; +} + struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) { int rv; - const struct attribute_group **groups; struct mgb4_vin_dev *vindev; struct pci_dev *pdev = mgbdev->pdev; struct device *dev = &pdev->dev; @@ -914,14 +961,13 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) INIT_WORK(&vindev->dma_work, dma_transfer); INIT_WORK(&vindev->err_work, signal_change); - /* IRQ callback */ + /* IRQ callbacks */ vin_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->vin_irq); rv = request_irq(vin_irq, vin_handler, 0, "mgb4-vin", vindev); if (rv) { dev_err(dev, "failed to register vin irq handler\n"); goto err_alloc; } - /* Error IRQ callback */ err_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->err_irq); rv = request_irq(err_irq, err_handler, 0, "mgb4-err", vindev); if (rv) { @@ -986,9 +1032,7 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) } /* Module sysfs attributes */ - groups = MGB4_IS_GMSL(mgbdev) - ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; - rv = device_add_groups(&vindev->vdev.dev, groups); + rv = device_add_groups(&vindev->vdev.dev, module_groups(mgbdev)); if (rv) { dev_err(dev, "failed to create sysfs attributes\n"); goto err_video_dev; @@ -1014,7 +1058,6 @@ err_alloc: void mgb4_vin_free(struct mgb4_vin_dev *vindev) { - const struct attribute_group **groups; int vin_irq = xdma_get_user_irq(vindev->mgbdev->xdev, vindev->config->vin_irq); int err_irq = xdma_get_user_irq(vindev->mgbdev->xdev, @@ -1025,9 +1068,7 @@ void mgb4_vin_free(struct mgb4_vin_dev *vindev) free_irq(vin_irq, vindev); free_irq(err_irq, vindev); - groups = MGB4_IS_GMSL(vindev->mgbdev) - ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; - device_remove_groups(&vindev->vdev.dev, groups); + device_remove_groups(&vindev->vdev.dev, module_groups(vindev->mgbdev)); mgb4_i2c_free(&vindev->deser); video_unregister_device(&vindev->vdev); diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index fd93fbbaf755..44e9565d4d06 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -25,7 +25,8 @@ #include "mgb4_vout.h" ATTRIBUTE_GROUPS(mgb4_fpdl3_out); -ATTRIBUTE_GROUPS(mgb4_gmsl_out); +ATTRIBUTE_GROUPS(mgb4_gmsl3_out); +ATTRIBUTE_GROUPS(mgb4_gmsl1_out); static const struct mgb4_vout_config vout_cfg[] = { {0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7C, 0xE0}}, @@ -37,10 +38,18 @@ static const struct i2c_board_info fpdl3_ser_info[] = { {I2C_BOARD_INFO("serializer2", 0x16)}, }; +static const struct i2c_board_info gmsl1_ser_info[] = { + {I2C_BOARD_INFO("serializer1", 0x24)}, + {I2C_BOARD_INFO("serializer2", 0x22)}, +}; + static const struct mgb4_i2c_kv fpdl3_i2c[] = { {0x05, 0xFF, 0x04}, {0x06, 0xFF, 0x01}, {0xC2, 0xFF, 0x80} }; +static const struct mgb4_i2c_kv gmsl1_i2c[] = { +}; + static const struct v4l2_dv_timings_cap video_timings_cap = { .type = V4L2_DV_BT_656_1120, .bt = { @@ -634,12 +643,24 @@ static irqreturn_t handler(int irq, void *ctx) static int ser_init(struct mgb4_vout_dev *voutdev, int id) { - int rv; - const struct i2c_board_info *info = &fpdl3_ser_info[id]; struct mgb4_i2c_client *ser = &voutdev->ser; struct device *dev = &voutdev->mgbdev->pdev->dev; + const struct i2c_board_info *info = NULL; + const struct mgb4_i2c_kv *values = NULL; + size_t count = 0; + int rv; + + if (MGB4_IS_FPDL3(voutdev->mgbdev)) { + info = &fpdl3_ser_info[id]; + values = fpdl3_i2c; + count = ARRAY_SIZE(fpdl3_i2c); + } else if (MGB4_IS_GMSL1(voutdev->mgbdev)) { + info = &gmsl1_ser_info[id]; + values = gmsl1_i2c; + count = ARRAY_SIZE(gmsl1_i2c); + } - if (MGB4_IS_GMSL(voutdev->mgbdev)) + if (!info) return 0; rv = mgb4_i2c_init(ser, voutdev->mgbdev->i2c_adap, info, 8); @@ -647,7 +668,7 @@ static int ser_init(struct mgb4_vout_dev *voutdev, int id) dev_err(dev, "failed to create serializer\n"); return rv; } - rv = mgb4_i2c_configure(ser, fpdl3_i2c, ARRAY_SIZE(fpdl3_i2c)); + rv = mgb4_i2c_configure(ser, values, count); if (rv < 0) { dev_err(dev, "failed to configure serializer\n"); goto err_i2c_dev; @@ -665,18 +686,19 @@ static void fpga_init(struct mgb4_vout_dev *voutdev) { struct mgb4_regs *video = &voutdev->mgbdev->video; const struct mgb4_vout_regs *regs = &voutdev->config->regs; + int dp = MGB4_IS_GMSL1(voutdev->mgbdev) ? 0 : 1; + u32 source = (voutdev->config->id + MGB4_VIN_DEVICES) << 2; - mgb4_write_reg(video, regs->config, 0x00000011); + mgb4_write_reg(video, regs->config, 0x00000001); mgb4_write_reg(video, regs->resolution, (1280 << 16) | 640); mgb4_write_reg(video, regs->hsync, 0x00283232); mgb4_write_reg(video, regs->vsync, 0x40141F1E); mgb4_write_reg(video, regs->frame_limit, MGB4_HW_FREQ / 60); mgb4_write_reg(video, regs->padding, 0x00000000); - voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> 1) << 1; + voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> dp) << dp; - mgb4_write_reg(video, regs->config, - (voutdev->config->id + MGB4_VIN_DEVICES) << 2 | 1 << 4); + mgb4_write_reg(video, regs->config, source | dp << 4); } static void create_debugfs(struct mgb4_vout_dev *voutdev) @@ -720,10 +742,21 @@ static void create_debugfs(struct mgb4_vout_dev *voutdev) #endif } +static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev) +{ + if (MGB4_IS_FPDL3(mgbdev)) + return mgb4_fpdl3_out_groups; + else if (MGB4_IS_GMSL3(mgbdev)) + return mgb4_gmsl3_out_groups; + else if (MGB4_IS_GMSL1(mgbdev)) + return mgb4_gmsl1_out_groups; + else + return NULL; +} + struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id) { int rv, irq; - const struct attribute_group **groups; struct mgb4_vout_dev *voutdev; struct pci_dev *pdev = mgbdev->pdev; struct device *dev = &pdev->dev; @@ -804,9 +837,7 @@ struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id) } /* Module sysfs attributes */ - groups = MGB4_IS_GMSL(mgbdev) - ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups; - rv = device_add_groups(&voutdev->vdev.dev, groups); + rv = device_add_groups(&voutdev->vdev.dev, module_groups(mgbdev)); if (rv) { dev_err(dev, "failed to create sysfs attributes\n"); goto err_video_dev; @@ -830,15 +861,10 @@ err_alloc: void mgb4_vout_free(struct mgb4_vout_dev *voutdev) { - const struct attribute_group **groups; int irq = xdma_get_user_irq(voutdev->mgbdev->xdev, voutdev->config->irq); free_irq(irq, voutdev); - - groups = MGB4_IS_GMSL(voutdev->mgbdev) - ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups; - device_remove_groups(&voutdev->vdev.dev, groups); - + device_remove_groups(&voutdev->vdev.dev, module_groups(voutdev->mgbdev)); mgb4_i2c_free(&voutdev->ser); video_unregister_device(&voutdev->vdev); v4l2_device_unregister(&voutdev->v4l2dev); diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c index 1b7c22a9bc94..8f53946c6792 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.c +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -166,7 +166,7 @@ static const u8 tbl_tw2865_pal_template[] = { 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, }; -#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) +#define is_tw286x(__solo, __id) (!((__solo)->tw2815 & (1U << (__id)))) static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, u8 tw_off) @@ -686,6 +686,9 @@ int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, chip_num = ch / 4; ch %= 4; + if (chip_num >= TW_NUM_CHIP) + return -EINVAL; + if (val > 255 || val < 0) return -ERANGE; @@ -758,6 +761,9 @@ int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, chip_num = ch / 4; ch %= 4; + if (chip_num >= TW_NUM_CHIP) + return -EINVAL; + switch (ctrl) { case V4L2_CID_SHARPNESS: /* Only 286x has sharpness */ diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index c0d2aabb9e0e..adf53b49020e 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -9,7 +9,6 @@ #include <linux/list.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/vmalloc.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -17,7 +16,6 @@ #include <media/v4l2-ioctl.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> -#include <media/videobuf2-vmalloc.h> #include "vpu.h" #include "vpu_defs.h" #include "vpu_core.h" @@ -724,6 +722,7 @@ static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd switch (cmd->cmd) { case V4L2_DEC_CMD_START: vdec_cmd_start(inst); + vb2_clear_last_buffer_dequeued(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx)); break; case V4L2_DEC_CMD_STOP: vdec_cmd_stop(inst); @@ -949,7 +948,7 @@ static void vdec_stop_done(struct vpu_inst *inst) vpu_inst_unlock(inst); } -static bool vdec_check_source_change(struct vpu_inst *inst) +static bool vdec_check_source_change(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr) { struct vdec_t *vdec = inst->priv; const struct vpu_format *sibling; @@ -961,26 +960,35 @@ static bool vdec_check_source_change(struct vpu_inst *inst) return false; sibling = vpu_helper_find_sibling(inst, inst->cap_format.type, inst->cap_format.pixfmt); - if (sibling && vdec->codec_info.pixfmt == sibling->pixfmt) - vdec->codec_info.pixfmt = inst->cap_format.pixfmt; + if (sibling && hdr->pixfmt == sibling->pixfmt) + hdr->pixfmt = inst->cap_format.pixfmt; if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx))) return true; - if (inst->cap_format.pixfmt != vdec->codec_info.pixfmt) + if (inst->cap_format.pixfmt != hdr->pixfmt) return true; - if (inst->cap_format.width != vdec->codec_info.decoded_width) + if (inst->cap_format.width != hdr->decoded_width) return true; - if (inst->cap_format.height != vdec->codec_info.decoded_height) + if (inst->cap_format.height != hdr->decoded_height) return true; if (vpu_get_num_buffers(inst, inst->cap_format.type) < inst->min_buffer_cap) return true; - if (inst->crop.left != vdec->codec_info.offset_x) + if (inst->crop.left != hdr->offset_x) return true; - if (inst->crop.top != vdec->codec_info.offset_y) + if (inst->crop.top != hdr->offset_y) return true; - if (inst->crop.width != vdec->codec_info.width) + if (inst->crop.width != hdr->width) return true; - if (inst->crop.height != vdec->codec_info.height) + if (inst->crop.height != hdr->height) + return true; + if (!hdr->progressive) + return true; + + if (vdec->seq_hdr_found && + (hdr->color_primaries != vdec->codec_info.color_primaries || + hdr->transfer_chars != vdec->codec_info.transfer_chars || + hdr->matrix_coeffs != vdec->codec_info.matrix_coeffs || + hdr->full_range != vdec->codec_info.full_range)) return true; return false; @@ -1332,20 +1340,25 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info struct vdec_t *vdec = inst->priv; vpu_inst_lock(inst); - memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info)); - vpu_trace(inst->dev, "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d\n", + vpu_trace(inst->dev, + "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d, colorspace: %d, %d, %d, %d\n", inst->id, - vdec->codec_info.decoded_width, - vdec->codec_info.decoded_height, - vdec->codec_info.offset_x, - vdec->codec_info.offset_y, - vdec->codec_info.width, - vdec->codec_info.height, + hdr->decoded_width, + hdr->decoded_height, + hdr->offset_x, + hdr->offset_y, + hdr->width, + hdr->height, hdr->num_ref_frms, - hdr->num_dpb_frms); + hdr->num_dpb_frms, + hdr->color_primaries, + hdr->transfer_chars, + hdr->matrix_coeffs, + hdr->full_range); inst->min_buffer_cap = hdr->num_ref_frms + hdr->num_dpb_frms; - vdec->is_source_changed = vdec_check_source_change(inst); + vdec->is_source_changed = vdec_check_source_change(inst, hdr); + memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info)); vdec_init_fmt(inst); vdec_init_crop(inst); vdec_init_mbi(inst); @@ -1374,7 +1387,12 @@ static void vdec_event_resolution_change(struct vpu_inst *inst) { struct vdec_t *vdec = inst->priv; - vpu_trace(inst->dev, "[%d]\n", inst->id); + vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n", + inst->id, + vdec->params.frame_count, + vdec->decoded_frame_count, + vdec->display_frame_count, + vdec->sequence); vpu_inst_lock(inst); vdec->seq_tag = vdec->codec_info.tag; vdec_clear_fs(&vdec->mbi); @@ -1642,9 +1660,9 @@ static void vdec_cleanup(struct vpu_inst *inst) vdec->slots = NULL; vdec->slot_count = 0; } - vfree(vdec); + kfree(vdec); inst->priv = NULL; - vfree(inst); + kfree(inst); } static void vdec_init_params(struct vdec_t *vdec) @@ -1909,13 +1927,13 @@ static int vdec_open(struct file *file) struct vdec_t *vdec; int ret; - inst = vzalloc(sizeof(*inst)); + inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) return -ENOMEM; - vdec = vzalloc(sizeof(*vdec)); + vdec = kzalloc(sizeof(*vdec), GFP_KERNEL); if (!vdec) { - vfree(inst); + kfree(inst); return -ENOMEM; } @@ -1923,8 +1941,8 @@ static int vdec_open(struct file *file) sizeof(*vdec->slots), GFP_KERNEL | __GFP_ZERO); if (!vdec->slots) { - vfree(vdec); - vfree(inst); + kfree(vdec); + kfree(inst); return -ENOMEM; } vdec->slot_count = VDEC_SLOT_CNT_DFT; diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index aced76401b69..9e5cbc2b0d3f 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -13,14 +13,12 @@ #include <linux/videodev2.h> #include <linux/ktime.h> #include <linux/rational.h> -#include <linux/vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-ioctl.h> #include <media/videobuf2-v4l2.h> #include <media/videobuf2-dma-contig.h> -#include <media/videobuf2-vmalloc.h> #include "vpu.h" #include "vpu_defs.h" #include "vpu_core.h" @@ -844,7 +842,7 @@ static int venc_get_encoded_frames(struct vpu_inst *inst) v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx))) break; list_del_init(&frame->list); - vfree(frame); + kfree(frame); } return 0; @@ -860,7 +858,7 @@ static int venc_frame_encoded(struct vpu_inst *inst, void *arg) if (!info) return -EINVAL; venc = inst->priv; - frame = vzalloc(sizeof(*frame)); + frame = kzalloc(sizeof(*frame), GFP_KERNEL); if (!frame) return -ENOMEM; @@ -912,9 +910,9 @@ static void venc_cleanup(struct vpu_inst *inst) return; venc = inst->priv; - vfree(venc); + kfree(venc); inst->priv = NULL; - vfree(inst); + kfree(inst); } static int venc_start_session(struct vpu_inst *inst, u32 type) @@ -1067,7 +1065,7 @@ static void venc_cleanup_frames(struct venc_t *venc) list_for_each_entry_safe(frame, tmp, &venc->frames, list) { list_del_init(&frame->list); - vfree(frame); + kfree(frame); } } @@ -1151,7 +1149,7 @@ static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb) return ret; list_del_init(&frame->list); - vfree(frame); + kfree(frame); return 0; } @@ -1309,13 +1307,13 @@ static int venc_open(struct file *file) struct venc_t *venc; int ret; - inst = vzalloc(sizeof(*inst)); + inst = kzalloc(sizeof(*inst), GFP_KERNEL); if (!inst) return -ENOMEM; - venc = vzalloc(sizeof(*venc)); + venc = kzalloc(sizeof(*venc), GFP_KERNEL); if (!venc) { - vfree(inst); + kfree(inst); return -ENOMEM; } diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c index 5695f5c1cb3e..ab69412e0aa7 100644 --- a/drivers/media/platform/amphion/vpu_cmds.c +++ b/drivers/media/platform/amphion/vpu_cmds.c @@ -13,7 +13,6 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/delay.h> -#include <linux/vmalloc.h> #include "vpu.h" #include "vpu_defs.h" #include "vpu_cmds.h" @@ -84,13 +83,13 @@ static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data int i; int ret; - cmd = vzalloc(sizeof(*cmd)); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return NULL; - cmd->pkt = vzalloc(sizeof(*cmd->pkt)); + cmd->pkt = kzalloc(sizeof(*cmd->pkt), GFP_KERNEL); if (!cmd->pkt) { - vfree(cmd); + kfree(cmd); return NULL; } @@ -98,8 +97,8 @@ static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data); if (ret) { dev_err(inst->dev, "iface pack cmd %s fail\n", vpu_id_name(id)); - vfree(cmd->pkt); - vfree(cmd); + kfree(cmd->pkt); + kfree(cmd); return NULL; } for (i = 0; i < ARRAY_SIZE(vpu_cmd_requests); i++) { @@ -118,8 +117,8 @@ static void vpu_free_cmd(struct vpu_cmd_t *cmd) return; if (cmd->last_response_cmd) atomic_long_set(cmd->last_response_cmd, cmd->key); - vfree(cmd->pkt); - vfree(cmd); + kfree(cmd->pkt); + kfree(cmd); } static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd) diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c index 168f0514851e..85cc4a14f8ed 100644 --- a/drivers/media/platform/amphion/vpu_core.c +++ b/drivers/media/platform/amphion/vpu_core.c @@ -17,7 +17,6 @@ #include <linux/pm_runtime.h> #include <linux/pm_domain.h> #include <linux/firmware.h> -#include <linux/vmalloc.h> #include "vpu.h" #include "vpu_defs.h" #include "vpu_core.h" @@ -265,7 +264,7 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core) INIT_WORK(&core->msg_work, vpu_msg_run_work); INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work); buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE); - core->msg_buffer = vzalloc(buffer_size); + core->msg_buffer = kzalloc(buffer_size, GFP_KERNEL); if (!core->msg_buffer) { dev_err(core->dev, "failed allocate buffer for fifo\n"); ret = -ENOMEM; @@ -282,10 +281,8 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core) return 0; error: - if (core->msg_buffer) { - vfree(core->msg_buffer); - core->msg_buffer = NULL; - } + kfree(core->msg_buffer); + core->msg_buffer = NULL; if (core->workqueue) { destroy_workqueue(core->workqueue); core->workqueue = NULL; @@ -308,7 +305,7 @@ static int vpu_core_unregister(struct device *dev, struct vpu_core *core) vpu_core_put_vpu(core); core->vpu = NULL; - vfree(core->msg_buffer); + kfree(core->msg_buffer); core->msg_buffer = NULL; if (core->workqueue) { diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index 47dff9a35bb4..64fc88d89ccc 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -102,7 +102,6 @@ static int vpu_notify_eos(struct vpu_inst *inst) int vpu_notify_source_change(struct vpu_inst *inst) { static const struct v4l2_event ev = { - .id = 0, .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; @@ -670,7 +669,6 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q src_vq->mem_ops = &vb2_vmalloc_memops; src_vq->drv_priv = inst; src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - src_vq->min_queued_buffers = 1; src_vq->dev = inst->vpu->dev; src_vq->lock = &inst->lock; ret = vb2_queue_init(src_vq); @@ -687,7 +685,6 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q dst_vq->mem_ops = &vb2_vmalloc_memops; dst_vq->drv_priv = inst; dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer); - dst_vq->min_queued_buffers = 1; dst_vq->dev = inst->vpu->dev; dst_vq->lock = &inst->lock; ret = vb2_queue_init(dst_vq); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index b83e43245277..41cb96f60110 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -26,6 +26,7 @@ #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/ktime.h> +#include <linux/reset.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <media/v4l2-ctrls.h> @@ -310,6 +311,7 @@ struct aspeed_video { void __iomem *base; struct clk *eclk; struct clk *vclk; + struct reset_control *reset; struct device *dev; struct v4l2_ctrl_handler ctrl_handler; @@ -720,6 +722,13 @@ static void aspeed_video_on(struct aspeed_video *video) set_bit(VIDEO_CLOCKS_ON, &video->flags); } +static void aspeed_video_reset(struct aspeed_video *v) +{ + reset_control_assert(v->reset); + usleep_range(100, 150); + reset_control_deassert(v->reset); +} + static void aspeed_video_bufs_done(struct aspeed_video *video, enum vb2_buffer_state state) { @@ -742,7 +751,9 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay) video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; - aspeed_video_off(video); + aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); + aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); + aspeed_video_reset(video); aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); schedule_delayed_work(&video->res_work, delay); @@ -1984,8 +1995,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q) * Need to force stop any DMA and try and get HW into a good * state for future calls to start streaming again. */ - aspeed_video_off(video); - aspeed_video_on(video); + aspeed_video_reset(video); aspeed_video_init_regs(video); @@ -2230,6 +2240,12 @@ static int aspeed_video_init(struct aspeed_video *video) } dev_info(video->dev, "irq %d\n", irq); + video->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(video->reset)) { + dev_err(dev, "Unable to get reset\n"); + return PTR_ERR(video->reset); + } + video->eclk = devm_clk_get(dev, "eclk"); if (IS_ERR(video->eclk)) { dev_err(dev, "Unable to get ECLK\n"); diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index f03ad9c0de22..53a0ac068c2e 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -27,6 +27,11 @@ const char *state_to_str(enum vpu_instance_state state) } } +int wave5_kfifo_alloc(struct vpu_instance *inst) +{ + return kfifo_alloc(&inst->irq_status, 16 * sizeof(int), GFP_KERNEL); +} + void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp) { int i; @@ -49,7 +54,7 @@ void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp) v4l2_fh_del(&inst->v4l2_fh, filp); v4l2_fh_exit(&inst->v4l2_fh); } - list_del_init(&inst->list); + kfifo_free(&inst->irq_status); ida_free(&inst->dev->inst_ida, inst->id); kfree(inst->codec_info); kfree(inst); @@ -61,8 +66,29 @@ int wave5_vpu_release_device(struct file *filp, { struct vpu_instance *inst = file_to_vpu_inst(filp); int ret = 0; + unsigned long flags; v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx); + /* + * To prevent Null reference exception, the existing irq handler were + * separated to two modules. + * One is to queue interrupt reason into the irq handler, + * the other is irq_thread to call the wave5_vpu_dec_finish_decode + * to get decoded frame. + * The list of instances should be protected between all flow of the + * decoding process, but to protect the list in the irq_handler, spin lock + * should be used, and mutex should be used in the irq_thread because spin lock + * is not able to be used because mutex is already being used + * in the wave5_vpu_dec_finish_decode. + * So the spin lock and mutex were used to protect the list in the release function. + */ + ret = mutex_lock_interruptible(&inst->dev->irq_lock); + if (ret) + return ret; + spin_lock_irqsave(&inst->dev->irq_spinlock, flags); + list_del_init(&inst->list); + spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags); + mutex_unlock(&inst->dev->irq_lock); if (inst->state != VPU_INST_STATE_NONE) { u32 fail_res; diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h index 976a402e426f..d61fdbda359d 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.h +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h @@ -33,4 +33,5 @@ void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width, unsigned int height, const struct v4l2_frmsize_stepwise *frmsize); +int wave5_kfifo_alloc(struct vpu_instance *inst); #endif diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index d94cf84c3ee5..687ce6ccf3ae 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -102,7 +102,7 @@ static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason dev_dbg(dev, "%s: queueing failure: 0x%x\n", func, reg_val); break; case WAVE5_SYSERR_RESULT_NOT_READY: - dev_err(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason); + dev_dbg(dev, "%s: result not ready: 0x%x\n", func, reg_fail_reason); break; case WAVE5_SYSERR_ACCESS_VIOLATION_HW: dev_err(dev, "%s: access violation: 0x%x\n", func, reg_fail_reason); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index e3038c18ca36..8917542b993c 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -136,6 +136,18 @@ valid_state_switch: return 0; } +static int set_instance_state(struct vpu_instance *inst, enum vpu_instance_state state) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&inst->state_spinlock, flags); + ret = switch_state(inst, state); + spin_unlock_irqrestore(&inst->state_spinlock, flags); + + return ret; +} + static int wave5_vpu_dec_set_eos_on_firmware(struct vpu_instance *inst) { int ret; @@ -227,7 +239,7 @@ static int start_decode(struct vpu_instance *inst, u32 *fail_res) src_buf = v4l2_m2m_src_buf_remove(m2m_ctx); if (src_buf) v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - switch_state(inst, VPU_INST_STATE_STOP); + set_instance_state(inst, VPU_INST_STATE_STOP); dev_dbg(inst->dev->dev, "%s: pic run failed / finish job", __func__); v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); @@ -268,6 +280,7 @@ static void send_eos_event(struct vpu_instance *inst) v4l2_event_queue_fh(&inst->v4l2_fh, &vpu_event_eos); inst->eos = false; + inst->sent_eos = true; } static int handle_dynamic_resolution_change(struct vpu_instance *inst) @@ -347,13 +360,12 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) struct vb2_v4l2_buffer *dec_buf = NULL; struct vb2_v4l2_buffer *disp_buf = NULL; struct vb2_queue *dst_vq = v4l2_m2m_get_dst_vq(m2m_ctx); - struct queue_status_info q_status; dev_dbg(inst->dev->dev, "%s: Fetch output info from firmware.", __func__); ret = wave5_vpu_dec_get_output_info(inst, &dec_info); if (ret) { - dev_warn(inst->dev->dev, "%s: could not get output info.", __func__); + dev_dbg(inst->dev->dev, "%s: could not get output info.", __func__); v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); return; } @@ -442,18 +454,17 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) spin_unlock_irqrestore(&inst->state_spinlock, flags); } - /* - * During a resolution change and while draining, the firmware may flush - * the reorder queue regardless of having a matching decoding operation - * pending. Only terminate the job if there are no more IRQ coming. - */ - wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); - if (q_status.report_queue_count == 0 && - (q_status.instance_queue_count == 0 || dec_info.sequence_changed)) { - dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__); - pm_runtime_put_autosuspend(inst->dev->dev); - v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + if (inst->sent_eos && + v4l2_m2m_get_curr_priv(inst->v4l2_m2m_dev)) { + struct queue_status_info q_status; + + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + if (q_status.report_queue_count == 0 && + q_status.instance_queue_count == 0) + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } + + inst->queuing_fail = false; } static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) @@ -1142,11 +1153,30 @@ static int write_to_ringbuffer(struct vpu_instance *inst, void *buffer, size_t b return 0; } +static struct vpu_src_buffer *inst_src_buf_remove(struct vpu_instance *inst) +{ + struct vpu_src_buffer *b; + int ret; + + ret = mutex_lock_interruptible(&inst->feed_lock); + if (ret) + return NULL; + + if (list_empty(&inst->avail_src_bufs)) { + mutex_unlock(&inst->feed_lock); + return NULL; + } + b = list_first_entry(&inst->avail_src_bufs, struct vpu_src_buffer, list); + list_del_init(&b->list); + mutex_unlock(&inst->feed_lock); + return b; +} + static int fill_ringbuffer(struct vpu_instance *inst) { struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; - struct v4l2_m2m_buffer *buf, *n; - int ret; + struct vpu_src_buffer *vpu_buf; + int ret = 0; if (m2m_ctx->last_src_buf) { struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(m2m_ctx->last_src_buf); @@ -1157,9 +1187,8 @@ static int fill_ringbuffer(struct vpu_instance *inst) } } - v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { - struct vb2_v4l2_buffer *vbuf = &buf->vb; - struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + while ((vpu_buf = inst_src_buf_remove(inst)) != NULL) { + struct vb2_v4l2_buffer *vbuf = &vpu_buf->v4l2_m2m_buf.vb; struct vpu_buf *ring_buffer = &inst->bitstream_vbuf; size_t src_size = vb2_get_plane_payload(&vbuf->vb2_buf, 0); void *src_buf = vb2_plane_vaddr(&vbuf->vb2_buf, 0); @@ -1219,9 +1248,12 @@ static int fill_ringbuffer(struct vpu_instance *inst) dev_dbg(inst->dev->dev, "last src buffer written to the ring buffer\n"); break; } + + inst->queuing_num++; + break; } - return 0; + return ret; } static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb) @@ -1230,10 +1262,16 @@ static void wave5_vpu_dec_buf_queue_src(struct vb2_buffer *vb) struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vpu_src_buffer *vpu_buf = wave5_to_vpu_src_buf(vbuf); + int ret; vpu_buf->consumed = false; vbuf->sequence = inst->queued_src_buf_num++; - + ret = mutex_lock_interruptible(&inst->feed_lock); + if (ret) + return; + INIT_LIST_HEAD(&vpu_buf->list); + list_add_tail(&vpu_buf->list, &inst->avail_src_bufs); + mutex_unlock(&inst->feed_lock); v4l2_m2m_buf_queue(m2m_ctx, vbuf); } @@ -1243,6 +1281,7 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) struct vpu_instance *inst = vb2_get_drv_priv(vb->vb2_queue); struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + pm_runtime_resume_and_get(inst->dev->dev); vbuf->sequence = inst->queued_dst_buf_num++; if (inst->state == VPU_INST_STATE_PIC_RUN) { @@ -1275,6 +1314,7 @@ static void wave5_vpu_dec_buf_queue_dst(struct vb2_buffer *vb) } else { v4l2_m2m_buf_queue(m2m_ctx, vbuf); } + pm_runtime_put_autosuspend(inst->dev->dev); } static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb) @@ -1286,10 +1326,13 @@ static void wave5_vpu_dec_buf_queue(struct vb2_buffer *vb) __func__, vb->type, vb->index, vb2_plane_size(&vbuf->vb2_buf, 0), vb2_plane_size(&vbuf->vb2_buf, 1), vb2_plane_size(&vbuf->vb2_buf, 2)); - if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { wave5_vpu_dec_buf_queue_src(vb); - else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (inst->empty_queue) + inst->empty_queue = false; + } else if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { wave5_vpu_dec_buf_queue_dst(vb); + } } static int wave5_vpu_dec_allocate_ring_buffer(struct vpu_instance *inst) @@ -1383,6 +1426,12 @@ static int streamoff_output(struct vb2_queue *q) dma_addr_t new_rd_ptr; struct dec_output_info dec_info; unsigned int i; + struct vpu_src_buffer *vpu_buf; + + inst->retry = false; + inst->queuing_num = 0; + while ((vpu_buf = inst_src_buf_remove(inst)) != NULL) + ; for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) { ret = wave5_vpu_dec_set_disp_flag(inst, i); @@ -1468,21 +1517,21 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) { struct vpu_instance *inst = vb2_get_drv_priv(q); struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + bool check_cmd = TRUE; dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); pm_runtime_resume_and_get(inst->dev->dev); - + inst->empty_queue = true; while (check_cmd) { struct queue_status_info q_status; struct dec_output_info dec_output_info; wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); - - if (q_status.report_queue_count == 0) - break; - - if (wave5_vpu_wait_interrupt(inst, VPU_DEC_TIMEOUT) < 0) + if ((inst->state == VPU_INST_STATE_STOP || + inst->state == VPU_INST_STATE_INIT_SEQ || + q_status.instance_queue_count == 0) && + q_status.report_queue_count == 0) break; if (wave5_vpu_dec_get_output_info(inst, &dec_output_info)) @@ -1496,6 +1545,8 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) else streamoff_capture(q); + inst->empty_queue = false; + inst->sent_eos = false; pm_runtime_put_autosuspend(inst->dev->dev); } @@ -1577,10 +1628,18 @@ static void wave5_vpu_dec_device_run(void *priv) dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); pm_runtime_resume_and_get(inst->dev->dev); - ret = fill_ringbuffer(inst); - if (ret) { - dev_warn(inst->dev->dev, "Filling ring buffer failed\n"); - goto finish_job_and_return; + if (!inst->retry) { + ret = fill_ringbuffer(inst); + if (ret < 0) { + dev_warn(inst->dev->dev, "Filling ring buffer failed\n"); + goto finish_job_and_return; + } else if (!inst->eos && + inst->queuing_num == 0 && + inst->state == VPU_INST_STATE_PIC_RUN) { + dev_dbg(inst->dev->dev, "%s: no bitstream for feeding, so skip ", __func__); + inst->empty_queue = true; + goto finish_job_and_return; + } } switch (inst->state) { @@ -1605,7 +1664,7 @@ static void wave5_vpu_dec_device_run(void *priv) } spin_unlock_irqrestore(&inst->state_spinlock, flags); } else { - switch_state(inst, VPU_INST_STATE_INIT_SEQ); + set_instance_state(inst, VPU_INST_STATE_INIT_SEQ); } break; @@ -1616,8 +1675,7 @@ static void wave5_vpu_dec_device_run(void *priv) * we had a chance to switch, which leads to an invalid state * change. */ - switch_state(inst, VPU_INST_STATE_PIC_RUN); - + set_instance_state(inst, VPU_INST_STATE_PIC_RUN); /* * During DRC, the picture decoding remains pending, so just leave the job * active until this decode operation completes. @@ -1631,14 +1689,12 @@ static void wave5_vpu_dec_device_run(void *priv) ret = wave5_prepare_fb(inst); if (ret) { dev_warn(inst->dev->dev, "Framebuffer preparation, fail: %d\n", ret); - switch_state(inst, VPU_INST_STATE_STOP); + set_instance_state(inst, VPU_INST_STATE_STOP); break; } - if (q_status.instance_queue_count) { - dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); - return; - } + if (q_status.instance_queue_count) + goto finish_job_and_return; fallthrough; case VPU_INST_STATE_PIC_RUN: @@ -1647,28 +1703,45 @@ static void wave5_vpu_dec_device_run(void *priv) dev_err(inst->dev->dev, "Frame decoding on m2m context (%p), fail: %d (result: %d)\n", m2m_ctx, ret, fail_res); - break; + goto finish_job_and_return; + } + + if (fail_res == WAVE5_SYSERR_QUEUEING_FAIL) { + inst->retry = true; + inst->queuing_fail = true; + } else { + inst->retry = false; + if (!inst->eos) + inst->queuing_num--; } - /* Return so that we leave this job active */ - dev_dbg(inst->dev->dev, "%s: leave with active job", __func__); - return; - default: - WARN(1, "Execution of a job in state %s illegal.\n", state_to_str(inst->state)); break; + default: + dev_dbg(inst->dev->dev, "Execution of a job in state %s illegal.\n", + state_to_str(inst->state)); } finish_job_and_return: dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__); pm_runtime_put_autosuspend(inst->dev->dev); - v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); + /* + * After receiving CMD_STOP, there is no input, but we have to run device_run + * to send DEC_PIC command until display index == -1, so job_finish was always + * called in the device_run to archive it, the logic was very wasteful + * in power and CPU time. + * If EOS is passed, device_run will not call job_finish no more, it is called + * only if HW is idle status in order to reduce overhead. + */ + if (!inst->sent_eos) + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } static void wave5_vpu_dec_job_abort(void *priv) { struct vpu_instance *inst = priv; + struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; int ret; - ret = switch_state(inst, VPU_INST_STATE_STOP); + ret = set_instance_state(inst, VPU_INST_STATE_STOP); if (ret) return; @@ -1676,6 +1749,8 @@ static void wave5_vpu_dec_job_abort(void *priv) if (ret) dev_warn(inst->dev->dev, "Setting EOS for the bitstream, fail: %d\n", ret); + + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } static int wave5_vpu_dec_job_ready(void *priv) @@ -1711,10 +1786,15 @@ static int wave5_vpu_dec_job_ready(void *priv) "No capture buffer ready to decode!\n"); break; } else if (!wave5_is_draining_or_eos(inst) && - !v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + (!v4l2_m2m_num_src_bufs_ready(m2m_ctx) || + inst->empty_queue)) { dev_dbg(inst->dev->dev, "No bitstream data to decode!\n"); break; + } else if (inst->state == VPU_INST_STATE_PIC_RUN && + !wave5_is_draining_or_eos(inst) && + inst->queuing_fail) { + break; } ret = 1; break; @@ -1751,10 +1831,14 @@ static int wave5_vpu_open_dec(struct file *filp) inst->ops = &wave5_vpu_dec_inst_ops; spin_lock_init(&inst->state_spinlock); + mutex_init(&inst->feed_lock); + INIT_LIST_HEAD(&inst->avail_src_bufs); inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); - if (!inst->codec_info) + if (!inst->codec_info) { + kfree(inst); return -ENOMEM; + } v4l2_fh_init(&inst->v4l2_fh, vdev); v4l2_fh_add(&inst->v4l2_fh, filp); @@ -1806,6 +1890,11 @@ static int wave5_vpu_open_dec(struct file *filp) inst->xfer_func = V4L2_XFER_FUNC_DEFAULT; init_completion(&inst->irq_done); + ret = wave5_kfifo_alloc(inst); + if (ret) { + dev_err(inst->dev->dev, "failed to allocate fifo\n"); + goto cleanup_inst; + } inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); if (inst->id < 0) { @@ -1825,9 +1914,6 @@ static int wave5_vpu_open_dec(struct file *filp) if (ret) goto cleanup_inst; - if (list_empty(&dev->instances)) - pm_runtime_use_autosuspend(inst->dev->dev); - list_add_tail(&inst->list, &dev->instances); mutex_unlock(&dev->dev_lock); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index 9bfaa9fb3ceb..24fc0d0d3f4a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -649,6 +649,8 @@ static int wave5_vpu_enc_encoder_cmd(struct file *file, void *fh, struct v4l2_en m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); m2m_ctx->is_draining = true; + + v4l2_m2m_try_schedule(m2m_ctx); break; case V4L2_ENC_CMD_START: break; @@ -1367,7 +1369,8 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count if (ret) goto return_buffers; } - if (inst->state == VPU_INST_STATE_OPEN && m2m_ctx->cap_q_ctx.q.streaming) { + if (inst->state == VPU_INST_STATE_OPEN && + (m2m_ctx->cap_q_ctx.q.streaming || q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) { ret = initialize_sequence(inst); if (ret) { dev_warn(inst->dev->dev, "Sequence not found: %d\n", ret); @@ -1578,8 +1581,10 @@ static int wave5_vpu_open_enc(struct file *filp) inst->ops = &wave5_vpu_enc_inst_ops; inst->codec_info = kzalloc(sizeof(*inst->codec_info), GFP_KERNEL); - if (!inst->codec_info) + if (!inst->codec_info) { + kfree(inst); return -ENOMEM; + } v4l2_fh_init(&inst->v4l2_fh, vdev); v4l2_fh_add(&inst->v4l2_fh, filp); @@ -1754,6 +1759,11 @@ static int wave5_vpu_open_enc(struct file *filp) inst->frame_rate = 30; init_completion(&inst->irq_done); + ret = wave5_kfifo_alloc(inst); + if (ret) { + dev_err(inst->dev->dev, "failed to allocate fifo\n"); + goto cleanup_inst; + } inst->id = ida_alloc(&inst->dev->inst_ida, GFP_KERNEL); if (inst->id < 0) { @@ -1768,9 +1778,6 @@ static int wave5_vpu_open_enc(struct file *filp) if (ret) goto cleanup_inst; - if (list_empty(&dev->instances)) - pm_runtime_use_autosuspend(inst->dev->dev); - list_add_tail(&inst->list, &dev->instances); mutex_unlock(&dev->dev_lock); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index e1715d3f43b0..76d57c6b636a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -51,8 +51,11 @@ static void wave5_vpu_handle_irq(void *dev_id) u32 seq_done; u32 cmd_done; u32 irq_reason; - struct vpu_instance *inst; + u32 irq_subreason; + struct vpu_instance *inst, *tmp; struct vpu_device *dev = dev_id; + int val; + unsigned long flags; irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON); seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO); @@ -60,7 +63,8 @@ static void wave5_vpu_handle_irq(void *dev_id) wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason); wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1); - list_for_each_entry(inst, &dev->instances, list) { + spin_lock_irqsave(&dev->irq_spinlock, flags); + list_for_each_entry_safe(inst, tmp, &dev->instances, list) { if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) || irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { @@ -82,22 +86,54 @@ static void wave5_vpu_handle_irq(void *dev_id) irq_reason & BIT(INT_WAVE5_ENC_PIC)) { if (cmd_done & BIT(inst->id)) { cmd_done &= ~BIT(inst->id); - wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, - cmd_done); - inst->ops->finish_process(inst); + if (dev->irq >= 0) { + irq_subreason = + wave5_vdi_read_register(dev, W5_VPU_VINT_REASON); + if (!(irq_subreason & BIT(INT_WAVE5_DEC_PIC))) + wave5_vdi_write_register(dev, + W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + } + val = BIT(INT_WAVE5_DEC_PIC); + kfifo_in(&inst->irq_status, &val, sizeof(int)); } } + } + spin_unlock_irqrestore(&dev->irq_spinlock, flags); + + if (dev->irq < 0) + up(&dev->irq_sem); +} - wave5_vpu_clear_interrupt(inst, irq_reason); +static irqreturn_t wave5_vpu_irq(int irq, void *dev_id) +{ + struct vpu_device *dev = dev_id; + + if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) { + wave5_vpu_handle_irq(dev); + return IRQ_WAKE_THREAD; } + + return IRQ_HANDLED; } static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id) { struct vpu_device *dev = dev_id; + struct vpu_instance *inst, *tmp; + int irq_status, ret; - if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) - wave5_vpu_handle_irq(dev); + mutex_lock(&dev->irq_lock); + list_for_each_entry_safe(inst, tmp, &dev->instances, list) { + while (kfifo_len(&inst->irq_status)) { + ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int)); + if (!ret) + break; + + inst->ops->finish_process(inst); + } + } + mutex_unlock(&dev->irq_lock); return IRQ_HANDLED; } @@ -121,6 +157,35 @@ static enum hrtimer_restart wave5_vpu_timer_callback(struct hrtimer *timer) return HRTIMER_RESTART; } +static int irq_thread(void *data) +{ + struct vpu_device *dev = (struct vpu_device *)data; + struct vpu_instance *inst, *tmp; + int irq_status, ret; + + while (!kthread_should_stop()) { + if (down_interruptible(&dev->irq_sem)) + continue; + + if (kthread_should_stop()) + break; + + mutex_lock(&dev->irq_lock); + list_for_each_entry_safe(inst, tmp, &dev->instances, list) { + while (kfifo_len(&inst->irq_status)) { + ret = kfifo_out(&inst->irq_status, &irq_status, sizeof(int)); + if (!ret) + break; + + inst->ops->finish_process(inst); + } + } + mutex_unlock(&dev->irq_lock); + } + + return 0; +} + static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name, u32 *revision) { @@ -224,6 +289,8 @@ static int wave5_vpu_probe(struct platform_device *pdev) mutex_init(&dev->dev_lock); mutex_init(&dev->hw_lock); + mutex_init(&dev->irq_lock); + spin_lock_init(&dev->irq_spinlock); dev_set_drvdata(&pdev->dev, dev); dev->dev = &pdev->dev; @@ -266,9 +333,13 @@ static int wave5_vpu_probe(struct platform_device *pdev) } dev->product = wave5_vpu_get_product_id(dev); + INIT_LIST_HEAD(&dev->instances); + dev->irq = platform_get_irq(pdev, 0); if (dev->irq < 0) { dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n"); + sema_init(&dev->irq_sem, 1); + dev->irq_thread = kthread_run(irq_thread, dev, "irq thread"); hrtimer_setup(&dev->hrtimer, &wave5_vpu_timer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); dev->worker = kthread_run_worker(0, "vpu_irq_thread"); @@ -280,7 +351,7 @@ static int wave5_vpu_probe(struct platform_device *pdev) dev->vpu_poll_interval = vpu_poll_interval; kthread_init_work(&dev->work, wave5_vpu_irq_work_fn); } else { - ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL, + ret = devm_request_threaded_irq(&pdev->dev, dev->irq, wave5_vpu_irq, wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev); if (ret) { dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret); @@ -288,11 +359,10 @@ static int wave5_vpu_probe(struct platform_device *pdev) } } - INIT_LIST_HEAD(&dev->instances); ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err(&pdev->dev, "v4l2_device_register, fail: %d\n", ret); - goto err_vdi_release; + goto err_irq_release; } if (match_data->flags & WAVE5_IS_DEC) { @@ -322,7 +392,7 @@ static int wave5_vpu_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code); dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision); - pm_runtime_set_autosuspend_delay(&pdev->dev, 100); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); wave5_vpu_sleep_wake(&pdev->dev, true, NULL, 0); @@ -337,7 +407,15 @@ err_dec_unreg: wave5_vpu_dec_unregister_device(dev); err_v4l2_unregister: v4l2_device_unregister(&dev->v4l2_dev); +err_irq_release: + if (dev->irq < 0) + kthread_destroy_worker(dev->worker); err_vdi_release: + if (dev->irq_thread) { + kthread_stop(dev->irq_thread); + up(&dev->irq_sem); + dev->irq_thread = NULL; + } wave5_vdi_release(&pdev->dev); err_clk_dis: clk_bulk_disable_unprepare(dev->num_clks, dev->clks); @@ -351,21 +429,30 @@ static void wave5_vpu_remove(struct platform_device *pdev) { struct vpu_device *dev = dev_get_drvdata(&pdev->dev); + wave5_vpu_enc_unregister_device(dev); + wave5_vpu_dec_unregister_device(dev); + v4l2_device_unregister(&dev->v4l2_dev); + if (dev->irq < 0) { - kthread_destroy_worker(dev->worker); + if (dev->irq_thread) { + kthread_stop(dev->irq_thread); + up(&dev->irq_sem); + dev->irq_thread = NULL; + } + hrtimer_cancel(&dev->hrtimer); + kthread_cancel_work_sync(&dev->work); + kthread_destroy_worker(dev->worker); } - pm_runtime_put_sync(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); mutex_destroy(&dev->dev_lock); mutex_destroy(&dev->hw_lock); + mutex_destroy(&dev->irq_lock); reset_control_assert(dev->resets); clk_bulk_disable_unprepare(dev->num_clks, dev->clks); - wave5_vpu_enc_unregister_device(dev); - wave5_vpu_dec_unregister_device(dev); - v4l2_device_unregister(&dev->v4l2_dev); wave5_vdi_release(&pdev->dev); ida_destroy(&dev->inst_ida); } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h index 5943bdaa9c4c..99c3be882192 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h @@ -22,8 +22,8 @@ struct vpu_src_buffer { struct v4l2_m2m_buffer v4l2_m2m_buf; - struct list_head list; bool consumed; + struct list_head list; }; struct vpu_dst_buffer { diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c index e5e879a13e8b..d26ffc942219 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c @@ -52,11 +52,12 @@ int wave5_vpu_init_with_bitcode(struct device *dev, u8 *bitcode, size_t size) int wave5_vpu_flush_instance(struct vpu_instance *inst) { int ret = 0; + int mutex_ret = 0; int retry = 0; - ret = mutex_lock_interruptible(&inst->dev->hw_lock); - if (ret) - return ret; + mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (mutex_ret) + return mutex_ret; do { /* * Repeat the FLUSH command until the firmware reports that the @@ -80,11 +81,16 @@ int wave5_vpu_flush_instance(struct vpu_instance *inst) mutex_unlock(&inst->dev->hw_lock); wave5_vpu_dec_get_output_info(inst, &dec_info); - ret = mutex_lock_interruptible(&inst->dev->hw_lock); - if (ret) - return ret; - if (dec_info.index_frame_display > 0) + mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (mutex_ret) + return mutex_ret; + if (dec_info.index_frame_display >= 0) { + mutex_unlock(&inst->dev->hw_lock); wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display); + mutex_ret = mutex_lock_interruptible(&inst->dev->hw_lock); + if (mutex_ret) + return mutex_ret; + } } } while (ret != 0); mutex_unlock(&inst->dev->hw_lock); @@ -207,8 +213,8 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) int retry = 0; struct vpu_device *vpu_dev = inst->dev; int i; - int inst_count = 0; - struct vpu_instance *inst_elm; + struct dec_output_info dec_info; + int ret_mutex; *fail_res = 0; if (!inst->codec_info) @@ -216,10 +222,10 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) pm_runtime_resume_and_get(inst->dev->dev); - ret = mutex_lock_interruptible(&vpu_dev->hw_lock); - if (ret) { + ret_mutex = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret_mutex) { pm_runtime_put_sync(inst->dev->dev); - return ret; + return ret_mutex; } do { @@ -229,11 +235,26 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) goto unlock_and_return; } - if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING && - retry++ >= MAX_FIRMWARE_CALL_RETRY) { + if (ret == 0) + break; + + if (*fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) { + dev_warn(inst->dev->dev, "dec_finish_seq timed out\n"); + goto unlock_and_return; + } + + if (retry++ >= MAX_FIRMWARE_CALL_RETRY) { ret = -ETIMEDOUT; goto unlock_and_return; } + + mutex_unlock(&vpu_dev->hw_lock); + wave5_vpu_dec_get_output_info(inst, &dec_info); + ret_mutex = mutex_lock_interruptible(&vpu_dev->hw_lock); + if (ret_mutex) { + pm_runtime_put_sync(inst->dev->dev); + return ret_mutex; + } } while (ret != 0); dev_dbg(inst->dev->dev, "%s: dec_finish_seq complete\n", __func__); @@ -250,10 +271,7 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res) wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task); - list_for_each_entry(inst_elm, &vpu_dev->instances, list) - inst_count++; - if (inst_count == 1) - pm_runtime_dont_use_autosuspend(vpu_dev->dev); + mutex_destroy(&inst->feed_lock); unlock_and_return: mutex_unlock(&vpu_dev->hw_lock); @@ -467,11 +485,11 @@ int wave5_vpu_dec_set_rd_ptr(struct vpu_instance *inst, dma_addr_t addr, int upd dma_addr_t wave5_vpu_dec_get_rd_ptr(struct vpu_instance *inst) { int ret; - dma_addr_t rd_ptr; + dma_addr_t rd_ptr = 0; ret = mutex_lock_interruptible(&inst->dev->hw_lock); if (ret) - return ret; + return rd_ptr; rd_ptr = wave5_dec_get_rd_ptr(inst); @@ -492,7 +510,7 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i struct vpu_device *vpu_dev = inst->dev; struct dec_output_info *disp_info; - if (!info) + if (WARN_ON(!info)) return -EINVAL; p_dec_info = &inst->codec_info->dec_info; @@ -720,8 +738,6 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) int ret; int retry = 0; struct vpu_device *vpu_dev = inst->dev; - int inst_count = 0; - struct vpu_instance *inst_elm; *fail_res = 0; if (!inst->codec_info) @@ -764,12 +780,6 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res) } wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task); - - list_for_each_entry(inst_elm, &vpu_dev->instances, list) - inst_count++; - if (inst_count == 1) - pm_runtime_dont_use_autosuspend(vpu_dev->dev); - mutex_unlock(&vpu_dev->hw_lock); pm_runtime_put_sync(inst->dev->dev); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index 45615c15beca..c64135769869 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -8,6 +8,7 @@ #ifndef VPUAPI_H_INCLUDED #define VPUAPI_H_INCLUDED +#include <linux/kfifo.h> #include <linux/idr.h> #include <linux/genalloc.h> #include <media/v4l2-device.h> @@ -747,6 +748,7 @@ struct vpu_device { struct video_device *video_dev_enc; struct mutex dev_lock; /* lock for the src, dst v4l2 queues */ struct mutex hw_lock; /* lock hw configurations */ + struct mutex irq_lock; int irq; enum product_id product; struct vpu_attr attr; @@ -764,7 +766,10 @@ struct vpu_device { struct kthread_worker *worker; int vpu_poll_interval; int num_clks; + struct task_struct *irq_thread; + struct semaphore irq_sem; /* signal to irq_thread when interrupt happens*/ struct reset_control *resets; + spinlock_t irq_spinlock; /* protect instances list */ }; struct vpu_instance; @@ -788,6 +793,7 @@ struct vpu_instance { enum v4l2_ycbcr_encoding ycbcr_enc; enum v4l2_quantization quantization; + struct kfifo irq_status; enum vpu_instance_state state; enum vpu_instance_type type; const struct vpu_instance_ops *ops; @@ -812,6 +818,12 @@ struct vpu_instance { bool cbcr_interleave; bool nv21; bool eos; + bool sent_eos; /* check if EOS is sent to application */ + bool retry; /* retry to feed bitstream if failure reason is WAVE5_SYSERR_QUEUEING_FAIL*/ + int queuing_num; /* count of bitstream queued */ + struct mutex feed_lock; /* lock for feeding bitstream buffers */ + bool queuing_fail; /* if there is the queuing failure */ + bool empty_queue; struct vpu_buf bitstream_vbuf; dma_addr_t last_rd_ptr; size_t remaining_consumed_bytes; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index 1ea9f5f31499..4ebd48d5550e 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -59,6 +59,7 @@ // application specific configuration #define VPU_ENC_TIMEOUT 60000 #define VPU_DEC_TIMEOUT 60000 +#define VPU_DEC_STOP_TIMEOUT 10 // for WAVE encoder #define USE_SRC_PRP_AXI 0 diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c index 80fdc6ff57e0..8432833814f3 100644 --- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c @@ -194,11 +194,17 @@ static int mtk_mdp_probe(struct platform_device *pdev) } mdp->vpu_dev = vpu_get_plat_device(pdev); + if (!mdp->vpu_dev) { + dev_err(&pdev->dev, "Failed to get vpu device\n"); + ret = -ENODEV; + goto err_vpu_get_dev; + } + ret = vpu_wdt_reg_handler(mdp->vpu_dev, mtk_mdp_reset_handler, mdp, VPU_RST_MDP); if (ret) { dev_err(&pdev->dev, "Failed to register reset handler\n"); - goto err_m2m_register; + goto err_reg_handler; } platform_set_drvdata(pdev, mdp); @@ -206,7 +212,7 @@ static int mtk_mdp_probe(struct platform_device *pdev) ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { dev_err(&pdev->dev, "Failed to set vb2 dma mag seg size\n"); - goto err_m2m_register; + goto err_reg_handler; } pm_runtime_enable(dev); @@ -214,6 +220,12 @@ static int mtk_mdp_probe(struct platform_device *pdev) return 0; +err_reg_handler: + platform_device_put(mdp->vpu_dev); + +err_vpu_get_dev: + mtk_mdp_unregister_m2m_device(mdp); + err_m2m_register: v4l2_device_unregister(&mdp->v4l2_dev); @@ -242,6 +254,7 @@ static void mtk_mdp_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); vb2_dma_contig_clear_max_seg_size(&pdev->dev); + platform_device_put(mdp->vpu_dev); mtk_mdp_unregister_m2m_device(mdp); v4l2_device_unregister(&mdp->v4l2_dev); diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 6d26d4aa1eef..f20d2ed1f2bb 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -268,14 +268,16 @@ static int mdp_probe(struct platform_device *pdev) goto err_free_mutex; } - mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 0); + mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, + WQ_FREEZABLE | WQ_PERCPU, 0); if (!mdp->job_wq) { dev_err(dev, "Unable to create job workqueue\n"); ret = -ENOMEM; goto err_deinit_comp; } - mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", WQ_FREEZABLE, + mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock", + WQ_FREEZABLE | WQ_PERCPU, 0); if (!mdp->clock_wq) { dev_err(dev, "Unable to create clock workqueue\n"); diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 1f32ba11a18c..d76e891f784b 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -882,8 +882,10 @@ void vb2ops_vdec_stop_streaming(struct vb2_queue *q) src_buf->vb2_buf.req_obj.req; v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - if (req) + if (req) { v4l2_ctrl_request_complete(req, &ctx->ctrl_hdl); + media_request_manual_complete(req); + } } } return; diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h index 9d68808e8f9c..c9d27534c63e 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h @@ -7,6 +7,8 @@ #ifndef _MTK_VCODEC_DEC_DRV_H_ #define _MTK_VCODEC_DEC_DRV_H_ +#include <linux/kref.h> + #include "../common/mtk_vcodec_cmn_drv.h" #include "../common/mtk_vcodec_dbgfs.h" #include "../common/mtk_vcodec_fw_priv.h" @@ -129,6 +131,16 @@ struct mtk_vcodec_dec_pdata { }; /** + * struct mtk_vcodec_dec_request - Media request private data. + * @refcount: Used to ensure we don't complete the request too soon + * @req: Media Request structure + */ +struct mtk_vcodec_dec_request { + struct kref refcount; + struct media_request req; +}; + +/** * struct mtk_vcodec_dec_ctx - Context (instance) private data. * * @type: type of decoder instance @@ -324,6 +336,11 @@ static inline struct mtk_vcodec_dec_ctx *ctrl_to_dec_ctx(struct v4l2_ctrl *ctrl) return container_of(ctrl->handler, struct mtk_vcodec_dec_ctx, ctrl_hdl); } +static inline struct mtk_vcodec_dec_request *req_to_dec_req(struct media_request *req) +{ + return container_of(req, struct mtk_vcodec_dec_request, req); +} + /* Wake up context wait_queue */ static inline void wake_up_dec_ctx(struct mtk_vcodec_dec_ctx *ctx, unsigned int reason, unsigned int hw_id) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c index aefd3e9e3061..f6f03c61c643 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_pm.c @@ -67,11 +67,7 @@ static int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm) static void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm) { - int ret; - - ret = pm_runtime_put(pm->dev); - if (ret && ret != -EAGAIN) - dev_err(pm->dev, "pm_runtime_put fail %d", ret); + pm_runtime_put(pm->dev); } static void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index d873159b9b30..8e1cf16a168a 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -242,10 +242,18 @@ static const struct v4l2_frmsize_stepwise stepwise_fhd = { .step_height = 16 }; +static void mtk_vcodec_dec_request_release(struct kref *ref) +{ + struct mtk_vcodec_dec_request *req = + container_of(ref, struct mtk_vcodec_dec_request, refcount); + media_request_manual_complete(&req->req); +} + static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_dec_ctx *ctx, int error, - struct media_request *src_buf_req) + struct media_request *mreq) { - struct vb2_v4l2_buffer *vb2_dst; + struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq); + struct vb2_v4l2_buffer *dst_buf; enum vb2_buffer_state state; if (error) @@ -253,17 +261,9 @@ static void mtk_vdec_stateless_cap_to_disp(struct mtk_vcodec_dec_ctx *ctx, int e else state = VB2_BUF_STATE_DONE; - vb2_dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - if (vb2_dst) { - v4l2_m2m_buf_done(vb2_dst, state); - mtk_v4l2_vdec_dbg(2, ctx, "free frame buffer id:%d to done list", - vb2_dst->vb2_buf.index); - } else { - mtk_v4l2_vdec_err(ctx, "dst buffer is NULL"); - } - - if (src_buf_req) - v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(dst_buf, state); + kref_put(&req->refcount, mtk_vcodec_dec_request_release); } static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_dec_ctx *ctx) @@ -306,6 +306,7 @@ static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb) struct mtk_vcodec_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); + media_request_manual_complete(vb->req_obj.req); } static void mtk_vdec_worker(struct work_struct *work) @@ -317,8 +318,9 @@ static void mtk_vdec_worker(struct work_struct *work) struct vb2_buffer *vb2_src; struct mtk_vcodec_mem *bs_src; struct mtk_video_dec_buf *dec_buf_src; - struct media_request *src_buf_req; - enum vb2_buffer_state state; + struct media_request *mreq; + struct mtk_vcodec_dec_request *req; + enum vb2_buffer_state buf_state; bool res_chg = false; int ret; @@ -350,14 +352,26 @@ static void mtk_vdec_worker(struct work_struct *work) mtk_v4l2_vdec_dbg(3, ctx, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src); /* Apply request controls. */ - src_buf_req = vb2_src->req_obj.req; - if (src_buf_req) - v4l2_ctrl_request_setup(src_buf_req, &ctx->ctrl_hdl); - else + mreq = vb2_src->req_obj.req; + if (WARN_ON(!mreq)) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); mtk_v4l2_vdec_err(ctx, "vb2 buffer media request is NULL"); + return; + } + + v4l2_ctrl_request_setup(mreq, &ctx->ctrl_hdl); + + /* Keep a reference so that if the processing completes before this function + * ends, we won't accidently update a freshly queued request. + */ + req = req_to_dec_req(mreq); + kref_get(&req->refcount); ret = vdec_if_decode(ctx, bs_src, NULL, &res_chg); - if (ret && ret != -EAGAIN) { + if (ret == -EAGAIN) + goto done; + + if (ret) { mtk_v4l2_vdec_err(ctx, "[%d] decode src_buf[%d] sz=0x%zx pts=%llu ret=%d res_chg=%d", ctx->id, vb2_src->index, bs_src->size, @@ -367,21 +381,23 @@ static void mtk_vdec_worker(struct work_struct *work) dec_buf_src->error = true; mutex_unlock(&ctx->lock); } - } - state = ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE; - if (!IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch) || - ctx->current_codec == V4L2_PIX_FMT_VP8_FRAME) { - v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, state); - if (src_buf_req) - v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); + buf_state = VB2_BUF_STATE_ERROR; } else { - if (ret != -EAGAIN) { - v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - v4l2_m2m_buf_done(vb2_v4l2_src, state); - } - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + buf_state = VB2_BUF_STATE_DONE; } + + v4l2_ctrl_request_complete(mreq, &ctx->ctrl_hdl); + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(vb2_v4l2_src, buf_state); + + if (ret || !IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch) || + ctx->current_codec == V4L2_PIX_FMT_VP8_FRAME) + mtk_vdec_stateless_cap_to_disp(ctx, ret, mreq); + +done: + kref_put(&req->refcount, mtk_vcodec_dec_request_release); + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); } static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb) @@ -502,6 +518,12 @@ static int mtk_vdec_s_ctrl(struct v4l2_ctrl *ctrl) mtk_v4l2_vdec_err(ctx, "VP9: bit_depth:%d", frame->bit_depth); return -EINVAL; } + + if (!(frame->flags & V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING) || + !(frame->flags & V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING)) { + mtk_v4l2_vdec_err(ctx, "VP9: only 420 subsampling is supported"); + return -EINVAL; + } break; case V4L2_CID_STATELESS_AV1_SEQUENCE: seq = (struct v4l2_ctrl_av1_sequence *)hdr_ctrl->p_new.p; @@ -709,6 +731,22 @@ static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_dec_ctx *ctx) return 0; } +static struct media_request *fops_media_request_alloc(struct media_device *mdev) +{ + struct mtk_vcodec_dec_request *req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + + return &req->req; +} + +static void fops_media_request_free(struct media_request *mreq) +{ + struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq); + + kfree(req); +} + static int fops_media_request_validate(struct media_request *mreq) { const unsigned int buffer_cnt = vb2_request_buffer_cnt(mreq); @@ -729,9 +767,20 @@ static int fops_media_request_validate(struct media_request *mreq) return vb2_request_validate(mreq); } +static void fops_media_request_queue(struct media_request *mreq) +{ + struct mtk_vcodec_dec_request *req = req_to_dec_req(mreq); + + media_request_mark_manual_completion(mreq); + kref_init(&req->refcount); + v4l2_m2m_request_queue(mreq); +} + const struct media_device_ops mtk_vcodec_media_ops = { + .req_alloc = fops_media_request_alloc, + .req_free = fops_media_request_free, .req_validate = fops_media_request_validate, - .req_queue = v4l2_m2m_request_queue, + .req_queue = fops_media_request_queue, }; static void mtk_vcodec_add_formats(unsigned int fourcc, diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c index eb3354192853..80554b2c26c0 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c @@ -548,10 +548,9 @@ static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_dec_ctx *ctx) { int result; - struct mtk_vcodec_mem mem; + struct mtk_vcodec_mem mem = { }; struct vdec_vp9_inst *inst; - memset(&mem, 0, sizeof(mem)); mem.size = sizeof(struct vdec_vp9_inst); result = mtk_vcodec_mem_alloc(ctx, &mem); if (result) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index 40b97f114cf6..b35759a0b353 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -182,12 +182,11 @@ static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) { - struct vdec_ap_ipi_cmd msg; + struct vdec_ap_ipi_cmd msg = { }; int err = 0; mtk_vdec_debug(vpu->ctx, "+ id=%X", msg_id); - memset(&msg, 0, sizeof(msg)); msg.msg_id = msg_id; if (vpu->fw_abi_version < 2) msg.vpu_inst_addr = vpu->inst_addr; @@ -202,7 +201,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) int vpu_dec_init(struct vdec_vpu_inst *vpu) { - struct vdec_ap_ipi_init msg; + struct vdec_ap_ipi_init msg = { }; int err; init_waitqueue_head(&vpu->wq); @@ -226,7 +225,6 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) } } - memset(&msg, 0, sizeof(msg)); msg.msg_id = AP_IPIMSG_DEC_INIT; msg.ap_inst_addr = (unsigned long)vpu; msg.codec_type = vpu->codec_type; @@ -246,7 +244,7 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) { - struct vdec_ap_ipi_dec_start msg; + struct vdec_ap_ipi_dec_start msg = { }; int i; int err = 0; @@ -255,7 +253,6 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) return -EINVAL; } - memset(&msg, 0, sizeof(msg)); msg.msg_id = AP_IPIMSG_DEC_START; if (vpu->fw_abi_version < 2) msg.vpu_inst_addr = vpu->inst_addr; @@ -274,7 +271,7 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len, unsigned int param_type) { - struct vdec_ap_ipi_get_param msg; + struct vdec_ap_ipi_get_param msg = { }; int err; if (len > ARRAY_SIZE(msg.data)) { @@ -282,7 +279,6 @@ int vpu_dec_get_param(struct vdec_vpu_inst *vpu, uint32_t *data, return -EINVAL; } - memset(&msg, 0, sizeof(msg)); msg.msg_id = AP_IPIMSG_DEC_GET_PARAM; msg.inst_id = vpu->inst_id; memcpy(msg.data, data, sizeof(unsigned int) * len); diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c index 6faf3f659e75..0d4e94463685 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c @@ -850,7 +850,7 @@ static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) { struct mtk_vcodec_enc_ctx *ctx = vb2_get_drv_priv(q); - struct venc_enc_param param; + struct venc_enc_param param = { }; int ret; int i; @@ -1004,7 +1004,7 @@ static int mtk_venc_encode_header(void *priv) int ret; struct vb2_v4l2_buffer *src_buf, *dst_buf; struct mtk_vcodec_mem bs_buf; - struct venc_done_result enc_result; + struct venc_done_result enc_result = { }; dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); if (!dst_buf) { @@ -1049,7 +1049,7 @@ static int mtk_venc_encode_header(void *priv) static int mtk_venc_param_change(struct mtk_vcodec_enc_ctx *ctx) { - struct venc_enc_param enc_prm; + struct venc_enc_param enc_prm = { }; struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); struct mtk_video_enc_buf *mtk_buf; int ret = 0; @@ -1060,7 +1060,6 @@ static int mtk_venc_param_change(struct mtk_vcodec_enc_ctx *ctx) mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, m2m_buf.vb); - memset(&enc_prm, 0, sizeof(enc_prm)); if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE) return 0; @@ -1123,9 +1122,9 @@ static void mtk_venc_worker(struct work_struct *work) struct mtk_vcodec_enc_ctx *ctx = container_of(work, struct mtk_vcodec_enc_ctx, encode_work); struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct venc_frm_buf frm_buf; + struct venc_frm_buf frm_buf = { }; struct mtk_vcodec_mem bs_buf; - struct venc_done_result enc_result; + struct venc_done_result enc_result = { }; int ret, i; /* check dst_buf, dst_buf may be removed in device_run @@ -1153,7 +1152,6 @@ static void mtk_venc_worker(struct work_struct *work) return; } - memset(&frm_buf, 0, sizeof(frm_buf)); for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) { frm_buf.fb_addr[i].dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i); diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c index 1a2b14a3e219..edf9133c0673 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c @@ -71,11 +71,7 @@ int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm) void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm) { - int ret; - - ret = pm_runtime_put(pm->dev); - if (ret && ret != -EAGAIN) - dev_err(pm->dev, "pm_runtime_put fail %d", ret); + pm_runtime_put(pm->dev); } void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c index 3c229b1f6b21..0c825aa7224d 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c @@ -133,7 +133,7 @@ static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, int vpu_enc_init(struct venc_vpu_inst *vpu) { int status; - struct venc_ap_ipi_msg_init out; + struct venc_ap_ipi_msg_init out = { }; init_waitqueue_head(&vpu->wq_hd); vpu->signaled = 0; @@ -149,7 +149,6 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) return -EINVAL; } - memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_INIT; out.venc_inst = (unsigned long)vpu; if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { @@ -192,11 +191,10 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, size_t msg_size = is_ext ? sizeof(struct venc_ap_ipi_msg_set_param_ext) : sizeof(struct venc_ap_ipi_msg_set_param); - struct venc_ap_ipi_msg_set_param_ext out; + struct venc_ap_ipi_msg_set_param_ext out = { }; mtk_venc_debug(vpu->ctx, "id %d ->", id); - memset(&out, 0, sizeof(out)); out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM; out.base.vpu_inst_addr = vpu->inst_addr; out.base.param_id = id; @@ -259,11 +257,10 @@ static int vpu_enc_encode_32bits(struct venc_vpu_inst *vpu, size_t msg_size = is_ext ? sizeof(struct venc_ap_ipi_msg_enc_ext) : sizeof(struct venc_ap_ipi_msg_enc); - struct venc_ap_ipi_msg_enc_ext out; + struct venc_ap_ipi_msg_enc_ext out = { }; mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode); - memset(&out, 0, sizeof(out)); out.base.msg_id = AP_IPIMSG_ENC_ENCODE; out.base.vpu_inst_addr = vpu->inst_addr; out.base.bs_mode = bs_mode; @@ -303,12 +300,11 @@ static int vpu_enc_encode_34bits(struct venc_vpu_inst *vpu, struct mtk_vcodec_mem *bs_buf, struct venc_frame_info *frame_info) { - struct venc_ap_ipi_msg_enc_ext_34 out; + struct venc_ap_ipi_msg_enc_ext_34 out = { }; size_t msg_size = sizeof(struct venc_ap_ipi_msg_enc_ext_34); mtk_venc_debug(vpu->ctx, "bs_mode %d ->", bs_mode); - memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_ENCODE; out.vpu_inst_addr = vpu->inst_addr; out.bs_mode = bs_mode; @@ -368,9 +364,8 @@ int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, int vpu_enc_deinit(struct venc_vpu_inst *vpu) { - struct venc_ap_ipi_msg_deinit out; + struct venc_ap_ipi_msg_deinit out = { }; - memset(&out, 0, sizeof(out)); out.msg_id = AP_IPIMSG_ENC_DEINIT; out.vpu_inst_addr = vpu->inst_addr; if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.h b/drivers/media/platform/mediatek/vpu/mtk_vpu.h index da05f3e74081..3951547e9ec5 100644 --- a/drivers/media/platform/mediatek/vpu/mtk_vpu.h +++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.h @@ -119,8 +119,7 @@ int vpu_ipi_send(struct platform_device *pdev, * @pdev: the platform device of the module requesting VPU platform * device for using VPU API. * - * Return: Return NULL if it is failed. - * otherwise it is VPU's platform device + * Return: a reference to the VPU's platform device, or NULL on failure. **/ struct platform_device *vpu_get_plat_device(struct platform_device *pdev); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index 9e4a813489c0..b558700d1d96 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -44,6 +44,7 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/clk.h> +#include <linux/genalloc.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -783,32 +784,40 @@ static int mxc_get_free_slot(struct mxc_jpeg_slot_data *slot_data) return -1; } +static void mxc_jpeg_free(struct mxc_jpeg_dev *jpeg, size_t size, void *addr, dma_addr_t handle) +{ + if (jpeg->sram_pool) + gen_pool_free(jpeg->sram_pool, (unsigned long)addr, size); + else + dma_free_coherent(jpeg->dev, size, addr, handle); +} + static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) { /* free descriptor for decoding/encoding phase */ - dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), - jpeg->slot_data.desc, - jpeg->slot_data.desc_handle); + mxc_jpeg_free(jpeg, sizeof(struct mxc_jpeg_desc), + jpeg->slot_data.desc, + jpeg->slot_data.desc_handle); jpeg->slot_data.desc = NULL; jpeg->slot_data.desc_handle = 0; /* free descriptor for encoder configuration phase / decoder DHT */ - dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc), - jpeg->slot_data.cfg_desc, - jpeg->slot_data.cfg_desc_handle); + mxc_jpeg_free(jpeg, sizeof(struct mxc_jpeg_desc), + jpeg->slot_data.cfg_desc, + jpeg->slot_data.cfg_desc_handle); jpeg->slot_data.cfg_desc_handle = 0; jpeg->slot_data.cfg_desc = NULL; /* free configuration stream */ - dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM, - jpeg->slot_data.cfg_stream_vaddr, - jpeg->slot_data.cfg_stream_handle); + mxc_jpeg_free(jpeg, MXC_JPEG_MAX_CFG_STREAM, + jpeg->slot_data.cfg_stream_vaddr, + jpeg->slot_data.cfg_stream_handle); jpeg->slot_data.cfg_stream_vaddr = NULL; jpeg->slot_data.cfg_stream_handle = 0; - dma_free_coherent(jpeg->dev, jpeg->slot_data.cfg_dec_size, - jpeg->slot_data.cfg_dec_vaddr, - jpeg->slot_data.cfg_dec_daddr); + mxc_jpeg_free(jpeg, jpeg->slot_data.cfg_dec_size, + jpeg->slot_data.cfg_dec_vaddr, + jpeg->slot_data.cfg_dec_daddr); jpeg->slot_data.cfg_dec_size = 0; jpeg->slot_data.cfg_dec_vaddr = NULL; jpeg->slot_data.cfg_dec_daddr = 0; @@ -816,6 +825,15 @@ static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg) jpeg->slot_data.used = false; } +static struct mxc_jpeg_desc *mxc_jpeg_alloc(struct mxc_jpeg_dev *jpeg, size_t size, + dma_addr_t *handle) +{ + if (jpeg->sram_pool) + return gen_pool_dma_zalloc(jpeg->sram_pool, size, handle); + else + return dma_alloc_coherent(jpeg->dev, size, handle, GFP_ATOMIC); +} + static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg) { struct mxc_jpeg_desc *desc; @@ -826,37 +844,29 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg) goto skip_alloc; /* already allocated, reuse it */ /* allocate descriptor for decoding/encoding phase */ - desc = dma_alloc_coherent(jpeg->dev, - sizeof(struct mxc_jpeg_desc), - &jpeg->slot_data.desc_handle, - GFP_ATOMIC); + desc = mxc_jpeg_alloc(jpeg, sizeof(struct mxc_jpeg_desc), + &jpeg->slot_data.desc_handle); if (!desc) goto err; jpeg->slot_data.desc = desc; /* allocate descriptor for configuration phase (encoder only) */ - cfg_desc = dma_alloc_coherent(jpeg->dev, - sizeof(struct mxc_jpeg_desc), - &jpeg->slot_data.cfg_desc_handle, - GFP_ATOMIC); + cfg_desc = mxc_jpeg_alloc(jpeg, sizeof(struct mxc_jpeg_desc), + &jpeg->slot_data.cfg_desc_handle); if (!cfg_desc) goto err; jpeg->slot_data.cfg_desc = cfg_desc; /* allocate configuration stream */ - cfg_stm = dma_alloc_coherent(jpeg->dev, - MXC_JPEG_MAX_CFG_STREAM, - &jpeg->slot_data.cfg_stream_handle, - GFP_ATOMIC); + cfg_stm = mxc_jpeg_alloc(jpeg, MXC_JPEG_MAX_CFG_STREAM, + &jpeg->slot_data.cfg_stream_handle); if (!cfg_stm) goto err; jpeg->slot_data.cfg_stream_vaddr = cfg_stm; jpeg->slot_data.cfg_dec_size = MXC_JPEG_PATTERN_WIDTH * MXC_JPEG_PATTERN_HEIGHT * 2; - jpeg->slot_data.cfg_dec_vaddr = dma_alloc_coherent(jpeg->dev, - jpeg->slot_data.cfg_dec_size, - &jpeg->slot_data.cfg_dec_daddr, - GFP_ATOMIC); + jpeg->slot_data.cfg_dec_vaddr = mxc_jpeg_alloc(jpeg, jpeg->slot_data.cfg_dec_size, + &jpeg->slot_data.cfg_dec_daddr); if (!jpeg->slot_data.cfg_dec_vaddr) goto err; @@ -2884,6 +2894,10 @@ static int mxc_jpeg_probe(struct platform_device *pdev) jpeg->dev = dev; jpeg->mode = mode; + /* SRAM pool is optional */ + jpeg->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + dev_info(dev, "Using DMA descriptor pool in %cRAM\n", jpeg->sram_pool ? 'S' : 'D'); + /* Get clocks */ ret = devm_clk_bulk_get_all(&pdev->dev, &jpeg->clks); if (ret < 0) { diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h index 44e46face6d1..9c5b4f053ded 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h @@ -141,6 +141,7 @@ struct mxc_jpeg_dev { int num_domains; struct device **pd_dev; struct device_link **pd_link; + struct gen_pool *sram_pool; }; /** diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 088b2945aee3..9a43fd1eb0bc 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -12,6 +12,7 @@ * */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> @@ -1349,27 +1350,26 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) .bus_type = V4L2_MBUS_CSI2_DPHY, }; struct v4l2_async_connection *asd; - struct fwnode_handle *ep; unsigned int i; int ret; v4l2_async_subdev_nf_init(&csis->notifier, &csis->sd); - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0, - FWNODE_GRAPH_ENDPOINT_NEXT); + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_endpoint_by_id(dev_fwnode(csis->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) return -ENOTCONN; ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) - goto err_parse; + return ret; for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { dev_err(csis->dev, "data lanes reordering is not supported"); - ret = -EINVAL; - goto err_parse; + return -EINVAL; } } @@ -1381,12 +1381,8 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep, struct v4l2_async_connection); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto err_parse; - } - - fwnode_handle_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); csis->notifier.ops = &mipi_csis_notify_ops; @@ -1395,11 +1391,6 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis) return ret; return v4l2_async_register_subdev(&csis->sd); - -err_parse: - fwnode_handle_put(ep); - - return ret; } /* ----------------------------------------------------------------------------- @@ -1547,10 +1538,8 @@ static int mipi_csis_probe(struct platform_device *pdev) /* Now that the hardware is initialized, request the interrupt. */ ret = devm_request_irq(dev, irq, mipi_csis_irq_handler, 0, dev_name(dev), csis); - if (ret) { - dev_err(dev, "Interrupt request failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Interrupt request failed\n"); /* Initialize and register the subdev. */ ret = mipi_csis_subdev_init(csis); diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c index 933a5f39f9f4..7ddc7ba06e3d 100644 --- a/drivers/media/platform/nxp/imx7-media-csi.c +++ b/drivers/media/platform/nxp/imx7-media-csi.c @@ -2218,11 +2218,9 @@ static int imx7_csi_probe(struct platform_device *pdev) /* Acquire resources and install interrupt handler. */ csi->mclk = devm_clk_get(&pdev->dev, "mclk"); - if (IS_ERR(csi->mclk)) { - ret = PTR_ERR(csi->mclk); - dev_err(dev, "Failed to get mclk: %d", ret); - return ret; - } + if (IS_ERR(csi->mclk)) + return dev_err_probe(dev, PTR_ERR(csi->mclk), + "Failed to get mclk\n"); csi->irq = platform_get_irq(pdev, 0); if (csi->irq < 0) @@ -2236,10 +2234,8 @@ static int imx7_csi_probe(struct platform_device *pdev) ret = devm_request_irq(dev, csi->irq, imx7_csi_irq_handler, 0, "csi", (void *)csi); - if (ret < 0) { - dev_err(dev, "Request CSI IRQ failed.\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Request CSI IRQ failed.\n"); /* Initialize all the media device infrastructure. */ ret = imx7_csi_media_init(csi); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index c3d411ddf492..18146978bdf5 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -499,13 +499,14 @@ static int mxc_isi_probe(struct platform_device *pdev) dma_size = isi->pdata->has_36bit_dma ? 36 : 32; dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size)); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = mxc_isi_crossbar_init(isi); - if (ret) { - dev_err(dev, "Failed to initialize crossbar: %d\n", ret); - goto err_pm; - } + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize crossbar\n"); for (i = 0; i < isi->pdata->num_channels; ++i) { ret = mxc_isi_pipe_init(isi, i); @@ -528,8 +529,7 @@ static int mxc_isi_probe(struct platform_device *pdev) err_xbar: mxc_isi_crossbar_cleanup(&isi->crossbar); -err_pm: - pm_runtime_disable(isi->dev); + return ret; } @@ -548,8 +548,6 @@ static void mxc_isi_remove(struct platform_device *pdev) mxc_isi_crossbar_cleanup(&isi->crossbar); mxc_isi_v4l2_cleanup(isi); - - pm_runtime_disable(isi->dev); } static const struct of_device_id mxc_isi_of_match[] = { @@ -575,7 +573,6 @@ static struct platform_driver mxc_isi_driver = { }; module_platform_driver(mxc_isi_driver); -MODULE_ALIAS("ISI"); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("IMX8 Image Sensing Interface driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index ede6cc74c023..3c26cfef93d1 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -114,7 +114,7 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, "invalid route from memory input (%u) to pipe %u\n", route->sink_pad, route->source_pad - xbar->num_sinks); - return -EINVAL; + return -ENXIO; } } diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index 371b4e81328c..e111b1debd69 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -6,6 +6,7 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> @@ -717,27 +718,26 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) .bus_type = V4L2_MBUS_CSI2_DPHY, }; struct v4l2_async_connection *asd; - struct fwnode_handle *ep; unsigned int i; int ret; v4l2_async_subdev_nf_init(&state->notifier, &state->sd); - ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, - FWNODE_GRAPH_ENDPOINT_NEXT); + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); if (!ep) return -ENOTCONN; ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) - goto err_parse; + return ret; for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) { if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) { dev_err(state->dev, "data lanes reordering is not supported"); - ret = -EINVAL; - goto err_parse; + return -EINVAL; } } @@ -749,12 +749,8 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep, struct v4l2_async_connection); - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - goto err_parse; - } - - fwnode_handle_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); state->notifier.ops = &imx8mq_mipi_csi_notify_ops; @@ -763,11 +759,6 @@ static int imx8mq_mipi_csi_async_register(struct csi_state *state) return ret; return v4l2_async_register_subdev(&state->sd); - -err_parse: - fwnode_handle_put(ep); - - return ret; } /* ----------------------------------------------------------------------------- @@ -951,10 +942,9 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state) int ret = 0; state->rst = devm_reset_control_array_get_exclusive(dev); - if (IS_ERR(state->rst)) { - dev_err(dev, "Failed to get reset: %pe\n", state->rst); - return PTR_ERR(state->rst); - } + if (IS_ERR(state->rst)) + return dev_err_probe(dev, PTR_ERR(state->rst), + "Failed to get reset\n"); if (state->pdata->use_reg_csr) { const struct regmap_config regmap_config = { @@ -977,24 +967,22 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state) ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val, ARRAY_SIZE(out_val)); - if (ret) { - dev_err(dev, "no fsl,mipi-phy-gpr property found: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "property %s not found\n", + "fsl,mipi-phy-gpr"); ph = *out_val; node = of_find_node_by_phandle(ph); - if (!node) { - dev_err(dev, "Error finding node by phandle\n"); - return -ENODEV; - } + if (!node) + return dev_err_probe(dev, -ENODEV, + "Error finding node by phandle\n"); + state->phy_gpr = syscon_node_to_regmap(node); of_node_put(node); - if (IS_ERR(state->phy_gpr)) { - dev_err(dev, "failed to get gpr regmap: %pe\n", state->phy_gpr); - return PTR_ERR(state->phy_gpr); - } + if (IS_ERR(state->phy_gpr)) + return dev_err_probe(dev, PTR_ERR(state->phy_gpr), + "failed to get gpr regmap\n"); state->phy_gpr_reg = out_val[1]; dev_dbg(dev, "phy gpr register set to 0x%x\n", state->phy_gpr_reg); @@ -1017,10 +1005,8 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev) state->pdata = of_device_get_match_data(dev); ret = imx8mq_mipi_csi_parse_dt(state); - if (ret < 0) { - dev_err(dev, "Failed to parse device tree: %d\n", ret); + if (ret < 0) return ret; - } /* Acquire resources. */ state->regs = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c index 22a30510fb79..2b50f9b96a34 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-340.c +++ b/drivers/media/platform/qcom/camss/camss-csid-340.c @@ -55,8 +55,7 @@ #define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0 #define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1 -static void __csid_configure_rx(struct csid_device *csid, - struct csid_phy_config *phy, int vc) +static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy) { u32 val; @@ -81,13 +80,9 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, input_format->code); - u8 lane_cnt = csid->phy.lane_cnt; u8 dt_id; u32 val; - if (!lane_cnt) - lane_cnt = 4; - /* * DT_ID is a two bit bitfield that is concatenated with * the four least significant bits of the five bit VC @@ -120,10 +115,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) { int i; + __csid_configure_rx(csid, &csid->phy); + for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) { if (csid->phy.en_vc & BIT(i)) { __csid_configure_rdi_stream(csid, enable, i); - __csid_configure_rx(csid, &csid->phy, i); __csid_ctrl_rdi(csid, enable, i); } } diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 5284b5857368..ed1820488c98 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -1187,24 +1187,12 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, /* Regulator */ for (i = 0; i < ARRAY_SIZE(res->regulators); i++) { - if (res->regulators[i]) + if (res->regulators[i].supply) csid->num_supplies++; } - if (csid->num_supplies) { - csid->supplies = devm_kmalloc_array(camss->dev, - csid->num_supplies, - sizeof(*csid->supplies), - GFP_KERNEL); - if (!csid->supplies) - return -ENOMEM; - } - - for (i = 0; i < csid->num_supplies; i++) - csid->supplies[i].supply = res->regulators[i]; - - ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies, - csid->supplies); + ret = devm_regulator_bulk_get_const(camss->dev, csid->num_supplies, + res->regulators, &csid->supplies); if (ret) return ret; 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 619abbf60781..415483274552 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 @@ -46,7 +46,8 @@ #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7) #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0) #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) -#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n)) +#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, common_status_offset, n) \ + ((offset) + (common_status_offset) + 0x4 * (n)) #define CSIPHY_DEFAULT_PARAMS 0 #define CSIPHY_LANE_ENABLE 1 @@ -810,13 +811,17 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy, CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6)); hw_version = readl_relaxed(csiphy->base + - CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12)); + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, + regs->common_status_offset, 12)); hw_version |= readl_relaxed(csiphy->base + - CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8; + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, + regs->common_status_offset, 13)) << 8; hw_version |= readl_relaxed(csiphy->base + - CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16; + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, + regs->common_status_offset, 14)) << 16; hw_version |= readl_relaxed(csiphy->base + - CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24; + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, + regs->common_status_offset, 15)) << 24; dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); } @@ -845,7 +850,8 @@ static irqreturn_t csiphy_isr(int irq, void *dev) for (i = 0; i < 11; i++) { int c = i + 22; u8 val = readl_relaxed(csiphy->base + - CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i)); + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, + regs->common_status_offset, i)); writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c)); @@ -1004,6 +1010,7 @@ static bool csiphy_is_gen2(u32 version) switch (version) { case CAMSS_2290: + case CAMSS_6150: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: @@ -1086,6 +1093,7 @@ static int csiphy_init(struct csiphy_device *csiphy) csiphy->regs = regs; regs->offset = 0x800; + regs->common_status_offset = 0xb0; switch (csiphy->camss->res->version) { case CAMSS_845: @@ -1093,6 +1101,7 @@ static int csiphy_init(struct csiphy_device *csiphy) regs->lane_array_size = ARRAY_SIZE(lane_regs_sdm845); break; case CAMSS_2290: + case CAMSS_6150: regs->lane_regs = &lane_regs_qcm2290[0]; regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290); break; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index a734fb7dde0a..62623393f414 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -695,24 +695,13 @@ int msm_csiphy_subdev_init(struct camss *camss, /* CSIPHY supplies */ for (i = 0; i < ARRAY_SIZE(res->regulators); i++) { - if (res->regulators[i]) + if (res->regulators[i].supply) csiphy->num_supplies++; } - if (csiphy->num_supplies) { - csiphy->supplies = devm_kmalloc_array(camss->dev, - csiphy->num_supplies, - sizeof(*csiphy->supplies), - GFP_KERNEL); - if (!csiphy->supplies) - return -ENOMEM; - } - - for (i = 0; i < csiphy->num_supplies; i++) - csiphy->supplies[i].supply = res->regulators[i]; - - ret = devm_regulator_bulk_get(camss->dev, csiphy->num_supplies, - csiphy->supplies); + if (csiphy->num_supplies > 0) + ret = devm_regulator_bulk_get_const(camss->dev, csiphy->num_supplies, + res->regulators, &csiphy->supplies); return ret; } diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 895f80003c44..2d5054819df7 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -90,6 +90,7 @@ struct csiphy_device_regs { const struct csiphy_lane_regs *lane_regs; int lane_array_size; u32 offset; + u32 common_status_offset; }; struct csiphy_device { diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c index 4feea590a47b..d73f733fde04 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-480.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c @@ -202,11 +202,13 @@ static irqreturn_t vfe_isr(int irq, void *dev) writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0)); writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL); - /* Loop through all WMs IRQs */ - for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) { + for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) { if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i)) vfe_isr_reg_update(vfe, i); + } + /* Loop through all WMs IRQs */ + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) { if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i))) vfe_buf_done(vfe, i); } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 9c7ad8aa4058..5baf0e3d4bc4 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -342,6 +342,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, break; case CAMSS_660: case CAMSS_2290: + case CAMSS_6150: case CAMSS_7280: case CAMSS_8x96: case CAMSS_8250: @@ -2001,6 +2002,7 @@ static int vfe_bpl_align(struct vfe_device *vfe) int ret = 8; switch (vfe->camss->res->version) { + case CAMSS_6150: case CAMSS_7280: case CAMSS_8250: case CAMSS_8280XP: diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index fcc2b2c3cba0..00b87fd9afbd 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -73,7 +73,9 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { static const struct camss_subdev_resources csid_res_8x16[] = { /* CSID0 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 40000 } + }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, @@ -95,7 +97,9 @@ static const struct camss_subdev_resources csid_res_8x16[] = { /* CSID1 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 40000 } + }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, @@ -157,7 +161,9 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { static const struct camss_subdev_resources csiphy_res_8x39[] = { /* CSIPHY0 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 40000 } + }, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, .clock_rate = { { 0 }, { 40000000, 80000000 }, @@ -174,7 +180,9 @@ static const struct camss_subdev_resources csiphy_res_8x39[] = { /* CSIPHY1 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 40000 } + }, .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, .clock_rate = { { 0 }, { 40000000, 80000000 }, @@ -300,7 +308,9 @@ static const struct camss_subdev_resources vfe_res_8x39[] = { static const struct camss_subdev_resources csid_res_8x53[] = { /* CSID0 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 9900 } + }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, @@ -323,7 +333,9 @@ static const struct camss_subdev_resources csid_res_8x53[] = { /* CSID1 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 9900 } + }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, @@ -346,7 +358,9 @@ static const struct camss_subdev_resources csid_res_8x53[] = { /* CSID2 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 9900 } + }, .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, .clock_rate = { { 0 }, @@ -507,7 +521,9 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { static const struct camss_subdev_resources csid_res_8x96[] = { /* CSID0 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 80160 } + }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, @@ -529,7 +545,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { /* CSID1 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 80160 } + }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, @@ -551,7 +569,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { /* CSID2 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 80160 } + }, .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, .clock_rate = { { 0 }, @@ -573,7 +593,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { /* CSID3 */ { - .regulators = { "vdda" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 80160 } + }, .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" }, .clock_rate = { { 0 }, @@ -661,7 +683,10 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { static const struct camss_subdev_resources csiphy_res_2290[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy-1p2", "vdd-csiphy-1p8" }, + .regulators = { + { .supply = "vdd-csiphy-1p2", .init_load_uA = 26700 }, + { .supply = "vdd-csiphy-1p8", .init_load_uA = 2600 } + }, .clock = { "top_ahb", "ahb", "csiphy0", "csiphy0_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -678,7 +703,10 @@ static const struct camss_subdev_resources csiphy_res_2290[] = { /* CSIPHY1 */ { - .regulators = { "vdd-csiphy-1p2", "vdd-csiphy-1p8" }, + .regulators = { + { .supply = "vdd-csiphy-1p2", .init_load_uA = 26700 }, + { .supply = "vdd-csiphy-1p8", .init_load_uA = 2600 } + }, .clock = { "top_ahb", "ahb", "csiphy1", "csiphy1_timer" }, .clock_rate = { { 0 }, { 0 }, @@ -854,7 +882,10 @@ static const struct camss_subdev_resources csiphy_res_660[] = { static const struct camss_subdev_resources csid_res_660[] = { /* CSID0 */ { - .regulators = { "vdda", "vdd_sec" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 0 }, + { .supply = "vdd_sec", .init_load_uA = 0 } + }, .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi", "cphy_csid0" }, @@ -879,7 +910,10 @@ static const struct camss_subdev_resources csid_res_660[] = { /* CSID1 */ { - .regulators = { "vdda", "vdd_sec" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 0 }, + { .supply = "vdd_sec", .init_load_uA = 0 } + }, .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi", "cphy_csid1" }, @@ -904,7 +938,10 @@ static const struct camss_subdev_resources csid_res_660[] = { /* CSID2 */ { - .regulators = { "vdda", "vdd_sec" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 0 }, + { .supply = "vdd_sec", .init_load_uA = 0 } + }, .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", "csi2", "csi2_phy", "csi2_pix", "csi2_rdi", "cphy_csid2" }, @@ -929,7 +966,10 @@ static const struct camss_subdev_resources csid_res_660[] = { /* CSID3 */ { - .regulators = { "vdda", "vdd_sec" }, + .regulators = { + { .supply = "vdda", .init_load_uA = 0 }, + { .supply = "vdd_sec", .init_load_uA = 0 } + }, .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", "csi3", "csi3_phy", "csi3_pix", "csi3_rdi", "cphy_csid3" }, @@ -1026,7 +1066,10 @@ static const struct camss_subdev_resources vfe_res_660[] = { static const struct camss_subdev_resources csiphy_res_670[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 42800 }, + { .supply = "vdda-pll", .init_load_uA = 13900 } + }, .clock = { "soc_ahb", "cpas_ahb", "csiphy0", "csiphy0_timer" }, .clock_rate = { { 0 }, @@ -1044,7 +1087,10 @@ static const struct camss_subdev_resources csiphy_res_670[] = { /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 42800 }, + { .supply = "vdda-pll", .init_load_uA = 13900 } + }, .clock = { "soc_ahb", "cpas_ahb", "csiphy1", "csiphy1_timer" }, .clock_rate = { { 0 }, @@ -1062,7 +1108,10 @@ static const struct camss_subdev_resources csiphy_res_670[] = { /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 42800 }, + { .supply = "vdda-pll", .init_load_uA = 13900 } + }, .clock = { "soc_ahb", "cpas_ahb", "csiphy2", "csiphy2_timer" }, .clock_rate = { { 0 }, @@ -1302,7 +1351,10 @@ static const struct camss_subdev_resources csiphy_res_845[] = { static const struct camss_subdev_resources csid_res_845[] = { /* CSID0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe0", "vfe0_src", "vfe0_cphy_rx", "csi0", @@ -1327,7 +1379,10 @@ static const struct camss_subdev_resources csid_res_845[] = { /* CSID1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe1", "vfe1_src", "vfe1_cphy_rx", "csi1", @@ -1352,7 +1407,10 @@ static const struct camss_subdev_resources csid_res_845[] = { /* CSID2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", "soc_ahb", "vfe_lite", "vfe_lite_src", "vfe_lite_cphy_rx", "csi2", @@ -1461,10 +1519,197 @@ static const struct camss_subdev_resources vfe_res_845[] = { } }; +static const struct camss_subdev_resources csiphy_res_sm6150[] = { + /* CSIPHY0 */ + { + .regulators = { + { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 }, + { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 } + }, + .clock = { "csiphy0", "csiphy0_timer" }, + .clock_rate = { { 269333333, 384000000 }, + { 269333333 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" }, + .csiphy = { + .id = 0, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY1 */ + { + .regulators = { + { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 }, + { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 } + }, + .clock = { "csiphy1", "csiphy1_timer" }, + .clock_rate = { { 269333333, 384000000 }, + { 269333333 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" }, + .csiphy = { + .id = 1, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, + /* CSIPHY2 */ + { + .regulators = { + { .supply = "vdd-csiphy-1p2", .init_load_uA = 35000 }, + { .supply = "vdd-csiphy-1p8", .init_load_uA = 5000 } + }, + .clock = { "csiphy2", "csiphy2_timer" }, + .clock_rate = { { 269333333, 384000000 }, + { 269333333 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" }, + .csiphy = { + .id = 2, + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 + } + }, +}; + +static const struct camss_subdev_resources csid_res_sm6150[] = { + /* CSID0 */ + { + .regulators = {}, + .clock = { "vfe0_cphy_rx", "vfe0_csid" }, + .clock_rate = { { 269333333, 384000000 }, + { 320000000, 540000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID1 */ + { + .regulators = {}, + .clock = { "vfe1_cphy_rx", "vfe1_csid" }, + .clock_rate = { { 269333333, 384000000 }, + { 320000000, 540000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" }, + .csid = { + .is_lite = false, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, + /* CSID2 */ + { + .regulators = {}, + .clock = { "vfe_lite_cphy_rx", "vfe_lite_csid" }, + .clock_rate = { { 269333333, 384000000 }, + { 320000000, 540000000 } }, + .reg = { "csid_lite" }, + .interrupt = { "csid_lite" }, + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, + .formats = &csid_formats_gen2 + } + }, +}; + +static const struct camss_subdev_resources vfe_res_sm6150[] = { + /* VFE0 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb", + "vfe0", "vfe0_axi"}, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 37500000, 40000000 }, + { 360000000, 432000000, 540000000, 600000000 }, + { 265000000, 426000000 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE1 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb", + "vfe1", "vfe1_axi"}, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 37500000, 40000000 }, + { 360000000, 432000000, 540000000, 600000000 }, + { 265000000, 426000000 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" }, + .vfe = { + .line_num = 3, + .is_lite = false, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, + /* VFE2 */ + { + .regulators = {}, + .clock = { "gcc_axi_hf", "camnoc_axi", "cpas_ahb", "soc_ahb", + "vfe_lite" }, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 37500000, 40000000 }, + { 360000000, 432000000, 540000000, 600000000 } }, + .reg = { "vfe_lite" }, + .interrupt = { "vfe_lite" }, + .vfe = { + .line_num = 4, + .is_lite = true, + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 + } + }, +}; + +static const struct resources_icc icc_res_sm6150[] = { + { + .name = "ahb", + .icc_bw_tbl.avg = 38400, + .icc_bw_tbl.peak = 76800, + }, + { + .name = "hf_0", + .icc_bw_tbl.avg = 2097152, + .icc_bw_tbl.peak = 2097152, + }, +}; + static const struct camss_subdev_resources csiphy_res_8250[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1478,7 +1723,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { }, /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1492,7 +1740,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { }, /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1506,7 +1757,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { }, /* CSIPHY3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy3", "csiphy3_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1520,7 +1774,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { }, /* CSIPHY4 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1534,7 +1791,10 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { }, /* CSIPHY5 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 17500 }, + { .supply = "vdda-pll", .init_load_uA = 10000 } + }, .clock = { "csiphy5", "csiphy5_timer" }, .clock_rate = { { 400000000 }, { 300000000 } }, @@ -1748,7 +2008,10 @@ static const struct resources_icc icc_res_sm8250[] = { static const struct camss_subdev_resources csiphy_res_7280[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 16100 }, + { .supply = "vdda-pll", .init_load_uA = 9000 } + }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 300000000, 400000000 }, @@ -1763,7 +2026,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { }, /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 16100 }, + { .supply = "vdda-pll", .init_load_uA = 9000 } + }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 300000000, 400000000 }, @@ -1778,7 +2044,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { }, /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 16100 }, + { .supply = "vdda-pll", .init_load_uA = 9000 } + }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 300000000, 400000000 }, @@ -1793,7 +2062,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { }, /* CSIPHY3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 16100 }, + { .supply = "vdda-pll", .init_load_uA = 9000 } + }, .clock = { "csiphy3", "csiphy3_timer" }, .clock_rate = { { 300000000, 400000000 }, @@ -1808,7 +2080,10 @@ static const struct camss_subdev_resources csiphy_res_7280[] = { }, /* CSIPHY4 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 16100 }, + { .supply = "vdda-pll", .init_load_uA = 9000 } + }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 300000000, 400000000 }, @@ -2121,7 +2396,10 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { static const struct camss_subdev_resources csid_res_sc8280xp[] = { /* CSID0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_axi" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2137,7 +2415,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_axi" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2153,7 +2434,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2", "vfe2_axi" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2169,7 +2453,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe3_csid", "vfe3_cphy_rx", "vfe3", "vfe3_axi" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2185,7 +2472,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID_LITE0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2201,7 +2491,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID_LITE1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2217,7 +2510,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID_LITE2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe_lite2_csid", "vfe_lite2_cphy_rx", "vfe_lite2" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2233,7 +2529,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { }, /* CSID_LITE3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 0 }, + { .supply = "vdda-pll", .init_load_uA = 0 } + }, .clock = { "vfe_lite3_csid", "vfe_lite3_cphy_rx", "vfe_lite3" }, .clock_rate = { { 400000000, 480000000, 600000000 }, { 0 }, @@ -2434,7 +2733,10 @@ static const struct resources_icc icc_res_sc8280xp[] = { static const struct camss_subdev_resources csiphy_res_8550[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2448,7 +2750,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2462,7 +2767,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2476,7 +2784,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy3", "csiphy3_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2490,7 +2801,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY4 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 37900 }, + { .supply = "vdda-pll", .init_load_uA = 18600 } + }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2504,7 +2818,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY5 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy5", "csiphy5_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2518,7 +2835,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY6 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 37900 }, + { .supply = "vdda-pll", .init_load_uA = 18600 } + }, .clock = { "csiphy6", "csiphy6_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2532,7 +2852,10 @@ static const struct camss_subdev_resources csiphy_res_8550[] = { }, /* CSIPHY7 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 32200 }, + { .supply = "vdda-pll", .init_load_uA = 18000 } + }, .clock = { "csiphy7", "csiphy7_timer" }, .clock_rate = { { 400000000, 480000000 }, { 400000000 } }, @@ -2704,12 +3027,11 @@ static const struct camss_subdev_resources vfe_res_8550[] = { /* VFE3 lite */ { .regulators = {}, - .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb", + .clock = { "gcc_axi_hf", "cpas_ahb", "vfe_lite_ahb", "vfe_lite", "cpas_ife_lite", "camnoc_axi" }, .clock_rate = { { 0 }, { 80000000 }, { 300000000, 400000000 }, - { 300000000, 400000000 }, { 400000000, 480000000 }, { 300000000, 400000000 }, { 300000000, 400000000 } }, @@ -2726,12 +3048,11 @@ static const struct camss_subdev_resources vfe_res_8550[] = { /* VFE4 lite */ { .regulators = {}, - .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb", + .clock = { "gcc_axi_hf", "cpas_ahb", "vfe_lite_ahb", "vfe_lite", "cpas_ife_lite", "camnoc_axi" }, .clock_rate = { { 0 }, { 80000000 }, { 300000000, 400000000 }, - { 300000000, 400000000 }, { 400000000, 480000000 }, { 300000000, 400000000 }, { 300000000, 400000000 } }, @@ -2763,7 +3084,10 @@ static const struct resources_icc icc_res_sm8550[] = { static const struct camss_subdev_resources csiphy_res_sm8650[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", }, + .regulators = { + { .supply = "vdd-csiphy01-0p9", .init_load_uA = 88000 }, + { .supply = "vdd-csiphy01-1p2", .init_load_uA = 17800 }, + }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -2777,7 +3101,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = { }, /* CSIPHY1 */ { - .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", }, + .regulators = { + { .supply = "vdd-csiphy01-0p9", .init_load_uA = 88000 }, + { .supply = "vdd-csiphy01-1p2", .init_load_uA = 17800 }, + }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -2791,7 +3118,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = { }, /* CSIPHY2 */ { - .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", }, + .regulators = { + { .supply = "vdd-csiphy24-0p9", .init_load_uA = 147000 }, + { .supply = "vdd-csiphy24-1p2", .init_load_uA = 24400 }, + }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -2805,7 +3135,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = { }, /* CSIPHY3 */ { - .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", }, + .regulators = { + { .supply = "vdd-csiphy35-0p9", .init_load_uA = 88000 }, + { .supply = "vdd-csiphy35-1p2", .init_load_uA = 17800 }, + }, .clock = { "csiphy3", "csiphy3_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -2819,7 +3152,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = { }, /* CSIPHY4 */ { - .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", }, + .regulators = { + { .supply = "vdd-csiphy24-0p9", .init_load_uA = 147000 }, + { .supply = "vdd-csiphy24-1p2", .init_load_uA = 24400 }, + }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -2833,7 +3169,10 @@ static const struct camss_subdev_resources csiphy_res_sm8650[] = { }, /* CSIPHY5 */ { - .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", }, + .regulators = { + { .supply = "vdd-csiphy35-0p9", .init_load_uA = 88000 }, + { .supply = "vdd-csiphy35-1p2", .init_load_uA = 17800 }, + }, .clock = { "csiphy5", "csiphy5_timer" }, .clock_rate = { { 400000000 }, { 400000000 } }, @@ -3074,7 +3413,10 @@ static const struct resources_icc icc_res_sm8650[] = { static const struct camss_subdev_resources csiphy_res_8300[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy0", "csiphy0_timer" }, .clock_rate = { @@ -3092,7 +3434,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = { }, /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy1", "csiphy1_timer" }, .clock_rate = { @@ -3110,7 +3455,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = { }, /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy2", "csiphy2_timer" }, .clock_rate = { @@ -3131,7 +3479,10 @@ static const struct camss_subdev_resources csiphy_res_8300[] = { static const struct camss_subdev_resources csiphy_res_8775p[] = { /* CSIPHY0 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy0", "csiphy0_timer"}, .clock_rate = { { 400000000 }, @@ -3148,7 +3499,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, /* CSIPHY1 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy1", "csiphy1_timer"}, .clock_rate = { { 400000000 }, @@ -3165,7 +3519,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, /* CSIPHY2 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy2", "csiphy2_timer"}, .clock_rate = { { 400000000 }, @@ -3182,7 +3539,10 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = { }, /* CSIPHY3 */ { - .regulators = { "vdda-phy", "vdda-pll" }, + .regulators = { + { .supply = "vdda-phy", .init_load_uA = 15900 }, + { .supply = "vdda-pll", .init_load_uA = 8900 } + }, .clock = { "csiphy_rx", "csiphy3", "csiphy3_timer"}, .clock_rate = { { 400000000 }, @@ -3535,8 +3895,10 @@ static const struct resources_icc icc_res_sa8775p[] = { static const struct camss_subdev_resources csiphy_res_x1e80100[] = { /* CSIPHY0 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, + .regulators = { + { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 }, + { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 } + }, .clock = { "csiphy0", "csiphy0_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -3550,8 +3912,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY1 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, + .regulators = { + { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 }, + { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 } + }, .clock = { "csiphy1", "csiphy1_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -3565,8 +3929,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY2 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, + .regulators = { + { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 }, + { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 } + }, .clock = { "csiphy2", "csiphy2_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -3580,8 +3946,10 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = { }, /* CSIPHY4 */ { - .regulators = { "vdd-csiphy-0p8", - "vdd-csiphy-1p2" }, + .regulators = { + { .supply = "vdd-csiphy-0p8", .init_load_uA = 105000 }, + { .supply = "vdd-csiphy-1p2", .init_load_uA = 58900 } + }, .clock = { "csiphy4", "csiphy4_timer" }, .clock_rate = { { 300000000, 400000000, 480000000 }, { 266666667, 400000000 } }, @@ -4022,16 +4390,16 @@ static const struct parent_dev_ops vfe_parent_dev_ops = { }; /* - * camss_of_parse_endpoint_node - Parse port endpoint node - * @dev: Device - * @node: Device node to be parsed + * camss_parse_endpoint_node - Parse port endpoint node + * @dev: CAMSS device + * @ep: Device endpoint to be parsed * @csd: Parsed data from port endpoint node * * Return 0 on success or a negative error code on failure */ -static int camss_of_parse_endpoint_node(struct device *dev, - struct device_node *node, - struct camss_async_subdev *csd) +static int camss_parse_endpoint_node(struct device *dev, + struct fwnode_handle *ep, + struct camss_async_subdev *csd) { struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; struct v4l2_mbus_config_mipi_csi2 *mipi_csi2; @@ -4039,7 +4407,7 @@ static int camss_of_parse_endpoint_node(struct device *dev, unsigned int i; int ret; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + ret = v4l2_fwnode_endpoint_parse(ep, &vep); if (ret) return ret; @@ -4074,49 +4442,37 @@ static int camss_of_parse_endpoint_node(struct device *dev, } /* - * camss_of_parse_ports - Parse ports node - * @dev: Device - * @notifier: v4l2_device notifier data + * camss_parse_ports - Parse ports node + * @dev: CAMSS device * - * Return number of "port" nodes found in "ports" node + * Return 0 on success or a negative error code on failure */ -static int camss_of_parse_ports(struct camss *camss) +static int camss_parse_ports(struct camss *camss) { struct device *dev = camss->dev; - struct device_node *node = NULL; - struct device_node *remote = NULL; - int ret, num_subdevs = 0; + struct fwnode_handle *fwnode = dev_fwnode(dev), *ep; + int ret; - for_each_endpoint_of_node(dev->of_node, node) { + fwnode_graph_for_each_endpoint(fwnode, ep) { struct camss_async_subdev *csd; - remote = of_graph_get_remote_port_parent(node); - if (!remote) { - dev_err(dev, "Cannot get remote parent\n"); - ret = -EINVAL; - goto err_cleanup; - } - - csd = v4l2_async_nf_add_fwnode(&camss->notifier, - of_fwnode_handle(remote), - struct camss_async_subdev); - of_node_put(remote); + csd = v4l2_async_nf_add_fwnode_remote(&camss->notifier, ep, + typeof(*csd)); if (IS_ERR(csd)) { ret = PTR_ERR(csd); goto err_cleanup; } - ret = camss_of_parse_endpoint_node(dev, node, csd); + ret = camss_parse_endpoint_node(dev, ep, csd); if (ret < 0) goto err_cleanup; - - num_subdevs++; } - return num_subdevs; + return 0; err_cleanup: - of_node_put(node); + fwnode_handle_put(ep); + return ret; } @@ -4673,7 +5029,7 @@ static int camss_probe(struct platform_device *pdev) pm_runtime_enable(dev); - ret = camss_of_parse_ports(camss); + ret = camss_parse_ports(camss); if (ret < 0) goto err_v4l2_device_unregister; @@ -4864,6 +5220,19 @@ static const struct camss_resources sdm845_resources = { .vfe_num = ARRAY_SIZE(vfe_res_845), }; +static const struct camss_resources sm6150_resources = { + .version = CAMSS_6150, + .pd_name = "top", + .csiphy_res = csiphy_res_sm6150, + .csid_res = csid_res_sm6150, + .vfe_res = vfe_res_sm6150, + .icc_res = icc_res_sm6150, + .icc_path_num = ARRAY_SIZE(icc_res_sm6150), + .csiphy_num = ARRAY_SIZE(csiphy_res_sm6150), + .csid_num = ARRAY_SIZE(csid_res_sm6150), + .vfe_num = ARRAY_SIZE(vfe_res_sm6150), +}; + static const struct camss_resources sm8250_resources = { .version = CAMSS_8250, .pd_name = "top", @@ -4959,6 +5328,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,sdm660-camss", .data = &sdm660_resources }, { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources }, { .compatible = "qcom,sdm845-camss", .data = &sdm845_resources }, + { .compatible = "qcom,sm6150-camss", .data = &sm6150_resources }, { .compatible = "qcom,sm8250-camss", .data = &sm8250_resources }, { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources }, { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources }, diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 9d9a62640e25..6d048414c919 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -44,7 +44,7 @@ #define CAMSS_INIT_BUF_COUNT 2 struct camss_subdev_resources { - char *regulators[CAMSS_RES_MAX]; + struct regulator_bulk_data regulators[CAMSS_RES_MAX]; char *clock[CAMSS_RES_MAX]; char *clock_for_reset[CAMSS_RES_MAX]; u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; @@ -80,6 +80,7 @@ enum pm_domain { enum camss_version { CAMSS_660, CAMSS_2290, + CAMSS_6150, CAMSS_7280, CAMSS_8x16, CAMSS_8x39, diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile index fad3be044e5f..2abbd3aeb4af 100644 --- a/drivers/media/platform/qcom/iris/Makefile +++ b/drivers/media/platform/qcom/iris/Makefile @@ -22,6 +22,7 @@ qcom-iris-objs += iris_buffer.o \ iris_venc.o \ iris_vpu2.o \ iris_vpu3x.o \ + iris_vpu4x.o \ iris_vpu_buffer.o \ iris_vpu_common.o \ diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index b89b1ee06cce..f1f003a787bf 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -351,12 +351,15 @@ static int iris_create_internal_buffer(struct iris_inst *inst, buffer->index = index; buffer->buffer_size = buffers->size; buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING; - list_add_tail(&buffer->list, &buffers->list); buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size, &buffer->device_addr, GFP_KERNEL, buffer->dma_attrs); - if (!buffer->kvaddr) + if (!buffer->kvaddr) { + kfree(buffer); return -ENOMEM; + } + + list_add_tail(&buffer->list, &buffers->list); return 0; } diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h index 325d30fce5c9..75bb76776182 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_buffer.h @@ -27,6 +27,7 @@ struct iris_inst; * @BUF_SCRATCH_1: buffer to store decoding/encoding context data for HW * @BUF_SCRATCH_2: buffer to store encoding context data for HW * @BUF_VPSS: buffer to store VPSS context data for HW + * @BUF_PARTIAL: buffer for AV1 IBC data * @BUF_TYPE_MAX: max buffer types */ enum iris_buffer_type { @@ -42,6 +43,7 @@ enum iris_buffer_type { BUF_SCRATCH_1, BUF_SCRATCH_2, BUF_VPSS, + BUF_PARTIAL, BUF_TYPE_MAX, }; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index c0b3a09ad3e3..3cec957580f5 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -98,6 +98,20 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return B_FRAME_QP_H264; case V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP: return B_FRAME_QP_HEVC; + case V4L2_CID_MPEG_VIDEO_AV1_PROFILE: + return PROFILE_AV1; + case V4L2_CID_MPEG_VIDEO_AV1_LEVEL: + return LEVEL_AV1; + case V4L2_CID_ROTATE: + return ROTATION; + case V4L2_CID_HFLIP: + return HFLIP; + case V4L2_CID_VFLIP: + return VFLIP; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE: + return IR_TYPE; + case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD: + return IR_PERIOD; default: return INST_FW_CAP_MAX; } @@ -185,6 +199,20 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP; case B_FRAME_QP_HEVC: return V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_QP; + case PROFILE_AV1: + return V4L2_CID_MPEG_VIDEO_AV1_PROFILE; + case LEVEL_AV1: + return V4L2_CID_MPEG_VIDEO_AV1_LEVEL; + case ROTATION: + return V4L2_CID_ROTATE; + case HFLIP: + return V4L2_CID_HFLIP; + case VFLIP: + return V4L2_CID_VFLIP; + case IR_TYPE: + return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE; + case IR_PERIOD: + return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD; default: return 0; } @@ -893,6 +921,81 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap &range, sizeof(range)); } +int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 hfi_val; + + switch (inst->fw_caps[cap_id].value) { + case 0: + hfi_val = HFI_ROTATION_NONE; + return 0; + case 90: + hfi_val = HFI_ROTATION_90; + break; + case 180: + hfi_val = HFI_ROTATION_180; + break; + case 270: + hfi_val = HFI_ROTATION_270; + break; + default: + return -EINVAL; + } + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &hfi_val, sizeof(u32)); +} + +int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 hfi_val = HFI_DISABLE_FLIP; + + if (inst->fw_caps[HFLIP].value) + hfi_val |= HFI_HORIZONTAL_FLIP; + + if (inst->fw_caps[VFLIP].value) + hfi_val |= HFI_VERTICAL_FLIP; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32_ENUM, + &hfi_val, sizeof(u32)); +} + +int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; + struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx); + u32 ir_period = inst->fw_caps[cap_id].value; + u32 ir_type = 0; + + if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) { + if (vb2_is_streaming(q)) + return 0; + ir_type = HFI_PROP_IR_RANDOM_PERIOD; + } else if (inst->fw_caps[IR_TYPE].value == + V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) { + ir_type = HFI_PROP_IR_CYCLIC_PERIOD; + } else { + return -EINVAL; + } + + return hfi_ops->session_set_property(inst, ir_type, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &ir_period, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 30af333cc494..9518803577bc 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -32,6 +32,9 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 679444327ed7..5f408024e967 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -64,9 +64,9 @@ err_release_fw: int iris_fw_load(struct iris_core *core) { - struct tz_cp_config *cp_config = core->iris_platform_data->tz_cp_config_data; + const struct tz_cp_config *cp_config; const char *fwpath = NULL; - int ret; + int i, ret; ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0, &fwpath); @@ -85,14 +85,17 @@ int iris_fw_load(struct iris_core *core) return ret; } - ret = qcom_scm_mem_protect_video_var(cp_config->cp_start, - cp_config->cp_size, - cp_config->cp_nonpixel_start, - cp_config->cp_nonpixel_size); - if (ret) { - dev_err(core->dev, "protect memory failed\n"); - qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); - return ret; + for (i = 0; i < core->iris_platform_data->tz_cp_config_data_size; i++) { + cp_config = &core->iris_platform_data->tz_cp_config_data[i]; + ret = qcom_scm_mem_protect_video_var(cp_config->cp_start, + cp_config->cp_size, + cp_config->cp_nonpixel_start, + cp_config->cp_nonpixel_size); + if (ret) { + dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret); + qcom_scm_pas_shutdown(core->iris_platform_data->pas_id); + return ret; + } } return ret; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h index b51471fb32c7..3edb5ae582b4 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_common.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h @@ -141,6 +141,9 @@ struct hfi_subscription_params { u32 profile; u32 level; u32 tier; + u32 drap; + u32 film_grain; + u32 super_block; }; u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 52da7ef7bab0..11815f6f5bac 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -282,7 +282,7 @@ static int iris_hfi_gen1_queue_input_buffer(struct iris_inst *inst, struct iris_ com_ip_pkt.shdr.session_id = inst->session_id; com_ip_pkt.time_stamp_hi = upper_32_bits(buf->timestamp); com_ip_pkt.time_stamp_lo = lower_32_bits(buf->timestamp); - com_ip_pkt.flags = buf->flags; + com_ip_pkt.flags = 0; com_ip_pkt.mark_target = 0; com_ip_pkt.mark_data = 0; com_ip_pkt.offset = buf->data_offset; @@ -441,6 +441,8 @@ static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct ir goto exit; ret = iris_wait_for_session_response(inst, false); + if (!ret) + ret = iris_destroy_internal_buffer(inst, buf); exit: kfree(pkt); @@ -733,7 +735,7 @@ static int iris_hfi_gen1_set_resolution(struct iris_inst *inst, u32 plane) struct hfi_framesize fs; int ret; - if (!iris_drc_pending(inst)) { + if (!iris_drc_pending(inst) && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) { fs.buffer_type = HFI_BUFFER_INPUT; fs.width = inst->fmt_src->fmt.pix_mp.width; fs.height = inst->fmt_src->fmt.pix_mp.height; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index f91295532099..715ec9575b90 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -10,6 +10,7 @@ #define UNSPECIFIED_COLOR_FORMAT 5 #define NUM_SYS_INIT_PACKETS 8 +#define NUM_COMV_AV1 18 #define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32))) @@ -121,6 +122,7 @@ static u32 iris_hfi_gen2_get_port_from_buf_type(struct iris_inst *inst, case BUF_COMV: case BUF_NON_COMV: case BUF_LINE: + case BUF_PARTIAL: return HFI_PORT_BITSTREAM; case BUF_OUTPUT: case BUF_DPB: @@ -168,8 +170,7 @@ static int iris_hfi_gen2_session_set_property(struct iris_inst *inst, u32 packet static int iris_hfi_gen2_set_raw_resolution(struct iris_inst *inst, u32 plane) { - u32 resolution = inst->fmt_src->fmt.pix_mp.width << 16 | - inst->fmt_src->fmt.pix_mp.height; + u32 resolution = inst->enc_raw_width << 16 | inst->enc_raw_height; u32 port = iris_hfi_gen2_get_port(inst, plane); return iris_hfi_gen2_session_set_property(inst, @@ -181,22 +182,36 @@ static int iris_hfi_gen2_set_raw_resolution(struct iris_inst *inst, u32 plane) sizeof(u32)); } +static inline u32 iris_hfi_get_aligned_resolution(struct iris_inst *inst, u32 width, u32 height) +{ + u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + + return (ALIGN(width, codec_align) << 16 | ALIGN(height, codec_align)); +} + static int iris_hfi_gen2_set_bitstream_resolution(struct iris_inst *inst, u32 plane) { struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 port = iris_hfi_gen2_get_port(inst, plane); enum hfi_packet_payload_info payload_type; - u32 resolution, codec_align; + u32 width, height; + u32 resolution; if (inst->domain == DECODER) { - resolution = inst->fmt_src->fmt.pix_mp.width << 16 | - inst->fmt_src->fmt.pix_mp.height; + width = inst->fmt_src->fmt.pix_mp.width; + height = inst->fmt_src->fmt.pix_mp.height; + resolution = iris_hfi_get_aligned_resolution(inst, width, height); inst_hfi_gen2->src_subcr_params.bitstream_resolution = resolution; payload_type = HFI_PAYLOAD_U32; } else { - codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; - resolution = ALIGN(inst->fmt_dst->fmt.pix_mp.width, codec_align) << 16 | - ALIGN(inst->fmt_dst->fmt.pix_mp.height, codec_align); + if (is_rotation_90_or_270(inst)) { + width = inst->enc_scale_height; + height = inst->enc_scale_width; + } else { + width = inst->enc_scale_width; + height = inst->enc_scale_height; + } + resolution = iris_hfi_get_aligned_resolution(inst, width, height); inst_hfi_gen2->dst_subcr_params.bitstream_resolution = resolution; payload_type = HFI_PAYLOAD_32_PACKED; } @@ -216,7 +231,7 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst, u32 plane) u32 port = iris_hfi_gen2_get_port(inst, plane); u32 bottom_offset, right_offset; u32 left_offset, top_offset; - u32 payload[2]; + u32 payload[2], codec_align; if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { @@ -231,10 +246,27 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst, u32 plane) top_offset = inst->compose.top; } } else { - bottom_offset = (inst->fmt_src->fmt.pix_mp.height - inst->crop.height); - right_offset = (inst->fmt_src->fmt.pix_mp.width - inst->crop.width); - left_offset = inst->crop.left; - top_offset = inst->crop.top; + codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + if (V4L2_TYPE_IS_OUTPUT(plane)) { + bottom_offset = (inst->enc_raw_height - inst->crop.height); + right_offset = (inst->enc_raw_width - inst->crop.width); + left_offset = inst->crop.left; + top_offset = inst->crop.top; + } else { + if (is_rotation_90_or_270(inst)) { + bottom_offset = (ALIGN(inst->enc_scale_width, codec_align) - + inst->enc_scale_width); + right_offset = (ALIGN(inst->enc_scale_height, codec_align) - + inst->enc_scale_height); + } else { + bottom_offset = (ALIGN(inst->enc_scale_height, codec_align) - + inst->enc_scale_height); + right_offset = (ALIGN(inst->enc_scale_width, codec_align) - + inst->enc_scale_width); + } + left_offset = 0; + top_offset = 0; + } } payload[0] = FIELD_PREP(GENMASK(31, 16), left_offset) | top_offset; @@ -380,6 +412,9 @@ static int iris_hfi_gen2_set_profile(struct iris_inst *inst, u32 plane) case V4L2_PIX_FMT_H264: profile = inst->fw_caps[PROFILE_H264].value; break; + case V4L2_PIX_FMT_AV1: + profile = inst->fw_caps[PROFILE_AV1].value; + break; } inst_hfi_gen2->src_subcr_params.profile = profile; @@ -409,6 +444,9 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst, u32 plane) case V4L2_PIX_FMT_H264: level = inst->fw_caps[LEVEL_H264].value; break; + case V4L2_PIX_FMT_AV1: + level = inst->fw_caps[LEVEL_AV1].value; + break; } inst_hfi_gen2->src_subcr_params.level = level; @@ -496,10 +534,12 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32 static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane) { - struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); u32 tier = inst->fw_caps[TIER].value; + tier = (inst->codec == V4L2_PIX_FMT_AV1) ? inst->fw_caps[TIER_AV1].value : + inst->fw_caps[TIER].value; inst_hfi_gen2->src_subcr_params.tier = tier; return iris_hfi_gen2_session_set_property(inst, @@ -525,6 +565,40 @@ static int iris_hfi_gen2_set_frame_rate(struct iris_inst *inst, u32 plane) sizeof(u32)); } +static int iris_hfi_gen2_set_film_grain(struct iris_inst *inst, u32 plane) +{ + u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + u32 film_grain = inst->fw_caps[FILM_GRAIN].value; + + inst_hfi_gen2->src_subcr_params.film_grain = film_grain; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_AV1_FILM_GRAIN_PRESENT, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ENUM, + &film_grain, + sizeof(u32)); +} + +static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane) +{ + u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); + u32 super_block = inst->fw_caps[SUPER_BLOCK].value; + + inst_hfi_gen2->src_subcr_params.super_block = super_block; + + return iris_hfi_gen2_session_set_property(inst, + HFI_PROP_AV1_SUPER_BLOCK_ENABLED, + HFI_HOST_FLAGS_NONE, + port, + HFI_PAYLOAD_U32_ENUM, + &super_block, + sizeof(u32)); +} + static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane) { const struct iris_platform_data *pdata = inst->core->iris_platform_data; @@ -548,6 +622,9 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline }, {HFI_PROP_TIER, iris_hfi_gen2_set_tier }, {HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate }, + {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain }, + {HFI_PROP_AV1_SUPER_BLOCK_ENABLED, iris_hfi_gen2_set_super_block }, + {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, }; if (inst->domain == DECODER) { @@ -561,6 +638,9 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p } else if (inst->codec == V4L2_PIX_FMT_VP9) { config_params = pdata->dec_input_config_params_vp9; config_params_size = pdata->dec_input_config_params_vp9_size; + } else if (inst->codec == V4L2_PIX_FMT_AV1) { + config_params = pdata->dec_input_config_params_av1; + config_params_size = pdata->dec_input_config_params_av1_size; } else { return -EINVAL; } @@ -615,6 +695,9 @@ static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst) break; case V4L2_PIX_FMT_VP9: codec = HFI_CODEC_DECODE_VP9; + break; + case V4L2_PIX_FMT_AV1: + codec = HFI_CODEC_DECODE_AV1; } iris_hfi_gen2_packet_session_property(inst, @@ -760,7 +843,7 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan if ((V4L2_TYPE_IS_OUTPUT(plane) && inst_hfi_gen2->ipsc_properties_set) || (V4L2_TYPE_IS_CAPTURE(plane) && inst_hfi_gen2->opsc_properties_set)) { - dev_err(core->dev, "invalid plane\n"); + dev_dbg(core->dev, "%cPSC already set\n", V4L2_TYPE_IS_OUTPUT(plane) ? 'I' : 'O'); return 0; } @@ -780,6 +863,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan change_param_size = core->iris_platform_data->dec_input_config_params_vp9_size; break; + case V4L2_PIX_FMT_AV1: + change_param = core->iris_platform_data->dec_input_config_params_av1; + change_param_size = + core->iris_platform_data->dec_input_config_params_av1_size; + break; } payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE; @@ -862,6 +950,16 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan payload_size = sizeof(u32); payload_type = HFI_PAYLOAD_U32; break; + case HFI_PROP_AV1_FILM_GRAIN_PRESENT: + payload[0] = subsc_params.film_grain; + payload_size = sizeof(u32); + payload_type = HFI_PAYLOAD_U32; + break; + case HFI_PROP_AV1_SUPER_BLOCK_ENABLED: + payload[0] = subsc_params.super_block; + payload_size = sizeof(u32); + payload_type = HFI_PAYLOAD_U32; + break; default: prop_type = 0; ret = -EINVAL; @@ -917,6 +1015,11 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane) subscribe_prop_size = core->iris_platform_data->dec_output_prop_vp9_size; break; + case V4L2_PIX_FMT_AV1: + subcribe_prop = core->iris_platform_data->dec_output_prop_av1; + subscribe_prop_size = + core->iris_platform_data->dec_output_prop_av1_size; + break; } } @@ -963,6 +1066,9 @@ static int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane) struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); int ret = 0; + if (!inst_hfi_gen2->packet) + return -EINVAL; + reinit_completion(&inst->completion); iris_hfi_gen2_packet_session_command(inst, @@ -1092,6 +1198,8 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type return HFI_BUFFER_ARP; case BUF_VPSS: return HFI_BUFFER_VPSS; + case BUF_PARTIAL: + return HFI_BUFFER_PARTIAL_DATA; default: return 0; } @@ -1104,7 +1212,13 @@ static int iris_set_num_comv(struct iris_inst *inst) u32 num_comv; caps = core->iris_platform_data->inst_caps; - num_comv = caps->num_comv; + + /* + * AV1 needs more comv buffers than other codecs. + * Update accordingly. + */ + num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ? + NUM_COMV_AV1 : caps->num_comv; return core->hfi_ops->session_set_property(inst, HFI_PROP_COMV_BUFFER_COUNT, @@ -1212,5 +1326,10 @@ void iris_hfi_gen2_command_ops_init(struct iris_core *core) struct iris_inst *iris_hfi_gen2_get_instance(void) { - return (struct iris_inst *)kzalloc(sizeof(struct iris_inst_hfi_gen2), GFP_KERNEL); + struct iris_inst_hfi_gen2 *out; + + /* The allocation is intentionally larger than struct iris_inst. */ + out = kzalloc(sizeof(*out), GFP_KERNEL); + + return &out->inst; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index 1b6a4dbac828..cecf771c55dd 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -70,6 +70,7 @@ enum hfi_rate_control { #define HFI_PROP_QP_PACKED 0x0300012e #define HFI_PROP_MIN_QP_PACKED 0x0300012f #define HFI_PROP_MAX_QP_PACKED 0x03000130 +#define HFI_PROP_IR_RANDOM_PERIOD 0x03000131 #define HFI_PROP_TOTAL_BITRATE 0x0300013b #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 @@ -83,16 +84,42 @@ enum hfi_seq_header_mode { }; #define HFI_PROP_SEQ_HEADER_MODE 0x03000149 + +enum hfi_rotation { + HFI_ROTATION_NONE = 0x00000000, + HFI_ROTATION_90 = 0x00000001, + HFI_ROTATION_180 = 0x00000002, + HFI_ROTATION_270 = 0x00000003, +}; + +#define HFI_PROP_ROTATION 0x0300014b + +enum hfi_flip { + HFI_DISABLE_FLIP = 0x00000000, + HFI_HORIZONTAL_FLIP = 0x00000001, + HFI_VERTICAL_FLIP = 0x00000002, +}; + +#define HFI_PROP_FLIP 0x0300014c #define HFI_PROP_SIGNAL_COLOR_INFO 0x03000155 #define HFI_PROP_PICTURE_TYPE 0x03000162 #define HFI_PROP_DEC_DEFAULT_HEADER 0x03000168 #define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169 #define HFI_PROP_NO_OUTPUT 0x0300016a #define HFI_PROP_BUFFER_MARK 0x0300016c +#define HFI_PROP_WORST_COMPRESSION_RATIO 0x03000174 +#define HFI_PROP_WORST_COMPLEXITY_FACTOR 0x03000175 #define HFI_PROP_RAW_RESOLUTION 0x03000178 #define HFI_PROP_TOTAL_PEAK_BITRATE 0x0300017C +#define HFI_PROP_AV1_FILM_GRAIN_PRESENT 0x03000180 +#define HFI_PROP_AV1_SUPER_BLOCK_ENABLED 0x03000181 +#define HFI_PROP_AV1_OP_POINT 0x03000182 +#define HFI_PROP_IR_CYCLIC_PERIOD 0x0300017E #define HFI_PROP_OPB_ENABLE 0x03000184 +#define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187 +#define HFI_PROP_AV1_DRAP_CONFIG 0x03000189 #define HFI_PROP_COMV_BUFFER_COUNT 0x03000193 +#define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197 #define HFI_PROP_END 0x03FFFFFF #define HFI_SESSION_ERROR_BEGIN 0x04000000 @@ -139,6 +166,7 @@ enum hfi_codec_type { HFI_CODEC_DECODE_HEVC = 3, HFI_CODEC_ENCODE_HEVC = 4, HFI_CODEC_DECODE_VP9 = 5, + HFI_CODEC_DECODE_AV1 = 7, }; enum hfi_picture_type { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 2f1f118eae4f..8e19f61bbbf9 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -54,6 +54,10 @@ static u32 iris_hfi_gen2_buf_type_to_driver(struct iris_inst *inst, return BUF_SCRATCH_2; case HFI_BUFFER_PERSIST: return BUF_PERSIST; + case HFI_BUFFER_PARTIAL_DATA: + return BUF_PARTIAL; + case HFI_BUFFER_VPSS: + return BUF_VPSS; default: return 0; } @@ -72,6 +76,7 @@ static bool iris_hfi_gen2_is_valid_hfi_buffer_type(u32 buffer_type) case HFI_BUFFER_DPB: case HFI_BUFFER_PERSIST: case HFI_BUFFER_VPSS: + case HFI_BUFFER_PARTIAL_DATA: return true; default: return false; @@ -596,6 +601,10 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) inst->fw_caps[PROFILE_H264].value = subsc_params.profile; inst->fw_caps[LEVEL_H264].value = subsc_params.level; break; + case V4L2_PIX_FMT_AV1: + inst->fw_caps[PROFILE_AV1].value = subsc_params.profile; + inst->fw_caps[LEVEL_AV1].value = subsc_params.level; + break; } inst->fw_caps[POC].value = subsc_params.pic_order_cnt; @@ -608,6 +617,11 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst) iris_inst_change_state(inst, IRIS_INST_ERROR); } + if (inst->codec == V4L2_PIX_FMT_AV1) { + inst->fw_caps[FILM_GRAIN].value = subsc_params.film_grain; + inst->fw_caps[SUPER_BLOCK].value = subsc_params.super_block; + } + inst->fw_min_count = subsc_params.fw_min_count; inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT); inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage; @@ -711,6 +725,12 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst, case HFI_PROP_NO_OUTPUT: inst_hfi_gen2->hfi_frame_info.no_output = 1; break; + case HFI_PROP_AV1_FILM_GRAIN_PRESENT: + inst_hfi_gen2->src_subcr_params.film_grain = pkt->payload[0]; + break; + case HFI_PROP_AV1_SUPER_BLOCK_ENABLED: + inst_hfi_gen2->src_subcr_params.super_block = pkt->payload[0]; + break; case HFI_PROP_QUALITY_MODE: case HFI_PROP_STAGE: case HFI_PROP_PIPE: @@ -841,6 +861,10 @@ static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst) subsc_params->profile = inst->fw_caps[PROFILE_H264].value; subsc_params->level = inst->fw_caps[LEVEL_H264].value; break; + case V4L2_PIX_FMT_AV1: + subsc_params->profile = inst->fw_caps[PROFILE_AV1].value; + subsc_params->level = inst->fw_caps[LEVEL_AV1].value; + break; } subsc_params->pic_order_cnt = inst->fw_caps[POC].value; diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h index 62fbb30691ff..16965150f427 100644 --- a/drivers/media/platform/qcom/iris/iris_instance.h +++ b/drivers/media/platform/qcom/iris/iris_instance.h @@ -19,6 +19,7 @@ enum iris_fmt_type_out { IRIS_FMT_H264, IRIS_FMT_HEVC, IRIS_FMT_VP9, + IRIS_FMT_AV1, }; enum iris_fmt_type_cap { @@ -69,6 +70,10 @@ struct iris_fmt { * @frame_rate: frame rate of current instance * @operating_rate: operating rate of current instance * @hfi_rc_type: rate control type + * @enc_raw_width: source image width for encoder instance + * @enc_raw_height: source image height for encoder instance + * @enc_scale_width: scale width for encoder instance + * @enc_scale_height: scale height for encoder instance */ struct iris_inst { @@ -107,6 +112,10 @@ struct iris_inst { u32 frame_rate; u32 operating_rate; u32 hfi_rc_type; + u32 enc_raw_width; + u32 enc_raw_height; + u32 enc_scale_width; + u32 enc_scale_height; }; #endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 8d8cdb56a3c7..5a489917580e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -57,6 +57,10 @@ enum platform_clk_type { IRIS_AXI1_CLK, IRIS_CTRL_FREERUN_CLK, IRIS_HW_FREERUN_CLK, + IRIS_BSE_HW_CLK, + IRIS_VPP0_HW_CLK, + IRIS_VPP1_HW_CLK, + IRIS_APV_HW_CLK, }; struct platform_clk_data { @@ -103,6 +107,13 @@ enum platform_inst_fw_cap_type { LEVEL_H264, LEVEL_HEVC, LEVEL_VP9, + PROFILE_AV1, + LEVEL_AV1, + TIER_AV1, + DRAP, + FILM_GRAIN, + SUPER_BLOCK, + ENH_LAYER_COUNT, INPUT_BUF_HOST_MAX_COUNT, OUTPUT_BUF_HOST_MAX_COUNT, STAGE, @@ -143,6 +154,11 @@ enum platform_inst_fw_cap_type { P_FRAME_QP_HEVC, B_FRAME_QP_H264, B_FRAME_QP_HEVC, + ROTATION, + HFLIP, + VFLIP, + IR_TYPE, + IR_PERIOD, INST_FW_CAP_MAX, }; @@ -191,6 +207,9 @@ struct icc_vote_data { enum platform_pm_domain_type { IRIS_CTRL_POWER_DOMAIN, IRIS_HW_POWER_DOMAIN, + IRIS_VPP0_HW_POWER_DOMAIN, + IRIS_VPP1_HW_POWER_DOMAIN, + IRIS_APV_HW_POWER_DOMAIN, }; struct iris_platform_data { @@ -209,6 +228,7 @@ struct iris_platform_data { const char * const *opp_pd_tbl; unsigned int opp_pd_tbl_size; const struct platform_clk_data *clk_tbl; + const char * const *opp_clk_tbl; unsigned int clk_tbl_size; const char * const *clk_rst_tbl; unsigned int clk_rst_tbl_size; @@ -217,12 +237,15 @@ struct iris_platform_data { u64 dma_mask; const char *fwname; u32 pas_id; + struct iris_fmt *inst_iris_fmts; + u32 inst_iris_fmts_size; struct platform_inst_caps *inst_caps; const struct platform_inst_fw_cap *inst_fw_caps_dec; u32 inst_fw_caps_dec_size; const struct platform_inst_fw_cap *inst_fw_caps_enc; u32 inst_fw_caps_enc_size; - struct tz_cp_config *tz_cp_config_data; + const struct tz_cp_config *tz_cp_config_data; + u32 tz_cp_config_data_size; u32 core_arch; u32 hw_response_timeout; struct ubwc_config_data *ubwc_config; @@ -239,6 +262,8 @@ struct iris_platform_data { unsigned int dec_input_config_params_hevc_size; const u32 *dec_input_config_params_vp9; unsigned int dec_input_config_params_vp9_size; + const u32 *dec_input_config_params_av1; + unsigned int dec_input_config_params_av1_size; const u32 *dec_output_config_params; unsigned int dec_output_config_params_size; const u32 *enc_input_config_params; @@ -253,6 +278,8 @@ struct iris_platform_data { unsigned int dec_output_prop_hevc_size; const u32 *dec_output_prop_vp9; unsigned int dec_output_prop_vp9_size; + const u32 *dec_output_prop_av1; + unsigned int dec_output_prop_av1_size; const u32 *dec_ip_int_buf_tbl; unsigned int dec_ip_int_buf_tbl_size; const u32 *dec_op_int_buf_tbl; diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c index 34cbeb8f52e2..df8e6bf9430e 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c @@ -11,6 +11,7 @@ #include "iris_hfi_gen1_defines.h" #include "iris_vpu_buffer.h" #include "iris_vpu_common.h" +#include "iris_instance.h" #include "iris_platform_sc7280.h" @@ -19,7 +20,22 @@ #define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2) #define BITRATE_STEP 100 -static const struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { +static struct iris_fmt platform_fmts_sm8250_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + +static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = { { .cap_id = PIPE, /* .max, .min and .value are set via platform data */ @@ -273,11 +289,18 @@ static const struct platform_clk_data sm8250_clk_table[] = { {IRIS_HW_CLK, "vcodec0_core" }, }; -static struct tz_cp_config tz_cp_config_sm8250 = { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, +static const char * const sm8250_opp_clk_table[] = { + "vcodec0_core", + NULL, +}; + +static const struct tz_cp_config tz_cp_config_sm8250[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { @@ -333,16 +356,20 @@ const struct iris_platform_data sm8250_data = { .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table), .clk_tbl = sm8250_clk_table, .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), + .opp_clk_tbl = sm8250_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu-1.0/venus.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8250_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), - .tz_cp_config_data = &tz_cp_config_sm8250, + .tz_cp_config_data = tz_cp_config_sm8250, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 4, .max_session_count = 16, @@ -382,16 +409,20 @@ const struct iris_platform_data sc7280_data = { .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table), .clk_tbl = sc7280_clk_table, .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table), + .opp_clk_tbl = sc7280_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu20_p1.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8250_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec), .inst_caps = &platform_inst_cap_sm8250, .inst_fw_caps_dec = inst_fw_cap_sm8250_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec), .inst_fw_caps_enc = inst_fw_cap_sm8250_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc), - .tz_cp_config_data = &tz_cp_config_sm8250, + .tz_cp_config_data = tz_cp_config_sm8250, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250), .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .num_vpp_pipe = 1, .no_aon = true, diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c index c1989240c248..5da90d47f9c6 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c @@ -19,6 +19,25 @@ #define VIDEO_ARCH_LX 1 #define BITRATE_MAX 245000000 +static struct iris_fmt platform_fmts_sm8550_dec[] = { + [IRIS_FMT_H264] = { + .pixfmt = V4L2_PIX_FMT_H264, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_HEVC] = { + .pixfmt = V4L2_PIX_FMT_HEVC, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_VP9] = { + .pixfmt = V4L2_PIX_FMT_VP9, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, + [IRIS_FMT_AV1] = { + .pixfmt = V4L2_PIX_FMT_AV1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, +}; + static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, @@ -46,6 +65,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { .set = iris_set_u32_enum, }, { + .cap_id = PROFILE_AV1, + .min = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN), + .value = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { .cap_id = PROFILE_VP9, .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, @@ -129,6 +158,33 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { .set = iris_set_u32_enum, }, { + .cap_id = LEVEL_AV1, + .min = V4L2_MPEG_VIDEO_AV1_LEVEL_2_0, + .max = V4L2_MPEG_VIDEO_AV1_LEVEL_6_1, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_2_3) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_3_3) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_4_3) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_1) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_2) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_5_3) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_6_0) | + BIT(V4L2_MPEG_VIDEO_AV1_LEVEL_6_1), + .value = V4L2_MPEG_VIDEO_AV1_LEVEL_6_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { .cap_id = TIER, .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, @@ -140,6 +196,53 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { .set = iris_set_u32_enum, }, { + .cap_id = TIER_AV1, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_TIER, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = DRAP, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_AV1_DRAP_CONFIG, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = FILM_GRAIN, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_AV1_FILM_GRAIN_PRESENT, + .flags = CAP_FLAG_VOLATILE, + }, + { + .cap_id = SUPER_BLOCK, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_AV1_SUPER_BLOCK_ENABLED, + }, + { + .cap_id = ENH_LAYER_COUNT, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_AV1_OP_POINT, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { .cap_id = INPUT_BUF_HOST_MAX_COUNT, .min = DEFAULT_MAX_HOST_BUF_COUNT, .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, @@ -586,6 +689,57 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT, .set = iris_set_u32, }, + { + .cap_id = ROTATION, + .min = 0, + .max = 270, + .step_or_mask = 90, + .value = 0, + .hfi_id = HFI_PROP_ROTATION, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_rotation, + }, + { + .cap_id = HFLIP, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_FLIP, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_flip, + }, + { + .cap_id = VFLIP, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_FLIP, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_flip, + }, + { + .cap_id = IR_TYPE, + .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC, + .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) | + BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC), + .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = IR_PERIOD, + .min = 0, + .max = INT_MAX, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_ir_period, + }, }; static struct platform_inst_caps platform_inst_cap_sm8550 = { @@ -631,6 +785,11 @@ static const struct platform_clk_data sm8550_clk_table[] = { {IRIS_HW_CLK, "vcodec0_core" }, }; +static const char * const sm8550_opp_clk_table[] = { + "vcodec0_core", + NULL, +}; + static struct ubwc_config_data ubwc_config_sm8550 = { .max_channels = 8, .mal_length = 32, @@ -641,11 +800,13 @@ static struct ubwc_config_data ubwc_config_sm8550 = { .bank_spreading = 1, }; -static struct tz_cp_config tz_cp_config_sm8550 = { - .cp_start = 0, - .cp_size = 0x25800000, - .cp_nonpixel_start = 0x01000000, - .cp_nonpixel_size = 0x24800000, +static const struct tz_cp_config tz_cp_config_sm8550[] = { + { + .cp_start = 0, + .cp_size = 0x25800000, + .cp_nonpixel_start = 0x01000000, + .cp_nonpixel_size = 0x24800000, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { @@ -680,6 +841,19 @@ static const u32 sm8550_vdec_input_config_param_vp9[] = { HFI_PROP_LEVEL, }; +static const u32 sm8550_vdec_input_config_param_av1[] = { + HFI_PROP_BITSTREAM_RESOLUTION, + HFI_PROP_CROP_OFFSETS, + HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, + HFI_PROP_PROFILE, + HFI_PROP_LEVEL, + HFI_PROP_TIER, + HFI_PROP_AV1_FILM_GRAIN_PRESENT, + HFI_PROP_AV1_SUPER_BLOCK_ENABLED, + HFI_PROP_SIGNAL_COLOR_INFO, +}; + static const u32 sm8550_venc_input_config_params[] = { HFI_PROP_COLOR_FORMAT, HFI_PROP_RAW_RESOLUTION, @@ -717,17 +891,28 @@ static const u32 sm8550_vdec_subscribe_output_properties_vp9[] = { HFI_PROP_PICTURE_TYPE, }; +static const u32 sm8550_vdec_subscribe_output_properties_av1[] = { + HFI_PROP_PICTURE_TYPE, + HFI_PROP_WORST_COMPRESSION_RATIO, + HFI_PROP_WORST_COMPLEXITY_FACTOR, +}; + static const u32 sm8550_dec_ip_int_buf_tbl[] = { BUF_BIN, BUF_COMV, BUF_NON_COMV, BUF_LINE, + BUF_PARTIAL, }; static const u32 sm8550_dec_op_int_buf_tbl[] = { BUF_DPB, }; +static const u32 sm8550_enc_ip_int_buf_tbl[] = { + BUF_VPSS, +}; + static const u32 sm8550_enc_op_int_buf_tbl[] = { BUF_BIN, BUF_COMV, @@ -755,16 +940,20 @@ const struct iris_platform_data sm8550_data = { .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), .clk_tbl = sm8550_clk_table, .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = &tz_cp_config_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .ubwc_config = &ubwc_config_sm8550, @@ -784,6 +973,10 @@ const struct iris_platform_data sm8550_data = { sm8550_vdec_input_config_param_vp9, .dec_input_config_params_vp9_size = ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), + .dec_input_config_params_av1 = + sm8550_vdec_input_config_param_av1, + .dec_input_config_params_av1_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_av1), .dec_output_config_params = sm8550_vdec_output_config_params, .dec_output_config_params_size = @@ -809,12 +1002,17 @@ const struct iris_platform_data sm8550_data = { .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, .dec_output_prop_vp9_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), + .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, + .dec_output_prop_av1_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), + .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; @@ -847,16 +1045,20 @@ const struct iris_platform_data sm8650_data = { .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), .clk_tbl = sm8550_clk_table, .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu33_p4.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = &tz_cp_config_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .ubwc_config = &ubwc_config_sm8550, @@ -876,6 +1078,10 @@ const struct iris_platform_data sm8650_data = { sm8550_vdec_input_config_param_vp9, .dec_input_config_params_vp9_size = ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), + .dec_input_config_params_av1 = + sm8550_vdec_input_config_param_av1, + .dec_input_config_params_av1_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_av1), .dec_output_config_params = sm8550_vdec_output_config_params, .dec_output_config_params_size = @@ -901,12 +1107,17 @@ const struct iris_platform_data sm8650_data = { .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, .dec_output_prop_vp9_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), + .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, + .dec_output_prop_av1_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), + .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; @@ -915,6 +1126,7 @@ const struct iris_platform_data sm8750_data = { .get_instance = iris_hfi_gen2_get_instance, .init_hfi_command_ops = iris_hfi_gen2_command_ops_init, .init_hfi_response_ops = iris_hfi_gen2_response_ops_init, + .get_vpu_buffer_size = iris_vpu33_buf_size, .vpu_ops = &iris_vpu35_ops, .set_preset_registers = iris_set_sm8550_preset_registers, .icc_tbl = sm8550_icc_table, @@ -929,22 +1141,27 @@ const struct iris_platform_data sm8750_data = { .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), .clk_tbl = sm8750_clk_table, .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu35_p4.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_sm8550, .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = &tz_cp_config_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .ubwc_config = &ubwc_config_sm8550, .num_vpp_pipe = 4, .max_session_count = 16, .max_core_mbpf = NUM_MBS_8K * 2, + .max_core_mbps = ((7680 * 4320) / 256) * 60, .dec_input_config_params_default = sm8550_vdec_input_config_params_default, .dec_input_config_params_default_size = @@ -957,6 +1174,10 @@ const struct iris_platform_data sm8750_data = { sm8550_vdec_input_config_param_vp9, .dec_input_config_params_vp9_size = ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), + .dec_input_config_params_av1 = + sm8550_vdec_input_config_param_av1, + .dec_input_config_params_av1_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_av1), .dec_output_config_params = sm8550_vdec_output_config_params, .dec_output_config_params_size = @@ -982,12 +1203,17 @@ const struct iris_platform_data sm8750_data = { .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, .dec_output_prop_vp9_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), + .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, + .dec_output_prop_av1_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), + .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; @@ -1015,16 +1241,20 @@ const struct iris_platform_data qcs8300_data = { .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table), .clk_tbl = sm8550_clk_table, .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table), + .opp_clk_tbl = sm8550_opp_clk_table, /* Upper bound of DMA address range */ .dma_mask = 0xe0000000 - 1, .fwname = "qcom/vpu/vpu30_p4_s6.mbn", .pas_id = IRIS_PAS_ID, + .inst_iris_fmts = platform_fmts_sm8550_dec, + .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec), .inst_caps = &platform_inst_cap_qcs8300, .inst_fw_caps_dec = inst_fw_cap_sm8550_dec, .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec), .inst_fw_caps_enc = inst_fw_cap_sm8550_enc, .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc), - .tz_cp_config_data = &tz_cp_config_sm8550, + .tz_cp_config_data = tz_cp_config_sm8550, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550), .core_arch = VIDEO_ARCH_LX, .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE, .ubwc_config = &ubwc_config_sm8550, @@ -1044,6 +1274,10 @@ const struct iris_platform_data qcs8300_data = { sm8550_vdec_input_config_param_vp9, .dec_input_config_params_vp9_size = ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), + .dec_input_config_params_av1 = + sm8550_vdec_input_config_param_av1, + .dec_input_config_params_av1_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_av1), .dec_output_config_params = sm8550_vdec_output_config_params, .dec_output_config_params_size = @@ -1069,12 +1303,17 @@ const struct iris_platform_data qcs8300_data = { .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, .dec_output_prop_vp9_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), + .dec_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1, + .dec_output_prop_av1_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1), .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), + .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; diff --git a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h index f1bef4d4bcfe..0ec8f334df67 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h +++ b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h @@ -23,4 +23,9 @@ static const struct platform_clk_data sc7280_clk_table[] = { {IRIS_HW_AHB_CLK, "vcodec_bus" }, }; +static const char * const sc7280_opp_clk_table[] = { + "vcodec_core", + NULL, +}; + #endif diff --git a/drivers/media/platform/qcom/iris/iris_power.c b/drivers/media/platform/qcom/iris/iris_power.c index dbca42df0910..91aa21d4070e 100644 --- a/drivers/media/platform/qcom/iris/iris_power.c +++ b/drivers/media/platform/qcom/iris/iris_power.c @@ -91,7 +91,7 @@ static int iris_set_clocks(struct iris_inst *inst) } core->power.clk_freq = freq; - ret = dev_pm_opp_set_rate(core->dev, freq); + ret = iris_opp_set_rate(core->dev, freq); mutex_unlock(&core->lock); return ret; diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 9bc9b34c2576..ddaacda523ec 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -40,8 +40,6 @@ static int iris_init_icc(struct iris_core *core) static int iris_init_power_domains(struct iris_core *core) { - const struct platform_clk_data *clk_tbl; - u32 clk_cnt, i; int ret; struct dev_pm_domain_attach_data iris_pd_data = { @@ -56,6 +54,11 @@ static int iris_init_power_domains(struct iris_core *core) .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP, }; + struct dev_pm_opp_config iris_opp_clk_data = { + .clk_names = core->iris_platform_data->opp_clk_tbl, + .config_clks = dev_pm_opp_config_clks_simple, + }; + ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl); if (ret < 0) return ret; @@ -64,16 +67,9 @@ static int iris_init_power_domains(struct iris_core *core) if (ret < 0) return ret; - clk_tbl = core->iris_platform_data->clk_tbl; - clk_cnt = core->iris_platform_data->clk_tbl_size; - - for (i = 0; i < clk_cnt; i++) { - if (clk_tbl[i].clk_type == IRIS_HW_CLK) { - ret = devm_pm_opp_set_clkname(core->dev, clk_tbl[i].clk_name); - if (ret) - return ret; - } - } + ret = devm_pm_opp_set_config(core->dev, &iris_opp_clk_data); + if (ret) + return ret; return devm_pm_opp_of_add_table(core->dev); } diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c index 164490c49c95..773f6548370a 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.c +++ b/drivers/media/platform/qcom/iris/iris_resources.c @@ -4,6 +4,7 @@ */ #include <linux/clk.h> +#include <linux/devfreq.h> #include <linux/interconnect.h> #include <linux/pm_domain.h> #include <linux/pm_opp.h> @@ -58,11 +59,22 @@ int iris_unset_icc_bw(struct iris_core *core) return icc_bulk_set_bw(core->icc_count, core->icc_tbl); } +int iris_opp_set_rate(struct device *dev, unsigned long freq) +{ + struct dev_pm_opp *opp __free(put_opp); + + opp = devfreq_recommended_opp(dev, &freq, 0); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + return dev_pm_opp_set_opp(dev, opp); +} + int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev) { int ret; - ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX); + ret = iris_opp_set_rate(core->dev, ULONG_MAX); if (ret) return ret; @@ -77,7 +89,7 @@ int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev) { int ret; - ret = dev_pm_opp_set_rate(core->dev, 0); + ret = iris_opp_set_rate(core->dev, 0); if (ret) return ret; diff --git a/drivers/media/platform/qcom/iris/iris_resources.h b/drivers/media/platform/qcom/iris/iris_resources.h index f723dfe5bd81..6bfbd2dc6db0 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.h +++ b/drivers/media/platform/qcom/iris/iris_resources.h @@ -8,6 +8,7 @@ struct iris_core; +int iris_opp_set_rate(struct device *dev, unsigned long freq); int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev); int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev); int iris_unset_icc_bw(struct iris_core *core); diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c index e2f1131de431..cfc5b576ec56 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.c +++ b/drivers/media/platform/qcom/iris/iris_utils.c @@ -125,3 +125,9 @@ int iris_check_core_mbps(struct iris_inst *inst) return 0; } + +bool is_rotation_90_or_270(struct iris_inst *inst) +{ + return inst->fw_caps[ROTATION].value == 90 || + inst->fw_caps[ROTATION].value == 270; +} diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h index 75740181122f..b5705d156431 100644 --- a/drivers/media/platform/qcom/iris/iris_utils.h +++ b/drivers/media/platform/qcom/iris/iris_utils.h @@ -51,5 +51,6 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type, int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush); int iris_check_core_mbpf(struct iris_inst *inst); int iris_check_core_mbps(struct iris_inst *inst); +bool is_rotation_90_or_270(struct iris_inst *inst); #endif diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c index db8768d8a8f6..bf0b8400996e 100644 --- a/drivers/media/platform/qcom/iris/iris_vb2.c +++ b/drivers/media/platform/qcom/iris/iris_vb2.c @@ -193,10 +193,14 @@ int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count) buf_type = iris_v4l2_type_to_driver(q->type); if (inst->domain == DECODER) { - if (inst->state == IRIS_INST_STREAMING) + if (buf_type == BUF_INPUT) + ret = iris_queue_deferred_buffers(inst, BUF_INPUT); + + if (!ret && inst->state == IRIS_INST_STREAMING) { ret = iris_queue_internal_deferred_buffers(inst, BUF_DPB); - if (!ret) - ret = iris_queue_deferred_buffers(inst, buf_type); + if (!ret) + ret = iris_queue_deferred_buffers(inst, BUF_OUTPUT); + } } else { if (inst->state == IRIS_INST_STREAMING) { ret = iris_queue_deferred_buffers(inst, BUF_INPUT); @@ -231,8 +235,6 @@ void iris_vb2_stop_streaming(struct vb2_queue *q) return; mutex_lock(&inst->lock); - if (inst->state == IRIS_INST_ERROR) - goto exit; if (!V4L2_TYPE_IS_OUTPUT(q->type) && !V4L2_TYPE_IS_CAPTURE(q->type)) @@ -243,10 +245,10 @@ void iris_vb2_stop_streaming(struct vb2_queue *q) goto exit; exit: - if (ret) { - iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); + iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR); + if (ret) iris_inst_change_state(inst, IRIS_INST_ERROR); - } + mutex_unlock(&inst->lock); } diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c index 69ffe52590d3..467d00044a2f 100644 --- a/drivers/media/platform/qcom/iris/iris_vdec.c +++ b/drivers/media/platform/qcom/iris/iris_vdec.c @@ -67,21 +67,6 @@ void iris_vdec_inst_deinit(struct iris_inst *inst) kfree(inst->fmt_src); } -static const struct iris_fmt iris_vdec_formats_out[] = { - [IRIS_FMT_H264] = { - .pixfmt = V4L2_PIX_FMT_H264, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_HEVC] = { - .pixfmt = V4L2_PIX_FMT_HEVC, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, - [IRIS_FMT_VP9] = { - .pixfmt = V4L2_PIX_FMT_VP9, - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - }, -}; - static const struct iris_fmt iris_vdec_formats_cap[] = { [IRIS_FMT_NV12] = { .pixfmt = V4L2_PIX_FMT_NV12, @@ -101,8 +86,8 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type) unsigned int i; switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - fmt = iris_vdec_formats_out; - size = ARRAY_SIZE(iris_vdec_formats_out); + fmt = inst->core->iris_platform_data->inst_iris_fmts; + size = inst->core->iris_platform_data->inst_iris_fmts_size; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = iris_vdec_formats_cap; @@ -131,8 +116,8 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type) switch (type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - fmt = iris_vdec_formats_out; - size = ARRAY_SIZE(iris_vdec_formats_out); + fmt = inst->core->iris_platform_data->inst_iris_fmts; + size = inst->core->iris_platform_data->inst_iris_fmts_size; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: fmt = iris_vdec_formats_cap; @@ -231,6 +216,14 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f) if (vb2_is_busy(q)) return -EBUSY; + /* Width and height are optional, so fall back to a valid placeholder + * resolution until the real one is decoded from the bitstream. + */ + if (f->fmt.pix_mp.width == 0 && f->fmt.pix_mp.height == 0) { + f->fmt.pix_mp.width = DEFAULT_WIDTH; + f->fmt.pix_mp.height = DEFAULT_HEIGHT; + } + iris_vdec_try_fmt(inst, f); switch (f->type) { diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c index 5830eba93c68..6461d9c9d598 100644 --- a/drivers/media/platform/qcom/iris/iris_venc.c +++ b/drivers/media/platform/qcom/iris/iris_venc.c @@ -62,12 +62,17 @@ int iris_venc_inst_init(struct iris_inst *inst) inst->crop.left = 0; inst->crop.top = 0; - inst->crop.width = f->fmt.pix_mp.width; - inst->crop.height = f->fmt.pix_mp.height; + inst->crop.width = DEFAULT_WIDTH; + inst->crop.height = DEFAULT_HEIGHT; inst->operating_rate = DEFAULT_FPS; inst->frame_rate = DEFAULT_FPS; + inst->enc_raw_width = DEFAULT_WIDTH; + inst->enc_raw_height = DEFAULT_HEIGHT; + inst->enc_scale_width = DEFAULT_WIDTH; + inst->enc_scale_height = DEFAULT_HEIGHT; + memcpy(&inst->fw_caps[0], &core->inst_fw_caps_enc[0], INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap)); @@ -223,15 +228,32 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f) static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f) { + const struct iris_fmt *venc_fmt; struct v4l2_format *fmt; + u32 codec_align; iris_venc_try_fmt(inst, f); - if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type))) + venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type); + if (!venc_fmt) return -EINVAL; + codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16; + fmt = inst->fmt_dst; fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + /* + * If output format size != input format size, + * it is considered a scaling case, + * and the scaled size needs to be saved. + */ + if (f->fmt.pix_mp.width != inst->fmt_src->fmt.pix_mp.width || + f->fmt.pix_mp.height != inst->fmt_src->fmt.pix_mp.height) { + inst->enc_scale_width = f->fmt.pix_mp.width; + inst->enc_scale_height = f->fmt.pix_mp.height; + fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align); + fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align); + } fmt->fmt.pix_mp.num_planes = 1; fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0; fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT); @@ -287,6 +309,11 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f) inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT); inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage; + inst->enc_raw_width = f->fmt.pix_mp.width; + inst->enc_raw_height = f->fmt.pix_mp.height; + inst->enc_scale_width = f->fmt.pix_mp.width; + inst->enc_scale_height = f->fmt.pix_mp.height; + if (f->fmt.pix_mp.width != inst->crop.width || f->fmt.pix_mp.height != inst->crop.height) { inst->crop.top = 0; @@ -382,8 +409,7 @@ int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm) struct v4l2_fract *timeperframe = NULL; u32 default_rate = DEFAULT_FPS; bool is_frame_rate = false; - u64 us_per_frame, fps; - u32 max_rate; + u32 fps, max_rate; int ret = 0; @@ -405,23 +431,19 @@ int iris_venc_s_param(struct iris_inst *inst, struct v4l2_streamparm *s_parm) timeperframe->denominator = default_rate; } - us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC; - do_div(us_per_frame, timeperframe->denominator); - - if (!us_per_frame) + fps = timeperframe->denominator / timeperframe->numerator; + if (!fps) return -EINVAL; - fps = (u64)USEC_PER_SEC; - do_div(fps, us_per_frame); if (fps > max_rate) { ret = -ENOMEM; goto reset_rate; } if (is_frame_rate) - inst->frame_rate = (u32)fps; + inst->frame_rate = fps; else - inst->operating_rate = (u32)fps; + inst->operating_rate = fps; if ((s_parm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_is_streaming(src_q)) || (s_parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vb2_is_streaming(dst_q))) { diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index c9b881923ef1..bd38d84c9cc7 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -178,6 +178,7 @@ int iris_open(struct file *filp) INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_1].list); INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_2].list); INIT_LIST_HEAD(&inst->buffers[BUF_VPSS].list); + INIT_LIST_HEAD(&inst->buffers[BUF_PARTIAL].list); init_completion(&inst->completion); init_completion(&inst->flush_completion); @@ -572,9 +573,10 @@ static int iris_dec_cmd(struct file *filp, void *fh, mutex_lock(&inst->lock); - ret = v4l2_m2m_ioctl_decoder_cmd(filp, fh, dec); - if (ret) + if (dec->cmd != V4L2_DEC_CMD_STOP && dec->cmd != V4L2_DEC_CMD_START) { + ret = -EINVAL; goto unlock; + } if (inst->state == IRIS_INST_DEINIT) goto unlock; @@ -605,9 +607,10 @@ static int iris_enc_cmd(struct file *filp, void *fh, mutex_lock(&inst->lock); - ret = v4l2_m2m_ioctl_encoder_cmd(filp, fh, enc); - if (ret) + if (enc->cmd != V4L2_ENC_CMD_STOP && enc->cmd != V4L2_ENC_CMD_START) { + ret = -EINVAL; goto unlock; + } if (inst->state == IRIS_INST_DEINIT) goto unlock; diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c index 339776a0b467..fe4423b951b1 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c @@ -11,55 +11,11 @@ #include "iris_vpu_common.h" #include "iris_vpu_register_defines.h" -#define WRAPPER_TZ_BASE_OFFS 0x000C0000 -#define AON_BASE_OFFS 0x000E0000 -#define AON_MVP_NOC_RESET 0x0001F000 - -#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54) -#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58) -#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C) -#define REQ_POWER_DOWN_PREP BIT(0) -#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60) -#define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */ -#define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */ -#define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */ -#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) -#define CORE_CLK_RUN 0x0 -/* VPU v3.5 */ -#define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78) - -#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14) -#define CTL_AXI_CLK_HALT BIT(0) -#define CTL_CLK_HALT BIT(1) - -#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18) -#define RESET_HIGH BIT(0) - -#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) -#define CORE_BRIDGE_SW_RESET BIT(0) -#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) - -#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168) -#define MSK_SIGNAL_FROM_TENSILICA BIT(0) -#define MSK_CORE_POWER_ON BIT(1) - -#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) -#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) - -#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004) - -#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) - -#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS) -#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4) - #define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18) #define SW_RESET BIT(0) #define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20) #define NOC_HALT BIT(0) #define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28) -#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C) -#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30) static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core) { @@ -304,155 +260,12 @@ static void iris_vpu35_power_off_hw(struct iris_core *core) iris_disable_unprepare_clock(core, IRIS_AXI_CLK); } -static int iris_vpu35_power_off_controller(struct iris_core *core) -{ - u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; - unsigned int count = 0; - u32 val = 0; - bool handshake_done, handshake_busy; - int ret; - - writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); - - writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); - - ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, - val, val & BIT(0), 200, 2000); - if (ret) - goto disable_power; - - writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); - - /* Retry up to 1000 times as recommended by hardware documentation */ - do { - /* set MNoC to low power */ - writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); - - udelay(15); - - val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS); - - handshake_done = val & NOC_LPI_STATUS_DONE; - handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); - - if (handshake_done || !handshake_busy) - break; - - writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); - - udelay(15); - - } while (++count < 1000); - - if (!handshake_done && handshake_busy) - dev_err(core->dev, "LPI handshake timeout\n"); - - ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS, - val, val & BIT(0), 200, 2000); - if (ret) - goto disable_power; - - writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); - - writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); - - ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, - val, val == 0, 200, 2000); - if (ret) - goto disable_power; - -disable_power: - iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); - iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); - iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); - - iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); - - reset_control_bulk_reset(clk_rst_tbl_size, core->resets); - - return 0; -} - -static int iris_vpu35_power_on_controller(struct iris_core *core) -{ - int ret; - - ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); - if (ret) - return ret; - - ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK); - if (ret) - goto err_disable_power; - - ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK); - if (ret) - goto err_disable_axi1_clk; - - ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); - if (ret) - goto err_disable_ctrl_free_clk; - - return 0; - -err_disable_ctrl_free_clk: - iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); -err_disable_axi1_clk: - iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); -err_disable_power: - iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); - - return ret; -} - -static void iris_vpu35_program_bootup_registers(struct iris_core *core) -{ - writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0); -} - -static u64 iris_vpu3x_calculate_frequency(struct iris_inst *inst, size_t data_size) -{ - struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; - struct v4l2_format *inp_f = inst->fmt_src; - u32 height, width, mbs_per_second, mbpf; - u64 fw_cycles, fw_vpp_cycles; - u64 vsp_cycles, vpp_cycles; - u32 fps = DEFAULT_FPS; - - width = max(inp_f->fmt.pix_mp.width, inst->crop.width); - height = max(inp_f->fmt.pix_mp.height, inst->crop.height); - - mbpf = NUM_MBS_PER_FRAME(height, width); - mbs_per_second = mbpf * fps; - - fw_cycles = fps * caps->mb_cycles_fw; - fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp; - - vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value); - /* 21 / 20 is minimum overhead factor */ - vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles); - - /* 1.059 is multi-pipe overhead */ - if (inst->fw_caps[PIPE].value > 1) - vpp_cycles += div_u64(vpp_cycles * 59, 1000); - - vsp_cycles = fps * data_size * 8; - vsp_cycles = div_u64(vsp_cycles, 2); - /* VSP FW overhead 1.05 */ - vsp_cycles = div_u64(vsp_cycles * 21, 20); - - if (inst->fw_caps[STAGE].value == STAGE_1) - vsp_cycles = vsp_cycles * 3; - - return max3(vpp_cycles, vsp_cycles, fw_cycles); -} - const struct vpu_ops iris_vpu3_ops = { .power_off_hw = iris_vpu3_power_off_hardware, .power_on_hw = iris_vpu_power_on_hw, .power_off_controller = iris_vpu_power_off_controller, .power_on_controller = iris_vpu_power_on_controller, - .calc_freq = iris_vpu3x_calculate_frequency, + .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, }; const struct vpu_ops iris_vpu33_ops = { @@ -460,14 +273,14 @@ const struct vpu_ops iris_vpu33_ops = { .power_on_hw = iris_vpu_power_on_hw, .power_off_controller = iris_vpu33_power_off_controller, .power_on_controller = iris_vpu_power_on_controller, - .calc_freq = iris_vpu3x_calculate_frequency, + .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, }; const struct vpu_ops iris_vpu35_ops = { .power_off_hw = iris_vpu35_power_off_hw, .power_on_hw = iris_vpu35_power_on_hw, - .power_off_controller = iris_vpu35_power_off_controller, - .power_on_controller = iris_vpu35_power_on_controller, - .program_bootup_registers = iris_vpu35_program_bootup_registers, - .calc_freq = iris_vpu3x_calculate_frequency, + .power_off_controller = iris_vpu35_vpu4x_power_off_controller, + .power_on_controller = iris_vpu35_vpu4x_power_on_controller, + .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers, + .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, }; diff --git a/drivers/media/platform/qcom/iris/iris_vpu4x.c b/drivers/media/platform/qcom/iris/iris_vpu4x.c new file mode 100644 index 000000000000..a8db02ce5c5e --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_vpu4x.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/iopoll.h> +#include <linux/reset.h> + +#include "iris_instance.h" +#include "iris_vpu_common.h" +#include "iris_vpu_register_defines.h" + +#define AON_WRAPPER_MVP_NOC_RESET_SYNCRST (AON_MVP_NOC_RESET + 0x08) +#define CPU_CS_APV_BRIDGE_SYNC_RESET (CPU_BASE_OFFS + 0x174) +#define MVP_NOC_RESET_REQ_MASK 0x70103 +#define VPU_IDLE_BITS 0x7103 +#define WRAPPER_EFUSE_MONITOR (WRAPPER_BASE_OFFS + 0x08) + +#define APV_CLK_HALT BIT(1) +#define CORE_CLK_HALT BIT(0) +#define CORE_PWR_ON BIT(1) +#define DISABLE_VIDEO_APV_BIT BIT(27) +#define DISABLE_VIDEO_VPP1_BIT BIT(28) +#define DISABLE_VIDEO_VPP0_BIT BIT(29) + +static int iris_vpu4x_genpd_set_hwmode(struct iris_core *core, bool hw_mode, u32 efuse_value) +{ + int ret; + + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], hw_mode); + if (ret) + return ret; + + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs + [IRIS_VPP0_HW_POWER_DOMAIN], hw_mode); + if (ret) + goto restore_hw_domain_mode; + } + + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs + [IRIS_VPP1_HW_POWER_DOMAIN], hw_mode); + if (ret) + goto restore_vpp0_domain_mode; + } + + if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) { + ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs + [IRIS_APV_HW_POWER_DOMAIN], hw_mode); + if (ret) + goto restore_vpp1_domain_mode; + } + + return 0; + +restore_vpp1_domain_mode: + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) + dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP1_HW_POWER_DOMAIN], + !hw_mode); +restore_vpp0_domain_mode: + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) + dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_VPP0_HW_POWER_DOMAIN], + !hw_mode); +restore_hw_domain_mode: + dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], !hw_mode); + + return ret; +} + +static int iris_vpu4x_power_on_apv(struct iris_core *core) +{ + int ret; + + ret = iris_enable_power_domains(core, + core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); + if (ret) + return ret; + + ret = iris_prepare_enable_clock(core, IRIS_APV_HW_CLK); + if (ret) + goto disable_apv_hw_power_domain; + + return 0; + +disable_apv_hw_power_domain: + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); + + return ret; +} + +static void iris_vpu4x_power_off_apv(struct iris_core *core) +{ + bool handshake_done, handshake_busy; + u32 value, count = 0; + int ret; + + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + if (value & APV_CLK_HALT) + writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + do { + writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + usleep_range(10, 20); + value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS); + + handshake_done = value & NOC_LPI_STATUS_DONE; + handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); + + if (handshake_done || !handshake_busy) + break; + + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + usleep_range(10, 20); + + } while (++count < 1000); + + if (!handshake_done && handshake_busy) + dev_err(core->dev, "LPI handshake timeout\n"); + + writel(0x080200, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + value, value & 0x080200, 200, 2000); + if (ret) + goto disable_clocks_and_power; + + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST); + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + value, value == 0x0, 200, 2000); + if (ret) + goto disable_clocks_and_power; + + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + + CPU_CS_APV_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); + +disable_clocks_and_power: + iris_disable_unprepare_clock(core, IRIS_APV_HW_CLK); + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_APV_HW_POWER_DOMAIN]); +} + +static void iris_vpu4x_ahb_sync_reset_apv(struct iris_core *core) +{ + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + + CPU_CS_APV_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_APV_BRIDGE_SYNC_RESET); +} + +static void iris_vpu4x_ahb_sync_reset_hardware(struct iris_core *core) +{ + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); +} + +static int iris_vpu4x_enable_hardware_clocks(struct iris_core *core, u32 efuse_value) +{ + int ret; + + ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK); + if (ret) + return ret; + + ret = iris_prepare_enable_clock(core, IRIS_HW_FREERUN_CLK); + if (ret) + goto disable_axi_clock; + + ret = iris_prepare_enable_clock(core, IRIS_HW_CLK); + if (ret) + goto disable_hw_free_run_clock; + + ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK); + if (ret) + goto disable_hw_clock; + + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { + ret = iris_prepare_enable_clock(core, IRIS_VPP0_HW_CLK); + if (ret) + goto disable_bse_hw_clock; + } + + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { + ret = iris_prepare_enable_clock(core, IRIS_VPP1_HW_CLK); + if (ret) + goto disable_vpp0_hw_clock; + } + + return 0; + +disable_vpp0_hw_clock: + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) + iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK); +disable_bse_hw_clock: + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); +disable_hw_clock: + iris_disable_unprepare_clock(core, IRIS_HW_CLK); +disable_hw_free_run_clock: + iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK); +disable_axi_clock: + iris_disable_unprepare_clock(core, IRIS_AXI_CLK); + + return ret; +} + +static void iris_vpu4x_disable_hardware_clocks(struct iris_core *core, u32 efuse_value) +{ + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) + iris_disable_unprepare_clock(core, IRIS_VPP1_HW_CLK); + + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) + iris_disable_unprepare_clock(core, IRIS_VPP0_HW_CLK); + + iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK); + iris_disable_unprepare_clock(core, IRIS_HW_CLK); + iris_disable_unprepare_clock(core, IRIS_HW_FREERUN_CLK); + iris_disable_unprepare_clock(core, IRIS_AXI_CLK); +} + +static int iris_vpu4x_power_on_hardware(struct iris_core *core) +{ + u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR); + int ret; + + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + if (ret) + return ret; + + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) { + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP0_HW_POWER_DOMAIN]); + if (ret) + goto disable_hw_power_domain; + } + + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) { + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP1_HW_POWER_DOMAIN]); + if (ret) + goto disable_vpp0_power_domain; + } + + ret = iris_vpu4x_enable_hardware_clocks(core, efuse_value); + if (ret) + goto disable_vpp1_power_domain; + + if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) { + ret = iris_vpu4x_power_on_apv(core); + if (ret) + goto disable_hw_clocks; + + iris_vpu4x_ahb_sync_reset_apv(core); + } + + iris_vpu4x_ahb_sync_reset_hardware(core); + + ret = iris_vpu4x_genpd_set_hwmode(core, true, efuse_value); + if (ret) + goto disable_apv_power_domain; + + return 0; + +disable_apv_power_domain: + if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) + iris_vpu4x_power_off_apv(core); +disable_hw_clocks: + iris_vpu4x_disable_hardware_clocks(core, efuse_value); +disable_vpp1_power_domain: + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP1_HW_POWER_DOMAIN]); +disable_vpp0_power_domain: + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP0_HW_POWER_DOMAIN]); +disable_hw_power_domain: + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); + + return ret; +} + +static void iris_vpu4x_power_off_hardware(struct iris_core *core) +{ + u32 efuse_value = readl(core->reg_base + WRAPPER_EFUSE_MONITOR); + bool handshake_done, handshake_busy; + u32 value, count = 0; + int ret; + + iris_vpu4x_genpd_set_hwmode(core, false, efuse_value); + + if (!(efuse_value & DISABLE_VIDEO_APV_BIT)) + iris_vpu4x_power_off_apv(core); + + value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS); + + if (!(value & CORE_PWR_ON)) + goto disable_clocks_and_power; + + value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + if (value & CORE_CLK_HALT) + writel(0x0, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG); + + readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN, value, + value & VPU_IDLE_BITS, 2000, 20000); + + do { + writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + usleep_range(10, 20); + value = readl(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS); + + handshake_done = value & NOC_LPI_STATUS_DONE; + handshake_busy = value & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); + + if (handshake_done || !handshake_busy) + break; + + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + usleep_range(10, 20); + + } while (++count < 1000); + + if (!handshake_done && handshake_busy) + dev_err(core->dev, "LPI handshake timeout\n"); + + writel(MVP_NOC_RESET_REQ_MASK, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + value, value & MVP_NOC_RESET_REQ_MASK, 200, 2000); + if (ret) + goto disable_clocks_and_power; + + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_SYNCRST); + writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ); + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK, + value, value == 0x0, 200, 2000); + if (ret) + goto disable_clocks_and_power; + + writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET); + +disable_clocks_and_power: + iris_vpu4x_disable_hardware_clocks(core, efuse_value); + + if (!(efuse_value & DISABLE_VIDEO_VPP1_BIT)) + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP1_HW_POWER_DOMAIN]); + + if (!(efuse_value & DISABLE_VIDEO_VPP0_BIT)) + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs + [IRIS_VPP0_HW_POWER_DOMAIN]); + + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]); +} + +const struct vpu_ops iris_vpu4x_ops = { + .power_off_hw = iris_vpu4x_power_off_hardware, + .power_on_hw = iris_vpu4x_power_on_hardware, + .power_off_controller = iris_vpu35_vpu4x_power_off_controller, + .power_on_controller = iris_vpu35_vpu4x_power_on_controller, + .program_bootup_registers = iris_vpu35_vpu4x_program_bootup_registers, + .calc_freq = iris_vpu3x_vpu4x_calculate_frequency, +}; diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index 4463be05ce16..9270422c1601 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -9,6 +9,17 @@ #include "iris_hfi_gen2_defines.h" #define HFI_MAX_COL_FRAME 6 +#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_HEIGHT (8) +#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH (32) +#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_HEIGHT (8) +#define HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_WIDTH (16) +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_HEIGHT (4) +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH (48) +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_HEIGHT (4) +#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_WIDTH (24) +#define AV1D_SIZE_BSE_COL_MV_64x64 512 +#define AV1D_SIZE_BSE_COL_MV_128x128 2816 +#define UBWC_TILE_SIZE 256 #ifndef SYSTEM_LAL_TILE10 #define SYSTEM_LAL_TILE10 192 @@ -39,6 +50,31 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes); } +static u32 size_av1d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 size_yuv, size_bin_hdr, size_bin_res; + + size_yuv = ((frame_width * frame_height) <= BIN_BUFFER_THRESHOLD) ? + ((BIN_BUFFER_THRESHOLD * 3) >> 1) : + ((frame_width * frame_height * 3) >> 1); + size_bin_hdr = size_yuv * AV1_CABAC_HDR_RATIO_HD_TOT; + size_bin_res = size_yuv * AV1_CABAC_RES_RATIO_HD_TOT; + size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes, + DMA_ALIGNMENT) * num_vpp_pipes; + size_bin_res = ALIGN(size_bin_res / num_vpp_pipes, + DMA_ALIGNMENT) * num_vpp_pipes; + + return size_bin_hdr + size_bin_res; +} + +static u32 hfi_buffer_bin_av1d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 n_aligned_h = ALIGN(frame_height, 16); + u32 n_aligned_w = ALIGN(frame_width, 16); + + return size_av1d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes); +} + static u32 size_h265d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) { u32 product = frame_width * frame_height; @@ -110,6 +146,26 @@ static u32 hfi_buffer_comv_h265d(u32 frame_width, u32 frame_height, u32 _comv_bu return (_size * (_comv_bufcount)) + 512; } +static u32 num_lcu(u32 frame_width, u32 frame_height, u32 lcu_size) +{ + return ((frame_width + lcu_size - 1) / lcu_size) * + ((frame_height + lcu_size - 1) / lcu_size); +} + +static u32 hfi_buffer_comv_av1d(u32 frame_width, u32 frame_height, u32 comv_bufcount) +{ + u32 size; + + size = 2 * ALIGN(max(num_lcu(frame_width, frame_height, 64) * + AV1D_SIZE_BSE_COL_MV_64x64, + num_lcu(frame_width, frame_height, 128) * + AV1D_SIZE_BSE_COL_MV_128x128), + DMA_ALIGNMENT); + size *= comv_bufcount; + + return size; +} + static u32 size_h264d_bse_cmd_buf(u32 frame_height) { u32 height = ALIGN(frame_height, 32); @@ -122,7 +178,7 @@ static u32 size_h265d_bse_cmd_buf(u32 frame_width, u32 frame_height) { u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) * (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) * - NUM_HW_PIC_BUF, DMA_ALIGNMENT); + NUM_HW_PIC_BUF, DMA_ALIGNMENT); _size = min_t(u32, _size, H265D_MAX_SLICE + 1); _size = 2 * _size * SIZE_H265D_BSE_CMD_PER_BUF; @@ -174,6 +230,20 @@ static u32 hfi_buffer_persist_h264d(void) DMA_ALIGNMENT); } +static u32 hfi_buffer_persist_av1d(u32 max_width, u32 max_height, u32 total_ref_count) +{ + u32 comv_size, size; + + comv_size = hfi_buffer_comv_av1d(max_width, max_height, total_ref_count); + size = ALIGN((SIZE_AV1D_SEQUENCE_HEADER * 2 + SIZE_AV1D_METADATA + + AV1D_NUM_HW_PIC_BUF * (SIZE_AV1D_TILE_OFFSET + SIZE_AV1D_QM) + + AV1D_NUM_FRAME_HEADERS * (SIZE_AV1D_FRAME_HEADER + + 2 * SIZE_AV1D_PROB_TABLE) + comv_size + HDR10_HIST_EXTRADATA_SIZE + + SIZE_AV1D_METADATA * AV1D_NUM_HW_PIC_BUF), DMA_ALIGNMENT); + + return ALIGN(size, DMA_ALIGNMENT); +} + static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) { u32 size_bse = size_h264d_bse_cmd_buf(frame_height); @@ -459,6 +529,182 @@ static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height, return ALIGN((size + vpss_lb_size), DMA_ALIGNMENT); } +static u32 size_av1d_lb_opb_wr1_nv12_ubwc(u32 frame_width, u32 frame_height) +{ + u32 size, y_width, y_width_a = 128; + + y_width = ALIGN(frame_width, y_width_a); + + size = ((y_width + HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH - 1) / + HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH + + (AV1D_MAX_TILE_COLS - 1)); + return size * UBWC_TILE_SIZE; +} + +static u32 size_av1d_lb_opb_wr1_tp10_ubwc(u32 frame_width, u32 frame_height) +{ + u32 size, y_width, y_width_a = 256; + + y_width = ALIGN(frame_width, y_width_a); + + size = ((y_width + HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH - 1) / + HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH + + (AV1D_MAX_TILE_COLS - 1)); + + return size * UBWC_TILE_SIZE; +} + +static u32 hfi_buffer_line_av1d(u32 frame_width, u32 frame_height, + bool is_opb, u32 num_vpp_pipes) +{ + u32 size, vpss_lb_size, opbwrbufsize, opbwr8, opbwr10; + + size = ALIGN(size_av1d_lb_fe_top_data(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_av1d_lb_fe_top_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_av1d_lb_fe_left_data(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_av1d_lb_fe_left_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_av1d_lb_se_left_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) * num_vpp_pipes + + ALIGN(size_av1d_lb_se_top_ctrl(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_av1d_lb_pe_top_data(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_av1d_lb_vsp_top(frame_width, frame_height), + DMA_ALIGNMENT) + + ALIGN(size_av1d_lb_recon_dma_metadata_wr + (frame_width, frame_height), DMA_ALIGNMENT) * 2 + + ALIGN(size_av1d_qp(frame_width, frame_height), DMA_ALIGNMENT); + opbwr8 = size_av1d_lb_opb_wr1_nv12_ubwc(frame_width, frame_height); + opbwr10 = size_av1d_lb_opb_wr1_tp10_ubwc(frame_width, frame_height); + opbwrbufsize = opbwr8 >= opbwr10 ? opbwr8 : opbwr10; + size = ALIGN((size + opbwrbufsize), DMA_ALIGNMENT); + if (is_opb) { + vpss_lb_size = size_vpss_lb(frame_width, frame_height); + size = ALIGN((size + vpss_lb_size) * 2, DMA_ALIGNMENT); + } + + return size; +} + +static u32 size_av1d_ibc_nv12_ubwc(u32 frame_width, u32 frame_height) +{ + u32 size; + u32 y_width_a = 128, y_height_a = 32; + u32 uv_width_a = 128, uv_height_a = 32; + u32 ybufsize, uvbufsize, y_width, y_height, uv_width, uv_height; + u32 y_meta_width_a = 64, y_meta_height_a = 16; + u32 uv_meta_width_a = 64, uv_meta_height_a = 16; + u32 meta_height, meta_stride, meta_size; + u32 tile_width_y = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_WIDTH; + u32 tile_height_y = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_Y_TILE_HEIGHT; + u32 tile_width_uv = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_WIDTH; + u32 tile_height_uv = HFI_COLOR_FORMAT_YUV420_NV12_UBWC_UV_TILE_HEIGHT; + + y_width = ALIGN(frame_width, y_width_a); + y_height = ALIGN(frame_height, y_height_a); + uv_width = ALIGN(frame_width, uv_width_a); + uv_height = ALIGN(((frame_height + 1) >> 1), uv_height_a); + ybufsize = ALIGN((y_width * y_height), HFI_ALIGNMENT_4096); + uvbufsize = ALIGN(uv_width * uv_height, HFI_ALIGNMENT_4096); + size = ybufsize + uvbufsize; + meta_stride = ALIGN(((frame_width + (tile_width_y - 1)) / tile_width_y), + y_meta_width_a); + meta_height = ALIGN(((frame_height + (tile_height_y - 1)) / tile_height_y), + y_meta_height_a); + meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096); + size += meta_size; + meta_stride = ALIGN(((((frame_width + 1) >> 1) + (tile_width_uv - 1)) / + tile_width_uv), uv_meta_width_a); + meta_height = ALIGN(((((frame_height + 1) >> 1) + (tile_height_uv - 1)) / + tile_height_uv), uv_meta_height_a); + meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096); + size += meta_size; + + return size; +} + +static u32 hfi_yuv420_tp10_calc_y_stride(u32 frame_width, u32 stride_multiple) +{ + u32 stride; + + stride = ALIGN(frame_width, 192); + stride = ALIGN(stride * 4 / 3, stride_multiple); + + return stride; +} + +static u32 hfi_yuv420_tp10_calc_y_bufheight(u32 frame_height, u32 min_buf_height_multiple) +{ + return ALIGN(frame_height, min_buf_height_multiple); +} + +static u32 hfi_yuv420_tp10_calc_uv_stride(u32 frame_width, u32 stride_multiple) +{ + u32 stride; + + stride = ALIGN(frame_width, 192); + stride = ALIGN(stride * 4 / 3, stride_multiple); + + return stride; +} + +static u32 hfi_yuv420_tp10_calc_uv_bufheight(u32 frame_height, u32 min_buf_height_multiple) +{ + return ALIGN(((frame_height + 1) >> 1), min_buf_height_multiple); +} + +static u32 size_av1d_ibc_tp10_ubwc(u32 frame_width, u32 frame_height) +{ + u32 size; + u32 y_width_a = 256, y_height_a = 16, + uv_width_a = 256, uv_height_a = 16; + u32 ybufsize, uvbufsize, y_width, y_height, uv_width, uv_height; + u32 y_meta_width_a = 64, y_meta_height_a = 16, + uv_meta_width_a = 64, uv_meta_height_a = 16; + u32 meta_height, meta_stride, meta_size; + u32 tile_width_y = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_WIDTH; + u32 tile_height_y = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_Y_TILE_HEIGHT; + u32 tile_width_uv = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_WIDTH; + u32 tile_height_uv = HFI_COLOR_FORMAT_YUV420_TP10_UBWC_UV_TILE_HEIGHT; + + y_width = hfi_yuv420_tp10_calc_y_stride(frame_width, y_width_a); + y_height = hfi_yuv420_tp10_calc_y_bufheight(frame_height, y_height_a); + uv_width = hfi_yuv420_tp10_calc_uv_stride(frame_width, uv_width_a); + uv_height = hfi_yuv420_tp10_calc_uv_bufheight(frame_height, uv_height_a); + ybufsize = ALIGN(y_width * y_height, HFI_ALIGNMENT_4096); + uvbufsize = ALIGN(uv_width * uv_height, HFI_ALIGNMENT_4096); + size = ybufsize + uvbufsize; + meta_stride = ALIGN(((frame_width + (tile_width_y - 1)) / tile_width_y), + y_meta_width_a); + meta_height = ALIGN(((frame_height + (tile_height_y - 1)) / tile_height_y), + y_meta_height_a); + meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096); + size += meta_size; + meta_stride = ALIGN(((((frame_width + 1) >> 1) + (tile_width_uv - 1)) / + tile_width_uv), uv_meta_width_a); + meta_height = ALIGN(((((frame_height + 1) >> 1) + (tile_height_uv - 1)) / + tile_height_uv), uv_meta_height_a); + meta_size = ALIGN(meta_stride * meta_height, HFI_ALIGNMENT_4096); + size += meta_size; + + return size; +} + +static u32 hfi_buffer_ibc_av1d(u32 frame_width, u32 frame_height) +{ + u32 size, ibc8, ibc10; + + ibc8 = size_av1d_ibc_nv12_ubwc(frame_width, frame_height); + ibc10 = size_av1d_ibc_tp10_ubwc(frame_width, frame_height); + size = ibc8 >= ibc10 ? ibc8 : ibc10; + + return ALIGN(size, DMA_ALIGNMENT); +} + static u32 iris_vpu_dec_bin_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; @@ -472,6 +718,8 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst) return hfi_buffer_bin_h265d(width, height, num_vpp_pipes); else if (inst->codec == V4L2_PIX_FMT_VP9) return hfi_buffer_bin_vp9d(width, height, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_AV1) + return hfi_buffer_bin_av1d(width, height, num_vpp_pipes); return 0; } @@ -487,18 +735,34 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst) return hfi_buffer_comv_h264d(width, height, num_comv); else if (inst->codec == V4L2_PIX_FMT_HEVC) return hfi_buffer_comv_h265d(width, height, num_comv); + else if (inst->codec == V4L2_PIX_FMT_AV1) { + if (inst->fw_caps[DRAP].value) + return 0; + else + return hfi_buffer_comv_av1d(width, height, num_comv); + } return 0; } static u32 iris_vpu_dec_persist_size(struct iris_inst *inst) { + struct platform_inst_caps *caps; + if (inst->codec == V4L2_PIX_FMT_H264) return hfi_buffer_persist_h264d(); else if (inst->codec == V4L2_PIX_FMT_HEVC) return hfi_buffer_persist_h265d(0); else if (inst->codec == V4L2_PIX_FMT_VP9) return hfi_buffer_persist_vp9d(); + else if (inst->codec == V4L2_PIX_FMT_AV1) { + caps = inst->core->iris_platform_data->inst_caps; + if (inst->fw_caps[DRAP].value) + return hfi_buffer_persist_av1d(caps->max_frame_width, + caps->max_frame_height, 16); + else + return hfi_buffer_persist_av1d(0, 0, 0); + } return 0; } @@ -545,6 +809,8 @@ static u32 iris_vpu_dec_line_size(struct iris_inst *inst) else if (inst->codec == V4L2_PIX_FMT_VP9) return hfi_buffer_line_vp9d(width, height, out_min_count, is_opb, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_AV1) + return hfi_buffer_line_av1d(width, height, is_opb, num_vpp_pipes); return 0; } @@ -556,6 +822,22 @@ static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst) iris_vpu_dec_line_size(inst); } +static inline u32 iris_vpu_enc_get_bitstream_width(struct iris_inst *inst) +{ + if (is_rotation_90_or_270(inst)) + return inst->fmt_dst->fmt.pix_mp.height; + else + return inst->fmt_dst->fmt.pix_mp.width; +} + +static inline u32 iris_vpu_enc_get_bitstream_height(struct iris_inst *inst) +{ + if (is_rotation_90_or_270(inst)) + return inst->fmt_dst->fmt.pix_mp.width; + else + return inst->fmt_dst->fmt.pix_mp.height; +} + static inline u32 size_bin_bitstream_enc(u32 width, u32 height, u32 rc_type) { @@ -638,10 +920,9 @@ static inline u32 hfi_buffer_bin_enc(u32 width, u32 height, static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; + u32 height = iris_vpu_enc_get_bitstream_height(inst); + u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 stage = inst->fw_caps[STAGE].value; - struct v4l2_format *f = inst->fmt_dst; - u32 height = f->fmt.pix_mp.height; - u32 width = f->fmt.pix_mp.width; u32 lcu_size; if (inst->codec == V4L2_PIX_FMT_HEVC) @@ -653,6 +934,15 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst) num_vpp_pipes, inst->hfi_rc_type); } +static u32 iris_vpu_dec_partial_size(struct iris_inst *inst) +{ + struct v4l2_format *f = inst->fmt_src; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + + return hfi_buffer_ibc_av1d(width, height); +} + static inline u32 hfi_buffer_comv_enc(u32 frame_width, u32 frame_height, u32 lcu_size, u32 num_recon, u32 standard) @@ -676,9 +966,8 @@ u32 hfi_buffer_comv_enc(u32 frame_width, u32 frame_height, u32 lcu_size, static u32 iris_vpu_enc_comv_size(struct iris_inst *inst) { - struct v4l2_format *f = inst->fmt_dst; - u32 height = f->fmt.pix_mp.height; - u32 width = f->fmt.pix_mp.width; + u32 height = iris_vpu_enc_get_bitstream_height(inst); + u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 num_recon = 1; u32 lcu_size = 16; @@ -958,9 +1247,8 @@ u32 hfi_buffer_non_comv_enc(u32 frame_width, u32 frame_height, static u32 iris_vpu_enc_non_comv_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; - struct v4l2_format *f = inst->fmt_dst; - u32 height = f->fmt.pix_mp.height; - u32 width = f->fmt.pix_mp.width; + u32 height = iris_vpu_enc_get_bitstream_height(inst); + u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 lcu_size = 16; if (inst->codec == V4L2_PIX_FMT_HEVC) { @@ -1051,9 +1339,8 @@ u32 hfi_buffer_line_enc_vpu33(u32 frame_width, u32 frame_height, bool is_ten_bit static u32 iris_vpu_enc_line_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; - struct v4l2_format *f = inst->fmt_dst; - u32 height = f->fmt.pix_mp.height; - u32 width = f->fmt.pix_mp.width; + u32 height = iris_vpu_enc_get_bitstream_height(inst); + u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 lcu_size = 16; if (inst->codec == V4L2_PIX_FMT_HEVC) { @@ -1069,9 +1356,8 @@ static u32 iris_vpu_enc_line_size(struct iris_inst *inst) static u32 iris_vpu33_enc_line_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; - struct v4l2_format *f = inst->fmt_dst; - u32 height = f->fmt.pix_mp.height; - u32 width = f->fmt.pix_mp.width; + u32 height = iris_vpu_enc_get_bitstream_height(inst); + u32 width = iris_vpu_enc_get_bitstream_width(inst); u32 lcu_size = 16; if (inst->codec == V4L2_PIX_FMT_HEVC) { @@ -1131,10 +1417,11 @@ static u32 iris_vpu_enc_arp_size(struct iris_inst *inst) inline bool is_scaling_enabled(struct iris_inst *inst) { - return inst->crop.left != inst->compose.left || - inst->crop.top != inst->compose.top || - inst->crop.width != inst->compose.width || - inst->crop.height != inst->compose.height; + struct v4l2_pix_format_mplane *dst_fmt = &inst->fmt_dst->fmt.pix_mp; + struct v4l2_pix_format_mplane *src_fmt = &inst->fmt_src->fmt.pix_mp; + + return dst_fmt->width != src_fmt->width || + dst_fmt->height != src_fmt->height; } static inline @@ -1291,9 +1578,8 @@ static inline u32 hfi_buffer_scratch1_enc(u32 frame_width, u32 frame_height, static u32 iris_vpu_enc_scratch1_size(struct iris_inst *inst) { u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; - struct v4l2_format *f = inst->fmt_dst; - u32 frame_height = f->fmt.pix_mp.height; - u32 frame_width = f->fmt.pix_mp.width; + u32 frame_height = iris_vpu_enc_get_bitstream_height(inst); + u32 frame_width = iris_vpu_enc_get_bitstream_width(inst); u32 num_ref = 1; u32 lcu_size; bool is_h265; @@ -1389,9 +1675,8 @@ static inline u32 hfi_buffer_scratch2_enc(u32 frame_width, u32 frame_height, static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst) { - struct v4l2_format *f = inst->fmt_dst; - u32 frame_width = f->fmt.pix_mp.width; - u32 frame_height = f->fmt.pix_mp.height; + u32 frame_height = iris_vpu_enc_get_bitstream_height(inst); + u32 frame_width = iris_vpu_enc_get_bitstream_width(inst); u32 num_ref = 1; return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, @@ -1408,13 +1693,313 @@ static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst) return hfi_buffer_vpss_enc(width, height, ds_enable, 0, 0); } +static inline u32 size_dpb_opb(u32 height, u32 lcu_size) +{ + u32 max_tile_height = ((height + lcu_size - 1) / lcu_size) * lcu_size + 8; + u32 dpb_opb = 3 * ((max_tile_height >> 3) * DMA_ALIGNMENT); + u32 num_luma_chrome_plane = 2; + + return ALIGN(dpb_opb, DMA_ALIGNMENT) * num_luma_chrome_plane; +} + +static u32 hfi_vpu4x_vp9d_lb_size(u32 frame_width, u32 frame_height, u32 num_vpp_pipes) +{ + u32 vp9_top_lb, vp9_fe_left_lb, vp9_se_left_lb, dpb_opb, vp9d_qp, num_lcu_per_pipe; + u32 lcu_size = 64; + + vp9_top_lb = ALIGN(size_vp9d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT); + vp9_top_lb += ALIGN(size_vpxd_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT); + vp9_top_lb += max3(DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_16_BYTES) * + MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE, + DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_32_BYTES) * + MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE, + DIV_ROUND_UP(frame_width, BUFFER_ALIGNMENT_64_BYTES) * + MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE); + vp9_top_lb = ALIGN(vp9_top_lb, DMA_ALIGNMENT); + vp9_top_lb += ALIGN((DMA_ALIGNMENT * DIV_ROUND_UP(frame_width, lcu_size)), + DMA_ALIGNMENT) * FE_TOP_CTRL_LINE_NUMBERS; + vp9_top_lb += ALIGN(DMA_ALIGNMENT * 8 * DIV_ROUND_UP(frame_width, lcu_size), + DMA_ALIGNMENT) * (FE_TOP_DATA_LUMA_LINE_NUMBERS + + FE_TOP_DATA_CHROMA_LINE_NUMBERS); + + num_lcu_per_pipe = (DIV_ROUND_UP(frame_height, lcu_size) / num_vpp_pipes) + + (DIV_ROUND_UP(frame_height, lcu_size) % num_vpp_pipes); + vp9_fe_left_lb = ALIGN((DMA_ALIGNMENT * num_lcu_per_pipe), DMA_ALIGNMENT) * + FE_LFT_CTRL_LINE_NUMBERS; + vp9_fe_left_lb += ((ALIGN((DMA_ALIGNMENT * 8 * num_lcu_per_pipe), DMA_ALIGNMENT) * + FE_LFT_DB_DATA_LINE_NUMBERS) + + ALIGN((DMA_ALIGNMENT * 3 * num_lcu_per_pipe), DMA_ALIGNMENT) + + ALIGN((DMA_ALIGNMENT * 4 * num_lcu_per_pipe), DMA_ALIGNMENT) + + (ALIGN((DMA_ALIGNMENT * 24 * num_lcu_per_pipe), DMA_ALIGNMENT) * + FE_LFT_LR_DATA_LINE_NUMBERS)); + vp9_fe_left_lb = vp9_fe_left_lb * num_vpp_pipes; + + vp9_se_left_lb = ALIGN(size_vpxd_lb_se_left_ctrl(frame_width, frame_height), + DMA_ALIGNMENT); + dpb_opb = size_dpb_opb(frame_height, lcu_size); + vp9d_qp = ALIGN(size_vp9d_qp(frame_width, frame_height), DMA_ALIGNMENT); + + return vp9_top_lb + vp9_fe_left_lb + (vp9_se_left_lb * num_vpp_pipes) + + (dpb_opb * num_vpp_pipes) + vp9d_qp; +} + +static u32 hfi_vpu4x_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min, + bool is_opb, u32 num_vpp_pipes) +{ + u32 lb_size = hfi_vpu4x_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes); + u32 dpb_obp_size = 0, lcu_size = 64; + + if (is_opb) + dpb_obp_size = size_dpb_opb(frame_height, lcu_size) * num_vpp_pipes; + + return lb_size + dpb_obp_size; +} + +static u32 iris_vpu4x_dec_line_size(struct iris_inst *inst) +{ + u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; + u32 out_min_count = inst->buffers[BUF_OUTPUT].min_count; + struct v4l2_format *f = inst->fmt_src; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + bool is_opb = false; + + if (iris_split_mode_enabled(inst)) + is_opb = true; + + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_buffer_line_h265d(width, height, is_opb, num_vpp_pipes); + else if (inst->codec == V4L2_PIX_FMT_VP9) + return hfi_vpu4x_buffer_line_vp9d(width, height, out_min_count, is_opb, + num_vpp_pipes); + + return 0; +} + +static u32 hfi_vpu4x_buffer_persist_h265d(u32 rpu_enabled) +{ + return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 + H265_NUM_FRM_INFO * + H265_DISPLAY_BUF_SIZE + (H265_NUM_TILE * sizeof(u32)) + (NUM_HW_PIC_BUF * + (SIZE_SEI_USERDATA + SIZE_H265D_ARP + SIZE_THREE_DIMENSION_USERDATA)) + + rpu_enabled * NUM_HW_PIC_BUF * SIZE_DOLBY_RPU_METADATA), DMA_ALIGNMENT); +} + +static u32 hfi_vpu4x_buffer_persist_vp9d(void) +{ + return ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, DMA_ALIGNMENT) + + (ALIGN(hfi_iris3_vp9d_comv_size(), DMA_ALIGNMENT) * 2) + + ALIGN(MAX_SUPERFRAME_HEADER_LEN, DMA_ALIGNMENT) + + ALIGN(VP9_UDC_HEADER_BUF_SIZE, DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, DMA_ALIGNMENT) + + ALIGN(VP9_NUM_FRAME_INFO_BUF * VP9_FRAME_INFO_BUF_SIZE_VPU4X, DMA_ALIGNMENT) + + HDR10_HIST_EXTRADATA_SIZE; +} + +static u32 iris_vpu4x_dec_persist_size(struct iris_inst *inst) +{ + if (inst->codec == V4L2_PIX_FMT_H264) + return hfi_buffer_persist_h264d(); + else if (inst->codec == V4L2_PIX_FMT_HEVC) + return hfi_vpu4x_buffer_persist_h265d(0); + else if (inst->codec == V4L2_PIX_FMT_VP9) + return hfi_vpu4x_buffer_persist_vp9d(); + + return 0; +} + +static u32 size_se_lb(u32 standard, u32 num_vpp_pipes_enc, + u32 frame_width_coded, u32 frame_height_coded) +{ + u32 se_tlb_size = ALIGN(frame_width_coded, DMA_ALIGNMENT); + u32 se_llb_size = (standard == HFI_CODEC_ENCODE_HEVC) ? + ((frame_height_coded + BUFFER_ALIGNMENT_32_BYTES - 1) / + BUFFER_ALIGNMENT_32_BYTES) * LOG2_16 * LLB_UNIT_SIZE : + ((frame_height_coded + BUFFER_ALIGNMENT_16_BYTES - 1) / + BUFFER_ALIGNMENT_16_BYTES) * LOG2_32 * LLB_UNIT_SIZE; + + se_llb_size = ALIGN(se_llb_size, BUFFER_ALIGNMENT_32_BYTES); + + if (num_vpp_pipes_enc > 1) + se_llb_size = ALIGN(se_llb_size + BUFFER_ALIGNMENT_512_BYTES, + DMA_ALIGNMENT) * num_vpp_pipes_enc; + + return ALIGN(se_tlb_size + se_llb_size, DMA_ALIGNMENT); +} + +static u32 size_te_lb(bool is_ten_bit, u32 num_vpp_pipes_enc, u32 width_in_lcus, + u32 frame_height_coded, u32 frame_width_coded) +{ + u32 num_pixel_10_bit = 3, num_pixel_8_bit = 2, num_pixel_te_llb = 3; + u32 te_llb_col_rc_size = ALIGN(32 * width_in_lcus / num_vpp_pipes_enc, + DMA_ALIGNMENT) * num_vpp_pipes_enc; + u32 te_tlb_recon_data_size = ALIGN((is_ten_bit ? num_pixel_10_bit : num_pixel_8_bit) * + frame_width_coded, DMA_ALIGNMENT); + u32 te_llb_recon_data_size = ((1 + is_ten_bit) * num_pixel_te_llb * frame_height_coded + + num_vpp_pipes_enc - 1) / num_vpp_pipes_enc; + te_llb_recon_data_size = ALIGN(te_llb_recon_data_size, DMA_ALIGNMENT) * num_vpp_pipes_enc; + + return ALIGN(te_llb_recon_data_size + te_llb_col_rc_size + te_tlb_recon_data_size, + DMA_ALIGNMENT); +} + +static inline u32 calc_fe_tlb_size(u32 size_per_lcu, bool is_ten_bit) +{ + u32 num_pixels_fe_tlb_10_bit = 128, num_pixels_fe_tlb_8_bit = 64; + + return is_ten_bit ? (num_pixels_fe_tlb_10_bit * (size_per_lcu + 1)) : + (size_per_lcu * num_pixels_fe_tlb_8_bit); +} + +static u32 size_fe_lb(bool is_ten_bit, u32 standard, u32 num_vpp_pipes_enc, + u32 frame_height_coded, u32 frame_width_coded) +{ + u32 log2_lcu_size, num_cu_in_height_pipe, num_cu_in_width, + fb_llb_db_ctrl_size, fb_llb_db_luma_size, fb_llb_db_chroma_size, + fb_tlb_db_ctrl_size, fb_tlb_db_luma_size, fb_tlb_db_chroma_size, + fb_llb_sao_ctrl_size, fb_llb_sao_luma_size, fb_llb_sao_chroma_size, + fb_tlb_sao_ctrl_size, fb_tlb_sao_luma_size, fb_tlb_sao_chroma_size, + fb_lb_top_sdc_size, fb_lb_se_ctrl_size, fe_tlb_size, size_per_lcu; + + log2_lcu_size = (standard == HFI_CODEC_ENCODE_HEVC) ? 5 : 4; + num_cu_in_height_pipe = ((frame_height_coded >> log2_lcu_size) + num_vpp_pipes_enc - 1) / + num_vpp_pipes_enc; + num_cu_in_width = frame_width_coded >> log2_lcu_size; + + size_per_lcu = 2; + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, 1); + fb_llb_db_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe; + fb_llb_db_ctrl_size = ALIGN(fb_llb_db_ctrl_size, DMA_ALIGNMENT) * num_vpp_pipes_enc; + + size_per_lcu = (1 << (log2_lcu_size - 3)); + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit); + fb_llb_db_luma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe; + fb_llb_db_luma_size = ALIGN(fb_llb_db_luma_size, DMA_ALIGNMENT) * num_vpp_pipes_enc; + + size_per_lcu = ((1 << (log2_lcu_size - 4)) * 2); + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit); + fb_llb_db_chroma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe; + fb_llb_db_chroma_size = ALIGN(fb_llb_db_chroma_size, DMA_ALIGNMENT) * num_vpp_pipes_enc; + + size_per_lcu = 1; + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, 1); + fb_tlb_db_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width; + fb_llb_sao_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_height_pipe; + fb_llb_sao_ctrl_size = fb_llb_sao_ctrl_size * num_vpp_pipes_enc; + fb_tlb_sao_ctrl_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width; + + size_per_lcu = ((1 << (log2_lcu_size - 3)) + 1); + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit); + fb_tlb_db_luma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width; + + size_per_lcu = (2 * ((1 << (log2_lcu_size - 4)) + 1)); + fe_tlb_size = calc_fe_tlb_size(size_per_lcu, is_ten_bit); + fb_tlb_db_chroma_size = ALIGN(fe_tlb_size, DMA_ALIGNMENT) * num_cu_in_width; + + fb_llb_sao_luma_size = BUFFER_ALIGNMENT_256_BYTES * num_vpp_pipes_enc; + fb_llb_sao_chroma_size = BUFFER_ALIGNMENT_256_BYTES * num_vpp_pipes_enc; + fb_tlb_sao_luma_size = BUFFER_ALIGNMENT_256_BYTES; + fb_tlb_sao_chroma_size = BUFFER_ALIGNMENT_256_BYTES; + fb_lb_top_sdc_size = ALIGN((FE_SDC_DATA_PER_BLOCK * (frame_width_coded >> 5)), + DMA_ALIGNMENT); + fb_lb_se_ctrl_size = ALIGN((SE_CTRL_DATA_PER_BLOCK * (frame_width_coded >> 5)), + DMA_ALIGNMENT); + + return fb_llb_db_ctrl_size + fb_llb_db_luma_size + fb_llb_db_chroma_size + + fb_tlb_db_ctrl_size + fb_tlb_db_luma_size + fb_tlb_db_chroma_size + + fb_llb_sao_ctrl_size + fb_llb_sao_luma_size + fb_llb_sao_chroma_size + + fb_tlb_sao_ctrl_size + fb_tlb_sao_luma_size + fb_tlb_sao_chroma_size + + fb_lb_top_sdc_size + fb_lb_se_ctrl_size; +} + +static u32 size_md_lb(u32 standard, u32 frame_width_coded, + u32 frame_height_coded, u32 num_vpp_pipes_enc) +{ + u32 md_tlb_size = ALIGN(frame_width_coded, DMA_ALIGNMENT); + u32 md_llb_size = (standard == HFI_CODEC_ENCODE_HEVC) ? + ((frame_height_coded + BUFFER_ALIGNMENT_32_BYTES - 1) / + BUFFER_ALIGNMENT_32_BYTES) * LOG2_16 * LLB_UNIT_SIZE : + ((frame_height_coded + BUFFER_ALIGNMENT_16_BYTES - 1) / + BUFFER_ALIGNMENT_16_BYTES) * LOG2_32 * LLB_UNIT_SIZE; + + md_llb_size = ALIGN(md_llb_size, BUFFER_ALIGNMENT_32_BYTES); + + if (num_vpp_pipes_enc > 1) + md_llb_size = ALIGN(md_llb_size + BUFFER_ALIGNMENT_512_BYTES, + DMA_ALIGNMENT) * num_vpp_pipes_enc; + + md_llb_size = ALIGN(md_llb_size, DMA_ALIGNMENT); + + return ALIGN(md_tlb_size + md_llb_size, DMA_ALIGNMENT); +} + +static u32 size_dma_opb_lb(u32 num_vpp_pipes_enc, u32 frame_width_coded, + u32 frame_height_coded) +{ + u32 opb_packet_bytes = 128, opb_bpp = 128, opb_size_per_row = 6; + u32 dma_opb_wr_tlb_y_size = DIV_ROUND_UP(frame_width_coded, 16) * opb_packet_bytes; + u32 dma_opb_wr_tlb_uv_size = DIV_ROUND_UP(frame_width_coded, 16) * opb_packet_bytes; + u32 dma_opb_wr2_tlb_y_size = ALIGN((opb_bpp * opb_size_per_row * frame_height_coded / 8), + DMA_ALIGNMENT) * num_vpp_pipes_enc; + u32 dma_opb_wr2_tlb_uv_size = ALIGN((opb_bpp * opb_size_per_row * frame_height_coded / 8), + DMA_ALIGNMENT) * num_vpp_pipes_enc; + + dma_opb_wr2_tlb_y_size = max(dma_opb_wr2_tlb_y_size, dma_opb_wr_tlb_y_size << 1); + dma_opb_wr2_tlb_uv_size = max(dma_opb_wr2_tlb_uv_size, dma_opb_wr_tlb_uv_size << 1); + + return ALIGN(dma_opb_wr_tlb_y_size + dma_opb_wr_tlb_uv_size + dma_opb_wr2_tlb_y_size + + dma_opb_wr2_tlb_uv_size, DMA_ALIGNMENT); +} + +static u32 hfi_vpu4x_buffer_line_enc(u32 frame_width, u32 frame_height, + bool is_ten_bit, u32 num_vpp_pipes_enc, + u32 lcu_size, u32 standard) +{ + u32 width_in_lcus = (frame_width + lcu_size - 1) / lcu_size; + u32 height_in_lcus = (frame_height + lcu_size - 1) / lcu_size; + u32 frame_width_coded = width_in_lcus * lcu_size; + u32 frame_height_coded = height_in_lcus * lcu_size; + + u32 se_lb_size = size_se_lb(standard, num_vpp_pipes_enc, frame_width_coded, + frame_height_coded); + u32 te_lb_size = size_te_lb(is_ten_bit, num_vpp_pipes_enc, width_in_lcus, + frame_height_coded, frame_width_coded); + u32 fe_lb_size = size_fe_lb(is_ten_bit, standard, num_vpp_pipes_enc, frame_height_coded, + frame_width_coded); + u32 md_lb_size = size_md_lb(standard, frame_width_coded, frame_height_coded, + num_vpp_pipes_enc); + u32 dma_opb_lb_size = size_dma_opb_lb(num_vpp_pipes_enc, frame_width_coded, + frame_height_coded); + u32 dse_lb_size = ALIGN((256 + (16 * (frame_width_coded >> 4))), DMA_ALIGNMENT); + u32 size_vpss_lb_enc = size_vpss_line_buf_vpu33(num_vpp_pipes_enc, frame_width_coded, + frame_height_coded); + + return se_lb_size + te_lb_size + fe_lb_size + md_lb_size + dma_opb_lb_size + + dse_lb_size + size_vpss_lb_enc; +} + +static u32 iris_vpu4x_enc_line_size(struct iris_inst *inst) +{ + u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe; + u32 lcu_size = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16; + struct v4l2_format *f = inst->fmt_dst; + u32 height = f->fmt.pix_mp.height; + u32 width = f->fmt.pix_mp.width; + + return hfi_vpu4x_buffer_line_enc(width, height, 0, num_vpp_pipes, + lcu_size, inst->codec); +} + static int output_min_count(struct iris_inst *inst) { int output_min_count = 4; /* fw_min_count > 0 indicates reconfig event has already arrived */ if (inst->fw_min_count) { - if (iris_split_mode_enabled(inst) && inst->codec == V4L2_PIX_FMT_VP9) + if (iris_split_mode_enabled(inst) && + (inst->codec == V4L2_PIX_FMT_VP9 || + inst->codec == V4L2_PIX_FMT_AV1)) return min_t(u32, 4, inst->fw_min_count); else return inst->fw_min_count; @@ -1422,6 +2007,8 @@ static int output_min_count(struct iris_inst *inst) if (inst->codec == V4L2_PIX_FMT_VP9) output_min_count = 9; + else if (inst->codec == V4L2_PIX_FMT_AV1) + output_min_count = 11; return output_min_count; } @@ -1444,6 +2031,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) {BUF_PERSIST, iris_vpu_dec_persist_size }, {BUF_DPB, iris_vpu_dec_dpb_size }, {BUF_SCRATCH_1, iris_vpu_dec_scratch1_size }, + {BUF_PARTIAL, iris_vpu_dec_partial_size }, }; static const struct iris_vpu_buf_type_handle enc_internal_buf_type_handle[] = { @@ -1503,6 +2091,50 @@ u32 iris_vpu33_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ return size; } +u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type) +{ + const struct iris_vpu_buf_type_handle *buf_type_handle_arr = NULL; + u32 size = 0, buf_type_handle_size = 0, i; + + static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = { + {BUF_BIN, iris_vpu_dec_bin_size }, + {BUF_COMV, iris_vpu_dec_comv_size }, + {BUF_NON_COMV, iris_vpu_dec_non_comv_size }, + {BUF_LINE, iris_vpu4x_dec_line_size }, + {BUF_PERSIST, iris_vpu4x_dec_persist_size }, + {BUF_DPB, iris_vpu_dec_dpb_size }, + {BUF_SCRATCH_1, iris_vpu_dec_scratch1_size }, + }; + + static const struct iris_vpu_buf_type_handle enc_internal_buf_type_handle[] = { + {BUF_BIN, iris_vpu_enc_bin_size }, + {BUF_COMV, iris_vpu_enc_comv_size }, + {BUF_NON_COMV, iris_vpu_enc_non_comv_size }, + {BUF_LINE, iris_vpu4x_enc_line_size }, + {BUF_ARP, iris_vpu_enc_arp_size }, + {BUF_VPSS, iris_vpu_enc_vpss_size }, + {BUF_SCRATCH_1, iris_vpu_enc_scratch1_size }, + {BUF_SCRATCH_2, iris_vpu_enc_scratch2_size }, + }; + + if (inst->domain == DECODER) { + buf_type_handle_size = ARRAY_SIZE(dec_internal_buf_type_handle); + buf_type_handle_arr = dec_internal_buf_type_handle; + } else if (inst->domain == ENCODER) { + buf_type_handle_size = ARRAY_SIZE(enc_internal_buf_type_handle); + buf_type_handle_arr = enc_internal_buf_type_handle; + } + + for (i = 0; i < buf_type_handle_size; i++) { + if (buf_type_handle_arr[i].type == buffer_type) { + size = buf_type_handle_arr[i].handle(inst); + break; + } + } + + return size; +} + static u32 internal_buffer_count(struct iris_inst *inst, enum iris_buffer_type buffer_type) { @@ -1510,14 +2142,20 @@ static u32 internal_buffer_count(struct iris_inst *inst, buffer_type == BUF_PERSIST) { return 1; } else if (buffer_type == BUF_COMV || buffer_type == BUF_NON_COMV) { - if (inst->codec == V4L2_PIX_FMT_H264 || inst->codec == V4L2_PIX_FMT_HEVC) + if (inst->codec == V4L2_PIX_FMT_H264 || + inst->codec == V4L2_PIX_FMT_HEVC || + inst->codec == V4L2_PIX_FMT_AV1) return 1; } + return 0; } static inline int iris_vpu_dpb_count(struct iris_inst *inst) { + if (inst->codec == V4L2_PIX_FMT_AV1) + return 11; + if (iris_split_mode_enabled(inst)) { return inst->fw_min_count ? inst->fw_min_count : inst->buffers[BUF_OUTPUT].min_count; @@ -1536,9 +2174,13 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type return MIN_BUFFERS; else return output_min_count(inst); + case BUF_NON_COMV: + if (inst->codec == V4L2_PIX_FMT_AV1) + return 0; + else + return 1; case BUF_BIN: case BUF_COMV: - case BUF_NON_COMV: case BUF_LINE: case BUF_PERSIST: return internal_buffer_count(inst, buffer_type); @@ -1546,6 +2188,7 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type case BUF_SCRATCH_2: case BUF_VPSS: case BUF_ARP: + case BUF_PARTIAL: return 1; /* internal buffer count needed by firmware is 1 */ case BUF_DPB: return iris_vpu_dpb_count(inst); diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h index 04f0b7400a1e..12640eb5ed8c 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h @@ -11,6 +11,7 @@ struct iris_inst; #define MIN_BUFFERS 4 #define DMA_ALIGNMENT 256 +#define HFI_ALIGNMENT_4096 4096 #define NUM_HW_PIC_BUF 32 #define LCU_MAX_SIZE_PELS 64 @@ -47,7 +48,12 @@ struct iris_inst; #define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4) #define VP9_PROB_TABLE_SIZE (3840) #define VP9_FRAME_INFO_BUF_SIZE (6144) +#define VP9_FRAME_INFO_BUF_SIZE_VPU4X (6400) +#define BUFFER_ALIGNMENT_16_BYTES 16 #define BUFFER_ALIGNMENT_32_BYTES 32 +#define BUFFER_ALIGNMENT_64_BYTES 64 +#define BUFFER_ALIGNMENT_256_BYTES 256 +#define BUFFER_ALIGNMENT_512_BYTES 512 #define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES) #define MAX_SUPERFRAME_HEADER_LEN (34) #define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 @@ -66,6 +72,8 @@ struct iris_inst; #define H265_CABAC_HDR_RATIO_HD_TOT 2 #define H265_CABAC_RES_RATIO_HD_TOT 2 #define SIZE_H265D_VPP_CMD_PER_BUF (256) +#define SIZE_THREE_DIMENSION_USERDATA 768 +#define SIZE_H265D_ARP 9728 #define VPX_DECODER_FRAME_CONCURENCY_LVL (2) #define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1 @@ -76,11 +84,39 @@ struct iris_inst; #define SIZE_H264D_HW_PIC_T (BIT(11)) +#define FE_LFT_CTRL_LINE_NUMBERS 4 +#define FE_LFT_DB_DATA_LINE_NUMBERS 2 +#define FE_LFT_LR_DATA_LINE_NUMBERS 4 +#define FE_TOP_CTRL_LINE_NUMBERS 3 +#define FE_TOP_DATA_LUMA_LINE_NUMBERS 2 +#define FE_TOP_DATA_CHROMA_LINE_NUMBERS 3 +#define FE_SDC_DATA_PER_BLOCK 16 +#define SE_CTRL_DATA_PER_BLOCK 2020 + +#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96 +#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192 + #define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64 #define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 16 #define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE 384 #define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640 +#define AV1_CABAC_HDR_RATIO_HD_TOT 2 +#define AV1_CABAC_RES_RATIO_HD_TOT 2 +#define AV1D_LCU_MAX_SIZE_PELS 128 +#define AV1D_LCU_MIN_SIZE_PELS 64 +#define AV1D_MAX_TILE_COLS 64 +#define MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE 192 +#define MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE 96 +#define AV1D_NUM_HW_PIC_BUF 16 +#define AV1D_NUM_FRAME_HEADERS 16 +#define SIZE_AV1D_SEQUENCE_HEADER 768 +#define SIZE_AV1D_METADATA 512 +#define SIZE_AV1D_FRAME_HEADER 1280 +#define SIZE_AV1D_TILE_OFFSET 65536 +#define SIZE_AV1D_QM 3328 +#define SIZE_AV1D_PROB_TABLE 22784 + #define SIZE_SLICE_CMD_BUFFER (ALIGN(20480, 256)) #define SIZE_SPS_PPS_SLICE_HDR (2048 + 4096) #define SIZE_BSE_SLICE_CMD_BUF ((((8192 << 2) + 7) & (~7)) * 3) @@ -96,11 +132,24 @@ struct iris_inst; #define HFI_BUFFER_ARP_ENC 204800 +#define LOG2_16 4 +#define LOG2_32 5 +#define LLB_UNIT_SIZE 16 + #define MAX_WIDTH 4096 #define MAX_HEIGHT 2304 #define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16)) #define NUM_MBS_720P (((ALIGN(1280, 16)) >> 4) * ((ALIGN(736, 16)) >> 4)) +#define BITS_PER_PIX 16 +#define NUM_LINES_LUMA 10 +#define NUM_LINES_CHROMA 6 +#define AV1D_LCU_MAX_SIZE_PELS 128 +#define AV1D_LCU_MIN_SIZE_PELS 64 +#define AV1D_MAX_TILE_COLS 64 +#define BITS_PER_CTRL_PACK 128 +#define NUM_CTRL_PACK_LCU 10 + static inline u32 size_h264d_lb_fe_top_data(u32 frame_width) { return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN(frame_width, 16) * 3; @@ -146,8 +195,99 @@ static inline u32 size_h264d_qp(u32 frame_width, u32 frame_height) return DIV_ROUND_UP(frame_width, 64) * DIV_ROUND_UP(frame_height, 64) * 128; } +static inline u32 size_av1d_lb_fe_top_data(u32 frame_width, u32 frame_height) +{ + return (ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) * + ((BITS_PER_PIX * NUM_LINES_LUMA) >> 3) + + ALIGN(frame_width, AV1D_LCU_MAX_SIZE_PELS) / 2 * + ((BITS_PER_PIX * NUM_LINES_CHROMA) >> 3) * 2); +} + +static inline u32 size_av1d_lb_fe_left_data(u32 frame_width, u32 frame_height) +{ + return (32 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 16) + + 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 8) * 2 + + 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 16) + + 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 12) * 2 + + 24 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 16) + + 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 16) + + 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 2 + + ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS * 12) * 2); +} + +static inline u32 size_av1d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height) +{ + return (NUM_CTRL_PACK_LCU * ((frame_width + AV1D_LCU_MIN_SIZE_PELS - 1) / + AV1D_LCU_MIN_SIZE_PELS) * BITS_PER_CTRL_PACK / 8); +} + +static inline u32 size_av1d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height) +{ + return (16 * ((ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / 16) + + (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS)) + + 3 * 16 * (ALIGN(frame_height, AV1D_LCU_MAX_SIZE_PELS) / + AV1D_LCU_MIN_SIZE_PELS)); +} + +static inline u32 size_av1d_lb_se_top_ctrl(u32 frame_width, u32 frame_height) +{ + return (((frame_width + 7) / 8) * MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE); +} + +static inline u32 size_av1d_lb_se_left_ctrl(u32 frame_width, u32 frame_height) +{ + return (max(((frame_height + 15) / 16) * + MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE, + max(((frame_height + 31) / 32) * + MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE, + ((frame_height + 63) / 64) * + MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE))); +} + +static inline u32 size_av1d_lb_pe_top_data(u32 frame_width, u32 frame_height) +{ + return (max(((frame_width + 15) / 16) * + MAX_PE_NBR_DATA_LCU16_LINE_BUFFER_SIZE, + max(((frame_width + 31) / 32) * + MAX_PE_NBR_DATA_LCU32_LINE_BUFFER_SIZE, + ((frame_width + 63) / 64) * + MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE))); +} + +static inline u32 size_av1d_lb_vsp_top(u32 frame_width, u32 frame_height) +{ + return (max(((frame_width + 63) / 64) * 1280, + ((frame_width + 127) / 128) * MAX_HEIGHT)); +} + +static inline u32 size_av1d_lb_recon_dma_metadata_wr(u32 frame_width, + u32 frame_height) +{ + return ((ALIGN(frame_height, 8) / (4 / 2)) * 64); +} + +static inline u32 size_av1d_qp(u32 frame_width, u32 frame_height) +{ + return size_h264d_qp(frame_width, frame_height); +} + u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type); u32 iris_vpu33_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type); +u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type); int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type); #endif diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index 515dd55a3377..548e5f1727fd 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -8,16 +8,12 @@ #include <linux/reset.h> #include "iris_core.h" +#include "iris_instance.h" #include "iris_vpu_common.h" #include "iris_vpu_register_defines.h" -#define WRAPPER_TZ_BASE_OFFS 0x000C0000 -#define AON_BASE_OFFS 0x000E0000 - -#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS) - -#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C) -#define CLEAR_XTENSA2HOST_INTR BIT(0) +#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL (AON_BASE_OFFS + 0x2C) +#define AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS (AON_BASE_OFFS + 0x30) #define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48) #define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C) @@ -35,42 +31,6 @@ #define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64) #define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68) -#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148) -#define HOST2XTENSA_INTR_ENABLE BIT(0) - -#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168) -#define MSK_SIGNAL_FROM_TENSILICA BIT(0) -#define MSK_CORE_POWER_ON BIT(1) - -#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150) -#define CPU_IC_SOFTINT_H2A_SHFT 0x0 - -#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C) -#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3) -#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2) - -#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10) -#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3) -#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2) - -#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54) -#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58) -#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C) -#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60) - -#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10) -#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14) -#define CTL_AXI_CLK_HALT BIT(0) -#define CTL_CLK_HALT BIT(1) - -#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18) -#define RESET_HIGH BIT(0) - -#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS) -#define REQ_POWER_DOWN_PREP BIT(0) - -#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4) - static void iris_vpu_interrupt_init(struct iris_core *core) { u32 mask_val; @@ -270,7 +230,7 @@ void iris_vpu_power_off_hw(struct iris_core *core) void iris_vpu_power_off(struct iris_core *core) { - dev_pm_opp_set_rate(core->dev, 0); + iris_opp_set_rate(core->dev, 0); core->iris_platform_data->vpu_ops->power_off_hw(core); core->iris_platform_data->vpu_ops->power_off_controller(core); iris_unset_icc_bw(core); @@ -348,6 +308,144 @@ err_disable_power: return ret; } +int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core) +{ + u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size; + bool handshake_done, handshake_busy; + u32 count = 0, val = 0; + int ret; + + writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH); + + writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); + + ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS, + val, val & BIT(0), 200, 2000); + if (ret) + goto disable_power; + + writel(0, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL); + + /* Retry up to 1000 times as recommended by hardware documentation */ + do { + /* set MNoC to low power */ + writel(REQ_POWER_DOWN_PREP, core->reg_base + + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); + usleep_range(10, 20); + val = readl(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS); + + handshake_done = val & NOC_LPI_STATUS_DONE; + handshake_busy = val & (NOC_LPI_STATUS_DENY | NOC_LPI_STATUS_ACTIVE); + + if (handshake_done || !handshake_busy) + break; + + writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); + usleep_range(10, 20); + + } while (++count < 1000); + + if (!handshake_done && handshake_busy) + dev_err(core->dev, "LPI handshake timeout\n"); + + ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_STATUS, + val, val & BIT(0), 200, 2000); + if (ret) + goto disable_power; + + writel(0, core->reg_base + AON_WRAPPER_MVP_VIDEO_CTL_NOC_LPI_CONTROL); + + writel(0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL); + + readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS, + val, val == 0, 200, 2000); + +disable_power: + iris_disable_unprepare_clock(core, IRIS_CTRL_CLK); + iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); + iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); + + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); + + reset_control_bulk_reset(clk_rst_tbl_size, core->resets); + + return 0; +} + +int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core) +{ + int ret; + + ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); + if (ret) + return ret; + + ret = iris_prepare_enable_clock(core, IRIS_AXI1_CLK); + if (ret) + goto err_disable_power; + + ret = iris_prepare_enable_clock(core, IRIS_CTRL_FREERUN_CLK); + if (ret) + goto err_disable_axi1_clk; + + ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK); + if (ret) + goto err_disable_ctrl_free_clk; + + return 0; + +err_disable_ctrl_free_clk: + iris_disable_unprepare_clock(core, IRIS_CTRL_FREERUN_CLK); +err_disable_axi1_clk: + iris_disable_unprepare_clock(core, IRIS_AXI1_CLK); +err_disable_power: + iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]); + + return ret; +} + +void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core) +{ + writel(0x1, core->reg_base + WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0); +} + +u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size) +{ + struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps; + struct v4l2_format *inp_f = inst->fmt_src; + u32 height, width, mbs_per_second, mbpf; + u64 fw_cycles, fw_vpp_cycles; + u64 vsp_cycles, vpp_cycles; + u32 fps = DEFAULT_FPS; + + width = max(inp_f->fmt.pix_mp.width, inst->crop.width); + height = max(inp_f->fmt.pix_mp.height, inst->crop.height); + + mbpf = NUM_MBS_PER_FRAME(height, width); + mbs_per_second = mbpf * fps; + + fw_cycles = fps * caps->mb_cycles_fw; + fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp; + + vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value); + /* 21 / 20 is minimum overhead factor */ + vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles); + + /* 1.059 is multi-pipe overhead */ + if (inst->fw_caps[PIPE].value > 1) + vpp_cycles += div_u64(vpp_cycles * 59, 1000); + + vsp_cycles = fps * data_size * 8; + vsp_cycles = div_u64(vsp_cycles, 2); + /* VSP FW overhead 1.05 */ + vsp_cycles = div_u64(vsp_cycles * 21, 20); + + if (inst->fw_caps[STAGE].value == STAGE_1) + vsp_cycles = vsp_cycles * 3; + + return max3(vpp_cycles, vsp_cycles, fw_cycles); +} + int iris_vpu_power_on(struct iris_core *core) { u32 freq; @@ -368,7 +466,7 @@ int iris_vpu_power_on(struct iris_core *core) freq = core->power.clk_freq ? core->power.clk_freq : (u32)ULONG_MAX; - dev_pm_opp_set_rate(core->dev, freq); + iris_opp_set_rate(core->dev, freq); core->iris_platform_data->set_preset_registers(core); diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h index d636e287457a..f6dffc613b82 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h @@ -12,6 +12,7 @@ extern const struct vpu_ops iris_vpu2_ops; extern const struct vpu_ops iris_vpu3_ops; extern const struct vpu_ops iris_vpu33_ops; extern const struct vpu_ops iris_vpu35_ops; +extern const struct vpu_ops iris_vpu4x_ops; struct vpu_ops { void (*power_off_hw)(struct iris_core *core); @@ -33,5 +34,9 @@ int iris_vpu_power_on(struct iris_core *core); int iris_vpu_power_off_controller(struct iris_core *core); void iris_vpu_power_off_hw(struct iris_core *core); void iris_vpu_power_off(struct iris_core *core); +int iris_vpu35_vpu4x_power_off_controller(struct iris_core *core); +int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core); +void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core); +u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size); #endif diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h index fe8a39e5e5a3..72168b9ffa73 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h +++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h @@ -7,11 +7,72 @@ #define __IRIS_VPU_REGISTER_DEFINES_H__ #define VCODEC_BASE_OFFS 0x00000000 +#define AON_MVP_NOC_RESET 0x0001F000 #define CPU_BASE_OFFS 0x000A0000 #define WRAPPER_BASE_OFFS 0x000B0000 +#define WRAPPER_TZ_BASE_OFFS 0x000C0000 +#define AON_BASE_OFFS 0x000E0000 + +#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70) + +#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000) +#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1)) + +#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004) #define CPU_CS_BASE_OFFS (CPU_BASE_OFFS) +#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS) + +#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C) +#define CLEAR_XTENSA2HOST_INTR BIT(0) + +#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148) +#define HOST2XTENSA_INTR_ENABLE BIT(0) + +#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150) +#define CPU_IC_SOFTINT_H2A_SHFT 0x0 + +#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160) +#define CORE_BRIDGE_SW_RESET BIT(0) +#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1) + +#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168) +#define MSK_SIGNAL_FROM_TENSILICA BIT(0) +#define MSK_CORE_POWER_ON BIT(1) +#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C) +#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3) +#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2) + +#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10) +#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3) +#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2) + +#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54) +#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58) +#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C) +#define REQ_POWER_DOWN_PREP BIT(0) + +#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60) +#define NOC_LPI_STATUS_DONE BIT(0) /* Indicates the NOC handshake is complete */ +#define NOC_LPI_STATUS_DENY BIT(1) /* Indicates the NOC handshake is denied */ +#define NOC_LPI_STATUS_ACTIVE BIT(2) /* Indicates the NOC is active */ + +#define WRAPPER_IRIS_VCODEC_VPU_WRAPPER_SPARE_0 (WRAPPER_BASE_OFFS + 0x78) #define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80) +#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88) +#define CORE_CLK_RUN 0x0 + +#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10) + +#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14) +#define CTL_AXI_CLK_HALT BIT(0) +#define CTL_CLK_HALT BIT(1) + +#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18) +#define RESET_HIGH BIT(0) + +#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS) +#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4) #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 4a6641fdffcf..21ca4947a849 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -433,9 +433,13 @@ vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s) static int vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct venus_inst *inst = to_inst(file); + struct venus_core *core = inst->core; + strscpy(cap->driver, "qcom-venus", sizeof(cap->driver)); strscpy(cap->card, "Qualcomm Venus video decoder", sizeof(cap->card)); - strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "plat:%s:dec", dev_name(core->dev)); return 0; } @@ -565,7 +569,13 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) fdata.buffer_type = HFI_BUFFER_INPUT; fdata.flags |= HFI_BUFFERFLAG_EOS; - if (IS_V6(inst->core) && is_fw_rev_or_older(inst->core, 1, 0, 87)) + + /* Send NULL EOS addr for only IRIS2 (SM8250),for firmware <= 1.0.87. + * SC7280 also reports "1.0.<hash>" parsed as 1.0.0; restricting to IRIS2 + * avoids misapplying this quirk and breaking VP9 decode on SC7280. + */ + + if (IS_IRIS2(inst->core) && is_fw_rev_or_older(inst->core, 1, 0, 87)) fdata.device_addr = 0; else fdata.device_addr = 0xdeadb000; @@ -1440,10 +1450,10 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, inst->drain_active = false; inst->codec_state = VENUS_DEC_STATE_STOPPED; } + } else { + if (!bytesused) + state = VB2_BUF_STATE_ERROR; } - - if (!bytesused) - state = VB2_BUF_STATE_ERROR; } else { vbuf->sequence = inst->sequence_out++; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index b478b982a80d..0b5843ba536f 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -144,9 +144,13 @@ static int venc_v4l2_to_hfi(int id, int value) static int venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct venus_inst *inst = to_inst(file); + struct venus_core *core = inst->core; + strscpy(cap->driver, "qcom-venus", sizeof(cap->driver)); strscpy(cap->card, "Qualcomm Venus video encoder", sizeof(cap->card)); - strscpy(cap->bus_info, "platform:qcom-venus", sizeof(cap->bus_info)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "plat:%s:enc", dev_name(core->dev)); return 0; } diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c index 2c5b4d24b4e6..104908afbf41 100644 --- a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c +++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c @@ -492,7 +492,7 @@ static int csi2_set_routing(struct v4l2_subdev *sd, const struct v4l2_subdev_route *route = &routing->routes[i]; if (route->source_stream != 0) - return -EINVAL; + return -ENXIO; } ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 730bdf98565a..bb575873f2b2 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -80,6 +80,9 @@ static int rga_buf_init(struct vb2_buffer *vb) struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type); size_t n_desc = 0; + if (IS_ERR(f)) + return PTR_ERR(f); + n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE); rbuf->n_desc = n_desc; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c index 6dcefd144d5a..867cdddf9f89 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c @@ -1123,7 +1123,6 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) struct rkisp1_capture *cap = queue->drv_priv; struct rkisp1_vdev_node *node = &cap->vnode; struct rkisp1_device *rkisp1 = cap->rkisp1; - int ret; mutex_lock(&cap->rkisp1->stream_lock); @@ -1132,9 +1131,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR); v4l2_pipeline_pm_put(&node->vdev.entity); - ret = pm_runtime_put(rkisp1->dev); - if (ret < 0) - dev_err(rkisp1->dev, "power down failed error:%d\n", ret); + pm_runtime_put(rkisp1->dev); rkisp1_dummy_buf_destroy(cap); diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index c9f88635224c..6442436a5e42 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -411,12 +411,6 @@ static void rkisp1_flt_config(struct rkisp1_params *params, rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_LUM_WEIGHT, arg->lum_weight); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE, - (arg->mode ? RKISP1_CIF_ISP_FLT_MODE_DNR : 0) | - RKISP1_CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) | - RKISP1_CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) | - RKISP1_CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1)); - /* avoid to override the old enable value */ filt_mode = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_FILT_MODE); filt_mode &= RKISP1_CIF_ISP_FLT_ENA; diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile index a77122641d14..e629d571e4d8 100644 --- a/drivers/media/platform/rockchip/rkvdec/Makefile +++ b/drivers/media/platform/rockchip/rkvdec/Makefile @@ -1,3 +1,15 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o -rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-hevc.o rkvdec-vp9.o +rockchip-vdec-y += \ + rkvdec.o \ + rkvdec-cabac.o \ + rkvdec-h264.o \ + rkvdec-h264-common.o \ + rkvdec-hevc.o \ + rkvdec-hevc-common.o \ + rkvdec-rcb.o \ + rkvdec-vdpu381-h264.o \ + rkvdec-vdpu381-hevc.o \ + rkvdec-vdpu383-h264.o \ + rkvdec-vdpu383-hevc.o \ + rkvdec-vp9.o diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.c index eac4ea604949..068bc0cd5f43 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.c @@ -1,19 +1,515 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Rockchip Video Decoder driver + * Rockchip Video Decoder CABAC tables * * Copyright (C) 2023 Collabora, Ltd. * Sebastian Fricke <sebastian.fricke@collabora.com> + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> */ -#include <linux/types.h> +#include "rkvdec-cabac.h" -#define RKV_CABAC_TABLE_SIZE 27456 +#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \ + idc2_m, idc2_n, intra_m, intra_n) \ + [0][(ctxidx)] = {idc0_m, idc0_n}, \ + [1][(ctxidx)] = {idc1_m, idc1_n}, \ + [2][(ctxidx)] = {idc2_m, idc2_n}, \ + [3][(ctxidx)] = {intra_m, intra_n} /* - * This file is #include from rkvdec-hevc.c and not compiled. + * Constant CABAC table. + * Built from the tables described in section '9.3.1.1 Initialisation process + * for context variables' of the H264 spec. */ -static const u8 rkvdec_hevc_cabac_table[RKV_CABAC_TABLE_SIZE] = { +const s8 rkvdec_h264_cabac_table[4][464][2] = { + /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */ + CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15), + CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54), + CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74), + CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15), + CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54), + CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74), + CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127), + CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104), + CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53), + CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54), + CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51), + + /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */ + CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0), + CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0), + CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0), + CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0), + CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0), + CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0), + CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0), + CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0), + CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0), + CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0), + CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0), + CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0), + CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0), + + /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */ + CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0), + CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0), + CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0), + CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0), + CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0), + CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0), + CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0), + CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0), + CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0), + CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0), + CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0), + CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0), + CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0), + CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0), + CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0), + CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0), + + /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */ + CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0), + CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0), + CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0), + CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0), + CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0), + CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0), + CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0), + CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0), + CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0), + CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0), + CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0), + CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0), + CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0), + CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0), + + /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */ + CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0), + CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0), + CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0), + CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0), + CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0), + CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0), + + /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */ + CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41), + CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63), + CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83), + CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86), + CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97), + CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72), + CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41), + CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62), + + /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */ + CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11), + CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55), + CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69), + CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127), + CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102), + CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82), + CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74), + CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107), + CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127), + CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127), + CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127), + CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95), + CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127), + CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114), + CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127), + CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123), + CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115), + CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122), + CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115), + CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63), + CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68), + CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84), + CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104), + CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70), + CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93), + CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90), + CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127), + CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74), + CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97), + CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91), + CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127), + CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56), + CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82), + CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76), + CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125), + + /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */ + CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93), + CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87), + CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77), + CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71), + CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63), + CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68), + CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84), + CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62), + CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65), + CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61), + CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56), + CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66), + CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64), + CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61), + CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78), + CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50), + CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52), + CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35), + CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44), + CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38), + CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45), + CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46), + CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44), + CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17), + CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51), + CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50), + CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19), + CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33), + CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62), + CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108), + CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100), + CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101), + CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91), + CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94), + CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88), + CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84), + CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86), + CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83), + CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87), + CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94), + CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70), + CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72), + CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74), + CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59), + CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102), + CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100), + CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95), + CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75), + CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72), + CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75), + CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71), + CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46), + CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69), + CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62), + CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65), + CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37), + CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72), + CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57), + CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54), + CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62), + CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72), + + /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */ + CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0), + CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9), + CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25), + CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18), + CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9), + CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19), + CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37), + CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18), + CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29), + CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33), + CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30), + CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45), + CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58), + CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62), + CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61), + CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38), + CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45), + CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39), + CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42), + CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44), + CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45), + CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41), + CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49), + CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34), + CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42), + CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55), + CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51), + CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46), + CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89), + CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19), + CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17), + CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17), + CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25), + CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20), + CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23), + CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27), + CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23), + CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28), + CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17), + CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11), + CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15), + CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6), + CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1), + CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17), + CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6), + CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3), + CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22), + CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16), + CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4), + CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8), + CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3), + CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3), + CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5), + CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0), + CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16), + CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22), + CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48), + CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37), + CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60), + CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68), + CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97), + + /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */ + CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71), + CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42), + CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50), + CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54), + CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62), + CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58), + CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63), + CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72), + CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74), + CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91), + CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67), + CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27), + CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39), + CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44), + CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46), + CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64), + CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68), + CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78), + CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77), + CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86), + CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92), + CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55), + CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60), + CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62), + CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65), + CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73), + CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76), + CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80), + CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88), + CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110), + CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97), + CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84), + CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79), + CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73), + CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74), + CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86), + CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96), + CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97), + CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117), + CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78), + CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33), + CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48), + CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53), + CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62), + CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71), + CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79), + CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86), + CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90), + CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97), + + /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */ + CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93), + CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84), + CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79), + CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66), + CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71), + CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62), + CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60), + CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59), + CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75), + CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62), + CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58), + CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66), + CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79), + CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71), + CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68), + CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44), + CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62), + CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36), + CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40), + CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27), + CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29), + CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44), + CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36), + CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32), + CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42), + CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48), + CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62), + CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46), + CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64), + CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104), + CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97), + CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96), + CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88), + CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85), + CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85), + CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85), + CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88), + CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66), + CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77), + CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76), + CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76), + CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58), + CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76), + CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83), + CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99), + CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95), + CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95), + CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76), + CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74), + CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70), + CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75), + CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68), + CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65), + CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73), + CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62), + CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62), + CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68), + CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75), + CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55), + CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64), + CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70), + + /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */ + CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6), + CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19), + CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16), + CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14), + CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13), + CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11), + CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15), + CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16), + CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23), + CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23), + CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20), + CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26), + CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44), + CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40), + CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47), + CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17), + CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21), + CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22), + CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27), + CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29), + CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35), + CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50), + CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57), + CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63), + CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77), + CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82), + CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94), + CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69), + CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109), + CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35), + CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34), + CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26), + CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30), + CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32), + CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18), + CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15), + CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15), + CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7), + CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5), + CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0), + CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2), + CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13), + CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35), + CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58), + CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3), + CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0), + CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30), + CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7), + CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15), + CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3), + CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3), + CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1), + CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5), + CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11), + CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5), + CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12), + CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11), + CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29), + CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26), + CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39), + CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66), + + /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */ + CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21), + CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31), + CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50), + CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120), + CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112), + CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114), + CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85), + CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92), + CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89), + CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71), + CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81), + CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80), + CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68), + CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70), + CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56), + CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68), + CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50), + CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74), + CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13), + CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13), + CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15), + CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14), + CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3), + CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6), + CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34), + CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54), + CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82), + CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75), + CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23), + CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34), + CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43), + CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54), + CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55), + CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61), + CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64), + CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68), + CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92), + CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106), + CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97), + CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90), + CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90), + CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88), + CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73), + CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79), + CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86), + CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73), + CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70), + CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69), + CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66), + CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64), + CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58), + CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59), + CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10), + CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11), + CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8), + CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1), + CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3), + CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9), + CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20), + CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36), + CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67), +}; + +const u8 rkvdec_hevc_cabac_table[RKV_HEVC_CABAC_TABLE_SIZE] = { 0x07, 0x0f, 0x48, 0x58, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f, 0x68, 0x48, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x40, 0x40, 0x68, 0x58, 0x60, 0x40, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x60, diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h new file mode 100644 index 000000000000..3b721c854b9b --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-cabac.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder CABAC tables + * + * Copyright (C) 2023 Collabora, Ltd. + * Sebastian Fricke <sebastian.fricke@collabora.com> + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + */ + +#ifndef RKVDEC_CABAC_H_ +#define RKVDEC_CABAC_H_ + +#include <linux/types.h> + +#define RKV_HEVC_CABAC_TABLE_SIZE 27456 + +extern const s8 rkvdec_h264_cabac_table[4][464][2]; +extern const u8 rkvdec_hevc_cabac_table[RKV_HEVC_CABAC_TABLE_SIZE]; + +#endif /* RKVDEC_CABAC_H_ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c new file mode 100644 index 000000000000..e28f06394470 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip video decoder h264 common functions + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <linux/v4l2-common.h> +#include <media/v4l2-h264.h> +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-h264-common.h" + +#define RKVDEC_NUM_REFLIST 3 + +static void set_dpb_info(struct rkvdec_rps_entry *entries, + u8 reflist, + u8 refnum, + u8 info, + bool bottom) +{ + struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8]; + u8 idx = refnum % 8; + + switch (idx) { + case 0: + entry->dpb_info0 = info; + entry->bottom_flag0 = bottom; + break; + case 1: + entry->dpb_info1 = info; + entry->bottom_flag1 = bottom; + break; + case 2: + entry->dpb_info2 = info; + entry->bottom_flag2 = bottom; + break; + case 3: + entry->dpb_info3 = info; + entry->bottom_flag3 = bottom; + break; + case 4: + entry->dpb_info4 = info; + entry->bottom_flag4 = bottom; + break; + case 5: + entry->dpb_info5 = info; + entry->bottom_flag5 = bottom; + break; + case 6: + entry->dpb_info6 = info; + entry->bottom_flag6 = bottom; + break; + case 7: + entry->dpb_info7 = info; + entry->bottom_flag7 = bottom; + break; + } +} + +void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + u32 i; + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf = NULL; + + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { + buf = vb2_find_buffer(cap_q, dpb[i].reference_ts); + if (!buf) + pr_debug("No buffer for reference_ts %llu", + dpb[i].reference_ts); + } + + run->ref_buf[i] = buf; + } +} + +void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, + struct rkvdec_h264_run *run, + struct rkvdec_h264_reflists *reflists, + struct rkvdec_rps *hw_rps) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + + u32 i, j; + + memset(hw_rps, 0, sizeof(*hw_rps)); + + /* + * Assign an invalid pic_num if DPB entry at that position is inactive. + * If we assign 0 in that position hardware will treat that as a real + * reference picture with pic_num 0, triggering output picture + * corruption. + */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + hw_rps->frame_num[i] = builder->refs[i].frame_num; + } + + for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { + for (i = 0; i < builder->num_valid; i++) { + struct v4l2_h264_reference *ref; + bool dpb_valid; + bool bottom; + + switch (j) { + case 0: + ref = &reflists->p[i]; + break; + case 1: + ref = &reflists->b0[i]; + break; + case 2: + ref = &reflists->b1[i]; + break; + } + + if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb))) + continue; + + dpb_valid = !!(run->ref_buf[ref->index]); + bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; + + set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom); + } + } +} + +void assemble_hw_scaling_list(struct rkvdec_h264_run *run, + struct rkvdec_h264_scaling_list *scaling_list) +{ + const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + + if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) + return; + + BUILD_BUG_ON(sizeof(scaling_list->scaling_list_4x4) != + sizeof(scaling->scaling_list_4x4)); + BUILD_BUG_ON(sizeof(scaling_list->scaling_list_8x8) != + sizeof(scaling->scaling_list_8x8)); + + memcpy(scaling_list->scaling_list_4x4, + scaling->scaling_list_4x4, + sizeof(scaling->scaling_list_4x4)); + + memcpy(scaling_list->scaling_list_8x8, + scaling->scaling_list_8x8, + sizeof(scaling->scaling_list_8x8)); +} + +#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2 + +int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; + + fmt->num_planes = 1; + if (!fmt->plane_fmt[0].sizeimage) + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * + RKVDEC_H264_MAX_DEPTH_IN_BYTES; + return 0; +} + +enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; + + if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) + return RKVDEC_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_8BIT; + else + return RKVDEC_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_10BIT; + else + return RKVDEC_IMG_FMT_420_10BIT; + } + + return RKVDEC_IMG_FMT_ANY; +} + +int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_h264_sps *sps) +{ + unsigned int width, height; + + if (sps->chroma_format_idc > 2) + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit is supported */ + return -EINVAL; + + width = (sps->pic_width_in_mbs_minus1 + 1) * 16; + height = (sps->pic_height_in_map_units_minus1 + 1) * 16; + + /* + * When frame_mbs_only_flag is not set, this is field height, + * which is half the final height (see (7-18) in the + * specification) + */ + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + height *= 2; + + if (width > ctx->coded_fmt.fmt.pix_mp.width || + height > ctx->coded_fmt.fmt.pix_mp.height) + return -EINVAL; + + return 0; +} + +void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_DECODE_PARAMS); + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + run->sps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_PPS); + run->pps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + 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/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h new file mode 100644 index 000000000000..5336370507d6 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip video decoder h264 common functions + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <media/v4l2-h264.h> +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" + +struct rkvdec_h264_scaling_list { + u8 scaling_list_4x4[6][16]; + u8 scaling_list_8x8[6][64]; + u8 padding[128]; +}; + +struct rkvdec_h264_reflists { + struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; + struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; +}; + +struct rkvdec_h264_run { + struct rkvdec_run base; + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_sps *sps; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; +}; + +struct rkvdec_rps_entry { + u32 dpb_info0: 5; + u32 bottom_flag0: 1; + u32 view_index_off0: 1; + u32 dpb_info1: 5; + u32 bottom_flag1: 1; + u32 view_index_off1: 1; + u32 dpb_info2: 5; + u32 bottom_flag2: 1; + u32 view_index_off2: 1; + u32 dpb_info3: 5; + u32 bottom_flag3: 1; + u32 view_index_off3: 1; + u32 dpb_info4: 5; + u32 bottom_flag4: 1; + u32 view_index_off4: 1; + u32 dpb_info5: 5; + u32 bottom_flag5: 1; + u32 view_index_off5: 1; + u32 dpb_info6: 5; + u32 bottom_flag6: 1; + u32 view_index_off6: 1; + u32 dpb_info7: 5; + u32 bottom_flag7: 1; + u32 view_index_off7: 1; +} __packed; + +struct rkvdec_rps { + u16 frame_num[16]; + u32 reserved0; + struct rkvdec_rps_entry entries[12]; + u32 reserved1[66]; +} __packed; + +void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run); +void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, + struct rkvdec_h264_run *run, + struct rkvdec_h264_reflists *reflists, + struct rkvdec_rps *hw_rps); +void assemble_hw_scaling_list(struct rkvdec_h264_run *run, + struct rkvdec_h264_scaling_list *scaling_list); +int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f); +enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); +int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, const struct v4l2_ctrl_h264_sps *sps); +void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c index d14b4d173448..a1af12c97914 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c @@ -14,20 +14,13 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +#include "rkvdec-cabac.h" +#include "rkvdec-h264-common.h" /* Size with u32 units. */ #define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128) -#define RKV_RPS_SIZE ((128 + 128) / 4) #define RKV_ERROR_INFO_SIZE (256 * 144 * 4) -#define RKVDEC_NUM_REFLIST 3 - -struct rkvdec_h264_scaling_list { - u8 scaling_list_4x4[6][16]; - u8 scaling_list_8x8[6][64]; - u8 padding[128]; -}; - struct rkvdec_sps_pps_packet { u32 info[8]; }; @@ -83,537 +76,19 @@ struct rkvdec_ps_field { #define SCALING_LIST_ADDRESS PS_FIELD(184, 32) #define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1) -#define DPB_OFFS(i, j) (288 + ((j) * 32 * 7) + ((i) * 7)) -#define DPB_INFO(i, j) PS_FIELD(DPB_OFFS(i, j), 5) -#define BOTTOM_FLAG(i, j) PS_FIELD(DPB_OFFS(i, j) + 5, 1) -#define VIEW_INDEX_OFF(i, j) PS_FIELD(DPB_OFFS(i, j) + 6, 1) - /* Data structure describing auxiliary buffer format. */ struct rkvdec_h264_priv_tbl { s8 cabac_table[4][464][2]; struct rkvdec_h264_scaling_list scaling_list; - u32 rps[RKV_RPS_SIZE]; + struct rkvdec_rps rps; struct rkvdec_sps_pps_packet param_set[256]; u8 err_info[RKV_ERROR_INFO_SIZE]; }; -struct rkvdec_h264_reflists { - struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; -}; - -struct rkvdec_h264_run { - struct rkvdec_run base; - const struct v4l2_ctrl_h264_decode_params *decode_params; - const struct v4l2_ctrl_h264_sps *sps; - const struct v4l2_ctrl_h264_pps *pps; - const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; - struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES]; -}; - struct rkvdec_h264_ctx { struct rkvdec_aux_buf priv_tbl; struct rkvdec_h264_reflists reflists; -}; - -#define CABAC_ENTRY(ctxidx, idc0_m, idc0_n, idc1_m, idc1_n, \ - idc2_m, idc2_n, intra_m, intra_n) \ - [0][(ctxidx)] = {idc0_m, idc0_n}, \ - [1][(ctxidx)] = {idc1_m, idc1_n}, \ - [2][(ctxidx)] = {idc2_m, idc2_n}, \ - [3][(ctxidx)] = {intra_m, intra_n} - -/* - * Constant CABAC table. - * Built from the tables described in section '9.3.1.1 Initialisation process - * for context variables' of the H264 spec. - */ -static const s8 rkvdec_h264_cabac_table[4][464][2] = { - /* Table 9-12 – Values of variables m and n for ctxIdx from 0 to 10 */ - CABAC_ENTRY(0, 20, -15, 20, -15, 20, -15, 20, -15), - CABAC_ENTRY(1, 2, 54, 2, 54, 2, 54, 2, 54), - CABAC_ENTRY(2, 3, 74, 3, 74, 3, 74, 3, 74), - CABAC_ENTRY(3, 20, -15, 20, -15, 20, -15, 20, -15), - CABAC_ENTRY(4, 2, 54, 2, 54, 2, 54, 2, 54), - CABAC_ENTRY(5, 3, 74, 3, 74, 3, 74, 3, 74), - CABAC_ENTRY(6, -28, 127, -28, 127, -28, 127, -28, 127), - CABAC_ENTRY(7, -23, 104, -23, 104, -23, 104, -23, 104), - CABAC_ENTRY(8, -6, 53, -6, 53, -6, 53, -6, 53), - CABAC_ENTRY(9, -1, 54, -1, 54, -1, 54, -1, 54), - CABAC_ENTRY(10, 7, 51, 7, 51, 7, 51, 7, 51), - - /* Table 9-13 – Values of variables m and n for ctxIdx from 11 to 23 */ - CABAC_ENTRY(11, 23, 33, 22, 25, 29, 16, 0, 0), - CABAC_ENTRY(12, 23, 2, 34, 0, 25, 0, 0, 0), - CABAC_ENTRY(13, 21, 0, 16, 0, 14, 0, 0, 0), - CABAC_ENTRY(14, 1, 9, -2, 9, -10, 51, 0, 0), - CABAC_ENTRY(15, 0, 49, 4, 41, -3, 62, 0, 0), - CABAC_ENTRY(16, -37, 118, -29, 118, -27, 99, 0, 0), - CABAC_ENTRY(17, 5, 57, 2, 65, 26, 16, 0, 0), - CABAC_ENTRY(18, -13, 78, -6, 71, -4, 85, 0, 0), - CABAC_ENTRY(19, -11, 65, -13, 79, -24, 102, 0, 0), - CABAC_ENTRY(20, 1, 62, 5, 52, 5, 57, 0, 0), - CABAC_ENTRY(21, 12, 49, 9, 50, 6, 57, 0, 0), - CABAC_ENTRY(22, -4, 73, -3, 70, -17, 73, 0, 0), - CABAC_ENTRY(23, 17, 50, 10, 54, 14, 57, 0, 0), - - /* Table 9-14 – Values of variables m and n for ctxIdx from 24 to 39 */ - CABAC_ENTRY(24, 18, 64, 26, 34, 20, 40, 0, 0), - CABAC_ENTRY(25, 9, 43, 19, 22, 20, 10, 0, 0), - CABAC_ENTRY(26, 29, 0, 40, 0, 29, 0, 0, 0), - CABAC_ENTRY(27, 26, 67, 57, 2, 54, 0, 0, 0), - CABAC_ENTRY(28, 16, 90, 41, 36, 37, 42, 0, 0), - CABAC_ENTRY(29, 9, 104, 26, 69, 12, 97, 0, 0), - CABAC_ENTRY(30, -46, 127, -45, 127, -32, 127, 0, 0), - CABAC_ENTRY(31, -20, 104, -15, 101, -22, 117, 0, 0), - CABAC_ENTRY(32, 1, 67, -4, 76, -2, 74, 0, 0), - CABAC_ENTRY(33, -13, 78, -6, 71, -4, 85, 0, 0), - CABAC_ENTRY(34, -11, 65, -13, 79, -24, 102, 0, 0), - CABAC_ENTRY(35, 1, 62, 5, 52, 5, 57, 0, 0), - CABAC_ENTRY(36, -6, 86, 6, 69, -6, 93, 0, 0), - CABAC_ENTRY(37, -17, 95, -13, 90, -14, 88, 0, 0), - CABAC_ENTRY(38, -6, 61, 0, 52, -6, 44, 0, 0), - CABAC_ENTRY(39, 9, 45, 8, 43, 4, 55, 0, 0), - - /* Table 9-15 – Values of variables m and n for ctxIdx from 40 to 53 */ - CABAC_ENTRY(40, -3, 69, -2, 69, -11, 89, 0, 0), - CABAC_ENTRY(41, -6, 81, -5, 82, -15, 103, 0, 0), - CABAC_ENTRY(42, -11, 96, -10, 96, -21, 116, 0, 0), - CABAC_ENTRY(43, 6, 55, 2, 59, 19, 57, 0, 0), - CABAC_ENTRY(44, 7, 67, 2, 75, 20, 58, 0, 0), - CABAC_ENTRY(45, -5, 86, -3, 87, 4, 84, 0, 0), - CABAC_ENTRY(46, 2, 88, -3, 100, 6, 96, 0, 0), - CABAC_ENTRY(47, 0, 58, 1, 56, 1, 63, 0, 0), - CABAC_ENTRY(48, -3, 76, -3, 74, -5, 85, 0, 0), - CABAC_ENTRY(49, -10, 94, -6, 85, -13, 106, 0, 0), - CABAC_ENTRY(50, 5, 54, 0, 59, 5, 63, 0, 0), - CABAC_ENTRY(51, 4, 69, -3, 81, 6, 75, 0, 0), - CABAC_ENTRY(52, -3, 81, -7, 86, -3, 90, 0, 0), - CABAC_ENTRY(53, 0, 88, -5, 95, -1, 101, 0, 0), - - /* Table 9-16 – Values of variables m and n for ctxIdx from 54 to 59 */ - CABAC_ENTRY(54, -7, 67, -1, 66, 3, 55, 0, 0), - CABAC_ENTRY(55, -5, 74, -1, 77, -4, 79, 0, 0), - CABAC_ENTRY(56, -4, 74, 1, 70, -2, 75, 0, 0), - CABAC_ENTRY(57, -5, 80, -2, 86, -12, 97, 0, 0), - CABAC_ENTRY(58, -7, 72, -5, 72, -7, 50, 0, 0), - CABAC_ENTRY(59, 1, 58, 0, 61, 1, 60, 0, 0), - - /* Table 9-17 – Values of variables m and n for ctxIdx from 60 to 69 */ - CABAC_ENTRY(60, 0, 41, 0, 41, 0, 41, 0, 41), - CABAC_ENTRY(61, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(62, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(63, 0, 63, 0, 63, 0, 63, 0, 63), - CABAC_ENTRY(64, -9, 83, -9, 83, -9, 83, -9, 83), - CABAC_ENTRY(65, 4, 86, 4, 86, 4, 86, 4, 86), - CABAC_ENTRY(66, 0, 97, 0, 97, 0, 97, 0, 97), - CABAC_ENTRY(67, -7, 72, -7, 72, -7, 72, -7, 72), - CABAC_ENTRY(68, 13, 41, 13, 41, 13, 41, 13, 41), - CABAC_ENTRY(69, 3, 62, 3, 62, 3, 62, 3, 62), - - /* Table 9-18 – Values of variables m and n for ctxIdx from 70 to 104 */ - CABAC_ENTRY(70, 0, 45, 13, 15, 7, 34, 0, 11), - CABAC_ENTRY(71, -4, 78, 7, 51, -9, 88, 1, 55), - CABAC_ENTRY(72, -3, 96, 2, 80, -20, 127, 0, 69), - CABAC_ENTRY(73, -27, 126, -39, 127, -36, 127, -17, 127), - CABAC_ENTRY(74, -28, 98, -18, 91, -17, 91, -13, 102), - CABAC_ENTRY(75, -25, 101, -17, 96, -14, 95, 0, 82), - CABAC_ENTRY(76, -23, 67, -26, 81, -25, 84, -7, 74), - CABAC_ENTRY(77, -28, 82, -35, 98, -25, 86, -21, 107), - CABAC_ENTRY(78, -20, 94, -24, 102, -12, 89, -27, 127), - CABAC_ENTRY(79, -16, 83, -23, 97, -17, 91, -31, 127), - CABAC_ENTRY(80, -22, 110, -27, 119, -31, 127, -24, 127), - CABAC_ENTRY(81, -21, 91, -24, 99, -14, 76, -18, 95), - CABAC_ENTRY(82, -18, 102, -21, 110, -18, 103, -27, 127), - CABAC_ENTRY(83, -13, 93, -18, 102, -13, 90, -21, 114), - CABAC_ENTRY(84, -29, 127, -36, 127, -37, 127, -30, 127), - CABAC_ENTRY(85, -7, 92, 0, 80, 11, 80, -17, 123), - CABAC_ENTRY(86, -5, 89, -5, 89, 5, 76, -12, 115), - CABAC_ENTRY(87, -7, 96, -7, 94, 2, 84, -16, 122), - CABAC_ENTRY(88, -13, 108, -4, 92, 5, 78, -11, 115), - CABAC_ENTRY(89, -3, 46, 0, 39, -6, 55, -12, 63), - CABAC_ENTRY(90, -1, 65, 0, 65, 4, 61, -2, 68), - CABAC_ENTRY(91, -1, 57, -15, 84, -14, 83, -15, 84), - CABAC_ENTRY(92, -9, 93, -35, 127, -37, 127, -13, 104), - CABAC_ENTRY(93, -3, 74, -2, 73, -5, 79, -3, 70), - CABAC_ENTRY(94, -9, 92, -12, 104, -11, 104, -8, 93), - CABAC_ENTRY(95, -8, 87, -9, 91, -11, 91, -10, 90), - CABAC_ENTRY(96, -23, 126, -31, 127, -30, 127, -30, 127), - CABAC_ENTRY(97, 5, 54, 3, 55, 0, 65, -1, 74), - CABAC_ENTRY(98, 6, 60, 7, 56, -2, 79, -6, 97), - CABAC_ENTRY(99, 6, 59, 7, 55, 0, 72, -7, 91), - CABAC_ENTRY(100, 6, 69, 8, 61, -4, 92, -20, 127), - CABAC_ENTRY(101, -1, 48, -3, 53, -6, 56, -4, 56), - CABAC_ENTRY(102, 0, 68, 0, 68, 3, 68, -5, 82), - CABAC_ENTRY(103, -4, 69, -7, 74, -8, 71, -7, 76), - CABAC_ENTRY(104, -8, 88, -9, 88, -13, 98, -22, 125), - - /* Table 9-19 – Values of variables m and n for ctxIdx from 105 to 165 */ - CABAC_ENTRY(105, -2, 85, -13, 103, -4, 86, -7, 93), - CABAC_ENTRY(106, -6, 78, -13, 91, -12, 88, -11, 87), - CABAC_ENTRY(107, -1, 75, -9, 89, -5, 82, -3, 77), - CABAC_ENTRY(108, -7, 77, -14, 92, -3, 72, -5, 71), - CABAC_ENTRY(109, 2, 54, -8, 76, -4, 67, -4, 63), - CABAC_ENTRY(110, 5, 50, -12, 87, -8, 72, -4, 68), - CABAC_ENTRY(111, -3, 68, -23, 110, -16, 89, -12, 84), - CABAC_ENTRY(112, 1, 50, -24, 105, -9, 69, -7, 62), - CABAC_ENTRY(113, 6, 42, -10, 78, -1, 59, -7, 65), - CABAC_ENTRY(114, -4, 81, -20, 112, 5, 66, 8, 61), - CABAC_ENTRY(115, 1, 63, -17, 99, 4, 57, 5, 56), - CABAC_ENTRY(116, -4, 70, -78, 127, -4, 71, -2, 66), - CABAC_ENTRY(117, 0, 67, -70, 127, -2, 71, 1, 64), - CABAC_ENTRY(118, 2, 57, -50, 127, 2, 58, 0, 61), - CABAC_ENTRY(119, -2, 76, -46, 127, -1, 74, -2, 78), - CABAC_ENTRY(120, 11, 35, -4, 66, -4, 44, 1, 50), - CABAC_ENTRY(121, 4, 64, -5, 78, -1, 69, 7, 52), - CABAC_ENTRY(122, 1, 61, -4, 71, 0, 62, 10, 35), - CABAC_ENTRY(123, 11, 35, -8, 72, -7, 51, 0, 44), - CABAC_ENTRY(124, 18, 25, 2, 59, -4, 47, 11, 38), - CABAC_ENTRY(125, 12, 24, -1, 55, -6, 42, 1, 45), - CABAC_ENTRY(126, 13, 29, -7, 70, -3, 41, 0, 46), - CABAC_ENTRY(127, 13, 36, -6, 75, -6, 53, 5, 44), - CABAC_ENTRY(128, -10, 93, -8, 89, 8, 76, 31, 17), - CABAC_ENTRY(129, -7, 73, -34, 119, -9, 78, 1, 51), - CABAC_ENTRY(130, -2, 73, -3, 75, -11, 83, 7, 50), - CABAC_ENTRY(131, 13, 46, 32, 20, 9, 52, 28, 19), - CABAC_ENTRY(132, 9, 49, 30, 22, 0, 67, 16, 33), - CABAC_ENTRY(133, -7, 100, -44, 127, -5, 90, 14, 62), - CABAC_ENTRY(134, 9, 53, 0, 54, 1, 67, -13, 108), - CABAC_ENTRY(135, 2, 53, -5, 61, -15, 72, -15, 100), - CABAC_ENTRY(136, 5, 53, 0, 58, -5, 75, -13, 101), - CABAC_ENTRY(137, -2, 61, -1, 60, -8, 80, -13, 91), - CABAC_ENTRY(138, 0, 56, -3, 61, -21, 83, -12, 94), - CABAC_ENTRY(139, 0, 56, -8, 67, -21, 64, -10, 88), - CABAC_ENTRY(140, -13, 63, -25, 84, -13, 31, -16, 84), - CABAC_ENTRY(141, -5, 60, -14, 74, -25, 64, -10, 86), - CABAC_ENTRY(142, -1, 62, -5, 65, -29, 94, -7, 83), - CABAC_ENTRY(143, 4, 57, 5, 52, 9, 75, -13, 87), - CABAC_ENTRY(144, -6, 69, 2, 57, 17, 63, -19, 94), - CABAC_ENTRY(145, 4, 57, 0, 61, -8, 74, 1, 70), - CABAC_ENTRY(146, 14, 39, -9, 69, -5, 35, 0, 72), - CABAC_ENTRY(147, 4, 51, -11, 70, -2, 27, -5, 74), - CABAC_ENTRY(148, 13, 68, 18, 55, 13, 91, 18, 59), - CABAC_ENTRY(149, 3, 64, -4, 71, 3, 65, -8, 102), - CABAC_ENTRY(150, 1, 61, 0, 58, -7, 69, -15, 100), - CABAC_ENTRY(151, 9, 63, 7, 61, 8, 77, 0, 95), - CABAC_ENTRY(152, 7, 50, 9, 41, -10, 66, -4, 75), - CABAC_ENTRY(153, 16, 39, 18, 25, 3, 62, 2, 72), - CABAC_ENTRY(154, 5, 44, 9, 32, -3, 68, -11, 75), - CABAC_ENTRY(155, 4, 52, 5, 43, -20, 81, -3, 71), - CABAC_ENTRY(156, 11, 48, 9, 47, 0, 30, 15, 46), - CABAC_ENTRY(157, -5, 60, 0, 44, 1, 7, -13, 69), - CABAC_ENTRY(158, -1, 59, 0, 51, -3, 23, 0, 62), - CABAC_ENTRY(159, 0, 59, 2, 46, -21, 74, 0, 65), - CABAC_ENTRY(160, 22, 33, 19, 38, 16, 66, 21, 37), - CABAC_ENTRY(161, 5, 44, -4, 66, -23, 124, -15, 72), - CABAC_ENTRY(162, 14, 43, 15, 38, 17, 37, 9, 57), - CABAC_ENTRY(163, -1, 78, 12, 42, 44, -18, 16, 54), - CABAC_ENTRY(164, 0, 60, 9, 34, 50, -34, 0, 62), - CABAC_ENTRY(165, 9, 69, 0, 89, -22, 127, 12, 72), - - /* Table 9-20 – Values of variables m and n for ctxIdx from 166 to 226 */ - CABAC_ENTRY(166, 11, 28, 4, 45, 4, 39, 24, 0), - CABAC_ENTRY(167, 2, 40, 10, 28, 0, 42, 15, 9), - CABAC_ENTRY(168, 3, 44, 10, 31, 7, 34, 8, 25), - CABAC_ENTRY(169, 0, 49, 33, -11, 11, 29, 13, 18), - CABAC_ENTRY(170, 0, 46, 52, -43, 8, 31, 15, 9), - CABAC_ENTRY(171, 2, 44, 18, 15, 6, 37, 13, 19), - CABAC_ENTRY(172, 2, 51, 28, 0, 7, 42, 10, 37), - CABAC_ENTRY(173, 0, 47, 35, -22, 3, 40, 12, 18), - CABAC_ENTRY(174, 4, 39, 38, -25, 8, 33, 6, 29), - CABAC_ENTRY(175, 2, 62, 34, 0, 13, 43, 20, 33), - CABAC_ENTRY(176, 6, 46, 39, -18, 13, 36, 15, 30), - CABAC_ENTRY(177, 0, 54, 32, -12, 4, 47, 4, 45), - CABAC_ENTRY(178, 3, 54, 102, -94, 3, 55, 1, 58), - CABAC_ENTRY(179, 2, 58, 0, 0, 2, 58, 0, 62), - CABAC_ENTRY(180, 4, 63, 56, -15, 6, 60, 7, 61), - CABAC_ENTRY(181, 6, 51, 33, -4, 8, 44, 12, 38), - CABAC_ENTRY(182, 6, 57, 29, 10, 11, 44, 11, 45), - CABAC_ENTRY(183, 7, 53, 37, -5, 14, 42, 15, 39), - CABAC_ENTRY(184, 6, 52, 51, -29, 7, 48, 11, 42), - CABAC_ENTRY(185, 6, 55, 39, -9, 4, 56, 13, 44), - CABAC_ENTRY(186, 11, 45, 52, -34, 4, 52, 16, 45), - CABAC_ENTRY(187, 14, 36, 69, -58, 13, 37, 12, 41), - CABAC_ENTRY(188, 8, 53, 67, -63, 9, 49, 10, 49), - CABAC_ENTRY(189, -1, 82, 44, -5, 19, 58, 30, 34), - CABAC_ENTRY(190, 7, 55, 32, 7, 10, 48, 18, 42), - CABAC_ENTRY(191, -3, 78, 55, -29, 12, 45, 10, 55), - CABAC_ENTRY(192, 15, 46, 32, 1, 0, 69, 17, 51), - CABAC_ENTRY(193, 22, 31, 0, 0, 20, 33, 17, 46), - CABAC_ENTRY(194, -1, 84, 27, 36, 8, 63, 0, 89), - CABAC_ENTRY(195, 25, 7, 33, -25, 35, -18, 26, -19), - CABAC_ENTRY(196, 30, -7, 34, -30, 33, -25, 22, -17), - CABAC_ENTRY(197, 28, 3, 36, -28, 28, -3, 26, -17), - CABAC_ENTRY(198, 28, 4, 38, -28, 24, 10, 30, -25), - CABAC_ENTRY(199, 32, 0, 38, -27, 27, 0, 28, -20), - CABAC_ENTRY(200, 34, -1, 34, -18, 34, -14, 33, -23), - CABAC_ENTRY(201, 30, 6, 35, -16, 52, -44, 37, -27), - CABAC_ENTRY(202, 30, 6, 34, -14, 39, -24, 33, -23), - CABAC_ENTRY(203, 32, 9, 32, -8, 19, 17, 40, -28), - CABAC_ENTRY(204, 31, 19, 37, -6, 31, 25, 38, -17), - CABAC_ENTRY(205, 26, 27, 35, 0, 36, 29, 33, -11), - CABAC_ENTRY(206, 26, 30, 30, 10, 24, 33, 40, -15), - CABAC_ENTRY(207, 37, 20, 28, 18, 34, 15, 41, -6), - CABAC_ENTRY(208, 28, 34, 26, 25, 30, 20, 38, 1), - CABAC_ENTRY(209, 17, 70, 29, 41, 22, 73, 41, 17), - CABAC_ENTRY(210, 1, 67, 0, 75, 20, 34, 30, -6), - CABAC_ENTRY(211, 5, 59, 2, 72, 19, 31, 27, 3), - CABAC_ENTRY(212, 9, 67, 8, 77, 27, 44, 26, 22), - CABAC_ENTRY(213, 16, 30, 14, 35, 19, 16, 37, -16), - CABAC_ENTRY(214, 18, 32, 18, 31, 15, 36, 35, -4), - CABAC_ENTRY(215, 18, 35, 17, 35, 15, 36, 38, -8), - CABAC_ENTRY(216, 22, 29, 21, 30, 21, 28, 38, -3), - CABAC_ENTRY(217, 24, 31, 17, 45, 25, 21, 37, 3), - CABAC_ENTRY(218, 23, 38, 20, 42, 30, 20, 38, 5), - CABAC_ENTRY(219, 18, 43, 18, 45, 31, 12, 42, 0), - CABAC_ENTRY(220, 20, 41, 27, 26, 27, 16, 35, 16), - CABAC_ENTRY(221, 11, 63, 16, 54, 24, 42, 39, 22), - CABAC_ENTRY(222, 9, 59, 7, 66, 0, 93, 14, 48), - CABAC_ENTRY(223, 9, 64, 16, 56, 14, 56, 27, 37), - CABAC_ENTRY(224, -1, 94, 11, 73, 15, 57, 21, 60), - CABAC_ENTRY(225, -2, 89, 10, 67, 26, 38, 12, 68), - CABAC_ENTRY(226, -9, 108, -10, 116, -24, 127, 2, 97), - - /* Table 9-21 – Values of variables m and n for ctxIdx from 227 to 275 */ - CABAC_ENTRY(227, -6, 76, -23, 112, -24, 115, -3, 71), - CABAC_ENTRY(228, -2, 44, -15, 71, -22, 82, -6, 42), - CABAC_ENTRY(229, 0, 45, -7, 61, -9, 62, -5, 50), - CABAC_ENTRY(230, 0, 52, 0, 53, 0, 53, -3, 54), - CABAC_ENTRY(231, -3, 64, -5, 66, 0, 59, -2, 62), - CABAC_ENTRY(232, -2, 59, -11, 77, -14, 85, 0, 58), - CABAC_ENTRY(233, -4, 70, -9, 80, -13, 89, 1, 63), - CABAC_ENTRY(234, -4, 75, -9, 84, -13, 94, -2, 72), - CABAC_ENTRY(235, -8, 82, -10, 87, -11, 92, -1, 74), - CABAC_ENTRY(236, -17, 102, -34, 127, -29, 127, -9, 91), - CABAC_ENTRY(237, -9, 77, -21, 101, -21, 100, -5, 67), - CABAC_ENTRY(238, 3, 24, -3, 39, -14, 57, -5, 27), - CABAC_ENTRY(239, 0, 42, -5, 53, -12, 67, -3, 39), - CABAC_ENTRY(240, 0, 48, -7, 61, -11, 71, -2, 44), - CABAC_ENTRY(241, 0, 55, -11, 75, -10, 77, 0, 46), - CABAC_ENTRY(242, -6, 59, -15, 77, -21, 85, -16, 64), - CABAC_ENTRY(243, -7, 71, -17, 91, -16, 88, -8, 68), - CABAC_ENTRY(244, -12, 83, -25, 107, -23, 104, -10, 78), - CABAC_ENTRY(245, -11, 87, -25, 111, -15, 98, -6, 77), - CABAC_ENTRY(246, -30, 119, -28, 122, -37, 127, -10, 86), - CABAC_ENTRY(247, 1, 58, -11, 76, -10, 82, -12, 92), - CABAC_ENTRY(248, -3, 29, -10, 44, -8, 48, -15, 55), - CABAC_ENTRY(249, -1, 36, -10, 52, -8, 61, -10, 60), - CABAC_ENTRY(250, 1, 38, -10, 57, -8, 66, -6, 62), - CABAC_ENTRY(251, 2, 43, -9, 58, -7, 70, -4, 65), - CABAC_ENTRY(252, -6, 55, -16, 72, -14, 75, -12, 73), - CABAC_ENTRY(253, 0, 58, -7, 69, -10, 79, -8, 76), - CABAC_ENTRY(254, 0, 64, -4, 69, -9, 83, -7, 80), - CABAC_ENTRY(255, -3, 74, -5, 74, -12, 92, -9, 88), - CABAC_ENTRY(256, -10, 90, -9, 86, -18, 108, -17, 110), - CABAC_ENTRY(257, 0, 70, 2, 66, -4, 79, -11, 97), - CABAC_ENTRY(258, -4, 29, -9, 34, -22, 69, -20, 84), - CABAC_ENTRY(259, 5, 31, 1, 32, -16, 75, -11, 79), - CABAC_ENTRY(260, 7, 42, 11, 31, -2, 58, -6, 73), - CABAC_ENTRY(261, 1, 59, 5, 52, 1, 58, -4, 74), - CABAC_ENTRY(262, -2, 58, -2, 55, -13, 78, -13, 86), - CABAC_ENTRY(263, -3, 72, -2, 67, -9, 83, -13, 96), - CABAC_ENTRY(264, -3, 81, 0, 73, -4, 81, -11, 97), - CABAC_ENTRY(265, -11, 97, -8, 89, -13, 99, -19, 117), - CABAC_ENTRY(266, 0, 58, 3, 52, -13, 81, -8, 78), - CABAC_ENTRY(267, 8, 5, 7, 4, -6, 38, -5, 33), - CABAC_ENTRY(268, 10, 14, 10, 8, -13, 62, -4, 48), - CABAC_ENTRY(269, 14, 18, 17, 8, -6, 58, -2, 53), - CABAC_ENTRY(270, 13, 27, 16, 19, -2, 59, -3, 62), - CABAC_ENTRY(271, 2, 40, 3, 37, -16, 73, -13, 71), - CABAC_ENTRY(272, 0, 58, -1, 61, -10, 76, -10, 79), - CABAC_ENTRY(273, -3, 70, -5, 73, -13, 86, -12, 86), - CABAC_ENTRY(274, -6, 79, -1, 70, -9, 83, -13, 90), - CABAC_ENTRY(275, -8, 85, -4, 78, -10, 87, -14, 97), - - /* Table 9-22 – Values of variables m and n for ctxIdx from 277 to 337 */ - CABAC_ENTRY(277, -13, 106, -21, 126, -22, 127, -6, 93), - CABAC_ENTRY(278, -16, 106, -23, 124, -25, 127, -6, 84), - CABAC_ENTRY(279, -10, 87, -20, 110, -25, 120, -8, 79), - CABAC_ENTRY(280, -21, 114, -26, 126, -27, 127, 0, 66), - CABAC_ENTRY(281, -18, 110, -25, 124, -19, 114, -1, 71), - CABAC_ENTRY(282, -14, 98, -17, 105, -23, 117, 0, 62), - CABAC_ENTRY(283, -22, 110, -27, 121, -25, 118, -2, 60), - CABAC_ENTRY(284, -21, 106, -27, 117, -26, 117, -2, 59), - CABAC_ENTRY(285, -18, 103, -17, 102, -24, 113, -5, 75), - CABAC_ENTRY(286, -21, 107, -26, 117, -28, 118, -3, 62), - CABAC_ENTRY(287, -23, 108, -27, 116, -31, 120, -4, 58), - CABAC_ENTRY(288, -26, 112, -33, 122, -37, 124, -9, 66), - CABAC_ENTRY(289, -10, 96, -10, 95, -10, 94, -1, 79), - CABAC_ENTRY(290, -12, 95, -14, 100, -15, 102, 0, 71), - CABAC_ENTRY(291, -5, 91, -8, 95, -10, 99, 3, 68), - CABAC_ENTRY(292, -9, 93, -17, 111, -13, 106, 10, 44), - CABAC_ENTRY(293, -22, 94, -28, 114, -50, 127, -7, 62), - CABAC_ENTRY(294, -5, 86, -6, 89, -5, 92, 15, 36), - CABAC_ENTRY(295, 9, 67, -2, 80, 17, 57, 14, 40), - CABAC_ENTRY(296, -4, 80, -4, 82, -5, 86, 16, 27), - CABAC_ENTRY(297, -10, 85, -9, 85, -13, 94, 12, 29), - CABAC_ENTRY(298, -1, 70, -8, 81, -12, 91, 1, 44), - CABAC_ENTRY(299, 7, 60, -1, 72, -2, 77, 20, 36), - CABAC_ENTRY(300, 9, 58, 5, 64, 0, 71, 18, 32), - CABAC_ENTRY(301, 5, 61, 1, 67, -1, 73, 5, 42), - CABAC_ENTRY(302, 12, 50, 9, 56, 4, 64, 1, 48), - CABAC_ENTRY(303, 15, 50, 0, 69, -7, 81, 10, 62), - CABAC_ENTRY(304, 18, 49, 1, 69, 5, 64, 17, 46), - CABAC_ENTRY(305, 17, 54, 7, 69, 15, 57, 9, 64), - CABAC_ENTRY(306, 10, 41, -7, 69, 1, 67, -12, 104), - CABAC_ENTRY(307, 7, 46, -6, 67, 0, 68, -11, 97), - CABAC_ENTRY(308, -1, 51, -16, 77, -10, 67, -16, 96), - CABAC_ENTRY(309, 7, 49, -2, 64, 1, 68, -7, 88), - CABAC_ENTRY(310, 8, 52, 2, 61, 0, 77, -8, 85), - CABAC_ENTRY(311, 9, 41, -6, 67, 2, 64, -7, 85), - CABAC_ENTRY(312, 6, 47, -3, 64, 0, 68, -9, 85), - CABAC_ENTRY(313, 2, 55, 2, 57, -5, 78, -13, 88), - CABAC_ENTRY(314, 13, 41, -3, 65, 7, 55, 4, 66), - CABAC_ENTRY(315, 10, 44, -3, 66, 5, 59, -3, 77), - CABAC_ENTRY(316, 6, 50, 0, 62, 2, 65, -3, 76), - CABAC_ENTRY(317, 5, 53, 9, 51, 14, 54, -6, 76), - CABAC_ENTRY(318, 13, 49, -1, 66, 15, 44, 10, 58), - CABAC_ENTRY(319, 4, 63, -2, 71, 5, 60, -1, 76), - CABAC_ENTRY(320, 6, 64, -2, 75, 2, 70, -1, 83), - CABAC_ENTRY(321, -2, 69, -1, 70, -2, 76, -7, 99), - CABAC_ENTRY(322, -2, 59, -9, 72, -18, 86, -14, 95), - CABAC_ENTRY(323, 6, 70, 14, 60, 12, 70, 2, 95), - CABAC_ENTRY(324, 10, 44, 16, 37, 5, 64, 0, 76), - CABAC_ENTRY(325, 9, 31, 0, 47, -12, 70, -5, 74), - CABAC_ENTRY(326, 12, 43, 18, 35, 11, 55, 0, 70), - CABAC_ENTRY(327, 3, 53, 11, 37, 5, 56, -11, 75), - CABAC_ENTRY(328, 14, 34, 12, 41, 0, 69, 1, 68), - CABAC_ENTRY(329, 10, 38, 10, 41, 2, 65, 0, 65), - CABAC_ENTRY(330, -3, 52, 2, 48, -6, 74, -14, 73), - CABAC_ENTRY(331, 13, 40, 12, 41, 5, 54, 3, 62), - CABAC_ENTRY(332, 17, 32, 13, 41, 7, 54, 4, 62), - CABAC_ENTRY(333, 7, 44, 0, 59, -6, 76, -1, 68), - CABAC_ENTRY(334, 7, 38, 3, 50, -11, 82, -13, 75), - CABAC_ENTRY(335, 13, 50, 19, 40, -2, 77, 11, 55), - CABAC_ENTRY(336, 10, 57, 3, 66, -2, 77, 5, 64), - CABAC_ENTRY(337, 26, 43, 18, 50, 25, 42, 12, 70), - - /* Table 9-23 – Values of variables m and n for ctxIdx from 338 to 398 */ - CABAC_ENTRY(338, 14, 11, 19, -6, 17, -13, 15, 6), - CABAC_ENTRY(339, 11, 14, 18, -6, 16, -9, 6, 19), - CABAC_ENTRY(340, 9, 11, 14, 0, 17, -12, 7, 16), - CABAC_ENTRY(341, 18, 11, 26, -12, 27, -21, 12, 14), - CABAC_ENTRY(342, 21, 9, 31, -16, 37, -30, 18, 13), - CABAC_ENTRY(343, 23, -2, 33, -25, 41, -40, 13, 11), - CABAC_ENTRY(344, 32, -15, 33, -22, 42, -41, 13, 15), - CABAC_ENTRY(345, 32, -15, 37, -28, 48, -47, 15, 16), - CABAC_ENTRY(346, 34, -21, 39, -30, 39, -32, 12, 23), - CABAC_ENTRY(347, 39, -23, 42, -30, 46, -40, 13, 23), - CABAC_ENTRY(348, 42, -33, 47, -42, 52, -51, 15, 20), - CABAC_ENTRY(349, 41, -31, 45, -36, 46, -41, 14, 26), - CABAC_ENTRY(350, 46, -28, 49, -34, 52, -39, 14, 44), - CABAC_ENTRY(351, 38, -12, 41, -17, 43, -19, 17, 40), - CABAC_ENTRY(352, 21, 29, 32, 9, 32, 11, 17, 47), - CABAC_ENTRY(353, 45, -24, 69, -71, 61, -55, 24, 17), - CABAC_ENTRY(354, 53, -45, 63, -63, 56, -46, 21, 21), - CABAC_ENTRY(355, 48, -26, 66, -64, 62, -50, 25, 22), - CABAC_ENTRY(356, 65, -43, 77, -74, 81, -67, 31, 27), - CABAC_ENTRY(357, 43, -19, 54, -39, 45, -20, 22, 29), - CABAC_ENTRY(358, 39, -10, 52, -35, 35, -2, 19, 35), - CABAC_ENTRY(359, 30, 9, 41, -10, 28, 15, 14, 50), - CABAC_ENTRY(360, 18, 26, 36, 0, 34, 1, 10, 57), - CABAC_ENTRY(361, 20, 27, 40, -1, 39, 1, 7, 63), - CABAC_ENTRY(362, 0, 57, 30, 14, 30, 17, -2, 77), - CABAC_ENTRY(363, -14, 82, 28, 26, 20, 38, -4, 82), - CABAC_ENTRY(364, -5, 75, 23, 37, 18, 45, -3, 94), - CABAC_ENTRY(365, -19, 97, 12, 55, 15, 54, 9, 69), - CABAC_ENTRY(366, -35, 125, 11, 65, 0, 79, -12, 109), - CABAC_ENTRY(367, 27, 0, 37, -33, 36, -16, 36, -35), - CABAC_ENTRY(368, 28, 0, 39, -36, 37, -14, 36, -34), - CABAC_ENTRY(369, 31, -4, 40, -37, 37, -17, 32, -26), - CABAC_ENTRY(370, 27, 6, 38, -30, 32, 1, 37, -30), - CABAC_ENTRY(371, 34, 8, 46, -33, 34, 15, 44, -32), - CABAC_ENTRY(372, 30, 10, 42, -30, 29, 15, 34, -18), - CABAC_ENTRY(373, 24, 22, 40, -24, 24, 25, 34, -15), - CABAC_ENTRY(374, 33, 19, 49, -29, 34, 22, 40, -15), - CABAC_ENTRY(375, 22, 32, 38, -12, 31, 16, 33, -7), - CABAC_ENTRY(376, 26, 31, 40, -10, 35, 18, 35, -5), - CABAC_ENTRY(377, 21, 41, 38, -3, 31, 28, 33, 0), - CABAC_ENTRY(378, 26, 44, 46, -5, 33, 41, 38, 2), - CABAC_ENTRY(379, 23, 47, 31, 20, 36, 28, 33, 13), - CABAC_ENTRY(380, 16, 65, 29, 30, 27, 47, 23, 35), - CABAC_ENTRY(381, 14, 71, 25, 44, 21, 62, 13, 58), - CABAC_ENTRY(382, 8, 60, 12, 48, 18, 31, 29, -3), - CABAC_ENTRY(383, 6, 63, 11, 49, 19, 26, 26, 0), - CABAC_ENTRY(384, 17, 65, 26, 45, 36, 24, 22, 30), - CABAC_ENTRY(385, 21, 24, 22, 22, 24, 23, 31, -7), - CABAC_ENTRY(386, 23, 20, 23, 22, 27, 16, 35, -15), - CABAC_ENTRY(387, 26, 23, 27, 21, 24, 30, 34, -3), - CABAC_ENTRY(388, 27, 32, 33, 20, 31, 29, 34, 3), - CABAC_ENTRY(389, 28, 23, 26, 28, 22, 41, 36, -1), - CABAC_ENTRY(390, 28, 24, 30, 24, 22, 42, 34, 5), - CABAC_ENTRY(391, 23, 40, 27, 34, 16, 60, 32, 11), - CABAC_ENTRY(392, 24, 32, 18, 42, 15, 52, 35, 5), - CABAC_ENTRY(393, 28, 29, 25, 39, 14, 60, 34, 12), - CABAC_ENTRY(394, 23, 42, 18, 50, 3, 78, 39, 11), - CABAC_ENTRY(395, 19, 57, 12, 70, -16, 123, 30, 29), - CABAC_ENTRY(396, 22, 53, 21, 54, 21, 53, 34, 26), - CABAC_ENTRY(397, 22, 61, 14, 71, 22, 56, 29, 39), - CABAC_ENTRY(398, 11, 86, 11, 83, 25, 61, 19, 66), - - /* Values of variables m and n for ctxIdx from 399 to 463 (not documented) */ - CABAC_ENTRY(399, 12, 40, 25, 32, 21, 33, 31, 21), - CABAC_ENTRY(400, 11, 51, 21, 49, 19, 50, 31, 31), - CABAC_ENTRY(401, 14, 59, 21, 54, 17, 61, 25, 50), - CABAC_ENTRY(402, -4, 79, -5, 85, -3, 78, -17, 120), - CABAC_ENTRY(403, -7, 71, -6, 81, -8, 74, -20, 112), - CABAC_ENTRY(404, -5, 69, -10, 77, -9, 72, -18, 114), - CABAC_ENTRY(405, -9, 70, -7, 81, -10, 72, -11, 85), - CABAC_ENTRY(406, -8, 66, -17, 80, -18, 75, -15, 92), - CABAC_ENTRY(407, -10, 68, -18, 73, -12, 71, -14, 89), - CABAC_ENTRY(408, -19, 73, -4, 74, -11, 63, -26, 71), - CABAC_ENTRY(409, -12, 69, -10, 83, -5, 70, -15, 81), - CABAC_ENTRY(410, -16, 70, -9, 71, -17, 75, -14, 80), - CABAC_ENTRY(411, -15, 67, -9, 67, -14, 72, 0, 68), - CABAC_ENTRY(412, -20, 62, -1, 61, -16, 67, -14, 70), - CABAC_ENTRY(413, -19, 70, -8, 66, -8, 53, -24, 56), - CABAC_ENTRY(414, -16, 66, -14, 66, -14, 59, -23, 68), - CABAC_ENTRY(415, -22, 65, 0, 59, -9, 52, -24, 50), - CABAC_ENTRY(416, -20, 63, 2, 59, -11, 68, -11, 74), - CABAC_ENTRY(417, 9, -2, 17, -10, 9, -2, 23, -13), - CABAC_ENTRY(418, 26, -9, 32, -13, 30, -10, 26, -13), - CABAC_ENTRY(419, 33, -9, 42, -9, 31, -4, 40, -15), - CABAC_ENTRY(420, 39, -7, 49, -5, 33, -1, 49, -14), - CABAC_ENTRY(421, 41, -2, 53, 0, 33, 7, 44, 3), - CABAC_ENTRY(422, 45, 3, 64, 3, 31, 12, 45, 6), - CABAC_ENTRY(423, 49, 9, 68, 10, 37, 23, 44, 34), - CABAC_ENTRY(424, 45, 27, 66, 27, 31, 38, 33, 54), - CABAC_ENTRY(425, 36, 59, 47, 57, 20, 64, 19, 82), - CABAC_ENTRY(426, -6, 66, -5, 71, -9, 71, -3, 75), - CABAC_ENTRY(427, -7, 35, 0, 24, -7, 37, -1, 23), - CABAC_ENTRY(428, -7, 42, -1, 36, -8, 44, 1, 34), - CABAC_ENTRY(429, -8, 45, -2, 42, -11, 49, 1, 43), - CABAC_ENTRY(430, -5, 48, -2, 52, -10, 56, 0, 54), - CABAC_ENTRY(431, -12, 56, -9, 57, -12, 59, -2, 55), - CABAC_ENTRY(432, -6, 60, -6, 63, -8, 63, 0, 61), - CABAC_ENTRY(433, -5, 62, -4, 65, -9, 67, 1, 64), - CABAC_ENTRY(434, -8, 66, -4, 67, -6, 68, 0, 68), - CABAC_ENTRY(435, -8, 76, -7, 82, -10, 79, -9, 92), - CABAC_ENTRY(436, -5, 85, -3, 81, -3, 78, -14, 106), - CABAC_ENTRY(437, -6, 81, -3, 76, -8, 74, -13, 97), - CABAC_ENTRY(438, -10, 77, -7, 72, -9, 72, -15, 90), - CABAC_ENTRY(439, -7, 81, -6, 78, -10, 72, -12, 90), - CABAC_ENTRY(440, -17, 80, -12, 72, -18, 75, -18, 88), - CABAC_ENTRY(441, -18, 73, -14, 68, -12, 71, -10, 73), - CABAC_ENTRY(442, -4, 74, -3, 70, -11, 63, -9, 79), - CABAC_ENTRY(443, -10, 83, -6, 76, -5, 70, -14, 86), - CABAC_ENTRY(444, -9, 71, -5, 66, -17, 75, -10, 73), - CABAC_ENTRY(445, -9, 67, -5, 62, -14, 72, -10, 70), - CABAC_ENTRY(446, -1, 61, 0, 57, -16, 67, -10, 69), - CABAC_ENTRY(447, -8, 66, -4, 61, -8, 53, -5, 66), - CABAC_ENTRY(448, -14, 66, -9, 60, -14, 59, -9, 64), - CABAC_ENTRY(449, 0, 59, 1, 54, -9, 52, -5, 58), - CABAC_ENTRY(450, 2, 59, 2, 58, -11, 68, 2, 59), - CABAC_ENTRY(451, 21, -13, 17, -10, 9, -2, 21, -10), - CABAC_ENTRY(452, 33, -14, 32, -13, 30, -10, 24, -11), - CABAC_ENTRY(453, 39, -7, 42, -9, 31, -4, 28, -8), - CABAC_ENTRY(454, 46, -2, 49, -5, 33, -1, 28, -1), - CABAC_ENTRY(455, 51, 2, 53, 0, 33, 7, 29, 3), - CABAC_ENTRY(456, 60, 6, 64, 3, 31, 12, 29, 9), - CABAC_ENTRY(457, 61, 17, 68, 10, 37, 23, 35, 20), - CABAC_ENTRY(458, 55, 34, 66, 27, 31, 38, 29, 36), - CABAC_ENTRY(459, 42, 62, 47, 57, 20, 64, 14, 67), + struct rkvdec_regs regs; }; static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) @@ -733,154 +208,43 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, } } -static void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) +/* + * Set the ref POC in the correct register. + * + * The 32 registers are spread across 3 regions, each alternating top and bottom ref POCs: + * - 1: ref 0 to 14 contain top 0 to 7 and bottoms 0 to 6 + * - 2: ref 15 to 29 contain top 8 to 14 and bottoms 7 to 14 + * - 3: ref 30 and 31 which correspond to top 15 and bottom 15 respectively. + */ +static void set_poc_reg(struct rkvdec_regs *regs, uint32_t poc, int id, bool bottom) { - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - u32 i; - - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf = NULL; - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { - buf = vb2_find_buffer(cap_q, dpb[i].reference_ts); - if (!buf) - pr_debug("No buffer for reference_ts %llu", - dpb[i].reference_ts); + if (!bottom) { + switch (id) { + case 0 ... 7: + regs->h26x.ref0_14_poc[id * 2] = poc; + break; + case 8 ... 14: + regs->h26x.ref15_29_poc[(id - 8) * 2 + 1] = poc; + break; + case 15: + regs->h26x.ref30_poc = poc; + break; } - - run->ref_buf[i] = buf; - } -} - -static void assemble_hw_rps(struct rkvdec_ctx *ctx, - struct v4l2_h264_reflist_builder *builder, - struct rkvdec_h264_run *run) -{ - const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; - const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; - - u32 *hw_rps = priv_tbl->rps; - u32 i, j; - u16 *p = (u16 *)hw_rps; - - memset(hw_rps, 0, sizeof(priv_tbl->rps)); - - /* - * Assign an invalid pic_num if DPB entry at that position is inactive. - * If we assign 0 in that position hardware will treat that as a real - * reference picture with pic_num 0, triggering output picture - * corruption. - */ - for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { - if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - continue; - - p[i] = builder->refs[i].frame_num; - } - - for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { - for (i = 0; i < builder->num_valid; i++) { - struct v4l2_h264_reference *ref; - bool dpb_valid; - bool bottom; - - switch (j) { - case 0: - ref = &h264_ctx->reflists.p[i]; - break; - case 1: - ref = &h264_ctx->reflists.b0[i]; - break; - case 2: - ref = &h264_ctx->reflists.b1[i]; - break; - } - - if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb))) - continue; - - dpb_valid = run->ref_buf[ref->index] != NULL; - bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; - - set_ps_field(hw_rps, DPB_INFO(i, j), - ref->index | dpb_valid << 4); - set_ps_field(hw_rps, BOTTOM_FLAG(i, j), bottom); + } else { + switch (id) { + case 0 ... 6: + regs->h26x.ref0_14_poc[id * 2 + 1] = poc; + break; + case 7 ... 14: + regs->h26x.ref15_29_poc[(id - 7) * 2] = poc; + break; + case 15: + regs->h26x.ref31_poc = poc; + break; } } } -static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; - const struct v4l2_ctrl_h264_pps *pps = run->pps; - struct rkvdec_h264_ctx *h264_ctx = ctx->priv; - struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; - - if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) - return; - - BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_4x4) != - sizeof(scaling->scaling_list_4x4)); - BUILD_BUG_ON(sizeof(tbl->scaling_list.scaling_list_8x8) != - sizeof(scaling->scaling_list_8x8)); - - memcpy(tbl->scaling_list.scaling_list_4x4, - scaling->scaling_list_4x4, - sizeof(scaling->scaling_list_4x4)); - - memcpy(tbl->scaling_list.scaling_list_8x8, - scaling->scaling_list_8x8, - sizeof(scaling->scaling_list_8x8)); -} - -/* - * dpb poc related registers table - */ -static const u32 poc_reg_tbl_top_field[16] = { - RKVDEC_REG_H264_POC_REFER0(0), - RKVDEC_REG_H264_POC_REFER0(2), - RKVDEC_REG_H264_POC_REFER0(4), - RKVDEC_REG_H264_POC_REFER0(6), - RKVDEC_REG_H264_POC_REFER0(8), - RKVDEC_REG_H264_POC_REFER0(10), - RKVDEC_REG_H264_POC_REFER0(12), - RKVDEC_REG_H264_POC_REFER0(14), - RKVDEC_REG_H264_POC_REFER1(1), - RKVDEC_REG_H264_POC_REFER1(3), - RKVDEC_REG_H264_POC_REFER1(5), - RKVDEC_REG_H264_POC_REFER1(7), - RKVDEC_REG_H264_POC_REFER1(9), - RKVDEC_REG_H264_POC_REFER1(11), - RKVDEC_REG_H264_POC_REFER1(13), - RKVDEC_REG_H264_POC_REFER2(0) -}; - -static const u32 poc_reg_tbl_bottom_field[16] = { - RKVDEC_REG_H264_POC_REFER0(1), - RKVDEC_REG_H264_POC_REFER0(3), - RKVDEC_REG_H264_POC_REFER0(5), - RKVDEC_REG_H264_POC_REFER0(7), - RKVDEC_REG_H264_POC_REFER0(9), - RKVDEC_REG_H264_POC_REFER0(11), - RKVDEC_REG_H264_POC_REFER0(13), - RKVDEC_REG_H264_POC_REFER1(0), - RKVDEC_REG_H264_POC_REFER1(2), - RKVDEC_REG_H264_POC_REFER1(4), - RKVDEC_REG_H264_POC_REFER1(6), - RKVDEC_REG_H264_POC_REFER1(8), - RKVDEC_REG_H264_POC_REFER1(10), - RKVDEC_REG_H264_POC_REFER1(12), - RKVDEC_REG_H264_POC_REFER1(14), - RKVDEC_REG_H264_POC_REFER2(1) -}; - static void config_registers(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run) { @@ -894,6 +258,7 @@ static void config_registers(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; const struct v4l2_format *f; + struct rkvdec_regs *regs = &h264_ctx->regs; dma_addr_t rlc_addr; dma_addr_t refer_addr; u32 rlc_len; @@ -903,10 +268,11 @@ static void config_registers(struct rkvdec_ctx *ctx, u32 yuv_virstride = 0; u32 offset; dma_addr_t dst_addr; - u32 reg, i; + u32 i; + + memset(regs, 0, sizeof(*regs)); - reg = RKVDEC_MODE(RKVDEC_MODE_H264); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL); + regs->common.reg02.dec_mode = RKVDEC_MODE_H264; f = &ctx->decoded_fmt; dst_fmt = &f->fmt.pix_mp; @@ -921,39 +287,35 @@ static void config_registers(struct rkvdec_ctx *ctx, else if (sps->chroma_format_idc == 2) yuv_virstride = 2 * y_virstride; - reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_SLICE_NUM_HIGHBIT | - RKVDEC_SLICE_NUM_LOWBITS(0x7ff); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR); + regs->common.reg03.uv_hor_virstride = hor_virstride / 16; + regs->common.reg03.y_hor_virstride = hor_virstride / 16; + regs->common.reg03.slice_num_highbit = 1; + regs->common.reg03.slice_num_lowbits = 0x7ff; /* config rlc base address */ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); - writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_RLCWRITE_BASE); + regs->common.strm_rlc_base = rlc_addr; + regs->h26x.rlcwrite_base = rlc_addr; rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - reg = RKVDEC_STRM_LEN(rlc_len); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN); + regs->common.stream_len = rlc_len; /* config cabac table */ offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); + regs->common.cabactbl_base = priv_start_addr + offset; /* config output base address */ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + regs->common.decout_base = dst_addr; - reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); + regs->common.reg08.y_virstride = y_virstride / 16; - reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + regs->common.reg09.yuv_virstride = yuv_virstride / 16; /* config ref pic address & poc */ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { struct vb2_buffer *vb_buf = run->ref_buf[i]; + struct ref_base *base; /* * If a DPB entry is unused or invalid, address of current destination @@ -963,124 +325,37 @@ static void config_registers(struct rkvdec_ctx *ctx, vb_buf = &dst_buf->vb2_buf; refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0); - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - refer_addr |= RKVDEC_COLMV_USED_FLAG_REF; - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD) - refer_addr |= RKVDEC_FIELD_REF; - - if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) - refer_addr |= RKVDEC_TOPFIELD_USED_REF; - if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) - refer_addr |= RKVDEC_BOTFIELD_USED_REF; - - writel_relaxed(dpb[i].top_field_order_cnt, - rkvdec->regs + poc_reg_tbl_top_field[i]); - writel_relaxed(dpb[i].bottom_field_order_cnt, - rkvdec->regs + poc_reg_tbl_bottom_field[i]); - if (i < V4L2_H264_NUM_DPB_ENTRIES - 1) - writel_relaxed(refer_addr, - rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i)); + base = ®s->h26x.ref0_14_base[i]; else - writel_relaxed(refer_addr, - rkvdec->regs + RKVDEC_REG_H264_BASE_REFER15); - } + base = ®s->h26x.ref15_base; + + base->base_addr = refer_addr >> 4; + base->field_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD); + base->colmv_use_flag_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE); + base->topfield_used_ref = !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF); + base->botfield_used_ref = !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF); - reg = RKVDEC_CUR_POC(dec_params->top_field_order_cnt); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0); + set_poc_reg(regs, dpb[i].top_field_order_cnt, i, false); + set_poc_reg(regs, dpb[i].bottom_field_order_cnt, i, true); + } - reg = RKVDEC_CUR_POC(dec_params->bottom_field_order_cnt); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC1); + regs->h26x.cur_poc = dec_params->top_field_order_cnt; + regs->h26x.cur_poc1 = dec_params->bottom_field_order_cnt; /* config hw pps address */ offset = offsetof(struct rkvdec_h264_priv_tbl, param_set); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_PPS_BASE); + regs->h26x.pps_base = priv_start_addr + offset; /* config hw rps address */ offset = offsetof(struct rkvdec_h264_priv_tbl, rps); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_RPS_BASE); - - reg = RKVDEC_AXI_DDR_RDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA); - - reg = RKVDEC_AXI_DDR_WDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA); + regs->h26x.rps_base = priv_start_addr + offset; offset = offsetof(struct rkvdec_h264_priv_tbl, err_info); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_H264_ERRINFO_BASE); -} - -#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2 + regs->h26x.errorinfo_base = priv_start_addr + offset; -static int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; - - fmt->num_planes = 1; - if (!fmt->plane_fmt[0].sizeimage) - fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * - RKVDEC_H264_MAX_DEPTH_IN_BYTES; - return 0; -} - -static enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, - struct v4l2_ctrl *ctrl) -{ - const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; - - if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) - return RKVDEC_IMG_FMT_ANY; - - if (sps->bit_depth_luma_minus8 == 0) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_8BIT; - else - return RKVDEC_IMG_FMT_420_8BIT; - } else if (sps->bit_depth_luma_minus8 == 2) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_10BIT; - else - return RKVDEC_IMG_FMT_420_10BIT; - } - - return RKVDEC_IMG_FMT_ANY; -} - -static int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, - const struct v4l2_ctrl_h264_sps *sps) -{ - unsigned int width, height; - - if (sps->chroma_format_idc > 2) - /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) - /* Only 8-bit and 10-bit is supported */ - return -EINVAL; - - width = (sps->pic_width_in_mbs_minus1 + 1) * 16; - height = (sps->pic_height_in_map_units_minus1 + 1) * 16; - - /* - * When frame_mbs_only_flag is not set, this is field height, - * which is half the final height (see (7-18) in the - * specification) - */ - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) - height *= 2; - - if (width > ctx->coded_fmt.fmt.pix_mp.width || - height > ctx->coded_fmt.fmt.pix_mp.height) - return -EINVAL; - - return 0; + rkvdec_memcpy_toio(rkvdec->regs, regs, + MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs)); } static int rkvdec_h264_start(struct rkvdec_ctx *ctx) @@ -1134,33 +409,13 @@ static void rkvdec_h264_stop(struct rkvdec_ctx *ctx) kfree(h264_ctx); } -static void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, - struct rkvdec_h264_run *run) -{ - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_DECODE_PARAMS); - run->decode_params = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_SPS); - run->sps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_PPS); - run->pps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_H264_SCALING_MATRIX); - run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; - - rkvdec_run_preamble(ctx, &run->base); -} - static int rkvdec_h264_run(struct rkvdec_ctx *ctx) { struct v4l2_h264_reflist_builder reflist_builder; struct rkvdec_dev *rkvdec = ctx->dev; struct rkvdec_h264_ctx *h264_ctx = ctx->priv; struct rkvdec_h264_run run; + struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; rkvdec_h264_run_preamble(ctx, &run); @@ -1171,18 +426,16 @@ static int rkvdec_h264_run(struct rkvdec_ctx *ctx) v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, h264_ctx->reflists.b1); - assemble_hw_scaling_list(ctx, &run); + assemble_hw_scaling_list(&run, &tbl->scaling_list); assemble_hw_pps(ctx, &run); lookup_ref_buf_idx(ctx, &run); - assemble_hw_rps(ctx, &reflist_builder, &run); + assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps); config_registers(ctx, &run); rkvdec_run_postamble(ctx, &run.base); schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); - writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); - writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E); writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c new file mode 100644 index 000000000000..28267ee30190 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip video decoder hevc common functions + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + * + * Copyright (C) 2023 Collabora, Ltd. + * Sebastian Fricke <sebastian.fricke@collabora.com> + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <linux/v4l2-common.h> +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-hevc-common.h" + +/* Store the Short term ref pic set calculated values */ +struct calculated_rps_st_set { + u8 num_delta_pocs; + u8 num_negative_pics; + u8 num_positive_pics; + u8 used_by_curr_pic_s0[16]; + u8 used_by_curr_pic_s1[16]; + s32 delta_poc_s0[16]; + s32 delta_poc_s1[16]; +}; + +void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size, + u16 width, u16 height, s32 pic_in_cts_width, + s32 pic_in_cts_height, u16 *column_width, u16 *row_height) +{ + const struct v4l2_ctrl_hevc_pps *pps = run->pps; + int i; + + for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++) + column_width[i] = ((i + 1) * pic_in_cts_width) / + (pps->num_tile_columns_minus1 + 1) - + (i * pic_in_cts_width) / + (pps->num_tile_columns_minus1 + 1); + + for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++) + row_height[i] = ((i + 1) * pic_in_cts_height) / + (pps->num_tile_rows_minus1 + 1) - + (i * pic_in_cts_height) / + (pps->num_tile_rows_minus1 + 1); +} + +void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size, + u16 width, u16 height, s32 pic_in_cts_width, + s32 pic_in_cts_height, u16 *column_width, u16 *row_height) +{ + const struct v4l2_ctrl_hevc_pps *pps = run->pps; + s32 sum = 0; + int i; + + for (i = 0; i < pps->num_tile_columns_minus1; i++) { + column_width[i] = pps->column_width_minus1[i] + 1; + sum += column_width[i]; + } + column_width[i] = pic_in_cts_width - sum; + + sum = 0; + for (i = 0; i < pps->num_tile_rows_minus1; i++) { + row_height[i] = pps->row_height_minus1[i] + 1; + sum += row_height[i]; + } + row_height[i] = pic_in_cts_height - sum; +} + +static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag) +{ + switch (poc) { + case 0: + set->delta_poc0 = value; + set->used_flag0 = flag; + break; + case 1: + set->delta_poc1 = value; + set->used_flag1 = flag; + break; + case 2: + set->delta_poc2 = value; + set->used_flag2 = flag; + break; + case 3: + set->delta_poc3 = value; + set->used_flag3 = flag; + break; + case 4: + set->delta_poc4 = value; + set->used_flag4 = flag; + break; + case 5: + set->delta_poc5 = value; + set->used_flag5 = flag; + break; + case 6: + set->delta_poc6 = value; + set->used_flag6 = flag; + break; + case 7: + set->delta_poc7 = value; + set->used_flag7 = flag; + break; + case 8: + set->delta_poc8 = value; + set->used_flag8 = flag; + break; + case 9: + set->delta_poc9 = value; + set->used_flag9 = flag; + break; + case 10: + set->delta_poc10 = value; + set->used_flag10 = flag; + break; + case 11: + set->delta_poc11 = value; + set->used_flag11 = flag; + break; + case 12: + set->delta_poc12 = value; + set->used_flag12 = flag; + break; + case 13: + set->delta_poc13 = value; + set->used_flag13 = flag; + break; + case 14: + set->delta_poc14 = value; + set->used_flag14 = flag; + break; + } +} + +static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output, + const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + const struct rkvdec_variant *variant = ctx->dev->variant; + int offset = 0; + + variant->ops->flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4); + offset = 6 * 16 * sizeof(u8); + variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8); + offset += 6 * 64 * sizeof(u8); + variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_16x16, + 6, 8); + offset += 6 * 64 * sizeof(u8); + /* Add a 128 byte padding with 0s between the two 32x32 matrices */ + variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_32x32, + 1, 8); + offset += 64 * sizeof(u8); + memset(output + offset, 0, 128); + offset += 128 * sizeof(u8); + variant->ops->flatten_matrices(output + offset, + (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)), + 1, 8); + offset += 64 * sizeof(u8); + memset(output + offset, 0, 128); +} + +/* + * Required layout: + * A = scaling_list_dc_coef_16x16 + * B = scaling_list_dc_coef_32x32 + * 0 = Padding + * + * A, A, A, A, A, A, B, 0, 0, B, 0, 0 + */ +static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + u8 list_32x32[6] = {0}; + + memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8)); + list_32x32[0] = input->scaling_list_dc_coef_32x32[0]; + list_32x32[3] = input->scaling_list_dc_coef_32x32[1]; + memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8)); +} + +static void translate_scaling_list(struct rkvdec_ctx *ctx, struct scaling_factor *output, + const struct v4l2_ctrl_hevc_scaling_matrix *input) +{ + assemble_scalingfactor0(ctx, output->scalingfactor0, input); + memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96); + assemble_scalingdc(output->scalingdc, input); + memset(output->reserved, 0, 4 * sizeof(u8)); +} + +void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run, + struct scaling_factor *scaling_factor, + struct v4l2_ctrl_hevc_scaling_matrix *cache) +{ + const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix; + + if (!memcmp(cache, scaling, + sizeof(struct v4l2_ctrl_hevc_scaling_matrix))) + return; + + translate_scaling_list(ctx, scaling_factor, scaling); + + memcpy(cache, scaling, + sizeof(struct v4l2_ctrl_hevc_scaling_matrix)); +} + +static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps) +{ + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + + if (!run->ext_sps_lt_rps) + return; + + for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { + rps->refs[i].lt_ref_pic_poc_lsb = + run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps; + rps->refs[i].used_by_curr_pic_lt_flag = + !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT); + } +} + +static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps, + struct calculated_rps_st_set *calculated_rps_st_sets) +{ + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + + for (int i = 0; i < sps->num_short_term_ref_pic_sets; i++) { + int poc = 0; + int j = 0; + const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i]; + + rps->short_term_ref_sets[i].num_negative = set->num_negative_pics; + rps->short_term_ref_sets[i].num_positive = set->num_positive_pics; + + for (; j < set->num_negative_pics; j++) { + set_ref_poc(&rps->short_term_ref_sets[i], j, + set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]); + } + poc = j; + + for (j = 0; j < set->num_positive_pics; j++) { + set_ref_poc(&rps->short_term_ref_sets[i], poc + j, + set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]); + } + } +} + +/* + * Compute the short term ref pic set parameters based on its reference short term ref pic + */ +static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx, + struct calculated_rps_st_set *calculated_rps_st_sets) +{ + const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx]; + struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx]; + struct calculated_rps_st_set *ref_rps; + u8 st_rps_idx = idx; + u8 ref_rps_idx = 0; + s16 delta_rps = 0; + u8 use_delta_flag[16] = { 0 }; + u8 used_by_curr_pic_flag[16] = { 0 }; + int i, j; + int dPoc; + + ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */ + delta_rps = (1 - 2 * rps_data->delta_rps_sign) * + (rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */ + + ref_rps = &calculated_rps_st_sets[ref_rps_idx]; + + for (j = 0; j <= ref_rps->num_delta_pocs; j++) { + used_by_curr_pic_flag[j] = !!(rps_data->used_by_curr_pic & (1 << j)); + use_delta_flag[j] = !!(rps_data->use_delta_flag & (1 << j)); + } + + /* 7-61: calculate num_negative_pics, delta_poc_s0 and used_by_curr_pic_s0 */ + i = 0; + for (j = (ref_rps->num_positive_pics - 1); j >= 0; j--) { + dPoc = ref_rps->delta_poc_s1[j] + delta_rps; + if (dPoc < 0 && use_delta_flag[ref_rps->num_negative_pics + j]) { + st_rps->delta_poc_s0[i] = dPoc; + st_rps->used_by_curr_pic_s0[i++] = + used_by_curr_pic_flag[ref_rps->num_negative_pics + j]; + } + } + if (delta_rps < 0 && use_delta_flag[ref_rps->num_delta_pocs]) { + st_rps->delta_poc_s0[i] = delta_rps; + st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs]; + } + for (j = 0; j < ref_rps->num_negative_pics; j++) { + dPoc = ref_rps->delta_poc_s0[j] + delta_rps; + if (dPoc < 0 && use_delta_flag[j]) { + st_rps->delta_poc_s0[i] = dPoc; + st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j]; + } + } + st_rps->num_negative_pics = i; + + /* 7-62: calculate num_positive_pics, delta_poc_s1 and used_by_curr_pic_s1 */ + i = 0; + for (j = (ref_rps->num_negative_pics - 1); j >= 0; j--) { + dPoc = ref_rps->delta_poc_s0[j] + delta_rps; + if (dPoc > 0 && use_delta_flag[j]) { + st_rps->delta_poc_s1[i] = dPoc; + st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j]; + } + } + if (delta_rps > 0 && use_delta_flag[ref_rps->num_delta_pocs]) { + st_rps->delta_poc_s1[i] = delta_rps; + st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs]; + } + for (j = 0; j < ref_rps->num_positive_pics; j++) { + dPoc = ref_rps->delta_poc_s1[j] + delta_rps; + if (dPoc > 0 && use_delta_flag[ref_rps->num_negative_pics + j]) { + st_rps->delta_poc_s1[i] = dPoc; + st_rps->used_by_curr_pic_s1[i++] = + used_by_curr_pic_flag[ref_rps->num_negative_pics + j]; + } + } + st_rps->num_positive_pics = i; + + st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics; +} + +/* + * Compute the short term ref pic set parameters based on the control's data. + */ +static void st_ref_pic_set_calculate(struct rkvdec_hevc_run *run, int idx, + struct calculated_rps_st_set *calculated_rps_st_sets) +{ + const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx]; + struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx]; + int j, i = 0; + + /* 7-63 */ + st_rps->num_negative_pics = rps_data->num_negative_pics; + /* 7-64 */ + st_rps->num_positive_pics = rps_data->num_positive_pics; + + for (i = 0; i < st_rps->num_negative_pics; i++) { + /* 7-65 */ + st_rps->used_by_curr_pic_s0[i] = !!(rps_data->used_by_curr_pic & (1 << i)); + + if (i == 0) { + /* 7-67 */ + st_rps->delta_poc_s0[i] = -(rps_data->delta_poc_s0_minus1[i] + 1); + } else { + /* 7-69 */ + st_rps->delta_poc_s0[i] = + st_rps->delta_poc_s0[i - 1] - + (rps_data->delta_poc_s0_minus1[i] + 1); + } + } + + for (j = 0; j < st_rps->num_positive_pics; j++) { + /* 7-66 */ + st_rps->used_by_curr_pic_s1[j] = !!(rps_data->used_by_curr_pic & (1 << (i + j))); + + if (j == 0) { + /* 7-68 */ + st_rps->delta_poc_s1[j] = rps_data->delta_poc_s1_minus1[j] + 1; + } else { + /* 7-70 */ + st_rps->delta_poc_s1[j] = + st_rps->delta_poc_s1[j - 1] + + (rps_data->delta_poc_s1_minus1[j] + 1); + } + } + + /* 7-71 */ + st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics; +} + +static void rkvdec_hevc_prepare_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps, + struct v4l2_ctrl_hevc_ext_sps_st_rps *cache) +{ + int idx; + + if (!run->ext_sps_st_rps) + return; + + if (!memcmp(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps))) + return; + + struct calculated_rps_st_set *calculated_rps_st_sets = + kzalloc(sizeof(struct calculated_rps_st_set) * + run->sps->num_short_term_ref_pic_sets, GFP_KERNEL); + + for (idx = 0; idx < run->sps->num_short_term_ref_pic_sets; idx++) { + const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx]; + + if (rps_data->flags & V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED) + st_ref_pic_set_prediction(run, idx, calculated_rps_st_sets); + else + st_ref_pic_set_calculate(run, idx, calculated_rps_st_sets); + } + + rkvdec_hevc_assemble_hw_st_rps(run, rps, calculated_rps_st_sets); + + kfree(calculated_rps_st_sets); + + memcpy(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps)); +} + +void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps, + struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache) +{ + rkvdec_hevc_prepare_hw_st_rps(run, rps, st_cache); + rkvdec_hevc_assemble_hw_lt_rps(run, rps); +} + +struct vb2_buffer * +get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run, + unsigned int dpb_idx) +{ + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; + const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params; + const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; + struct vb2_buffer *buf = NULL; + + if (dpb_idx < decode_params->num_active_dpb_entries) + buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp); + + /* + * If a DPB entry is unused or invalid, the address of current destination + * buffer is returned. + */ + if (!buf) + return &run->base.bufs.dst->vb2_buf; + + return buf; +} + +#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2 + +int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; + + fmt->num_planes = 1; + if (!fmt->plane_fmt[0].sizeimage) + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * + RKVDEC_HEVC_MAX_DEPTH_IN_BYTES; + return 0; +} + +enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, + struct v4l2_ctrl *ctrl) +{ + const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; + + if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS) + return RKVDEC_IMG_FMT_ANY; + + if (sps->bit_depth_luma_minus8 == 0) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_8BIT; + else + return RKVDEC_IMG_FMT_420_8BIT; + } else if (sps->bit_depth_luma_minus8 == 2) { + if (sps->chroma_format_idc == 2) + return RKVDEC_IMG_FMT_422_10BIT; + else + return RKVDEC_IMG_FMT_420_10BIT; + } + + return RKVDEC_IMG_FMT_ANY; +} + +void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SLICE_PARAMS); + run->slices_params = ctrl ? ctrl->p_cur.p : NULL; + run->num_slices = ctrl ? ctrl->new_elems : 0; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SPS); + run->sps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_PPS); + run->pps = ctrl ? ctrl->p_cur.p : NULL; + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); + run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; + + if (ctx->has_sps_st_rps) { + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS); + run->ext_sps_st_rps = ctrl ? ctrl->p_cur.p : NULL; + } + if (ctx->has_sps_lt_rps) { + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS); + run->ext_sps_lt_rps = ctrl ? ctrl->p_cur.p : NULL; + } + + rkvdec_run_preamble(ctx, &run->base); +} diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h new file mode 100644 index 000000000000..6f4faca4c091 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip video decoder hevc common functions + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + * + * Copyright (C) 2023 Collabora, Ltd. + * Sebastian Fricke <sebastian.fricke@collabora.com> + * + * Copyright (C) 2019 Collabora, Ltd. + * Boris Brezillon <boris.brezillon@collabora.com> + * + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. + * Jeffy Chen <jeffy.chen@rock-chips.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <linux/types.h> + +#include "rkvdec.h" + +struct rkvdec_rps_refs { + u16 lt_ref_pic_poc_lsb; + u16 used_by_curr_pic_lt_flag : 1; + u16 reserved : 15; +} __packed; + +struct rkvdec_rps_short_term_ref_set { + u32 num_negative : 4; + u32 num_positive : 4; + u32 delta_poc0 : 16; + u32 used_flag0 : 1; + u32 delta_poc1 : 16; + u32 used_flag1 : 1; + u32 delta_poc2 : 16; + u32 used_flag2 : 1; + u32 delta_poc3 : 16; + u32 used_flag3 : 1; + u32 delta_poc4 : 16; + u32 used_flag4 : 1; + u32 delta_poc5 : 16; + u32 used_flag5 : 1; + u32 delta_poc6 : 16; + u32 used_flag6 : 1; + u32 delta_poc7 : 16; + u32 used_flag7 : 1; + u32 delta_poc8 : 16; + u32 used_flag8 : 1; + u32 delta_poc9 : 16; + u32 used_flag9 : 1; + u32 delta_poc10 : 16; + u32 used_flag10 : 1; + u32 delta_poc11 : 16; + u32 used_flag11 : 1; + u32 delta_poc12 : 16; + u32 used_flag12 : 1; + u32 delta_poc13 : 16; + u32 used_flag13 : 1; + u32 delta_poc14 : 16; + u32 used_flag14 : 1; + u32 reserved_bits : 25; + u32 reserved[3]; +} __packed; + +struct rkvdec_rps { + struct rkvdec_rps_refs refs[32]; + struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64]; +} __packed; + +struct rkvdec_hevc_run { + struct rkvdec_run base; + const struct v4l2_ctrl_hevc_slice_params *slices_params; + const struct v4l2_ctrl_hevc_decode_params *decode_params; + const struct v4l2_ctrl_hevc_sps *sps; + const struct v4l2_ctrl_hevc_pps *pps; + const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix; + const struct v4l2_ctrl_hevc_ext_sps_st_rps *ext_sps_st_rps; + const struct v4l2_ctrl_hevc_ext_sps_lt_rps *ext_sps_lt_rps; + int num_slices; +}; + +struct scaling_factor { + u8 scalingfactor0[1248]; + u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/ + u8 scalingdc[12]; /*N1005 Vienna Meeting*/ + u8 reserved[4]; /*16Bytes align*/ +}; + +void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size, + u16 width, u16 height, s32 pic_in_cts_width, + s32 pic_in_cts_height, u16 *column_width, u16 *row_height); +void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size, + u16 width, u16 height, s32 pic_in_cts_width, + s32 pic_in_cts_height, u16 *column_width, u16 *row_height); +void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps, + struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache); +void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run, + struct scaling_factor *scaling_factor, + struct v4l2_ctrl_hevc_scaling_matrix *cache); +struct vb2_buffer *get_ref_buf(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run, + unsigned int dpb_idx); +int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f); +enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl); +void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index fc7e6a260b0a..3276f584f5c7 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -16,7 +16,8 @@ #include "rkvdec.h" #include "rkvdec-regs.h" -#include "rkvdec-hevc-data.c" +#include "rkvdec-cabac.h" +#include "rkvdec-hevc-common.h" /* Size in u8/u32 units. */ #define RKV_SCALING_LIST_SIZE 1360 @@ -110,32 +111,16 @@ struct rkvdec_ps_field { /* Data structure describing auxiliary buffer format. */ struct rkvdec_hevc_priv_tbl { - u8 cabac_table[RKV_CABAC_TABLE_SIZE]; - u8 scaling_list[RKV_SCALING_LIST_SIZE]; + u8 cabac_table[RKV_HEVC_CABAC_TABLE_SIZE]; + struct scaling_factor scaling_list; struct rkvdec_sps_pps_packet param_set[RKV_PPS_LEN]; struct rkvdec_rps_packet rps[RKV_RPS_LEN]; }; -struct rkvdec_hevc_run { - struct rkvdec_run base; - const struct v4l2_ctrl_hevc_slice_params *slices_params; - const struct v4l2_ctrl_hevc_decode_params *decode_params; - const struct v4l2_ctrl_hevc_sps *sps; - const struct v4l2_ctrl_hevc_pps *pps; - const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix; - int num_slices; -}; - struct rkvdec_hevc_ctx { struct rkvdec_aux_buf priv_tbl; struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache; -}; - -struct scaling_factor { - u8 scalingfactor0[1248]; - u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/ - u8 scalingdc[12]; /*N1005 Vienna Meeting*/ - u8 reserved[4]; /*16Bytes align*/ + struct rkvdec_regs regs; }; static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value) @@ -414,131 +399,6 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx, } } -/* - * Flip one or more matrices along their main diagonal and flatten them - * before writing it to the memory. - * Convert: - * ABCD AEIM - * EFGH => BFJN => AEIMBFJNCGKODHLP - * IJKL CGKO - * MNOP DHLP - */ -static void transpose_and_flatten_matrices(u8 *output, const u8 *input, - int matrices, int row_length) -{ - int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value; - - matrix_size = row_length * row_length; - for (i = 0; i < matrices; i++) { - row = 0; - x_offset = 0; - matrix_offset = i * matrix_size; - for (j = 0; j < matrix_size; j++) { - y_offset = j - (row * row_length); - rot_index = y_offset * row_length + x_offset; - new_value = *(input + i * matrix_size + j); - output[matrix_offset + rot_index] = new_value; - if ((j + 1) % row_length == 0) { - row += 1; - x_offset += 1; - } - } - } -} - -static void assemble_scalingfactor0(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) -{ - int offset = 0; - - transpose_and_flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4); - offset = 6 * 16 * sizeof(u8); - transpose_and_flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8); - offset += 6 * 64 * sizeof(u8); - transpose_and_flatten_matrices(output + offset, - (const u8 *)input->scaling_list_16x16, 6, 8); - offset += 6 * 64 * sizeof(u8); - /* Add a 128 byte padding with 0s between the two 32x32 matrices */ - transpose_and_flatten_matrices(output + offset, - (const u8 *)input->scaling_list_32x32, 1, 8); - offset += 64 * sizeof(u8); - memset(output + offset, 0, 128); - offset += 128 * sizeof(u8); - transpose_and_flatten_matrices(output + offset, - (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)), - 1, 8); - offset += 64 * sizeof(u8); - memset(output + offset, 0, 128); -} - -/* - * Required layout: - * A = scaling_list_dc_coef_16x16 - * B = scaling_list_dc_coef_32x32 - * 0 = Padding - * - * A, A, A, A, A, A, B, 0, 0, B, 0, 0 - */ -static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input) -{ - u8 list_32x32[6] = {0}; - - memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8)); - list_32x32[0] = input->scaling_list_dc_coef_32x32[0]; - list_32x32[3] = input->scaling_list_dc_coef_32x32[1]; - memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8)); -} - -static void translate_scaling_list(struct scaling_factor *output, - const struct v4l2_ctrl_hevc_scaling_matrix *input) -{ - assemble_scalingfactor0(output->scalingfactor0, input); - memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96); - assemble_scalingdc(output->scalingdc, input); - memset(output->reserved, 0, 4 * sizeof(u8)); -} - -static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx, - struct rkvdec_hevc_run *run) -{ - const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix; - struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; - struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu; - u8 *dst; - - if (!memcmp((void *)&hevc_ctx->scaling_matrix_cache, scaling, - sizeof(struct v4l2_ctrl_hevc_scaling_matrix))) - return; - - dst = tbl->scaling_list; - translate_scaling_list((struct scaling_factor *)dst, scaling); - - memcpy((void *)&hevc_ctx->scaling_matrix_cache, scaling, - sizeof(struct v4l2_ctrl_hevc_scaling_matrix)); -} - -static struct vb2_buffer * -get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run, - unsigned int dpb_idx) -{ - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params; - const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf = NULL; - - if (dpb_idx < decode_params->num_active_dpb_entries) - buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp); - - /* - * If a DPB entry is unused or invalid, the address of current destination - * buffer is returned. - */ - if (!buf) - return &run->base.bufs.dst->vb2_buf; - - return buf; -} - static void config_registers(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run) { @@ -548,6 +408,7 @@ static void config_registers(struct rkvdec_ctx *ctx, const struct v4l2_ctrl_hevc_slice_params *sl_params = &run->slices_params[0]; const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_regs *regs = &hevc_ctx->regs; dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma; const struct v4l2_pix_format_mplane *dst_fmt; struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; @@ -564,8 +425,9 @@ static void config_registers(struct rkvdec_ctx *ctx, dma_addr_t dst_addr; u32 reg, i; - reg = RKVDEC_MODE(RKVDEC_MODE_HEVC); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL); + memset(regs, 0, sizeof(*regs)); + + regs->common.reg02.dec_mode = RKVDEC_MODE_HEVC; f = &ctx->decoded_fmt; dst_fmt = &f->fmt.pix_mp; @@ -580,33 +442,27 @@ static void config_registers(struct rkvdec_ctx *ctx, else if (sps->chroma_format_idc == 2) yuv_virstride = 2 * y_virstride; - reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) | - RKVDEC_SLICE_NUM_LOWBITS(run->num_slices); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR); + regs->common.reg03.slice_num_lowbits = run->num_slices; + regs->common.reg03.uv_hor_virstride = hor_virstride / 16; + regs->common.reg03.y_hor_virstride = hor_virstride / 16; /* config rlc base address */ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); + regs->common.strm_rlc_base = rlc_addr; rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - reg = RKVDEC_STRM_LEN(round_up(rlc_len, 16) + 64); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN); + regs->common.stream_len = round_up(rlc_len, 16) + 64; /* config cabac table */ offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); + regs->common.cabactbl_base = priv_start_addr + offset; /* config output base address */ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + regs->common.decout_base = dst_addr; - reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); - - reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + regs->common.reg08.y_virstride = y_virstride / 16; + regs->common.reg09.yuv_virstride = yuv_virstride / 16; /* config ref pic address */ for (i = 0; i < 15; i++) { @@ -620,70 +476,30 @@ static void config_registers(struct rkvdec_ctx *ctx, } refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0); - writel_relaxed(refer_addr | reg, - rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i)); - reg = RKVDEC_POC_REFER(i < decode_params->num_active_dpb_entries ? - dpb[i].pic_order_cnt_val : 0); - writel_relaxed(reg, - rkvdec->regs + RKVDEC_REG_H264_POC_REFER0(i)); + regs->h26x.ref0_14_base[i].base_addr = refer_addr >> 4; + regs->h26x.ref0_14_base[i].field_ref = !!(reg & 1); + regs->h26x.ref0_14_base[i].topfield_used_ref = !!(reg & 2); + regs->h26x.ref0_14_base[i].botfield_used_ref = !!(reg & 4); + regs->h26x.ref0_14_base[i].colmv_use_flag_ref = !!(reg & 8); + + regs->h26x.ref0_14_poc[i] = i < decode_params->num_active_dpb_entries + ? dpb[i].pic_order_cnt_val + : 0; } - reg = RKVDEC_CUR_POC(sl_params->slice_pic_order_cnt); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0); + regs->h26x.cur_poc = sl_params->slice_pic_order_cnt; /* config hw pps address */ offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_PPS_BASE); + regs->h26x.pps_base = priv_start_addr + offset; /* config hw rps address */ offset = offsetof(struct rkvdec_hevc_priv_tbl, rps); - writel_relaxed(priv_start_addr + offset, - rkvdec->regs + RKVDEC_REG_RPS_BASE); + regs->h26x.rps_base = priv_start_addr + offset; - reg = RKVDEC_AXI_DDR_RDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA); - - reg = RKVDEC_AXI_DDR_WDATA(0); - writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA); -} - -#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2 - -static int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; - - fmt->num_planes = 1; - if (!fmt->plane_fmt[0].sizeimage) - fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * - RKVDEC_HEVC_MAX_DEPTH_IN_BYTES; - return 0; -} - -static enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, - struct v4l2_ctrl *ctrl) -{ - const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; - - if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS) - return RKVDEC_IMG_FMT_ANY; - - if (sps->bit_depth_luma_minus8 == 0) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_8BIT; - else - return RKVDEC_IMG_FMT_420_8BIT; - } else if (sps->bit_depth_luma_minus8 == 2) { - if (sps->chroma_format_idc == 2) - return RKVDEC_IMG_FMT_422_10BIT; - else - return RKVDEC_IMG_FMT_420_10BIT; - } - - return RKVDEC_IMG_FMT_ANY; + rkvdec_memcpy_toio(rkvdec->regs, regs, + MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs)); } static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx, @@ -696,7 +512,7 @@ static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx, /* Luma and chroma bit depth mismatch */ return -EINVAL; if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) - /* Only 8-bit and 10-bit is supported */ + /* Only 8-bit and 10-bit are supported */ return -EINVAL; if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width || @@ -742,40 +558,18 @@ static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx) kfree(hevc_ctx); } -static void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, - struct rkvdec_hevc_run *run) -{ - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); - run->decode_params = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_HEVC_SLICE_PARAMS); - run->slices_params = ctrl ? ctrl->p_cur.p : NULL; - run->num_slices = ctrl ? ctrl->new_elems : 0; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_HEVC_SPS); - run->sps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_HEVC_PPS); - run->pps = ctrl ? ctrl->p_cur.p : NULL; - ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, - V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); - run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; - - rkvdec_run_preamble(ctx, &run->base); -} - static int rkvdec_hevc_run(struct rkvdec_ctx *ctx) { struct rkvdec_dev *rkvdec = ctx->dev; struct rkvdec_hevc_run run; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu; u32 reg; rkvdec_hevc_run_preamble(ctx, &run); - assemble_hw_scaling_list(ctx, &run); + rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list, + &hevc_ctx->scaling_matrix_cache); assemble_hw_pps(ctx, &run); assemble_sw_rps(ctx, &run); config_registers(ctx, &run); @@ -784,8 +578,6 @@ static int rkvdec_hevc_run(struct rkvdec_ctx *ctx) schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000)); - writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); - writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E); writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c new file mode 100644 index 000000000000..fdcf1f177379 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip video decoder Rows and Cols Buffers manager + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include "rkvdec.h" +#include "rkvdec-rcb.h" + +#include <linux/iommu.h> +#include <linux/genalloc.h> +#include <linux/sizes.h> +#include <linux/types.h> + +struct rkvdec_rcb_config { + struct rkvdec_aux_buf *rcb_bufs; + size_t rcb_count; +}; + +static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info, + unsigned int width, unsigned int height) +{ + return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width); +} + +dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id) +{ + return ctx->rcb_config->rcb_bufs[id].dma; +} + +size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id) +{ + return ctx->rcb_config->rcb_bufs[id].size; +} + +int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx) +{ + return ctx->rcb_config->rcb_count; +} + +void rkvdec_free_rcb(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *dev = ctx->dev; + struct rkvdec_rcb_config *cfg = ctx->rcb_config; + unsigned long virt_addr; + int i; + + if (!cfg) + return; + + for (i = 0; i < cfg->rcb_count; i++) { + size_t rcb_size = cfg->rcb_bufs[i].size; + + if (!cfg->rcb_bufs[i].cpu) + continue; + + switch (cfg->rcb_bufs[i].type) { + case RKVDEC_ALLOC_SRAM: + virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu; + + if (dev->iommu_domain) + iommu_unmap(dev->iommu_domain, virt_addr, rcb_size); + gen_pool_free(dev->sram_pool, virt_addr, rcb_size); + break; + case RKVDEC_ALLOC_DMA: + dma_free_coherent(dev->dev, + rcb_size, + cfg->rcb_bufs[i].cpu, + cfg->rcb_bufs[i].dma); + break; + } + } + + if (cfg->rcb_bufs) + devm_kfree(dev->dev, cfg->rcb_bufs); + + devm_kfree(dev->dev, cfg); +} + +int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, + const struct rcb_size_info *size_info, + size_t rcb_count) +{ + int ret, i; + u32 width, height; + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_rcb_config *cfg; + + if (!size_info || !rcb_count) { + ctx->rcb_config = NULL; + return 0; + } + + ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL); + if (!ctx->rcb_config) + return -ENOMEM; + + cfg = ctx->rcb_config; + + cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL); + if (!cfg->rcb_bufs) { + ret = -ENOMEM; + goto err_alloc; + } + + width = ctx->decoded_fmt.fmt.pix_mp.width; + height = ctx->decoded_fmt.fmt.pix_mp.height; + + for (i = 0; i < rcb_count; i++) { + void *cpu = NULL; + dma_addr_t dma; + size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height); + enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM; + + /* Try allocating an SRAM buffer */ + if (ctx->dev->sram_pool) { + if (rkvdec->iommu_domain) + rcb_size = ALIGN(rcb_size, SZ_4K); + + cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool, + rcb_size, + &dma, + SZ_4K); + } + + /* If an IOMMU is used, map the SRAM address through it */ + if (cpu && rkvdec->iommu_domain) { + unsigned long virt_addr = (unsigned long)cpu; + phys_addr_t phys_addr = dma; + + ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr, + rcb_size, IOMMU_READ | IOMMU_WRITE, 0); + if (ret) { + gen_pool_free(ctx->dev->sram_pool, + (unsigned long)cpu, + rcb_size); + cpu = NULL; + goto ram_fallback; + } + + /* + * The registers will be configured with the virtual + * address so that it goes through the IOMMU + */ + dma = virt_addr; + } + +ram_fallback: + /* Fallback to RAM */ + if (!cpu) { + cpu = dma_alloc_coherent(ctx->dev->dev, + rcb_size, + &dma, + GFP_KERNEL); + alloc_type = RKVDEC_ALLOC_DMA; + } + + if (!cpu) { + ret = -ENOMEM; + goto err_alloc; + } + + cfg->rcb_bufs[i].cpu = cpu; + cfg->rcb_bufs[i].dma = dma; + cfg->rcb_bufs[i].size = rcb_size; + cfg->rcb_bufs[i].type = alloc_type; + + cfg->rcb_count += 1; + } + + return 0; + +err_alloc: + rkvdec_free_rcb(ctx); + + return ret; +} diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h new file mode 100644 index 000000000000..30e8002555c8 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-rcb.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip video decoder Rows and Cols Buffers manager + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <linux/types.h> + +struct rkvdec_ctx; + +enum rcb_axis { + PIC_WIDTH = 0, + PIC_HEIGHT = 1 +}; + +struct rcb_size_info { + u8 multiplier; + enum rcb_axis axis; +}; + +int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx, + const struct rcb_size_info *size_info, + size_t rcb_count); +dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id); +size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id); +int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx); +void rkvdec_free_rcb(struct rkvdec_ctx *ctx); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h index c627b6b6f53a..1af66c5f1c9b 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h @@ -3,7 +3,12 @@ #ifndef RKVDEC_REGS_H_ #define RKVDEC_REGS_H_ -/* rkvcodec registers */ +#include <linux/types.h> + +/* + * REG_INTERRUPT is accessed via writel to enable the decoder after + * configuring it and clear interrupt strmd_error_status + */ #define RKVDEC_REG_INTERRUPT 0x004 #define RKVDEC_INTERRUPT_DEC_E BIT(0) #define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1) @@ -30,198 +35,399 @@ #define RKVDEC_SOFTRESET_RDY BIT(22) #define RKVDEC_WR_DDR_ALIGN_EN BIT(23) -#define RKVDEC_REG_SYSCTRL 0x008 -#define RKVDEC_IN_ENDIAN BIT(0) -#define RKVDEC_IN_SWAP32_E BIT(1) -#define RKVDEC_IN_SWAP64_E BIT(2) -#define RKVDEC_STR_ENDIAN BIT(3) -#define RKVDEC_STR_SWAP32_E BIT(4) -#define RKVDEC_STR_SWAP64_E BIT(5) -#define RKVDEC_OUT_ENDIAN BIT(6) -#define RKVDEC_OUT_SWAP32_E BIT(7) -#define RKVDEC_OUT_CBCR_SWAP BIT(8) -#define RKVDEC_RLC_MODE_DIRECT_WRITE BIT(10) -#define RKVDEC_RLC_MODE BIT(11) -#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12) -#define RKVDEC_MODE(x) (((x) & 0x03) << 20) -#define RKVDEC_MODE_HEVC 0 -#define RKVDEC_MODE_H264 1 -#define RKVDEC_MODE_VP9 2 -#define RKVDEC_RPS_MODE BIT(24) -#define RKVDEC_STRM_MODE BIT(25) -#define RKVDEC_H264_STRM_LASTPKT BIT(26) -#define RKVDEC_H264_FIRSTSLICE_FLAG BIT(27) -#define RKVDEC_H264_FRAME_ORSLICE BIT(28) -#define RKVDEC_BUSPR_SLOT_DIS BIT(29) - -#define RKVDEC_REG_PICPAR 0x00C -#define RKVDEC_Y_HOR_VIRSTRIDE(x) ((x) & 0x1ff) -#define RKVDEC_SLICE_NUM_HIGHBIT BIT(11) -#define RKVDEC_UV_HOR_VIRSTRIDE(x) (((x) & 0x1ff) << 12) -#define RKVDEC_SLICE_NUM_LOWBITS(x) (((x) & 0x7ff) << 21) - -#define RKVDEC_REG_STRM_RLC_BASE 0x010 - -#define RKVDEC_REG_STRM_LEN 0x014 -#define RKVDEC_STRM_LEN(x) ((x) & 0x7ffffff) - -#define RKVDEC_REG_CABACTBL_PROB_BASE 0x018 -#define RKVDEC_REG_DECOUT_BASE 0x01C - -#define RKVDEC_REG_Y_VIRSTRIDE 0x020 -#define RKVDEC_Y_VIRSTRIDE(x) ((x) & 0xfffff) - -#define RKVDEC_REG_YUV_VIRSTRIDE 0x024 -#define RKVDEC_YUV_VIRSTRIDE(x) ((x) & 0x1fffff) -#define RKVDEC_REG_H264_BASE_REFER(i) (((i) * 0x04) + 0x028) - -#define RKVDEC_REG_H264_BASE_REFER15 0x0C0 -#define RKVDEC_FIELD_REF BIT(0) -#define RKVDEC_TOPFIELD_USED_REF BIT(1) -#define RKVDEC_BOTFIELD_USED_REF BIT(2) -#define RKVDEC_COLMV_USED_FLAG_REF BIT(3) - -#define RKVDEC_REG_VP9_LAST_FRAME_BASE 0x02c -#define RKVDEC_REG_VP9_GOLDEN_FRAME_BASE 0x030 -#define RKVDEC_REG_VP9_ALTREF_FRAME_BASE 0x034 - -#define RKVDEC_REG_VP9_CPRHEADER_OFFSET 0x028 -#define RKVDEC_VP9_CPRHEADER_OFFSET(x) ((x) & 0xffff) - -#define RKVDEC_REG_VP9_REFERLAST_BASE 0x02C -#define RKVDEC_REG_VP9_REFERGOLDEN_BASE 0x030 -#define RKVDEC_REG_VP9_REFERALFTER_BASE 0x034 - -#define RKVDEC_REG_VP9COUNT_BASE 0x038 -#define RKVDEC_VP9COUNT_UPDATE_EN BIT(0) - -#define RKVDEC_REG_VP9_SEGIDLAST_BASE 0x03C -#define RKVDEC_REG_VP9_SEGIDCUR_BASE 0x040 -#define RKVDEC_REG_VP9_FRAME_SIZE(i) ((i) * 0x04 + 0x044) -#define RKVDEC_VP9_FRAMEWIDTH(x) (((x) & 0xffff) << 0) -#define RKVDEC_VP9_FRAMEHEIGHT(x) (((x) & 0xffff) << 16) - -#define RKVDEC_VP9_SEGID_GRP(i) ((i) * 0x04 + 0x050) -#define RKVDEC_SEGID_ABS_DELTA(x) ((x) & 0x1) -#define RKVDEC_SEGID_FRAME_QP_DELTA_EN(x) (((x) & 0x1) << 1) -#define RKVDEC_SEGID_FRAME_QP_DELTA(x) (((x) & 0x1ff) << 2) -#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(x) (((x) & 0x1) << 11) -#define RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(x) (((x) & 0x7f) << 12) -#define RKVDEC_SEGID_REFERINFO_EN(x) (((x) & 0x1) << 19) -#define RKVDEC_SEGID_REFERINFO(x) (((x) & 0x03) << 20) -#define RKVDEC_SEGID_FRAME_SKIP_EN(x) (((x) & 0x1) << 22) - -#define RKVDEC_VP9_CPRHEADER_CONFIG 0x070 -#define RKVDEC_VP9_TX_MODE(x) ((x) & 0x07) -#define RKVDEC_VP9_FRAME_REF_MODE(x) (((x) & 0x03) << 3) - -#define RKVDEC_VP9_REF_SCALE(i) ((i) * 0x04 + 0x074) -#define RKVDEC_VP9_REF_HOR_SCALE(x) ((x) & 0xffff) -#define RKVDEC_VP9_REF_VER_SCALE(x) (((x) & 0xffff) << 16) - -#define RKVDEC_VP9_REF_DELTAS_LASTFRAME 0x080 -#define RKVDEC_REF_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) - -#define RKVDEC_VP9_INFO_LASTFRAME 0x084 -#define RKVDEC_MODE_DELTAS_LASTFRAME(pos, val) (((val) & 0x7f) << ((pos) * 7)) -#define RKVDEC_SEG_EN_LASTFRAME BIT(16) -#define RKVDEC_LAST_SHOW_FRAME BIT(17) -#define RKVDEC_LAST_INTRA_ONLY BIT(18) -#define RKVDEC_LAST_WIDHHEIGHT_EQCUR BIT(19) -#define RKVDEC_COLOR_SPACE_LASTKEYFRAME(x) (((x) & 0x07) << 20) - -#define RKVDEC_VP9_INTERCMD_BASE 0x088 - -#define RKVDEC_VP9_INTERCMD_NUM 0x08C -#define RKVDEC_INTERCMD_NUM(x) ((x) & 0xffffff) - -#define RKVDEC_VP9_LASTTILE_SIZE 0x090 -#define RKVDEC_LASTTILE_SIZE(x) ((x) & 0xffffff) - -#define RKVDEC_VP9_HOR_VIRSTRIDE(i) ((i) * 0x04 + 0x094) -#define RKVDEC_HOR_Y_VIRSTRIDE(x) ((x) & 0x1ff) -#define RKVDEC_HOR_UV_VIRSTRIDE(x) (((x) & 0x1ff) << 16) - -#define RKVDEC_REG_H264_POC_REFER0(i) (((i) * 0x04) + 0x064) -#define RKVDEC_REG_H264_POC_REFER1(i) (((i) * 0x04) + 0x0C4) -#define RKVDEC_REG_H264_POC_REFER2(i) (((i) * 0x04) + 0x120) -#define RKVDEC_POC_REFER(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_CUR_POC0 0x0A0 -#define RKVDEC_REG_CUR_POC1 0x128 -#define RKVDEC_CUR_POC(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_RLCWRITE_BASE 0x0A4 -#define RKVDEC_REG_PPS_BASE 0x0A8 -#define RKVDEC_REG_RPS_BASE 0x0AC - -#define RKVDEC_REG_STRMD_ERR_EN 0x0B0 -#define RKVDEC_STRMD_ERR_EN(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_STRMD_ERR_STA 0x0B4 -#define RKVDEC_STRMD_ERR_STA(x) ((x) & 0xfffffff) -#define RKVDEC_COLMV_ERR_REF_PICIDX(x) (((x) & 0x0f) << 28) - -#define RKVDEC_REG_STRMD_ERR_CTU 0x0B8 -#define RKVDEC_STRMD_ERR_CTU(x) ((x) & 0xff) -#define RKVDEC_STRMD_ERR_CTU_YOFFSET(x) (((x) & 0xff) << 8) -#define RKVDEC_STRMFIFO_SPACE2FULL(x) (((x) & 0x7f) << 16) -#define RKVDEC_VP9_ERR_EN_CTU0 BIT(24) - -#define RKVDEC_REG_SAO_CTU_POS 0x0BC -#define RKVDEC_SAOWR_XOFFSET(x) ((x) & 0x1ff) -#define RKVDEC_SAOWR_YOFFSET(x) (((x) & 0x3ff) << 16) - -#define RKVDEC_VP9_LAST_FRAME_YSTRIDE 0x0C0 -#define RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE 0x0C4 -#define RKVDEC_VP9_ALTREF_FRAME_YSTRIDE 0x0C8 -#define RKVDEC_VP9_REF_YSTRIDE(x) (((x) & 0xfffff) << 0) - -#define RKVDEC_VP9_LAST_FRAME_YUVSTRIDE 0x0CC -#define RKVDEC_VP9_REF_YUVSTRIDE(x) (((x) & 0x1fffff) << 0) - -#define RKVDEC_VP9_REF_COLMV_BASE 0x0D0 - -#define RKVDEC_REG_PERFORMANCE_CYCLE 0x100 -#define RKVDEC_PERFORMANCE_CYCLE(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_AXI_DDR_RDATA 0x104 -#define RKVDEC_AXI_DDR_RDATA(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_AXI_DDR_WDATA 0x108 -#define RKVDEC_AXI_DDR_WDATA(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_FPGADEBUG_RESET 0x10C -#define RKVDEC_BUSIFD_RESETN BIT(0) -#define RKVDEC_CABAC_RESETN BIT(1) -#define RKVDEC_DEC_CTRL_RESETN BIT(2) -#define RKVDEC_TRANSD_RESETN BIT(3) -#define RKVDEC_INTRA_RESETN BIT(4) -#define RKVDEC_INTER_RESETN BIT(5) -#define RKVDEC_RECON_RESETN BIT(6) -#define RKVDEC_FILER_RESETN BIT(7) - -#define RKVDEC_REG_PERFORMANCE_SEL 0x110 -#define RKVDEC_PERF_SEL_CNT0(x) ((x) & 0x3f) -#define RKVDEC_PERF_SEL_CNT1(x) (((x) & 0x3f) << 8) -#define RKVDEC_PERF_SEL_CNT2(x) (((x) & 0x3f) << 16) - -#define RKVDEC_REG_PERFORMANCE_CNT(i) ((i) * 0x04 + 0x114) -#define RKVDEC_PERF_CNT(x) ((x) & 0xffffffff) - -#define RKVDEC_REG_H264_ERRINFO_BASE 0x12C - -#define RKVDEC_REG_H264_ERRINFO_NUM 0x130 -#define RKVDEC_SLICEDEC_NUM(x) ((x) & 0x3fff) -#define RKVDEC_STRMD_DECT_ERR_FLAG BIT(15) -#define RKVDEC_ERR_PKT_NUM(x) (((x) & 0x3fff) << 16) - -#define RKVDEC_REG_H264_ERR_E 0x134 -#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff) - #define RKVDEC_REG_QOS_CTRL 0x18C +/* + * Cache configuration is not covered in the range of the register struct + */ #define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410 #define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450 +/* + * Define the mode values + */ +#define RKVDEC_MODE_HEVC 0 +#define RKVDEC_MODE_H264 1 +#define RKVDEC_MODE_VP9 2 + +/* rkvcodec registers */ +struct rkvdec_common_regs { + struct rkvdec_id { + u32 minor_ver : 8; + u32 level : 1; + u32 dec_support : 3; + u32 profile : 1; + u32 reserved0 : 1; + u32 codec_flag : 1; + u32 reserved1 : 1; + u32 prod_num : 16; + } reg00; + + struct rkvdec_int { + u32 dec_e : 1; + u32 dec_clkgate_e : 1; + u32 dec_e_strmd_clkgate_dis : 1; + u32 timeout_mode : 1; + u32 dec_irq_dis : 1; + u32 dec_timeout_e : 1; + u32 buf_empty_en : 1; + u32 stmerror_waitdecfifo_empty : 1; + u32 dec_irq : 1; + u32 dec_irq_raw : 1; + u32 reserved2 : 2; + u32 dec_rdy_sta : 1; + u32 dec_bus_sta : 1; + u32 dec_error_sta : 1; + u32 dec_timeout_sta : 1; + u32 dec_empty_sta : 1; + u32 colmv_ref_error_sta : 1; + u32 cabu_end_sta : 1; + u32 h264orvp9_error_mode : 1; + u32 softrst_en_p : 1; + u32 force_softreset_valid : 1; + u32 softreset_rdy : 1; + u32 wr_ddr_align_en : 1; + u32 scl_down_en : 1; + u32 allow_not_wr_unref_bframe : 1; + u32 reserved1 : 6; + } reg01; + + struct rkvdec_sysctrl { + u32 in_endian : 1; + u32 in_swap32_e : 1; + u32 in_swap64_e : 1; + u32 str_endian : 1; + u32 str_swap32_e : 1; + u32 str_swap64_e : 1; + u32 out_endian : 1; + u32 out_swap32_e : 1; + u32 out_cbcr_swap : 1; + u32 reserved0 : 1; + u32 rlc_mode_direct_write : 1; + u32 rlc_mode : 1; + u32 strm_start_bit : 7; + u32 reserved1 : 1; + u32 dec_mode : 2; + u32 reserved2 : 2; + u32 rps_mode : 1; + u32 stream_mode : 1; + u32 stream_lastpacket : 1; + u32 firstslice_flag : 1; + u32 frame_orslice : 1; + u32 buspr_slot_disable : 1; + u32 colmv_mode : 1; + u32 ycacherd_prior : 1; + } reg02; + + struct rkvdec_picpar { + u32 y_hor_virstride : 9; + u32 reserved : 2; + u32 slice_num_highbit : 1; + u32 uv_hor_virstride : 9; + u32 slice_num_lowbits : 11; + } reg03; + + u32 strm_rlc_base; + u32 stream_len; + u32 cabactbl_base; + u32 decout_base; + + struct rkvdec_y_virstride { + u32 y_virstride : 20; + u32 reserved0 : 12; + } reg08; + + struct rkvdec_yuv_virstride { + u32 yuv_virstride : 21; + u32 reserved0 : 11; + } reg09; +} __packed; + +struct ref_base { + u32 field_ref : 1; + u32 topfield_used_ref : 1; + u32 botfield_used_ref : 1; + u32 colmv_use_flag_ref : 1; + u32 base_addr : 28; +}; + +struct rkvdec_h26x_regs { + struct ref_base ref0_14_base[15]; + u32 ref0_14_poc[15]; + + u32 cur_poc; + u32 rlcwrite_base; + u32 pps_base; + u32 rps_base; + + u32 strmd_error_e; + + struct { + u32 strmd_error_status : 28; + u32 colmv_error_ref_picidx : 4; + } reg45; + + struct { + u32 strmd_error_ctu_xoffset : 8; + u32 strmd_error_ctu_yoffset : 8; + u32 streamfifo_space2full : 7; + u32 reserved0 : 1; + u32 vp9_error_ctu0_en : 1; + u32 reserved1 : 7; + } reg46; + + struct { + u32 saowr_xoffet : 9; + u32 reserved0 : 7; + u32 saowr_yoffset : 10; + u32 reserved1 : 6; + } reg47; + + struct ref_base ref15_base; + + u32 ref15_29_poc[15]; + + u32 performance_cycle; + u32 axi_ddr_rdata; + u32 axi_ddr_wdata; + + struct { + u32 busifd_resetn : 1; + u32 cabac_resetn : 1; + u32 dec_ctrl_resetn : 1; + u32 transd_resetn : 1; + u32 intra_resetn : 1; + u32 inter_resetn : 1; + u32 recon_resetn : 1; + u32 filer_resetn : 1; + u32 reserved0 : 24; + } reg67; + + struct { + u32 perf_cnt0_sel : 6; + u32 reserved0 : 2; + u32 perf_cnt1_sel : 6; + u32 reserved1 : 2; + u32 perf_cnt2_sel : 6; + u32 reserved2 : 10; + } reg68; + + u32 perf_cnt0; + u32 perf_cnt1; + u32 perf_cnt2; + u32 ref30_poc; + u32 ref31_poc; + u32 cur_poc1; + u32 errorinfo_base; + + struct { + u32 slicedec_num : 14; + u32 reserved0 : 1; + u32 strmd_detect_error_flag : 1; + u32 error_packet_num : 14; + u32 reserved1 : 2; + } reg76; + + struct { + u32 error_en_highbits : 30; + u32 strmd_error_slice_en : 1; + u32 strmd_error_frame_en : 1; + } reg77; + + u32 colmv_cur_base; + u32 colmv_ref_base[16]; + u32 scanlist_addr; + u32 reg96_sd_decout_base; + u32 sd_y_virstride; + u32 sd_hor_stride; + u32 qos_ctrl; + u32 perf[8]; + u32 qos1; +} __packed; + +struct rkvdec_vp9_regs { + struct cprheader_offset { + u32 cprheader_offset : 16; + u32 reserved : 16; + } reg10; + + u32 refer_bases[3]; + u32 count_base; + u32 segidlast_base; + u32 segidcur_base; + + struct frame_sizes { + u32 framewidth : 16; + u32 frameheight : 16; + } reg17_19[3]; + + struct segid_grp { + u32 segid_abs_delta : 1; + u32 segid_frame_qp_delta_en : 1; + u32 segid_frame_qp_delta : 9; + u32 segid_frame_loopfilter_value_en : 1; + u32 segid_frame_loopfilter_value : 7; + u32 segid_referinfo_en : 1; + u32 segid_referinfo : 2; + u32 segid_frame_skip_en : 1; + u32 reserved : 9; + } reg20_27[8]; + + struct cprheader_config { + u32 tx_mode : 3; + u32 frame_reference_mode : 2; + u32 reserved : 27; + } reg28; + + struct ref_scale { + u32 ref_hor_scale : 16; + u32 ref_ver_scale : 16; + } reg29_31[3]; + + struct ref_deltas_lastframe { + u32 ref_deltas_lastframe0 : 7; + u32 ref_deltas_lastframe1 : 7; + u32 ref_deltas_lastframe2 : 7; + u32 ref_deltas_lastframe3 : 7; + u32 reserved : 4; + } reg32; + + struct info_lastframe { + u32 mode_deltas_lastframe0 : 7; + u32 mode_deltas_lastframe1 : 7; + u32 reserved0 : 2; + u32 segmentation_enable_lstframe : 1; + u32 last_show_frame : 1; + u32 last_intra_only : 1; + u32 last_widthheight_eqcur : 1; + u32 color_space_lastkeyframe : 3; + u32 reserved1 : 9; + } reg33; + + u32 intercmd_base; + + struct intercmd_num { + u32 intercmd_num : 24; + u32 reserved : 8; + } reg35; + + struct lasttile_size { + u32 lasttile_size : 24; + u32 reserved : 8; + } reg36; + + struct hor_virstride { + u32 y_hor_virstride : 9; + u32 reserved0 : 7; + u32 uv_hor_virstride : 9; + u32 reserved1 : 7; + } reg37_39[3]; + + u32 cur_poc; + + struct rlcwrite_base { + u32 reserved : 3; + u32 rlcwrite_base : 29; + } reg41; + + struct pps_base { + u32 reserved : 4; + u32 pps_base : 28; + } reg42; + + struct rps_base { + u32 reserved : 4; + u32 rps_base : 28; + } reg43; + + struct strmd_error_en { + u32 strmd_error_e : 28; + u32 reserved : 4; + } reg44; + + u32 vp9_error_info0; + + struct strmd_error_ctu { + u32 strmd_error_ctu_xoffset : 8; + u32 strmd_error_ctu_yoffset : 8; + u32 streamfifo_space2full : 7; + u32 reserved0 : 1; + u32 error_ctu0_en : 1; + u32 reserved1 : 7; + } reg46; + + struct sao_ctu_position { + u32 saowr_xoffet : 9; + u32 reserved0 : 7; + u32 saowr_yoffset : 10; + u32 reserved1 : 6; + } reg47; + + struct ystride { + u32 virstride : 20; + u32 reserved : 12; + } reg48_50[3]; + + struct lastref_yuvstride { + u32 lastref_yuv_virstride : 21; + u32 reserved : 11; + } reg51; + + u32 refcolmv_base; + + u32 reserved0[11]; + + u32 performance_cycle; + u32 axi_ddr_rdata; + u32 axi_ddr_wdata; + + struct fpgadebug_reset { + u32 busifd_resetn : 1; + u32 cabac_resetn : 1; + u32 dec_ctrl_resetn : 1; + u32 transd_resetn : 1; + u32 intra_resetn : 1; + u32 inter_resetn : 1; + u32 recon_resetn : 1; + u32 filer_resetn : 1; + u32 reserved : 24; + } reg67; + + struct performance_sel { + u32 perf_cnt0_sel : 6; + u32 reserved0 : 2; + u32 perf_cnt1_sel : 6; + u32 reserved1 : 2; + u32 perf_cnt2_sel : 6; + u32 reserved : 10; + } reg68; + + u32 perf_cnt0; + u32 perf_cnt1; + u32 perf_cnt2; + + u32 reserved1[3]; + + u32 vp9_error_info1; + + struct error_ctu1 { + u32 vp9_error_ctu1_x : 6; + u32 reserved0 : 2; + u32 vp9_error_ctu1_y : 6; + u32 reserved1 : 1; + u32 vp9_error_ctu1_en : 1; + u32 reserved2 : 16; + } reg76; + + u32 reserved2; +} __packed; + +struct rkvdec_regs { + struct rkvdec_common_regs common; + union { + struct rkvdec_h26x_regs h26x; + struct rkvdec_vp9_regs vp9; + }; +} __packed; + #endif /* RKVDEC_REGS_H_ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c new file mode 100644 index 000000000000..cd0aa3f3a13d --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-h264.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VDPU381 Video Decoder H264 backend + * + * Copyright (C) 2024 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <media/v4l2-h264.h> +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-cabac.h" +#include "rkvdec-rcb.h" +#include "rkvdec-h264-common.h" +#include "rkvdec-vdpu381-regs.h" + +struct rkvdec_sps { + u16 seq_parameter_set_id: 4; + u16 profile_idc: 8; + u16 constraint_set3_flag: 1; + u16 chroma_format_idc: 2; + u16 bit_depth_luma: 3; + u16 bit_depth_chroma: 3; + u16 qpprime_y_zero_transform_bypass_flag: 1; + u16 log2_max_frame_num_minus4: 4; + u16 max_num_ref_frames: 5; + u16 pic_order_cnt_type: 2; + u16 log2_max_pic_order_cnt_lsb_minus4: 4; + u16 delta_pic_order_always_zero_flag: 1; + u16 pic_width_in_mbs: 12; + u16 pic_height_in_mbs: 12; + u16 frame_mbs_only_flag: 1; + u16 mb_adaptive_frame_field_flag: 1; + u16 direct_8x8_inference_flag: 1; + u16 mvc_extension_enable: 1; + u16 num_views: 2; + + u16 reserved_bits: 12; + u16 reserved[11]; +} __packed; + +struct rkvdec_pps { + u16 pic_parameter_set_id: 8; + u16 pps_seq_parameter_set_id: 5; + u16 entropy_coding_mode_flag: 1; + u16 bottom_field_pic_order_in_frame_present_flag: 1; + u16 num_ref_idx_l0_default_active_minus1: 5; + u16 num_ref_idx_l1_default_active_minus1: 5; + u16 weighted_pred_flag: 1; + u16 weighted_bipred_idc: 2; + u16 pic_init_qp_minus26: 7; + u16 pic_init_qs_minus26: 6; + u16 chroma_qp_index_offset: 5; + u16 deblocking_filter_control_present_flag: 1; + u16 constrained_intra_pred_flag: 1; + u16 redundant_pic_cnt_present: 1; + u16 transform_8x8_mode_flag: 1; + u16 second_chroma_qp_index_offset: 5; + u16 scaling_list_enable_flag: 1; + u32 scaling_list_address; + u16 is_longterm; + + u8 reserved[3]; +} __packed; + +struct rkvdec_sps_pps { + struct rkvdec_sps sps; + struct rkvdec_pps pps; +} __packed; + +/* Data structure describing auxiliary buffer format. */ +struct rkvdec_h264_priv_tbl { + s8 cabac_table[4][464][2]; + struct rkvdec_h264_scaling_list scaling_list; + struct rkvdec_sps_pps param_set[256]; + struct rkvdec_rps rps; +}; + +struct rkvdec_h264_ctx { + struct rkvdec_aux_buf priv_tbl; + struct rkvdec_h264_reflists reflists; + struct rkvdec_vdpu381_regs_h264 regs; +}; + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; + struct rkvdec_sps_pps *hw_ps; + dma_addr_t scaling_list_address; + u32 scaling_distance; + u32 i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 32 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; + memset(hw_ps, 0, sizeof(*hw_ps)); + + /* write sps */ + hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id; + hw_ps->sps.profile_idc = sps->profile_idc; + hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3)); + hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; + hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8; + hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8; + hw_ps->sps.qpprime_y_zero_transform_bypass_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); + hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4; + hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames; + hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type; + hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 = + sps->log2_max_pic_order_cnt_lsb_minus4; + hw_ps->sps.delta_pic_order_always_zero_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); + hw_ps->sps.mvc_extension_enable = 1; + hw_ps->sps.num_views = 1; + + /* + * Use the SPS values since they are already in macroblocks + * dimensions, height can be field height (halved) if + * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows + * decoding smaller images into larger allocation which can be used + * to implementing SVC spatial layer support. + */ + hw_ps->sps.pic_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1; + hw_ps->sps.pic_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1; + hw_ps->sps.frame_mbs_only_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); + hw_ps->sps.mb_adaptive_frame_field_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); + hw_ps->sps.direct_8x8_inference_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); + + /* write pps */ + hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id; + hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id; + hw_ps->pps.entropy_coding_mode_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); + hw_ps->pps.bottom_field_pic_order_in_frame_present_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); + hw_ps->pps.num_ref_idx_l0_default_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1; + hw_ps->pps.num_ref_idx_l1_default_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1; + hw_ps->pps.weighted_pred_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED); + hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc; + hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26; + hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26; + hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset; + hw_ps->pps.deblocking_filter_control_present_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); + hw_ps->pps.constrained_intra_pred_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); + hw_ps->pps.redundant_pic_cnt_present = + !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); + hw_ps->pps.transform_8x8_mode_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); + hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset; + hw_ps->pps.scaling_list_enable_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); + + /* + * To be on the safe side, program the scaling matrix address + */ + scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list); + scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance; + hw_ps->pps.scaling_list_address = scaling_list_address; + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + hw_ps->pps.is_longterm |= (1 << i); + } +} + +static void rkvdec_write_regs(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS, + &h264_ctx->regs.common, + sizeof(h264_ctx->regs.common)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS, + &h264_ctx->regs.h264_param, + sizeof(h264_ctx->regs.h264_param)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS, + &h264_ctx->regs.common_addr, + sizeof(h264_ctx->regs.common_addr)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS, + &h264_ctx->regs.h264_addr, + sizeof(h264_ctx->regs.h264_addr)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS, + &h264_ctx->regs.h264_highpoc, + sizeof(h264_ctx->regs.h264_highpoc)); +} + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + struct rkvdec_vdpu381_regs_h264 *regs = &h264_ctx->regs; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + dma_addr_t dst_addr; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; + u32 offset; + u32 pixels; + u32 i; + + memset(regs, 0, sizeof(*regs)); + + /* Set H264 mode */ + regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_H264; + + /* Set config */ + regs->common.reg011_important_en.buf_empty_en = 1; + regs->common.reg011_important_en.dec_clkgate_e = 1; + regs->common.reg011_important_en.dec_timeout_e = 1; + regs->common.reg011_important_en.pix_range_det_e = 1; + + /* + * Even though the scan list address can be set in RPS, + * with some frames, it will try to use the address set in the register. + */ + regs->common.reg012_secondary_en.scanlist_addr_valid_en = 1; + + /* Set IDR flag */ + regs->common.reg013_en_mode_set.cur_pic_is_idr = + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC); + + /* Set input stream length */ + regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + /* Set max slice number */ + regs->common.reg017_slice_number.slice_num = MAX_SLICE_NUMBER; + + /* Set strides */ + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16; + regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16; + regs->common.reg020_y_stride.y_virstride = y_virstride / 16; + + /* Activate block gating */ + regs->common.reg026_block_gating_en.inter_auto_gating_e = 1; + regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1; + regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0; + regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1; + regs->common.reg026_block_gating_en.intra_auto_gating_e = 1; + regs->common.reg026_block_gating_en.mc_auto_gating_e = 1; + regs->common.reg026_block_gating_en.transd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.sram_auto_gating_e = 1; + regs->common.reg026_block_gating_en.cru_auto_gating_e = 1; + regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1; + + /* Set timeout threshold */ + pixels = dst_fmt->height * dst_fmt->width; + if (pixels < RKVDEC_1080P_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p; + else if (pixels < RKVDEC_4K_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K; + else if (pixels < RKVDEC_8K_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K; + else + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX; + + /* Set TOP and BOTTOM POCs */ + regs->h264_param.reg065_cur_top_poc = dec_params->top_field_order_cnt; + regs->h264_param.reg066_cur_bot_poc = dec_params->bottom_field_order_cnt; + + /* Set ref pic address & poc */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct vb2_buffer *vb_buf = run->ref_buf[i]; + dma_addr_t buf_dma; + + /* + * If a DPB entry is unused or invalid, address of current destination + * buffer is returned. + */ + if (!vb_buf) + vb_buf = &dst_buf->vb2_buf; + + buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + + /* Set reference addresses */ + regs->h264_addr.reg164_180_ref_base[i] = buf_dma; + + /* Set COLMV addresses */ + regs->h264_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset; + + struct rkvdec_vdpu381_h264_ref_info *ref_info = + ®s->h264_param.reg099_102_ref_info_regs[i / 4].ref_info[i % 4]; + + ref_info->ref_field = + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD); + ref_info->ref_colmv_use_flag = + !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE); + ref_info->ref_topfield_used = + !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF); + ref_info->ref_botfield_used = + !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF); + + regs->h264_param.reg067_098_ref_poc[i * 2] = + dpb[i].top_field_order_cnt; + regs->h264_param.reg067_098_ref_poc[i * 2 + 1] = + dpb[i].bottom_field_order_cnt; + } + + /* Set rlc base address (input stream) */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + regs->common_addr.rlc_base = rlc_addr; + regs->common_addr.rlcwrite_base = rlc_addr; + + /* Set output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + regs->common_addr.decout_base = dst_addr; + regs->common_addr.error_ref_base = dst_addr; + + /* Set colmv address */ + regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset; + + /* Set RCB addresses */ + for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) + regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i); + + /* Set hw pps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, param_set); + regs->h264_addr.reg161_pps_base = priv_start_addr + offset; + + /* Set hw rps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, rps); + regs->h264_addr.reg163_rps_base = priv_start_addr + offset; + + /* Set cabac table */ + offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table); + regs->h264_addr.reg199_cabactbl_base = priv_start_addr + offset; + + offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list); + regs->h264_addr.reg181_scanlist_addr = priv_start_addr + offset; + + rkvdec_write_regs(ctx); +} + +static int rkvdec_h264_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_priv_tbl *priv_tbl; + struct rkvdec_h264_ctx *h264_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + if (!ctrl) + return -EINVAL; + + ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + if (ret) + return ret; + + h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL); + if (!h264_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &h264_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + h264_ctx->priv_tbl.size = sizeof(*priv_tbl); + h264_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table, + sizeof(rkvdec_h264_cabac_table)); + + ctx->priv = h264_ctx; + return 0; + +err_free_ctx: + kfree(h264_ctx); + return ret; +} + +static void rkvdec_h264_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size, + h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma); + kfree(h264_ctx); +} + +static int rkvdec_h264_run(struct rkvdec_ctx *ctx) +{ + struct v4l2_h264_reflist_builder reflist_builder; + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; + struct rkvdec_h264_run run; + + rkvdec_h264_run_preamble(ctx, &run); + + /* Build the P/B{0,1} ref lists. */ + v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params, + run.sps, run.decode_params->dpb); + v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); + v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, + h264_ctx->reflists.b1); + + assemble_hw_scaling_list(&run, &tbl->scaling_list); + assemble_hw_pps(ctx, &run); + lookup_ref_buf_idx(ctx, &run); + assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps); + + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + rkvdec_schedule_watchdog(rkvdec, h264_ctx->regs.common.reg032_timeout_threshold); + + /* Start decoding! */ + writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E); + + return 0; +} + +static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) + return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops = { + .adjust_fmt = rkvdec_h264_adjust_fmt, + .get_image_fmt = rkvdec_h264_get_image_fmt, + .start = rkvdec_h264_start, + .stop = rkvdec_h264_stop, + .run = rkvdec_h264_run, + .try_ctrl = rkvdec_h264_try_ctrl, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c new file mode 100644 index 000000000000..5f1c11ffb9f0 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VDPU381 HEVC backend + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-cabac.h" +#include "rkvdec-rcb.h" +#include "rkvdec-hevc-common.h" +#include "rkvdec-vdpu381-regs.h" + +// SPS +struct rkvdec_hevc_sps { + u16 video_parameters_set_id : 4; + u16 seq_parameters_set_id_sps : 4; + u16 chroma_format_idc : 2; + u16 width : 16; + u16 height : 16; + u16 bit_depth_luma : 4; + u16 bit_depth_chroma : 4; + u16 max_pic_order_count_lsb : 5; + u16 diff_max_min_luma_coding_block_size : 2; + u16 min_luma_coding_block_size : 3; + u16 min_transform_block_size : 3; + u16 diff_max_min_transform_block_size : 2; + u16 max_transform_hierarchy_depth_inter : 3; + u16 max_transform_hierarchy_depth_intra : 3; + u16 scaling_list_enabled_flag : 1; + u16 amp_enabled_flag : 1; + u16 sample_adaptive_offset_enabled_flag : 1; + u16 pcm_enabled_flag : 1; + u16 pcm_sample_bit_depth_luma : 4; + u16 pcm_sample_bit_depth_chroma : 4; + u16 pcm_loop_filter_disabled_flag : 1; + u16 diff_max_min_pcm_luma_coding_block_size : 3; + u16 min_pcm_luma_coding_block_size : 3; + u16 num_short_term_ref_pic_sets : 7; + u16 long_term_ref_pics_present_flag : 1; + u16 num_long_term_ref_pics_sps : 6; + u16 sps_temporal_mvp_enabled_flag : 1; + u16 strong_intra_smoothing_enabled_flag : 1; + u16 reserved_0 : 7; + u16 sps_max_dec_pic_buffering_minus1 : 4; + u16 reserved_0_2 : 3; + u16 reserved_f : 8; +} __packed; + +//PPS +struct rkvdec_hevc_pps { + u16 picture_parameters_set_id : 6; + u16 seq_parameters_set_id_pps : 4; + u16 dependent_slice_segments_enabled_flag : 1; + u16 output_flag_present_flag : 1; + u16 num_extra_slice_header_bits : 13; + u16 sign_data_hiding_enabled_flag : 1; + u16 cabac_init_present_flag : 1; + u16 num_ref_idx_l0_default_active : 4; + u16 num_ref_idx_l1_default_active : 4; + u16 init_qp_minus26 : 7; + u16 constrained_intra_pred_flag : 1; + u16 transform_skip_enabled_flag : 1; + u16 cu_qp_delta_enabled_flag : 1; + u16 log2_min_cb_size : 3; + u16 pps_cb_qp_offset : 5; + u16 pps_cr_qp_offset : 5; + u16 pps_slice_chroma_qp_offsets_present_flag : 1; + u16 weighted_pred_flag : 1; + u16 weighted_bipred_flag : 1; + u16 transquant_bypass_enabled_flag : 1; + u16 tiles_enabled_flag : 1; + u16 entropy_coding_sync_enabled_flag : 1; + u16 pps_loop_filter_across_slices_enabled_flag : 1; + u16 loop_filter_across_tiles_enabled_flag : 1; + u16 deblocking_filter_override_enabled_flag : 1; + u16 pps_deblocking_filter_disabled_flag : 1; + u16 pps_beta_offset_div2 : 4; + u16 pps_tc_offset_div2 : 4; + u16 lists_modification_present_flag : 1; + u16 log2_parallel_merge_level : 3; + u16 slice_segment_header_extension_present_flag : 1; + u16 zeroes : 3; + u16 num_tile_columns : 5; + u16 num_tile_rows : 5; + u16 sps_pps_mode : 4; + u16 reserved_bits : 14; + u16 reserved; +} __packed; + +struct rkvdec_hevc_tile { + u16 value0 : 12; + u16 value1 : 12; +} __packed; + +struct rkvdec_sps_pps_packet { + struct rkvdec_hevc_sps sps; + struct rkvdec_hevc_pps pps; + struct rkvdec_hevc_tile column_width[10]; + struct rkvdec_hevc_tile row_height[11]; + u32 zeroes[3]; + u32 zeroes_bits : 6; + u32 padding_bits : 2; + u32 padding; +} __packed; + +struct rkvdec_hevc_priv_tbl { + struct rkvdec_sps_pps_packet param_set[64]; + struct rkvdec_rps rps; + struct scaling_factor scaling_list; + u8 cabac_table[27456]; +}; + +struct rkvdec_hevc_ctx { + struct rkvdec_aux_buf priv_tbl; + struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache; + struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache; + struct rkvdec_vdpu381_regs_hevc regs; +}; + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + const struct v4l2_ctrl_hevc_pps *pps = run->pps; + struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu; + struct rkvdec_sps_pps_packet *hw_ps; + bool tiles_enabled; + s32 max_cu_width; + s32 pic_in_cts_width; + s32 pic_in_cts_height; + u16 log2_min_cb_size, width, height; + u16 column_width[20]; + u16 row_height[22]; + u8 pcm_enabled; + u32 i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 32 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; + memset(hw_ps, 0, sizeof(*hw_ps)); + + /* write sps */ + hw_ps->sps.video_parameters_set_id = sps->video_parameter_set_id; + hw_ps->sps.seq_parameters_set_id_sps = sps->seq_parameter_set_id; + hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; + + log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; + width = sps->pic_width_in_luma_samples; + height = sps->pic_height_in_luma_samples; + hw_ps->sps.width = width; + hw_ps->sps.height = height; + hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8 + 8; + hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; + hw_ps->sps.max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + hw_ps->sps.diff_max_min_luma_coding_block_size = + sps->log2_diff_max_min_luma_coding_block_size; + hw_ps->sps.min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3; + hw_ps->sps.min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2; + hw_ps->sps.diff_max_min_transform_block_size = + sps->log2_diff_max_min_luma_transform_block_size; + hw_ps->sps.max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter; + hw_ps->sps.max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra; + hw_ps->sps.scaling_list_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); + hw_ps->sps.amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED); + hw_ps->sps.sample_adaptive_offset_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + + pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED); + hw_ps->sps.pcm_enabled_flag = pcm_enabled; + hw_ps->sps.pcm_sample_bit_depth_luma = + pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0; + hw_ps->sps.pcm_sample_bit_depth_chroma = + pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0; + hw_ps->sps.pcm_loop_filter_disabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); + hw_ps->sps.diff_max_min_pcm_luma_coding_block_size = + sps->log2_diff_max_min_pcm_luma_coding_block_size; + hw_ps->sps.min_pcm_luma_coding_block_size = + pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0; + hw_ps->sps.num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; + hw_ps->sps.long_term_ref_pics_present_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); + hw_ps->sps.num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; + hw_ps->sps.sps_temporal_mvp_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); + hw_ps->sps.strong_intra_smoothing_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); + hw_ps->sps.sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1; + hw_ps->sps.reserved_f = 0xff; + + /* write pps */ + hw_ps->pps.picture_parameters_set_id = pps->pic_parameter_set_id; + hw_ps->pps.seq_parameters_set_id_pps = sps->seq_parameter_set_id; + hw_ps->pps.dependent_slice_segments_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); + hw_ps->pps.output_flag_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); + hw_ps->pps.num_extra_slice_header_bits = pps->num_extra_slice_header_bits; + hw_ps->pps.sign_data_hiding_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); + hw_ps->pps.cabac_init_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); + hw_ps->pps.num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1; + hw_ps->pps.num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1; + hw_ps->pps.init_qp_minus26 = pps->init_qp_minus26; + hw_ps->pps.constrained_intra_pred_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); + hw_ps->pps.transform_skip_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); + hw_ps->pps.cu_qp_delta_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); + hw_ps->pps.log2_min_cb_size = log2_min_cb_size + + sps->log2_diff_max_min_luma_coding_block_size - + pps->diff_cu_qp_delta_depth; + hw_ps->pps.pps_cb_qp_offset = pps->pps_cb_qp_offset; + hw_ps->pps.pps_cr_qp_offset = pps->pps_cr_qp_offset; + hw_ps->pps.pps_slice_chroma_qp_offsets_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); + hw_ps->pps.weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); + hw_ps->pps.weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); + hw_ps->pps.transquant_bypass_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + + tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); + hw_ps->pps.tiles_enabled_flag = tiles_enabled; + hw_ps->pps.entropy_coding_sync_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); + hw_ps->pps.pps_loop_filter_across_slices_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); + hw_ps->pps.loop_filter_across_tiles_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); + hw_ps->pps.deblocking_filter_override_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); + hw_ps->pps.pps_deblocking_filter_disabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); + hw_ps->pps.pps_beta_offset_div2 = pps->pps_beta_offset_div2; + hw_ps->pps.pps_tc_offset_div2 = pps->pps_tc_offset_div2; + hw_ps->pps.lists_modification_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); + hw_ps->pps.log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2; + hw_ps->pps.slice_segment_header_extension_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); + hw_ps->pps.num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 0; + hw_ps->pps.num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 0; + hw_ps->pps.sps_pps_mode = 0; + hw_ps->pps.reserved_bits = 0x3fff; + hw_ps->pps.reserved = 0xffff; + + // Setup tiles information + memset(column_width, 0, sizeof(column_width)); + memset(row_height, 0, sizeof(row_height)); + + max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size); + pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width; + pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width; + + if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) { + if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) { + compute_tiles_uniform(run, log2_min_cb_size, width, height, + pic_in_cts_width, pic_in_cts_height, + column_width, row_height); + } else { + compute_tiles_non_uniform(run, log2_min_cb_size, width, height, + pic_in_cts_width, pic_in_cts_height, + column_width, row_height); + } + } else { + column_width[0] = (width + max_cu_width - 1) / max_cu_width; + row_height[0] = (height + max_cu_width - 1) / max_cu_width; + } + + for (i = 0; i < 20; i++) { + if (column_width[i] > 0) + column_width[i]--; + + if (i & 1) + hw_ps->column_width[i / 2].value1 = column_width[i]; + else + hw_ps->column_width[i / 2].value0 = column_width[i]; + } + + for (i = 0; i < 22; i++) { + if (row_height[i] > 0) + row_height[i]--; + + if (i & 1) + hw_ps->row_height[i / 2].value1 = row_height[i]; + else + hw_ps->row_height[i / 2].value0 = row_height[i]; + } + + hw_ps->padding = 0xffffffff; + hw_ps->padding_bits = 0x3; +} + +static void set_ref_valid(struct rkvdec_vdpu381_regs_hevc *regs, int id, u32 valid) +{ + switch (id) { + case 0: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_0 = valid; + break; + case 1: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_1 = valid; + break; + case 2: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_2 = valid; + break; + case 3: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_3 = valid; + break; + case 4: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_4 = valid; + break; + case 5: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_5 = valid; + break; + case 6: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_6 = valid; + break; + case 7: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_7 = valid; + break; + case 8: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_8 = valid; + break; + case 9: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_9 = valid; + break; + case 10: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_10 = valid; + break; + case 11: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_11 = valid; + break; + case 12: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_12 = valid; + break; + case 13: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_13 = valid; + break; + case 14: + regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_14 = valid; + break; + } +} + +static void rkvdec_write_regs(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS, + &hevc_ctx->regs.common, + sizeof(hevc_ctx->regs.common)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS, + &hevc_ctx->regs.hevc_param, + sizeof(hevc_ctx->regs.hevc_param)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS, + &hevc_ctx->regs.common_addr, + sizeof(hevc_ctx->regs.common_addr)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS, + &hevc_ctx->regs.hevc_addr, + sizeof(hevc_ctx->regs.hevc_addr)); + rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS, + &hevc_ctx->regs.hevc_highpoc, + sizeof(hevc_ctx->regs.hevc_highpoc)); +} + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params; + const struct v4l2_hevc_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_vdpu381_regs_hevc *regs = &hevc_ctx->regs; + dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + u32 hor_virstride = 0; + u32 ver_virstride = 0; + u32 y_virstride = 0; + u32 offset; + u32 pixels; + dma_addr_t dst_addr; + u32 i; + + memset(regs, 0, sizeof(*regs)); + + /* Set HEVC mode */ + regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_HEVC; + + /* Set config */ + regs->common.reg011_important_en.buf_empty_en = 1; + regs->common.reg011_important_en.dec_clkgate_e = 1; + regs->common.reg011_important_en.dec_timeout_e = 1; + regs->common.reg011_important_en.pix_range_det_e = 1; + + /* Set IDR flag */ + regs->common.reg013_en_mode_set.cur_pic_is_idr = + !!(dec_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC); + + /* Set input stream length */ + regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + /* Set max slice number */ + regs->common.reg017_slice_number.slice_num = 1; + + /* Set strides */ + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16; + regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16; + regs->common.reg020_y_stride.y_virstride = y_virstride / 16; + + /* Activate block gating */ + regs->common.reg026_block_gating_en.inter_auto_gating_e = 1; + regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1; + regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0; + regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1; + regs->common.reg026_block_gating_en.intra_auto_gating_e = 1; + regs->common.reg026_block_gating_en.mc_auto_gating_e = 1; + regs->common.reg026_block_gating_en.transd_auto_gating_e = 1; + regs->common.reg026_block_gating_en.sram_auto_gating_e = 1; + regs->common.reg026_block_gating_en.cru_auto_gating_e = 1; + regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1; + + /* Set timeout threshold */ + pixels = dst_fmt->height * dst_fmt->width; + if (pixels < RKVDEC_1080P_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p; + else if (pixels < RKVDEC_4K_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K; + else if (pixels < RKVDEC_8K_PIXELS) + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K; + else + regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX; + + /* Set POC val */ + regs->hevc_param.reg065_cur_top_poc = dec_params->pic_order_cnt_val; + + /* Set ref pic address & poc */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i); + dma_addr_t buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + u32 valid = !!(dec_params->num_active_dpb_entries > i); + + /* Set reference addresses */ + regs->hevc_addr.reg164_180_ref_base[i] = buf_dma; + + /* Set COLMV addresses */ + regs->hevc_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset; + + regs->hevc_param.reg067_082_ref_poc[i] = + dpb[i].pic_order_cnt_val; + + set_ref_valid(regs, i, valid); + regs->hevc_param.reg103_hevc_mvc0.ref_pic_layer_same_with_cur |= 1 << i; + } + + /* Set rlc base address (input stream) */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + regs->common_addr.rlc_base = rlc_addr; + regs->common_addr.rlcwrite_base = rlc_addr; + + /* Set output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + regs->common_addr.decout_base = dst_addr; + regs->common_addr.error_ref_base = dst_addr; + + /* Set colmv address */ + regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset; + + /* Set RCB addresses */ + for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) + regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i); + + /* Set hw pps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set); + regs->hevc_addr.reg161_pps_base = priv_start_addr + offset; + + /* Set hw rps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, rps); + regs->hevc_addr.reg163_rps_base = priv_start_addr + offset; + + /* Set cabac table */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table); + regs->hevc_addr.reg199_cabactbl_base = priv_start_addr + offset; + + /* Set scaling matrix */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list); + regs->hevc_addr.reg181_scanlist_addr = priv_start_addr + offset; + + rkvdec_write_regs(ctx); +} + +static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_hevc_sps *sps) +{ + if (sps->chroma_format_idc != 1) + /* Only 4:2:0 is supported */ + return -EINVAL; + + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit are supported */ + return -EINVAL; + + if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width || + sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height) + return -EINVAL; + + return 0; +} + +static int rkvdec_hevc_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_priv_tbl *priv_tbl; + struct rkvdec_hevc_ctx *hevc_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SPS); + if (!ctrl) + return -EINVAL; + + ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps); + if (ret) + return ret; + + hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL); + if (!hevc_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &hevc_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + hevc_ctx->priv_tbl.size = sizeof(*priv_tbl); + hevc_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table, + sizeof(rkvdec_hevc_cabac_table)); + + ctx->priv = hevc_ctx; + return 0; + +err_free_ctx: + kfree(hevc_ctx); + return ret; +} + +static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size, + hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma); + kfree(hevc_ctx); +} + +static int rkvdec_hevc_run(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_run run; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu; + + rkvdec_hevc_run_preamble(ctx, &run); + + rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list, + &hevc_ctx->scaling_matrix_cache); + assemble_hw_pps(ctx, &run); + + /* + * On vdpu381, not setting the long and short term ref sets will just output wrong frames. + * Let's just warn about it and let the decoder run anyway. + */ + if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) || + (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) { + dev_warn_ratelimited(rkvdec->dev, "Long and short term RPS not set\n"); + } else { + rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache); + } + + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + rkvdec_schedule_watchdog(rkvdec, hevc_ctx->regs.common.reg032_timeout_threshold); + + /* Start decoding! */ + writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E); + + return 0; +} + +static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) + return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops = { + .adjust_fmt = rkvdec_hevc_adjust_fmt, + .start = rkvdec_hevc_start, + .stop = rkvdec_hevc_stop, + .run = rkvdec_hevc_run, + .try_ctrl = rkvdec_hevc_try_ctrl, + .get_image_fmt = rkvdec_hevc_get_image_fmt, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h new file mode 100644 index 000000000000..6da36031df2d --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-regs.h @@ -0,0 +1,430 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip VDPU381 Video Decoder driver registers description + * + * Copyright (C) 2024 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <linux/types.h> + +#ifndef _RKVDEC_REGS_H_ +#define _RKVDEC_REGS_H_ + +#define OFFSET_COMMON_REGS (8 * sizeof(u32)) +#define OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32)) +#define OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32)) +#define OFFSET_CODEC_ADDR_REGS (160 * sizeof(u32)) +#define OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32)) + +#define VDPU381_MODE_HEVC 0 +#define VDPU381_MODE_H264 1 +#define VDPU381_MODE_VP9 2 +#define VDPU381_MODE_AVS2 3 + +#define MAX_SLICE_NUMBER 0x3fff + +#define RKVDEC_TIMEOUT_1080p (0xefffff) +#define RKVDEC_TIMEOUT_4K (0x2cfffff) +#define RKVDEC_TIMEOUT_8K (0x4ffffff) +#define RKVDEC_TIMEOUT_MAX (0xffffffff) + +#define VDPU381_REG_DEC_E 0x028 +#define VDPU381_DEC_E_BIT 1 + +#define VDPU381_REG_IMPORTANT_EN 0x02c +#define VDPU381_DEC_IRQ_DISABLE BIT(4) + +#define VDPU381_REG_STA_INT 0x380 +#define VDPU381_STA_INT_DEC_RDY_STA BIT(2) +#define VDPU381_STA_INT_ERROR BIT(4) +#define VDPU381_STA_INT_TIMEOUT BIT(5) +#define VDPU381_STA_INT_SOFTRESET_RDY BIT(9) + +/* base: OFFSET_COMMON_REGS */ +struct rkvdec_vdpu381_regs_common { + struct { + u32 in_endian : 1; + u32 in_swap32_e : 1; + u32 in_swap64_e : 1; + u32 str_endian : 1; + u32 str_swap32_e : 1; + u32 str_swap64_e : 1; + u32 out_endian : 1; + u32 out_swap32_e : 1; + u32 out_cbcr_swap : 1; + u32 out_swap64_e : 1; + u32 reserved : 22; + } reg008_in_out; + + struct { + u32 dec_mode : 10; + u32 reserved : 22; + } reg009_dec_mode; + + struct { + u32 dec_e : 1; + u32 reserved : 31; + } reg010_dec_e; + + struct { + u32 reserved0 : 1; + u32 dec_clkgate_e : 1; + u32 dec_e_strmd_clkgate_dis : 1; + u32 reserved1 : 1; + + u32 dec_irq_dis : 1; + u32 dec_timeout_e : 1; + u32 buf_empty_en : 1; + u32 reserved2 : 3; + + u32 dec_e_rewrite_valid : 1; + u32 reserved3 : 9; + u32 softrst_en_p : 1; + u32 force_softreset_valid : 1; + u32 reserved4 : 2; + u32 pix_range_det_e : 1; + u32 reserved5 : 7; + } reg011_important_en; + + struct { + u32 reserved0 : 1; + u32 colmv_compress_en : 1; + u32 fbc_e : 1; + u32 reserved1 : 1; + + u32 buspr_slot_disable : 1; + u32 error_info_en : 1; + u32 collect_info_en : 1; + u32 error_auto_rst_disable : 1; + + u32 scanlist_addr_valid_en : 1; + u32 scale_down_en : 1; + u32 error_cfg_wr_disable : 1; + u32 reserved2 : 21; + } reg012_secondary_en; + + struct { + u32 reserved0 : 1; + u32 req_timeout_rst_sel : 1; + u32 reserved1 : 1; + u32 dec_commonirq_mode : 1; + u32 reserved2 : 2; + u32 stmerror_waitdecfifo_empty : 1; + u32 reserved3 : 5; + u32 allow_not_wr_unref_bframe : 1; + u32 fbc_output_wr_disable : 1; + u32 reserved4 : 4; + u32 error_mode : 1; + u32 reserved5 : 2; + u32 ycacherd_prior : 1; + u32 reserved6 : 2; + u32 cur_pic_is_idr : 1; + u32 reserved7 : 1; + u32 right_auto_rst_disable : 1; + u32 frame_end_err_rst_flag : 1; + u32 rd_prior_mode : 1; + u32 rd_ctrl_prior_mode : 1; + u32 reserved8 : 1; + u32 filter_outbuf_mode : 1; + } reg013_en_mode_set; + + struct { + u32 fbc_force_uncompress : 1; + + u32 reserved0 : 2; + u32 allow_16x8_cp_flag : 1; + u32 reserved1 : 2; + + u32 fbc_h264_exten_4or8_flag : 1; + u32 reserved2 : 25; + } reg014_fbc_param_set; + + struct { + u32 rlc_mode_direct_write : 1; + u32 rlc_mode : 1; + u32 reserved0 : 3; + + u32 strm_start_bit : 7; + u32 reserved1 : 20; + } reg015_stream_param_set; + + u32 reg016_stream_len; + + struct { + u32 slice_num : 25; + u32 reserved : 7; + } reg017_slice_number; + + struct { + u32 y_hor_virstride : 16; + u32 reserved : 16; + } reg018_y_hor_stride; + + struct { + u32 uv_hor_virstride : 16; + u32 reserved : 16; + } reg019_uv_hor_stride; + + struct { + u32 y_virstride : 28; + u32 reserved : 4; + } reg020_y_stride; + + struct { + u32 inter_error_prc_mode : 1; + u32 error_intra_mode : 1; + u32 error_deb_en : 1; + u32 picidx_replace : 5; + u32 error_spread_e : 1; + u32 reserved0 : 3; + u32 error_inter_pred_cross_slice : 1; + u32 reserved1 : 11; + u32 roi_error_ctu_cal_en : 1; + u32 reserved2 : 7; + } reg021_error_ctrl_set; + + struct { + u32 roi_x_ctu_offset_st : 12; + u32 reserved0 : 4; + u32 roi_y_ctu_offset_st : 12; + u32 reserved1 : 4; + } reg022_err_roi_ctu_offset_start; + + struct { + u32 roi_x_ctu_offset_end : 12; + u32 reserved0 : 4; + u32 roi_y_ctu_offset_end : 12; + u32 reserved1 : 4; + } reg023_err_roi_ctu_offset_end; + + struct { + u32 cabac_err_en_lowbits : 32; + } reg024_cabac_error_en_lowbits; + + struct { + u32 cabac_err_en_highbits : 30; + u32 reserved : 2; + } reg025_cabac_error_en_highbits; + + struct { + u32 inter_auto_gating_e : 1; + u32 filterd_auto_gating_e : 1; + u32 strmd_auto_gating_e : 1; + u32 mcp_auto_gating_e : 1; + u32 busifd_auto_gating_e : 1; + u32 reserved0 : 3; + u32 dec_ctrl_auto_gating_e : 1; + u32 intra_auto_gating_e : 1; + u32 mc_auto_gating_e : 1; + u32 transd_auto_gating_e : 1; + u32 reserved1 : 4; + u32 sram_auto_gating_e : 1; + u32 cru_auto_gating_e : 1; + u32 reserved2 : 13; + u32 reg_cfg_gating_en : 1; + } reg026_block_gating_en; + + struct { + u32 core_safe_x_pixels : 16; + u32 core_safe_y_pixels : 16; + } reg027_core_safe_pixels; + + struct { + u32 vp9_wr_prob_idx : 3; + u32 reserved0 : 1; + u32 vp9_rd_prob_idx : 3; + u32 reserved1 : 1; + + u32 ref_req_advance_flag : 1; + u32 colmv_req_advance_flag : 1; + u32 poc_only_highbit_flag : 1; + u32 poc_arb_flag : 1; + + u32 reserved2 : 4; + u32 film_idx : 10; + u32 reserved3 : 2; + u32 pu_req_mismatch_dis : 1; + u32 colmv_req_mismatch_dis : 1; + u32 reserved4 : 2; + } reg028_multiply_core_ctrl; + + struct { + u32 scale_down_hor_ratio : 2; + u32 reserved0 : 6; + u32 scale_down_vrz_ratio : 2; + u32 reserved1 : 22; + } reg029_scale_down_ctrl; + + struct { + u32 y_scale_down_tile8x8_hor_stride : 20; + u32 reserved0 : 12; + } reg030_y_scale_down_tile8x8_hor_stride; + + struct { + u32 uv_scale_down8x8_tile_hor_stride : 20; + u32 reserved0 : 12; + } reg031_uv_scale_down_tile8x8_hor_stride; + + u32 reg032_timeout_threshold; +} __packed; + +/* base: OFFSET_COMMON_ADDR_REGS */ +struct rkvdec_vdpu381_regs_common_addr { + u32 rlc_base; + u32 rlcwrite_base; + u32 decout_base; + u32 colmv_cur_base; + u32 error_ref_base; + u32 rcb_base[10]; +} __packed; + +struct rkvdec_vdpu381_h26x_set { + u32 h26x_frame_orslice : 1; + u32 h26x_rps_mode : 1; + u32 h26x_stream_mode : 1; + u32 h26x_stream_lastpacket : 1; + u32 h264_firstslice_flag : 1; + u32 reserved : 27; +} __packed; + +/* base: OFFSET_CODEC_PARAMS_REGS */ +struct rkvdec_vdpu381_regs_h264_params { + struct rkvdec_vdpu381_h26x_set reg064_h26x_set; + + u32 reg065_cur_top_poc; + u32 reg066_cur_bot_poc; + u32 reg067_098_ref_poc[32]; + + struct rkvdec_vdpu381_h264_info { + struct rkvdec_vdpu381_h264_ref_info { + u32 ref_field : 1; + u32 ref_topfield_used : 1; + u32 ref_botfield_used : 1; + u32 ref_colmv_use_flag : 1; + u32 reserved : 4; + } __packed ref_info[4]; + } __packed reg099_102_ref_info_regs[4]; + + u32 reserved_103_111[9]; + + struct { + u32 avs2_ref_error_field : 1; + u32 avs2_ref_error_topfield : 1; + u32 ref_error_topfield_used : 1; + u32 ref_error_botfield_used : 1; + u32 reserved : 28; + } reg112_error_ref_info; +} __packed; + +struct rkvdec_vdpu381_regs_hevc_params { + struct rkvdec_vdpu381_h26x_set reg064_h26x_set; + + u32 reg065_cur_top_poc; + u32 reg066_cur_bot_poc; + u32 reg067_082_ref_poc[16]; + + u32 reserved_083_098[16]; + + struct { + u32 hevc_ref_valid_0 : 1; + u32 hevc_ref_valid_1 : 1; + u32 hevc_ref_valid_2 : 1; + u32 hevc_ref_valid_3 : 1; + u32 reserve0 : 4; + u32 hevc_ref_valid_4 : 1; + u32 hevc_ref_valid_5 : 1; + u32 hevc_ref_valid_6 : 1; + u32 hevc_ref_valid_7 : 1; + u32 reserve1 : 4; + u32 hevc_ref_valid_8 : 1; + u32 hevc_ref_valid_9 : 1; + u32 hevc_ref_valid_10 : 1; + u32 hevc_ref_valid_11 : 1; + u32 reserve2 : 4; + u32 hevc_ref_valid_12 : 1; + u32 hevc_ref_valid_13 : 1; + u32 hevc_ref_valid_14 : 1; + u32 reserve3 : 5; + } reg099_hevc_ref_valid; + + u32 reserved_100_102[3]; + + struct { + u32 ref_pic_layer_same_with_cur : 16; + u32 reserve : 16; + } reg103_hevc_mvc0; + + struct { + u32 poc_lsb_not_present_flag : 1; + u32 num_direct_ref_layers : 6; + u32 reserve0 : 1; + + u32 num_reflayer_pics : 6; + u32 default_ref_layers_active_flag : 1; + u32 max_one_active_ref_layer_flag : 1; + + u32 poc_reset_info_present_flag : 1; + u32 vps_poc_lsb_aligned_flag : 1; + u32 mvc_poc15_valid_flag : 1; + u32 reserve1 : 13; + } reg104_hevc_mvc1; + + u32 reserved_105_111[7]; + + struct { + u32 avs2_ref_error_field : 1; + u32 avs2_ref_error_topfield : 1; + u32 ref_error_topfield_used : 1; + u32 ref_error_botfield_used : 1; + u32 reserve : 28; + } reg112_hevc_ref_info; + +} __packed; + +/* base: OFFSET_CODEC_ADDR_REGS */ +struct rkvdec_vdpu381_regs_h26x_addr { + u32 reserved_160; + u32 reg161_pps_base; + u32 reserved_162; + u32 reg163_rps_base; + u32 reg164_180_ref_base[16]; + u32 reg181_scanlist_addr; + u32 reg182_198_colmv_base[16]; + u32 reg199_cabactbl_base; +} __packed; + +struct rkvdec_vdpu381_regs_h26x_highpoc { + struct { + u32 ref0_poc_highbit : 4; + u32 ref1_poc_highbit : 4; + u32 ref2_poc_highbit : 4; + u32 ref3_poc_highbit : 4; + u32 ref4_poc_highbit : 4; + u32 ref5_poc_highbit : 4; + u32 ref6_poc_highbit : 4; + u32 ref7_poc_highbit : 4; + } reg200_203_ref_poc_highbit[4]; + struct { + u32 cur_poc_highbit : 4; + u32 reserved : 28; + } reg204_cur_poc_highbit; +} __packed; + +struct rkvdec_vdpu381_regs_h264 { + struct rkvdec_vdpu381_regs_common common; + struct rkvdec_vdpu381_regs_h264_params h264_param; + struct rkvdec_vdpu381_regs_common_addr common_addr; + struct rkvdec_vdpu381_regs_h26x_addr h264_addr; + struct rkvdec_vdpu381_regs_h26x_highpoc h264_highpoc; +} __packed; + +struct rkvdec_vdpu381_regs_hevc { + struct rkvdec_vdpu381_regs_common common; + struct rkvdec_vdpu381_regs_hevc_params hevc_param; + struct rkvdec_vdpu381_regs_common_addr common_addr; + struct rkvdec_vdpu381_regs_h26x_addr hevc_addr; + struct rkvdec_vdpu381_regs_h26x_highpoc hevc_highpoc; +} __packed; + +#endif /* __RKVDEC_REGS_H__ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c new file mode 100644 index 000000000000..6ab3167addc8 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip Video Decoder VDPU383 H264 backend + * + * Copyright (C) 2024 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <media/v4l2-h264.h> +#include <media/v4l2-mem2mem.h> + +#include <linux/iopoll.h> + +#include "rkvdec-rcb.h" +#include "rkvdec-cabac.h" +#include "rkvdec-vdpu383-regs.h" +#include "rkvdec-h264-common.h" + +struct rkvdec_sps { + u16 seq_parameter_set_id: 4; + u16 profile_idc: 8; + u16 constraint_set3_flag: 1; + u16 chroma_format_idc: 2; + u16 bit_depth_luma: 3; + u16 bit_depth_chroma: 3; + u16 qpprime_y_zero_transform_bypass_flag: 1; + u16 log2_max_frame_num_minus4: 4; + u16 max_num_ref_frames: 5; + u16 pic_order_cnt_type: 2; + u16 log2_max_pic_order_cnt_lsb_minus4: 4; + u16 delta_pic_order_always_zero_flag: 1; + + u16 pic_width_in_mbs: 16; + u16 pic_height_in_mbs: 16; + + u16 frame_mbs_only_flag: 1; + u16 mb_adaptive_frame_field_flag: 1; + u16 direct_8x8_inference_flag: 1; + u16 mvc_extension_enable: 1; + u16 num_views: 2; + u16 view_id0: 10; + u16 view_id1: 10; +} __packed; + +struct rkvdec_pps { + u32 pic_parameter_set_id: 8; + u32 pps_seq_parameter_set_id: 5; + u32 entropy_coding_mode_flag: 1; + u32 bottom_field_pic_order_in_frame_present_flag: 1; + u32 num_ref_idx_l0_default_active_minus1: 5; + u32 num_ref_idx_l1_default_active_minus1: 5; + u32 weighted_pred_flag: 1; + u32 weighted_bipred_idc: 2; + u32 pic_init_qp_minus26: 7; + u32 pic_init_qs_minus26: 6; + u32 chroma_qp_index_offset: 5; + u32 deblocking_filter_control_present_flag: 1; + u32 constrained_intra_pred_flag: 1; + u32 redundant_pic_cnt_present: 1; + u32 transform_8x8_mode_flag: 1; + u32 second_chroma_qp_index_offset: 5; + u32 scaling_list_enable_flag: 1; + u32 is_longterm: 16; + u32 voidx: 16; + + // dpb + u32 pic_field_flag: 1; + u32 pic_associated_flag: 1; + u32 cur_top_field: 32; + u32 cur_bot_field: 32; + + u32 top_field_order_cnt0: 32; + u32 bot_field_order_cnt0: 32; + u32 top_field_order_cnt1: 32; + u32 bot_field_order_cnt1: 32; + u32 top_field_order_cnt2: 32; + u32 bot_field_order_cnt2: 32; + u32 top_field_order_cnt3: 32; + u32 bot_field_order_cnt3: 32; + u32 top_field_order_cnt4: 32; + u32 bot_field_order_cnt4: 32; + u32 top_field_order_cnt5: 32; + u32 bot_field_order_cnt5: 32; + u32 top_field_order_cnt6: 32; + u32 bot_field_order_cnt6: 32; + u32 top_field_order_cnt7: 32; + u32 bot_field_order_cnt7: 32; + u32 top_field_order_cnt8: 32; + u32 bot_field_order_cnt8: 32; + u32 top_field_order_cnt9: 32; + u32 bot_field_order_cnt9: 32; + u32 top_field_order_cnt10: 32; + u32 bot_field_order_cnt10: 32; + u32 top_field_order_cnt11: 32; + u32 bot_field_order_cnt11: 32; + u32 top_field_order_cnt12: 32; + u32 bot_field_order_cnt12: 32; + u32 top_field_order_cnt13: 32; + u32 bot_field_order_cnt13: 32; + u32 top_field_order_cnt14: 32; + u32 bot_field_order_cnt14: 32; + u32 top_field_order_cnt15: 32; + u32 bot_field_order_cnt15: 32; + + u32 ref_field_flags: 16; + u32 ref_topfield_used: 16; + u32 ref_botfield_used: 16; + u32 ref_colmv_use_flag: 16; + + u32 reserved0: 30; + u32 reserved[3]; +} __packed; + +struct rkvdec_sps_pps { + struct rkvdec_sps sps; + struct rkvdec_pps pps; +} __packed; + +/* Data structure describing auxiliary buffer format. */ +struct rkvdec_h264_priv_tbl { + s8 cabac_table[4][464][2]; + struct rkvdec_h264_scaling_list scaling_list; + struct rkvdec_sps_pps param_set[256]; + struct rkvdec_rps rps; +} __packed; + +struct rkvdec_h264_ctx { + struct rkvdec_aux_buf priv_tbl; + struct rkvdec_h264_reflists reflists; + struct vdpu383_regs_h26x regs; +}; + +static void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb) +{ + pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt; + pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt; + pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt; + pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt; + pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt; + pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt; + pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt; + pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt; + pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt; + pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt; + pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt; + pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt; + pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt; + pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt; + pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt; + pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt; + pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt; + pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt; + pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt; + pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt; + pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt; + pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt; + pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt; + pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt; + pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt; + pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt; + pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt; + pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt; + pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt; + pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt; + pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt; + pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt; +} + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + const struct v4l2_ctrl_h264_sps *sps = run->sps; + const struct v4l2_ctrl_h264_pps *pps = run->pps; + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; + struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; + struct rkvdec_sps_pps *hw_ps; + u32 pic_width, pic_height; + u32 i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 32 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id]; + memset(hw_ps, 0, sizeof(*hw_ps)); + + /* write sps */ + hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id; + hw_ps->sps.profile_idc = sps->profile_idc; + hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3)); + hw_ps->sps.chroma_format_idc = sps->chroma_format_idc; + hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8; + hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8; + hw_ps->sps.qpprime_y_zero_transform_bypass_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); + hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4; + hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames; + hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type; + hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 = + sps->log2_max_pic_order_cnt_lsb_minus4; + hw_ps->sps.delta_pic_order_always_zero_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); + hw_ps->sps.mvc_extension_enable = 0; + hw_ps->sps.num_views = 0; + + /* + * Use the SPS values since they are already in macroblocks + * dimensions, height can be field height (halved) if + * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows + * decoding smaller images into larger allocation which can be used + * to implementing SVC spatial layer support. + */ + pic_width = 16 * (sps->pic_width_in_mbs_minus1 + 1); + pic_height = 16 * (sps->pic_height_in_map_units_minus1 + 1); + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + pic_height *= 2; + if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) + pic_height /= 2; + + hw_ps->sps.pic_width_in_mbs = pic_width; + hw_ps->sps.pic_height_in_mbs = pic_height; + + hw_ps->sps.frame_mbs_only_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); + hw_ps->sps.mb_adaptive_frame_field_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); + hw_ps->sps.direct_8x8_inference_flag = + !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); + + /* write pps */ + hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id; + hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id; + hw_ps->pps.entropy_coding_mode_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); + hw_ps->pps.bottom_field_pic_order_in_frame_present_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); + hw_ps->pps.num_ref_idx_l0_default_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1; + hw_ps->pps.num_ref_idx_l1_default_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1; + hw_ps->pps.weighted_pred_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED); + hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc; + hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26; + hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26; + hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset; + hw_ps->pps.deblocking_filter_control_present_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); + hw_ps->pps.constrained_intra_pred_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); + hw_ps->pps.redundant_pic_cnt_present = + !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); + hw_ps->pps.transform_8x8_mode_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); + hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset; + hw_ps->pps.scaling_list_enable_flag = + !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); + + set_field_order_cnt(&hw_ps->pps, dpb); + + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) + hw_ps->pps.is_longterm |= (1 << i); + + hw_ps->pps.ref_field_flags |= + (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i; + hw_ps->pps.ref_colmv_use_flag |= + (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i; + hw_ps->pps.ref_topfield_used |= + (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i; + hw_ps->pps.ref_botfield_used |= + (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i; + } + + hw_ps->pps.pic_field_flag = + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC); + hw_ps->pps.pic_associated_flag = + !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD); + + hw_ps->pps.cur_top_field = dec_params->top_field_order_cnt; + hw_ps->pps.cur_bot_field = dec_params->bottom_field_order_cnt; +} + +static void rkvdec_write_regs(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS, + &h264_ctx->regs.common, + sizeof(h264_ctx->regs.common)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS, + &h264_ctx->regs.common_addr, + sizeof(h264_ctx->regs.common_addr)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS, + &h264_ctx->regs.h26x_params, + sizeof(h264_ctx->regs.h26x_params)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS, + &h264_ctx->regs.h26x_addr, + sizeof(h264_ctx->regs.h26x_addr)); +} + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_h264_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + struct vdpu383_regs_h26x *regs = &h264_ctx->regs; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + dma_addr_t dst_addr; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; + u32 offset; + u32 pixels; + u32 i; + + memset(regs, 0, sizeof(*regs)); + + /* Set H264 mode */ + regs->common.reg008_dec_mode = VDPU383_MODE_H264; + + /* Set input stream length */ + regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + /* Set strides */ + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + pixels = dst_fmt->height * dst_fmt->width; + + regs->h26x_params.reg068_hor_virstride = hor_virstride / 16; + regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16; + regs->h26x_params.reg070_y_virstride = y_virstride / 16; + + /* Activate block gating */ + regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.inter_auto_gating_e = 1; + regs->common.reg010_block_gating_en.intra_auto_gating_e = 1; + regs->common.reg010_block_gating_en.transd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.recon_auto_gating_e = 1; + regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.bus_auto_gating_e = 1; + regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1; + regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1; + regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1; + + /* Set timeout threshold */ + if (pixels < RKVDEC_1080P_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p; + else if (pixels < RKVDEC_4K_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K; + else if (pixels < RKVDEC_8K_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K; + else + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX; + + regs->common.reg016_error_ctrl_set.error_proc_disable = 1; + + /* Set ref pic address & poc */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + struct vb2_buffer *vb_buf = run->ref_buf[i]; + dma_addr_t buf_dma; + + /* + * If a DPB entry is unused or invalid, address of current destination + * buffer is returned. + */ + if (!vb_buf) + vb_buf = &dst_buf->vb2_buf; + + buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + + /* Set reference addresses */ + regs->h26x_addr.reg170_185_ref_base[i] = buf_dma; + regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma; + + /* Set COLMV addresses */ + regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset; + } + + /* Set rlc base address (input stream) */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + regs->common_addr.reg128_strm_base = rlc_addr; + + /* Set output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + regs->h26x_addr.reg168_decout_base = dst_addr; + regs->h26x_addr.reg169_error_ref_base = dst_addr; + regs->h26x_addr.reg192_payload_st_cur_base = dst_addr; + + /* Set colmv address */ + regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset; + + /* Set RCB addresses */ + for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) { + regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i); + regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i); + } + + /* Set hw pps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, param_set); + regs->common_addr.reg131_gbl_base = priv_start_addr + offset; + regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_sps_pps) / 16; + + /* Set hw rps address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, rps); + regs->common_addr.reg129_rps_base = priv_start_addr + offset; + + /* Set cabac table */ + offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table); + regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset; + + /* Set scaling list address */ + offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list); + regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset; + + rkvdec_write_regs(ctx); +} + +static int rkvdec_h264_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_priv_tbl *priv_tbl; + struct rkvdec_h264_ctx *h264_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_H264_SPS); + if (!ctrl) + return -EINVAL; + + ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + if (ret) + return ret; + + h264_ctx = kzalloc(sizeof(*h264_ctx), GFP_KERNEL); + if (!h264_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &h264_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + h264_ctx->priv_tbl.size = sizeof(*priv_tbl); + h264_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table, + sizeof(rkvdec_h264_cabac_table)); + + ctx->priv = h264_ctx; + + return 0; + +err_free_ctx: + kfree(h264_ctx); + return ret; +} + +static void rkvdec_h264_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size, + h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma); + kfree(h264_ctx); +} + +static int rkvdec_h264_run(struct rkvdec_ctx *ctx) +{ + struct v4l2_h264_reflist_builder reflist_builder; + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_h264_ctx *h264_ctx = ctx->priv; + struct rkvdec_h264_run run; + struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu; + u32 timeout_threshold; + + rkvdec_h264_run_preamble(ctx, &run); + + /* Build the P/B{0,1} ref lists. */ + v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params, + run.sps, run.decode_params->dpb); + v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); + v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, + h264_ctx->reflists.b1); + + assemble_hw_scaling_list(&run, &tbl->scaling_list); + assemble_hw_pps(ctx, &run); + lookup_ref_buf_idx(ctx, &run); + assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps); + + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + timeout_threshold = h264_ctx->regs.common.reg013_core_timeout_threshold; + rkvdec_schedule_watchdog(rkvdec, timeout_threshold); + + /* Start decoding! */ + writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD); + writel(0, rkvdec->link + VDPU383_LINK_IP_ENABLE); + writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE); + + return 0; +} + +static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) + return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops = { + .adjust_fmt = rkvdec_h264_adjust_fmt, + .get_image_fmt = rkvdec_h264_get_image_fmt, + .start = rkvdec_h264_start, + .stop = rkvdec_h264_stop, + .run = rkvdec_h264_run, + .try_ctrl = rkvdec_h264_try_ctrl, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c new file mode 100644 index 000000000000..085c0a63a80c --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VDPU383 HEVC backend + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#include <media/v4l2-mem2mem.h> + +#include "rkvdec.h" +#include "rkvdec-cabac.h" +#include "rkvdec-rcb.h" +#include "rkvdec-hevc-common.h" +#include "rkvdec-vdpu383-regs.h" + +struct rkvdec_hevc_sps_pps { + // SPS + u16 video_parameters_set_id : 4; + u16 seq_parameters_set_id_sps : 4; + u16 chroma_format_idc : 2; + u16 width : 16; + u16 height : 16; + u16 bit_depth_luma : 3; + u16 bit_depth_chroma : 3; + u16 max_pic_order_count_lsb : 5; + u16 diff_max_min_luma_coding_block_size : 2; + u16 min_luma_coding_block_size : 3; + u16 min_transform_block_size : 3; + u16 diff_max_min_transform_block_size : 2; + u16 max_transform_hierarchy_depth_inter : 3; + u16 max_transform_hierarchy_depth_intra : 3; + u16 scaling_list_enabled_flag : 1; + u16 amp_enabled_flag : 1; + u16 sample_adaptive_offset_enabled_flag : 1; + u16 pcm_enabled_flag : 1; + u16 pcm_sample_bit_depth_luma : 4; + u16 pcm_sample_bit_depth_chroma : 4; + u16 pcm_loop_filter_disabled_flag : 1; + u16 diff_max_min_pcm_luma_coding_block_size : 3; + u16 min_pcm_luma_coding_block_size : 3; + u16 num_short_term_ref_pic_sets : 7; + u16 long_term_ref_pics_present_flag : 1; + u16 num_long_term_ref_pics_sps : 6; + u16 sps_temporal_mvp_enabled_flag : 1; + u16 strong_intra_smoothing_enabled_flag : 1; + u16 reserved0 : 7; + u16 sps_max_dec_pic_buffering_minus1 : 4; + u16 separate_colour_plane_flag : 1; + u16 high_precision_offsets_enabled_flag : 1; + u16 persistent_rice_adaptation_enabled_flag : 1; + + // PPS + u16 picture_parameters_set_id : 6; + u16 seq_parameters_set_id_pps : 4; + u16 dependent_slice_segments_enabled_flag : 1; + u16 output_flag_present_flag : 1; + u16 num_extra_slice_header_bits : 13; + u16 sign_data_hiding_enabled_flag : 1; + u16 cabac_init_present_flag : 1; + u16 num_ref_idx_l0_default_active : 4; + u16 num_ref_idx_l1_default_active : 4; + u16 init_qp_minus26 : 7; + u16 constrained_intra_pred_flag : 1; + u16 transform_skip_enabled_flag : 1; + u16 cu_qp_delta_enabled_flag : 1; + u16 log2_min_cb_size : 3; + u16 pps_cb_qp_offset : 5; + u16 pps_cr_qp_offset : 5; + u16 pps_slice_chroma_qp_offsets_present_flag : 1; + u16 weighted_pred_flag : 1; + u16 weighted_bipred_flag : 1; + u16 transquant_bypass_enabled_flag : 1; + u16 tiles_enabled_flag : 1; + u16 entropy_coding_sync_enabled_flag : 1; + u16 pps_loop_filter_across_slices_enabled_flag : 1; + u16 loop_filter_across_tiles_enabled_flag : 1; + u16 deblocking_filter_override_enabled_flag : 1; + u16 pps_deblocking_filter_disabled_flag : 1; + u16 pps_beta_offset_div2 : 4; + u16 pps_tc_offset_div2 : 4; + u16 lists_modification_present_flag : 1; + u16 log2_parallel_merge_level : 3; + u16 slice_segment_header_extension_present_flag : 1; + u16 reserved1 : 3; + + // pps extensions + u16 log2_max_transform_skip_block_size : 2; + u16 cross_component_prediction_enabled_flag : 1; + u16 chroma_qp_offset_list_enabled_flag : 1; + u16 log2_min_cu_chroma_qp_delta_size : 3; + u16 cb_qp_offset_list0 : 5; + u16 cb_qp_offset_list1 : 5; + u16 cb_qp_offset_list2 : 5; + u16 cb_qp_offset_list3 : 5; + u16 cb_qp_offset_list4 : 5; + u16 cb_qp_offset_list5 : 5; + u16 cb_cr_offset_list0 : 5; + u16 cb_cr_offset_list1 : 5; + u16 cb_cr_offset_list2 : 5; + u16 cb_cr_offset_list3 : 5; + u16 cb_cr_offset_list4 : 5; + u16 cb_cr_offset_list5 : 5; + u16 chroma_qp_offset_list_len_minus1 : 3; + + /* mvc0 && mvc1 */ + u16 mvc_ff : 16; + u16 mvc_00 : 9; + + /* poc info */ + u16 reserved2 : 3; + u32 current_poc : 32; + u32 ref_pic_poc0 : 32; + u32 ref_pic_poc1 : 32; + u32 ref_pic_poc2 : 32; + u32 ref_pic_poc3 : 32; + u32 ref_pic_poc4 : 32; + u32 ref_pic_poc5 : 32; + u32 ref_pic_poc6 : 32; + u32 ref_pic_poc7 : 32; + u32 ref_pic_poc8 : 32; + u32 ref_pic_poc9 : 32; + u32 ref_pic_poc10 : 32; + u32 ref_pic_poc11 : 32; + u32 ref_pic_poc12 : 32; + u32 ref_pic_poc13 : 32; + u32 ref_pic_poc14 : 32; + u32 reserved3 : 32; + u32 ref_is_valid : 15; + u32 reserved4 : 1; + + /* tile info*/ + u16 num_tile_columns : 5; + u16 num_tile_rows : 5; + u32 column_width0 : 24; + u32 column_width1 : 24; + u32 column_width2 : 24; + u32 column_width3 : 24; + u32 column_width4 : 24; + u32 column_width5 : 24; + u32 column_width6 : 24; + u32 column_width7 : 24; + u32 column_width8 : 24; + u32 column_width9 : 24; + u32 row_height0 : 24; + u32 row_height1 : 24; + u32 row_height2 : 24; + u32 row_height3 : 24; + u32 row_height4 : 24; + u32 row_height5 : 24; + u32 row_height6 : 24; + u32 row_height7 : 24; + u32 row_height8 : 24; + u32 row_height9 : 24; + u32 row_height10 : 24; + u32 reserved5 : 2; + u32 padding; +} __packed; + +struct rkvdec_hevc_priv_tbl { + struct rkvdec_hevc_sps_pps param_set; + struct rkvdec_rps rps; + struct scaling_factor scaling_list; + u8 cabac_table[27456]; +} __packed; + +struct rkvdec_hevc_ctx { + struct rkvdec_aux_buf priv_tbl; + struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache; + struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache; + struct vdpu383_regs_h26x regs; +}; + +static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row) +{ + hw_ps->column_width0 = column[0] | (column[1] << 12); + hw_ps->row_height0 = row[0] | (row[1] << 12); + hw_ps->column_width1 = column[2] | (column[3] << 12); + hw_ps->row_height1 = row[2] | (row[3] << 12); + hw_ps->column_width2 = column[4] | (column[5] << 12); + hw_ps->row_height2 = row[4] | (row[5] << 12); + hw_ps->column_width3 = column[6] | (column[7] << 12); + hw_ps->row_height3 = row[6] | (row[7] << 12); + hw_ps->column_width4 = column[8] | (column[9] << 12); + hw_ps->row_height4 = row[8] | (row[9] << 12); + hw_ps->column_width5 = column[10] | (column[11] << 12); + hw_ps->row_height5 = row[10] | (row[11] << 12); + hw_ps->column_width6 = column[12] | (column[13] << 12); + hw_ps->row_height6 = row[12] | (row[13] << 12); + hw_ps->column_width7 = column[14] | (column[15] << 12); + hw_ps->row_height7 = row[14] | (row[15] << 12); + hw_ps->column_width8 = column[16] | (column[17] << 12); + hw_ps->row_height8 = row[16] | (row[17] << 12); + hw_ps->column_width9 = column[18] | (column[19] << 12); + hw_ps->row_height9 = row[18] | (row[19] << 12); + + hw_ps->row_height10 = row[20] | (row[21] << 12); +} + +static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb) +{ + hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val; + hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val; + hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val; + hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val; + hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val; + hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val; + hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val; + hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val; + hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val; + hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val; + hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val; + hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val; + hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val; + hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val; + hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val; +} + +static void assemble_hw_pps(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + struct rkvdec_hevc_ctx *h264_ctx = ctx->priv; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + const struct v4l2_ctrl_hevc_pps *pps = run->pps; + const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params; + struct rkvdec_hevc_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu; + struct rkvdec_hevc_sps_pps *hw_ps; + bool tiles_enabled; + s32 max_cu_width; + s32 pic_in_cts_width; + s32 pic_in_cts_height; + u16 log2_min_cb_size, width, height; + u16 column_width[22]; + u16 row_height[22]; + u8 pcm_enabled; + u32 i; + + /* + * HW read the SPS/PPS information from PPS packet index by PPS id. + * offset from the base can be calculated by PPS_id * 32 (size per PPS + * packet unit). so the driver copy SPS/PPS information to the exact PPS + * packet unit for HW accessing. + */ + hw_ps = &priv_tbl->param_set; + memset(hw_ps, 0, sizeof(*hw_ps)); + + /* write sps */ + hw_ps->video_parameters_set_id = sps->video_parameter_set_id; + hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id; + hw_ps->chroma_format_idc = sps->chroma_format_idc; + + log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; + width = sps->pic_width_in_luma_samples; + height = sps->pic_height_in_luma_samples; + hw_ps->width = width; + hw_ps->height = height; + hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8; + hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8; + hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size; + hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3; + hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2; + hw_ps->diff_max_min_transform_block_size = + sps->log2_diff_max_min_luma_transform_block_size; + hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter; + hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra; + hw_ps->scaling_list_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); + hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED); + hw_ps->sample_adaptive_offset_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + + pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED); + hw_ps->pcm_enabled_flag = pcm_enabled; + hw_ps->pcm_sample_bit_depth_luma = + pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0; + hw_ps->pcm_sample_bit_depth_chroma = + pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0; + hw_ps->pcm_loop_filter_disabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); + hw_ps->diff_max_min_pcm_luma_coding_block_size = + sps->log2_diff_max_min_pcm_luma_coding_block_size; + hw_ps->min_pcm_luma_coding_block_size = + pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0; + hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets; + hw_ps->long_term_ref_pics_present_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); + hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps; + hw_ps->sps_temporal_mvp_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); + hw_ps->strong_intra_smoothing_enabled_flag = + !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); + hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1; + + /* write pps */ + hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id; + hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id; + hw_ps->dependent_slice_segments_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); + hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); + hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits; + hw_ps->sign_data_hiding_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); + hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); + hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1; + hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1; + hw_ps->init_qp_minus26 = pps->init_qp_minus26; + hw_ps->constrained_intra_pred_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); + hw_ps->transform_skip_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); + hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); + hw_ps->log2_min_cb_size = log2_min_cb_size + + sps->log2_diff_max_min_luma_coding_block_size - + pps->diff_cu_qp_delta_depth; + hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset; + hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset; + hw_ps->pps_slice_chroma_qp_offsets_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); + hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); + hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); + hw_ps->transquant_bypass_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); + hw_ps->tiles_enabled_flag = tiles_enabled; + hw_ps->entropy_coding_sync_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); + hw_ps->pps_loop_filter_across_slices_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); + hw_ps->loop_filter_across_tiles_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); + hw_ps->deblocking_filter_override_enabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); + hw_ps->pps_deblocking_filter_disabled_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); + hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2; + hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2; + hw_ps->lists_modification_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); + hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2; + hw_ps->slice_segment_header_extension_present_flag = + !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); + hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1; + hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1; + hw_ps->mvc_ff = 0xffff; + + // Setup tiles information + memset(column_width, 0, sizeof(column_width)); + memset(row_height, 0, sizeof(row_height)); + + max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size); + pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width; + pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width; + + if (tiles_enabled) { + if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) { + compute_tiles_uniform(run, log2_min_cb_size, width, height, + pic_in_cts_width, pic_in_cts_height, + column_width, row_height); + } else { + compute_tiles_non_uniform(run, log2_min_cb_size, width, height, + pic_in_cts_width, pic_in_cts_height, + column_width, row_height); + } + } else { + column_width[0] = (width + max_cu_width - 1) / max_cu_width; + row_height[0] = (height + max_cu_width - 1) / max_cu_width; + } + + set_column_row(hw_ps, column_width, row_height); + + // Setup POC information + hw_ps->current_poc = dec_params->pic_order_cnt_val; + + set_pps_ref_pic_poc(hw_ps, dec_params->dpb); + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { + u32 valid = !!(dec_params->num_active_dpb_entries > i); + hw_ps->ref_is_valid |= valid << i; + } +} + +static void rkvdec_write_regs(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_ctx *h265_ctx = ctx->priv; + + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS, + &h265_ctx->regs.common, + sizeof(h265_ctx->regs.common)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS, + &h265_ctx->regs.common_addr, + sizeof(h265_ctx->regs.common_addr)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS, + &h265_ctx->regs.h26x_params, + sizeof(h265_ctx->regs.h26x_params)); + rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS, + &h265_ctx->regs.h26x_addr, + sizeof(h265_ctx->regs.h26x_addr)); +} + +static void config_registers(struct rkvdec_ctx *ctx, + struct rkvdec_hevc_run *run) +{ + const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params; + struct rkvdec_hevc_ctx *h265_ctx = ctx->priv; + const struct v4l2_ctrl_hevc_sps *sps = run->sps; + dma_addr_t priv_start_addr = h265_ctx->priv_tbl.dma; + const struct v4l2_pix_format_mplane *dst_fmt; + struct vb2_v4l2_buffer *src_buf = run->base.bufs.src; + struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst; + struct vdpu383_regs_h26x *regs = &h265_ctx->regs; + const struct v4l2_format *f; + dma_addr_t rlc_addr; + dma_addr_t dst_addr; + u32 hor_virstride; + u32 ver_virstride; + u32 y_virstride; + u32 offset; + u32 pixels; + u32 i; + + memset(regs, 0, sizeof(*regs)); + + /* Set HEVC mode */ + regs->common.reg008_dec_mode = VDPU383_MODE_HEVC; + + /* Set input stream length */ + regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + + /* Set strides */ + f = &ctx->decoded_fmt; + dst_fmt = &f->fmt.pix_mp; + hor_virstride = dst_fmt->plane_fmt[0].bytesperline; + ver_virstride = dst_fmt->height; + y_virstride = hor_virstride * ver_virstride; + + pixels = dst_fmt->height * dst_fmt->width; + + regs->h26x_params.reg068_hor_virstride = hor_virstride / 16; + regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16; + regs->h26x_params.reg070_y_virstride = y_virstride / 16; + + /* Activate block gating */ + regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.inter_auto_gating_e = 1; + regs->common.reg010_block_gating_en.intra_auto_gating_e = 1; + regs->common.reg010_block_gating_en.transd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.recon_auto_gating_e = 1; + regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1; + regs->common.reg010_block_gating_en.bus_auto_gating_e = 1; + regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1; + regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1; + regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1; + + /* Set timeout threshold */ + if (pixels < RKVDEC_1080P_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p; + else if (pixels < RKVDEC_4K_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K; + else if (pixels < RKVDEC_8K_PIXELS) + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K; + else + regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX; + + regs->common.reg016_error_ctrl_set.error_proc_disable = 1; + + /* Set ref pic address & poc */ + for (i = 0; i < ARRAY_SIZE(dec_params->dpb) - 1; i++) { + struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i); + dma_addr_t buf_dma; + + buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0); + + /* Set reference addresses */ + regs->h26x_addr.reg170_185_ref_base[i] = buf_dma; + regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma; + + /* Set COLMV addresses */ + regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset; + } + + /* Set rlc base address (input stream) */ + rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + regs->common_addr.reg128_strm_base = rlc_addr; + + /* Set output base address */ + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + regs->h26x_addr.reg168_decout_base = dst_addr; + regs->h26x_addr.reg169_error_ref_base = dst_addr; + regs->h26x_addr.reg192_payload_st_cur_base = dst_addr; + + /* Set colmv address */ + regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset; + + /* Set RCB addresses */ + for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) { + regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i); + regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i); + } + + if (sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) { + /* Set scaling matrix */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list); + regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset; + } + + /* Set hw pps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set); + regs->common_addr.reg131_gbl_base = priv_start_addr + offset; + regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_hevc_sps_pps) / 16; + + /* Set hw rps address */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, rps); + regs->common_addr.reg129_rps_base = priv_start_addr + offset; + + /* Set cabac table */ + offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table); + regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset; + + rkvdec_write_regs(ctx); +} + +static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx, + const struct v4l2_ctrl_hevc_sps *sps) +{ + if (sps->chroma_format_idc != 1) + /* Only 4:2:0 is supported */ + return -EINVAL; + + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) + /* Luma and chroma bit depth mismatch */ + return -EINVAL; + + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) + /* Only 8-bit and 10-bit are supported */ + return -EINVAL; + + if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width || + sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height) + return -EINVAL; + + return 0; +} + +static int rkvdec_hevc_start(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_priv_tbl *priv_tbl; + struct rkvdec_hevc_ctx *hevc_ctx; + struct v4l2_ctrl *ctrl; + int ret; + + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, + V4L2_CID_STATELESS_HEVC_SPS); + if (!ctrl) + return -EINVAL; + + ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps); + if (ret) + return ret; + + hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL); + if (!hevc_ctx) + return -ENOMEM; + + priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl), + &hevc_ctx->priv_tbl.dma, GFP_KERNEL); + if (!priv_tbl) { + ret = -ENOMEM; + goto err_free_ctx; + } + + hevc_ctx->priv_tbl.size = sizeof(*priv_tbl); + hevc_ctx->priv_tbl.cpu = priv_tbl; + memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table, + sizeof(rkvdec_hevc_cabac_table)); + + ctx->priv = hevc_ctx; + return 0; + +err_free_ctx: + kfree(hevc_ctx); + return ret; +} + +static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx) +{ + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_dev *rkvdec = ctx->dev; + + dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size, + hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma); + kfree(hevc_ctx); +} + +static int rkvdec_hevc_run(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_hevc_run run; + struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv; + struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu; + u32 timeout_threshold; + + rkvdec_hevc_run_preamble(ctx, &run); + + /* + * On vdpu383, not setting the long and short term ref sets leads to IOMMU page faults. + * To be on the safe side for this new v4l2 control, write an error in the log and mark + * the buffer as failed by returning an error here. + */ + if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) || + (!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) { + dev_err_ratelimited(rkvdec->dev, "Long and short term RPS not set\n"); + return -EINVAL; + } + + rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list, + &hevc_ctx->scaling_matrix_cache); + assemble_hw_pps(ctx, &run); + rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache); + + config_registers(ctx, &run); + + rkvdec_run_postamble(ctx, &run.base); + + timeout_threshold = hevc_ctx->regs.common.reg013_core_timeout_threshold; + rkvdec_schedule_watchdog(rkvdec, timeout_threshold); + + /* Start decoding! */ + writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD); + writel(VDPU383_IP_CRU_MODE, rkvdec->link + VDPU383_LINK_IP_ENABLE); + writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE); + + return 0; +} + +static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl) +{ + if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) + return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps); + + return 0; +} + +const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops = { + .adjust_fmt = rkvdec_hevc_adjust_fmt, + .start = rkvdec_hevc_start, + .stop = rkvdec_hevc_stop, + .run = rkvdec_hevc_run, + .try_ctrl = rkvdec_hevc_try_ctrl, + .get_image_fmt = rkvdec_hevc_get_image_fmt, +}; diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h new file mode 100644 index 000000000000..04eb23297e37 --- /dev/null +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-regs.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip Video Decoder VDPU383 driver registers description + * + * Copyright (C) 2025 Collabora, Ltd. + * Detlev Casanova <detlev.casanova@collabora.com> + */ + +#ifndef _RKVDEC_VDPU838_REGS_H_ +#define _RKVDEC_VDPU838_REGS_H_ + +#include <linux/types.h> + +#define VDPU383_OFFSET_COMMON_REGS (8 * sizeof(u32)) +#define VDPU383_OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32)) +#define VDPU383_OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32)) +#define VDPU383_OFFSET_CODEC_ADDR_REGS (168 * sizeof(u32)) +#define VDPU383_OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32)) + +#define VDPU383_MODE_HEVC 0 +#define VDPU383_MODE_H264 1 + +#define VDPU383_TIMEOUT_1080p (0xffffff) +#define VDPU383_TIMEOUT_4K (0x2cfffff) +#define VDPU383_TIMEOUT_8K (0x4ffffff) +#define VDPU383_TIMEOUT_MAX (0xffffffff) + +#define VDPU383_LINK_TIMEOUT_THRESHOLD 0x54 + +#define VDPU383_LINK_IP_ENABLE 0x58 +#define VDPU383_IP_CRU_MODE BIT(24) + +#define VDPU383_LINK_DEC_ENABLE 0x40 +#define VDPU383_DEC_E_BIT BIT(0) + +#define VDPU383_LINK_INT_EN 0x048 +#define VDPU383_INT_EN_IRQ BIT(0) +#define VDPU383_INT_EN_LINE_IRQ BIT(1) + +#define VDPU383_LINK_STA_INT 0x04c +#define VDPU383_STA_INT_DEC_RDY_STA BIT(0) +#define VDPU383_STA_INT_SOFTRESET_RDY (BIT(10) | BIT(11)) +#define VDPU383_STA_INT_ALL 0x3ff + +struct vdpu383_regs_common { + u32 reg008_dec_mode; + + struct { + u32 fbc_e : 1; + u32 tile_e : 1; + u32 reserve0 : 2; + u32 buf_empty_en : 1; + u32 scale_down_en : 1; + u32 reserve1 : 1; + u32 pix_range_det_e : 1; + u32 av1_fgs_en : 1; + u32 reserve2 : 7; + u32 line_irq_en : 1; + u32 out_cbcr_swap : 1; + u32 fbc_force_uncompress : 1; + u32 fbc_sparse_mode : 1; + u32 reserve3 : 12; + } reg009_important_en; + + struct { + u32 strmd_auto_gating_e : 1; + u32 inter_auto_gating_e : 1; + u32 intra_auto_gating_e : 1; + u32 transd_auto_gating_e : 1; + u32 recon_auto_gating_e : 1; + u32 filterd_auto_gating_e : 1; + u32 bus_auto_gating_e : 1; + u32 ctrl_auto_gating_e : 1; + u32 rcb_auto_gating_e : 1; + u32 err_prc_auto_gating_e : 1; + u32 reserve0 : 22; + } reg010_block_gating_en; + + struct { + u32 reserve0 : 9; + u32 dec_timeout_dis : 1; + u32 reserve1 : 22; + } reg011_cfg_para; + + struct { + u32 reserve0 : 7; + u32 cache_hash_mask : 25; + } reg012_cache_hash_mask; + + u32 reg013_core_timeout_threshold; + + struct { + u32 dec_line_irq_step : 16; + u32 dec_line_offset_y_st : 16; + } reg014_line_irq_ctrl; + + struct { + u32 rkvdec_frame_rdy_sta : 1; + u32 rkvdec_strm_error_sta : 1; + u32 rkvdec_core_timeout_sta : 1; + u32 rkvdec_ip_timeout_sta : 1; + u32 rkvdec_bus_error_sta : 1; + u32 rkvdec_buffer_empty_sta : 1; + u32 rkvdec_colmv_ref_error_sta : 1; + u32 rkvdec_error_spread_sta : 1; + u32 create_core_timeout_sta : 1; + u32 wlast_miss_match_sta : 1; + u32 rkvdec_core_rst_rdy_sta : 1; + u32 rkvdec_ip_rst_rdy_sta : 1; + u32 force_busidle_rdy_sta : 1; + u32 ltb_pause_rdy_sta : 1; + u32 ltb_end_flag : 1; + u32 unsupport_decmode_error_sta : 1; + u32 wmask_bits : 15; + u32 reserve0 : 1; + } reg015_irq_sta; + + struct { + u32 error_proc_disable : 1; + u32 reserve0 : 7; + u32 error_spread_disable : 1; + u32 reserve1 : 15; + u32 roi_error_ctu_cal_en : 1; + u32 reserve2 : 7; + } reg016_error_ctrl_set; + + struct { + u32 roi_x_ctu_offset_st : 12; + u32 reserve0 : 4; + u32 roi_y_ctu_offset_st : 12; + u32 reserve1 : 4; + } reg017_err_roi_ctu_offset_start; + + struct { + u32 roi_x_ctu_offset_end : 12; + u32 reserve0 : 4; + u32 roi_y_ctu_offset_end : 12; + u32 reserve1 : 4; + } reg018_err_roi_ctu_offset_end; + + struct { + u32 avs2_ref_error_field : 1; + u32 avs2_ref_error_topfield : 1; + u32 ref_error_topfield_used : 1; + u32 ref_error_botfield_used : 1; + u32 reserve0 : 28; + } reg019_error_ref_info; + + u32 reg020_cabac_error_en_lowbits; + u32 reg021_cabac_error_en_highbits; + + u32 reg022_reserved; + + struct { + u32 fill_y : 10; + u32 fill_u : 10; + u32 fill_v : 10; + u32 reserve0 : 2; + } reg023_invalid_pixel_fill; + + u32 reg024_026_reserved[3]; + + struct { + u32 reserve0 : 4; + u32 ctu_align_wr_en : 1; + u32 reserve1 : 27; + } reg027_align_en; + + struct { + u32 axi_perf_work_e : 1; + u32 reserve0 : 2; + u32 axi_cnt_type : 1; + u32 rd_latency_id : 8; + u32 reserve1 : 4; + u32 rd_latency_thr : 12; + u32 reserve2 : 4; + } reg028_debug_perf_latency_ctrl0; + + struct { + u32 addr_align_type : 2; + u32 ar_cnt_id_type : 1; + u32 aw_cnt_id_type : 1; + u32 ar_count_id : 8; + u32 reserve0 : 4; + u32 aw_count_id : 8; + u32 rd_band_width_mode : 1; + u32 reserve1 : 7; + } reg029_debug_perf_latency_ctrl1; + + struct { + u32 axi_wr_qos_level : 4; + u32 reserve0 : 4; + u32 axi_wr_qos : 4; + u32 reserve1 : 4; + u32 axi_rd_qos_level : 4; + u32 reserve2 : 4; + u32 axi_rd_qos : 4; + u32 reserve3 : 4; + } reg030_qos_ctrl; +}; + +struct vdpu383_regs_common_addr { + u32 reg128_strm_base; + u32 reg129_rps_base; + u32 reg130_cabactbl_base; + u32 reg131_gbl_base; + u32 reg132_scanlist_addr; + u32 reg133_scale_down_base; + u32 reg134_fgs_base; + u32 reg135_139_reserved[5]; + + struct rcb_info { + u32 offset; + u32 size; + } reg140_162_rcb_info[11]; +}; + +struct vdpu383_regs_h26x_addr { + u32 reg168_decout_base; + u32 reg169_error_ref_base; + u32 reg170_185_ref_base[16]; + u32 reg186_191_reserved[6]; + u32 reg192_payload_st_cur_base; + u32 reg193_fbc_payload_offset; + u32 reg194_payload_st_error_ref_base; + u32 reg195_210_payload_st_ref_base[16]; + u32 reg211_215_reserved[5]; + u32 reg216_colmv_cur_base; + u32 reg217_232_colmv_ref_base[16]; +}; + +struct vdpu383_regs_h26x_params { + u32 reg064_start_decoder; + u32 reg065_strm_start_bit; + u32 reg066_stream_len; + u32 reg067_global_len; + u32 reg068_hor_virstride; + u32 reg069_raster_uv_hor_virstride; + u32 reg070_y_virstride; + u32 reg071_scl_ref_hor_virstride; + u32 reg072_scl_ref_raster_uv_hor_virstride; + u32 reg073_scl_ref_virstride; + u32 reg074_fgs_ref_hor_virstride; + u32 reg075_079_reserved[5]; + u32 reg080_error_ref_hor_virstride; + u32 reg081_error_ref_raster_uv_hor_virstride; + u32 reg082_error_ref_virstride; + u32 reg083_ref0_hor_virstride; + u32 reg084_ref0_raster_uv_hor_virstride; + u32 reg085_ref0_virstride; + u32 reg086_ref1_hor_virstride; + u32 reg087_ref1_raster_uv_hor_virstride; + u32 reg088_ref1_virstride; + u32 reg089_ref2_hor_virstride; + u32 reg090_ref2_raster_uv_hor_virstride; + u32 reg091_ref2_virstride; + u32 reg092_ref3_hor_virstride; + u32 reg093_ref3_raster_uv_hor_virstride; + u32 reg094_ref3_virstride; + u32 reg095_ref4_hor_virstride; + u32 reg096_ref4_raster_uv_hor_virstride; + u32 reg097_ref4_virstride; + u32 reg098_ref5_hor_virstride; + u32 reg099_ref5_raster_uv_hor_virstride; + u32 reg100_ref5_virstride; + u32 reg101_ref6_hor_virstride; + u32 reg102_ref6_raster_uv_hor_virstride; + u32 reg103_ref6_virstride; + u32 reg104_ref7_hor_virstride; + u32 reg105_ref7_raster_uv_hor_virstride; + u32 reg106_ref7_virstride; +}; + +struct vdpu383_regs_h26x { + struct vdpu383_regs_common common; /* 8-30 */ + struct vdpu383_regs_h26x_params h26x_params; /* 64-74, 80-106 */ + struct vdpu383_regs_common_addr common_addr; /* 128-134, 140-161 */ + struct vdpu383_regs_h26x_addr h26x_addr; /* 168-185, 192-210, 216-232 */ +} __packed; + +#endif /* __RKVDEC_VDPU838_REGS_H__ */ diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c index b4bf01e839ef..ba51a7c2fe55 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c @@ -163,6 +163,7 @@ struct rkvdec_vp9_ctx { struct v4l2_vp9_frame_context frame_context[4]; struct rkvdec_vp9_frame_info cur; struct rkvdec_vp9_frame_info last; + struct rkvdec_regs regs; }; static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane) @@ -347,38 +348,6 @@ static void init_probs(struct rkvdec_ctx *ctx, init_inter_probs(ctx, run); } -struct rkvdec_vp9_ref_reg { - u32 reg_frm_size; - u32 reg_hor_stride; - u32 reg_y_stride; - u32 reg_yuv_stride; - u32 reg_ref_base; -}; - -static struct rkvdec_vp9_ref_reg ref_regs[] = { - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(0), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(0), - .reg_y_stride = RKVDEC_VP9_LAST_FRAME_YSTRIDE, - .reg_yuv_stride = RKVDEC_VP9_LAST_FRAME_YUVSTRIDE, - .reg_ref_base = RKVDEC_REG_VP9_LAST_FRAME_BASE, - }, - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(1), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(1), - .reg_y_stride = RKVDEC_VP9_GOLDEN_FRAME_YSTRIDE, - .reg_yuv_stride = 0, - .reg_ref_base = RKVDEC_REG_VP9_GOLDEN_FRAME_BASE, - }, - { - .reg_frm_size = RKVDEC_REG_VP9_FRAME_SIZE(2), - .reg_hor_stride = RKVDEC_VP9_HOR_VIRSTRIDE(2), - .reg_y_stride = RKVDEC_VP9_ALTREF_FRAME_YSTRIDE, - .reg_yuv_stride = 0, - .reg_ref_base = RKVDEC_REG_VP9_ALTREF_FRAME_BASE, - } -}; - static struct rkvdec_decoded_buffer * get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) { @@ -412,18 +381,17 @@ static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf) static void config_ref_registers(struct rkvdec_ctx *ctx, const struct rkvdec_vp9_run *run, struct rkvdec_decoded_buffer *ref_buf, - struct rkvdec_vp9_ref_reg *ref_reg) + int i) { unsigned int aligned_pitch, aligned_height, y_len, yuv_len; - struct rkvdec_dev *rkvdec = ctx->dev; + struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_regs *regs = &vp9_ctx->regs; aligned_height = round_up(ref_buf->vp9.height, 64); - writel_relaxed(RKVDEC_VP9_FRAMEWIDTH(ref_buf->vp9.width) | - RKVDEC_VP9_FRAMEHEIGHT(ref_buf->vp9.height), - rkvdec->regs + ref_reg->reg_frm_size); + regs->vp9.reg17_19[i].frameheight = ref_buf->vp9.height; + regs->vp9.reg17_19[i].framewidth = ref_buf->vp9.width; - writel_relaxed(vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0), - rkvdec->regs + ref_reg->reg_ref_base); + regs->vp9.refer_bases[i] = vb2_dma_contig_plane_dma_addr(&ref_buf->base.vb.vb2_buf, 0); if (&ref_buf->base.vb == run->base.bufs.dst) return; @@ -432,59 +400,50 @@ static void config_ref_registers(struct rkvdec_ctx *ctx, y_len = aligned_height * aligned_pitch; yuv_len = (y_len * 3) / 2; - writel_relaxed(RKVDEC_HOR_Y_VIRSTRIDE(aligned_pitch / 16) | - RKVDEC_HOR_UV_VIRSTRIDE(aligned_pitch / 16), - rkvdec->regs + ref_reg->reg_hor_stride); - writel_relaxed(RKVDEC_VP9_REF_YSTRIDE(y_len / 16), - rkvdec->regs + ref_reg->reg_y_stride); - - if (!ref_reg->reg_yuv_stride) - return; + regs->vp9.reg37_39[i].y_hor_virstride = aligned_pitch / 16; + regs->vp9.reg37_39[i].uv_hor_virstride = aligned_pitch / 16; + regs->vp9.reg48_50[i].virstride = y_len / 16; - writel_relaxed(RKVDEC_VP9_REF_YUVSTRIDE(yuv_len / 16), - rkvdec->regs + ref_reg->reg_yuv_stride); + if (!i) + regs->vp9.reg51.lastref_yuv_virstride = yuv_len / 16; } static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid) { struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; + struct rkvdec_regs *regs = &vp9_ctx->regs; const struct v4l2_vp9_segmentation *seg; - struct rkvdec_dev *rkvdec = ctx->dev; s16 feature_val; int feature_id; - u32 val = 0; seg = vp9_ctx->last.valid ? &vp9_ctx->last.seg : &vp9_ctx->cur.seg; feature_id = V4L2_VP9_SEG_LVL_ALT_Q; if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_FRAME_QP_DELTA_EN(1) | - RKVDEC_SEGID_FRAME_QP_DELTA(feature_val); + regs->vp9.reg20_27[segid].segid_frame_qp_delta_en = 1; + regs->vp9.reg20_27[segid].segid_frame_qp_delta = feature_val; } feature_id = V4L2_VP9_SEG_LVL_ALT_L; if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE_EN(1) | - RKVDEC_SEGID_FRAME_LOOPFILTER_VALUE(feature_val); + regs->vp9.reg20_27[segid].segid_frame_loopfilter_value_en = 1; + regs->vp9.reg20_27[segid].segid_frame_loopfilter_value = feature_val; } feature_id = V4L2_VP9_SEG_LVL_REF_FRAME; if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) { feature_val = seg->feature_data[segid][feature_id]; - val |= RKVDEC_SEGID_REFERINFO_EN(1) | - RKVDEC_SEGID_REFERINFO(feature_val); + regs->vp9.reg20_27[segid].segid_referinfo_en = 1; + regs->vp9.reg20_27[segid].segid_referinfo = feature_val; } feature_id = V4L2_VP9_SEG_LVL_SKIP; - if (v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid)) - val |= RKVDEC_SEGID_FRAME_SKIP_EN(1); - - if (!segid && - (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE)) - val |= RKVDEC_SEGID_ABS_DELTA(1); + regs->vp9.reg20_27[segid].segid_frame_skip_en = + v4l2_vp9_seg_feat_enabled(seg->feature_enabled, feature_id, segid); - writel_relaxed(val, rkvdec->regs + RKVDEC_VP9_SEGID_GRP(segid)); + regs->vp9.reg20_27[segid].segid_abs_delta = !segid && + (seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE); } static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf, @@ -521,7 +480,7 @@ static void config_registers(struct rkvdec_ctx *ctx, struct rkvdec_decoded_buffer *ref_bufs[3]; struct rkvdec_decoded_buffer *dst, *last, *mv_ref; struct rkvdec_vp9_ctx *vp9_ctx = ctx->priv; - u32 val, last_frame_info = 0; + struct rkvdec_regs *regs = &vp9_ctx->regs; const struct v4l2_vp9_segmentation *seg; struct rkvdec_dev *rkvdec = ctx->dev; dma_addr_t addr; @@ -547,8 +506,7 @@ static void config_registers(struct rkvdec_ctx *ctx, (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); - writel_relaxed(RKVDEC_MODE(RKVDEC_MODE_VP9), - rkvdec->regs + RKVDEC_REG_SYSCTRL); + regs->common.reg02.dec_mode = RKVDEC_MODE_VP9; bit_depth = dec_params->bit_depth; aligned_height = round_up(ctx->decoded_fmt.fmt.pix_mp.height, 64); @@ -560,17 +518,14 @@ static void config_registers(struct rkvdec_ctx *ctx, uv_len = y_len / 2; yuv_len = y_len + uv_len; - writel_relaxed(RKVDEC_Y_HOR_VIRSTRIDE(aligned_pitch / 16) | - RKVDEC_UV_HOR_VIRSTRIDE(aligned_pitch / 16), - rkvdec->regs + RKVDEC_REG_PICPAR); - writel_relaxed(RKVDEC_Y_VIRSTRIDE(y_len / 16), - rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE); - writel_relaxed(RKVDEC_YUV_VIRSTRIDE(yuv_len / 16), - rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE); + regs->common.reg03.y_hor_virstride = aligned_pitch / 16; + regs->common.reg03.uv_hor_virstride = aligned_pitch / 16; + regs->common.reg08.y_virstride = y_len / 16; + regs->common.reg09.yuv_virstride = yuv_len / 16; stream_len = vb2_get_plane_payload(&run->base.bufs.src->vb2_buf, 0); - writel_relaxed(RKVDEC_STRM_LEN(stream_len), - rkvdec->regs + RKVDEC_REG_STRM_LEN); + + regs->common.stream_len = stream_len; /* * Reset count buffer, because decoder only output intra related syntax @@ -588,14 +543,13 @@ static void config_registers(struct rkvdec_ctx *ctx, vp9_ctx->cur.segmapid++; for (i = 0; i < ARRAY_SIZE(ref_bufs); i++) - config_ref_registers(ctx, run, ref_bufs[i], &ref_regs[i]); + config_ref_registers(ctx, run, ref_bufs[i], i); for (i = 0; i < 8; i++) config_seg_registers(ctx, i); - writel_relaxed(RKVDEC_VP9_TX_MODE(vp9_ctx->cur.tx_mode) | - RKVDEC_VP9_FRAME_REF_MODE(dec_params->reference_mode), - rkvdec->regs + RKVDEC_VP9_CPRHEADER_CONFIG); + regs->vp9.reg28.tx_mode = vp9_ctx->cur.tx_mode; + regs->vp9.reg28.frame_reference_mode = dec_params->reference_mode; if (!intra_only) { const struct v4l2_vp9_loop_filter *lf; @@ -606,46 +560,58 @@ static void config_registers(struct rkvdec_ctx *ctx, else lf = &vp9_ctx->cur.lf; - val = 0; for (i = 0; i < ARRAY_SIZE(lf->ref_deltas); i++) { delta = lf->ref_deltas[i]; - val |= RKVDEC_REF_DELTAS_LASTFRAME(i, delta); + switch (i) { + case 0: + regs->vp9.reg32.ref_deltas_lastframe0 = delta; + break; + case 1: + regs->vp9.reg32.ref_deltas_lastframe1 = delta; + break; + case 2: + regs->vp9.reg32.ref_deltas_lastframe2 = delta; + break; + case 3: + regs->vp9.reg32.ref_deltas_lastframe3 = delta; + break; + } } - writel_relaxed(val, - rkvdec->regs + RKVDEC_VP9_REF_DELTAS_LASTFRAME); - for (i = 0; i < ARRAY_SIZE(lf->mode_deltas); i++) { delta = lf->mode_deltas[i]; - last_frame_info |= RKVDEC_MODE_DELTAS_LASTFRAME(i, - delta); + switch (i) { + case 0: + regs->vp9.reg33.mode_deltas_lastframe0 = delta; + break; + case 1: + regs->vp9.reg33.mode_deltas_lastframe1 = delta; + break; + } } } - if (vp9_ctx->last.valid && !intra_only && - vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED) - last_frame_info |= RKVDEC_SEG_EN_LASTFRAME; + regs->vp9.reg33.segmentation_enable_lstframe = + vp9_ctx->last.valid && !intra_only && + vp9_ctx->last.seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED; - if (vp9_ctx->last.valid && - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME) - last_frame_info |= RKVDEC_LAST_SHOW_FRAME; + regs->vp9.reg33.last_show_frame = + vp9_ctx->last.valid && + vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME; - if (vp9_ctx->last.valid && - vp9_ctx->last.flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY)) - last_frame_info |= RKVDEC_LAST_INTRA_ONLY; + regs->vp9.reg33.last_intra_only = + vp9_ctx->last.valid && + vp9_ctx->last.flags & + (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); - if (vp9_ctx->last.valid && - last->vp9.width == dst->vp9.width && - last->vp9.height == dst->vp9.height) - last_frame_info |= RKVDEC_LAST_WIDHHEIGHT_EQCUR; + regs->vp9.reg33.last_widthheight_eqcur = + vp9_ctx->last.valid && + last->vp9.width == dst->vp9.width && + last->vp9.height == dst->vp9.height; - writel_relaxed(last_frame_info, - rkvdec->regs + RKVDEC_VP9_INFO_LASTFRAME); - - writel_relaxed(stream_len - dec_params->compressed_header_size - - dec_params->uncompressed_header_size, - rkvdec->regs + RKVDEC_VP9_LASTTILE_SIZE); + regs->vp9.reg36.lasttile_size = + stream_len - dec_params->compressed_header_size - + dec_params->uncompressed_header_size; for (i = 0; !intra_only && i < ARRAY_SIZE(ref_bufs); i++) { unsigned int refw = ref_bufs[i]->vp9.width; @@ -654,29 +620,28 @@ static void config_registers(struct rkvdec_ctx *ctx, hscale = (refw << 14) / dst->vp9.width; vscale = (refh << 14) / dst->vp9.height; - writel_relaxed(RKVDEC_VP9_REF_HOR_SCALE(hscale) | - RKVDEC_VP9_REF_VER_SCALE(vscale), - rkvdec->regs + RKVDEC_VP9_REF_SCALE(i)); + + regs->vp9.reg29_31[i].ref_hor_scale = hscale; + regs->vp9.reg29_31[i].ref_ver_scale = vscale; } addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 0); - writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE); + regs->common.decout_base = addr; addr = vb2_dma_contig_plane_dma_addr(&run->base.bufs.src->vb2_buf, 0); - writel_relaxed(addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE); - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, probs), - rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE); - writel_relaxed(vp9_ctx->count_tbl.dma, - rkvdec->regs + RKVDEC_REG_VP9COUNT_BASE); - - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, segmap) + - (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid), - rkvdec->regs + RKVDEC_REG_VP9_SEGIDCUR_BASE); - writel_relaxed(vp9_ctx->priv_tbl.dma + - offsetof(struct rkvdec_vp9_priv_tbl, segmap) + - (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)), - rkvdec->regs + RKVDEC_REG_VP9_SEGIDLAST_BASE); + regs->common.strm_rlc_base = addr; + + regs->common.cabactbl_base = vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, probs); + + regs->vp9.count_base = vp9_ctx->count_tbl.dma; + + regs->vp9.segidlast_base = vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, segmap) + + (RKVDEC_VP9_MAX_SEGMAP_SIZE * (!vp9_ctx->cur.segmapid)); + + regs->vp9.segidcur_base = vp9_ctx->priv_tbl.dma + + offsetof(struct rkvdec_vp9_priv_tbl, segmap) + + (RKVDEC_VP9_MAX_SEGMAP_SIZE * vp9_ctx->cur.segmapid); if (!intra_only && !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && @@ -685,12 +650,15 @@ static void config_registers(struct rkvdec_ctx *ctx, else mv_ref = dst; - writel_relaxed(get_mv_base_addr(mv_ref), - rkvdec->regs + RKVDEC_VP9_REF_COLMV_BASE); + regs->vp9.refcolmv_base = get_mv_base_addr(mv_ref); - writel_relaxed(ctx->decoded_fmt.fmt.pix_mp.width | - (ctx->decoded_fmt.fmt.pix_mp.height << 16), - rkvdec->regs + RKVDEC_REG_PERFORMANCE_CYCLE); + regs->vp9.performance_cycle = ctx->decoded_fmt.fmt.pix_mp.width | + (ctx->decoded_fmt.fmt.pix_mp.height << 16); + + regs->vp9.reg44.strmd_error_e = 0xe; + + rkvdec_memcpy_toio(rkvdec->regs, regs, + MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs)); } static int validate_dec_params(struct rkvdec_ctx *ctx, @@ -823,8 +791,6 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx) writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND); writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND); - writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN); - if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS) rkvdec_quirks_disable_qos(ctx); diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 5af9aa5ab353..967c452ab61f 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -9,7 +9,9 @@ * Copyright (C) 2011 Samsung Electronics Co., Ltd. */ +#include <linux/hw_bitfield.h> #include <linux/clk.h> +#include <linux/genalloc.h> #include <linux/interrupt.h> #include <linux/iommu.h> #include <linux/module.h> @@ -28,6 +30,9 @@ #include "rkvdec.h" #include "rkvdec-regs.h" +#include "rkvdec-vdpu381-regs.h" +#include "rkvdec-vdpu383-regs.h" +#include "rkvdec-rcb.h" static bool rkvdec_image_fmt_match(enum rkvdec_image_fmt fmt1, enum rkvdec_image_fmt fmt2) @@ -83,14 +88,26 @@ static bool rkvdec_is_valid_fmt(struct rkvdec_ctx *ctx, u32 fourcc, return false; } +static u32 rkvdec_colmv_size(u16 width, u16 height) +{ + return 128 * DIV_ROUND_UP(width, 16) * DIV_ROUND_UP(height, 16); +} + +static u32 vdpu383_colmv_size(u16 width, u16 height) +{ + return ALIGN(width, 64) * ALIGN(height, 16); +} + static void rkvdec_fill_decoded_pixfmt(struct rkvdec_ctx *ctx, struct v4l2_pix_format_mplane *pix_mp) { - v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, - pix_mp->width, pix_mp->height); - pix_mp->plane_fmt[0].sizeimage += 128 * - DIV_ROUND_UP(pix_mp->width, 16) * - DIV_ROUND_UP(pix_mp->height, 16); + const struct rkvdec_variant *variant = ctx->dev->variant; + + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, pix_mp->width, pix_mp->height); + + ctx->colmv_offset = pix_mp->plane_fmt[0].sizeimage; + + pix_mp->plane_fmt[0].sizeimage += variant->ops->colmv_size(pix_mp->width, pix_mp->height); } static void rkvdec_reset_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f, @@ -136,6 +153,16 @@ static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl) enum rkvdec_image_fmt image_fmt; struct vb2_queue *vq; + if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS) { + ctx->has_sps_st_rps |= !!(ctrl->has_changed); + return 0; + } + + if (ctrl->id == V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS) { + ctx->has_sps_lt_rps |= !!(ctrl->has_changed); + return 0; + } + /* Check if this change requires a capture format reset */ if (!desc->ops->get_image_fmt) return 0; @@ -209,6 +236,62 @@ static const struct rkvdec_ctrls rkvdec_hevc_ctrls = { .num_ctrls = ARRAY_SIZE(rkvdec_hevc_ctrl_descs), }; +static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SPS, + .cfg.ops = &rkvdec_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_PPS, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE, + .cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .cfg.menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + }, + { + .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS, + .cfg.ops = &rkvdec_ctrl_ops, + .cfg.dims = { 65 }, + }, + { + .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS, + .cfg.ops = &rkvdec_ctrl_ops, + .cfg.dims = { 65 }, + }, +}; + +static const struct rkvdec_ctrls vdpu38x_hevc_ctrls = { + .ctrls = vdpu38x_hevc_ctrl_descs, + .num_ctrls = ARRAY_SIZE(vdpu38x_hevc_ctrl_descs), +}; + static const struct rkvdec_decoded_fmt_desc rkvdec_hevc_decoded_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, @@ -267,6 +350,53 @@ static const struct rkvdec_ctrls rkvdec_h264_ctrls = { .num_ctrls = ARRAY_SIZE(rkvdec_h264_ctrl_descs), }; +static const struct rkvdec_ctrl_desc vdpu38x_h264_ctrl_descs[] = { + { + .cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SPS, + .cfg.ops = &rkvdec_ctrl_ops, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_PPS, + }, + { + .cfg.id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + { + .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_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_CONSTRAINED_BASELINE, + .cfg.max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA, + .cfg.menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE), + .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_6_0, + }, +}; + +static const struct rkvdec_ctrls vdpu38x_h264_ctrls = { + .ctrls = vdpu38x_h264_ctrl_descs, + .num_ctrls = ARRAY_SIZE(vdpu38x_h264_ctrl_descs), +}; + static const struct rkvdec_decoded_fmt_desc rkvdec_h264_decoded_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, @@ -328,7 +458,6 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .ops = &rkvdec_hevc_fmt_ops, .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts), .decoded_fmts = rkvdec_hevc_decoded_fmts, - .capability = RKVDEC_CAPABILITY_HEVC, }, { .fourcc = V4L2_PIX_FMT_H264_SLICE, @@ -345,7 +474,6 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), .decoded_fmts = rkvdec_h264_decoded_fmts, .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, - .capability = RKVDEC_CAPABILITY_H264, }, { .fourcc = V4L2_PIX_FMT_VP9_FRAME, @@ -361,27 +489,108 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = { .ops = &rkvdec_vp9_fmt_ops, .num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts), .decoded_fmts = rkvdec_vp9_decoded_fmts, - .capability = RKVDEC_CAPABILITY_VP9, } }; -static bool rkvdec_is_capable(struct rkvdec_ctx *ctx, unsigned int capability) -{ - return (ctx->dev->variant->capabilities & capability) == capability; -} +static const struct rkvdec_coded_fmt_desc rk3288_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 4096, + .step_width = 64, + .min_height = 64, + .max_height = 2304, + .step_height = 16, + }, + .ctrls = &rkvdec_hevc_ctrls, + .ops = &rkvdec_hevc_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts), + .decoded_fmts = rkvdec_hevc_decoded_fmts, + } +}; + +static const struct rkvdec_coded_fmt_desc vdpu381_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 65472, + .step_width = 64, + .min_height = 64, + .max_height = 65472, + .step_height = 16, + }, + .ctrls = &vdpu38x_hevc_ctrls, + .ops = &rkvdec_vdpu381_hevc_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts), + .decoded_fmts = rkvdec_hevc_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 65520, + .step_width = 64, + .min_height = 64, + .max_height = 65520, + .step_height = 16, + }, + .ctrls = &vdpu38x_h264_ctrls, + .ops = &rkvdec_vdpu381_h264_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), + .decoded_fmts = rkvdec_h264_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + }, +}; + +static const struct rkvdec_coded_fmt_desc vdpu383_coded_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_HEVC_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 65472, + .step_width = 64, + .min_height = 64, + .max_height = 65472, + .step_height = 16, + }, + .ctrls = &vdpu38x_hevc_ctrls, + .ops = &rkvdec_vdpu383_hevc_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts), + .decoded_fmts = rkvdec_hevc_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .frmsize = { + .min_width = 64, + .max_width = 65520, + .step_width = 64, + .min_height = 64, + .max_height = 65520, + .step_height = 16, + }, + .ctrls = &vdpu38x_h264_ctrls, + .ops = &rkvdec_vdpu383_h264_fmt_ops, + .num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts), + .decoded_fmts = rkvdec_h264_decoded_fmts, + .subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF, + }, +}; static const struct rkvdec_coded_fmt_desc * rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index) { + const struct rkvdec_variant *variant = ctx->dev->variant; int fmt_idx = -1; unsigned int i; - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - if (!rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) - continue; + for (i = 0; i < variant->num_coded_fmts; i++) { fmt_idx++; if (index == fmt_idx) - return &rkvdec_coded_fmts[i]; + return &variant->coded_fmts[i]; } return NULL; @@ -390,12 +599,12 @@ rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index) static const struct rkvdec_coded_fmt_desc * rkvdec_find_coded_fmt_desc(struct rkvdec_ctx *ctx, u32 fourcc) { + const struct rkvdec_variant *variant = ctx->dev->variant; unsigned int i; - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability) && - rkvdec_coded_fmts[i].fourcc == fourcc) - return &rkvdec_coded_fmts[i]; + for (i = 0; i < variant->num_coded_fmts; i++) { + if (variant->coded_fmts[i].fourcc == fourcc) + return &variant->coded_fmts[i]; } return NULL; @@ -769,6 +978,7 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct rkvdec_ctx *ctx = vb2_get_drv_priv(q); const struct rkvdec_coded_fmt_desc *desc; + const struct rkvdec_variant *variant = ctx->dev->variant; int ret; if (V4L2_TYPE_IS_CAPTURE(q->type)) @@ -778,13 +988,22 @@ static int rkvdec_start_streaming(struct vb2_queue *q, unsigned int count) if (WARN_ON(!desc)) return -EINVAL; + ret = rkvdec_allocate_rcb(ctx, variant->rcb_sizes, variant->num_rcb_sizes); + if (ret) + return ret; + if (desc->ops->start) { ret = desc->ops->start(ctx); if (ret) - return ret; + goto err_ops_start; } return 0; + +err_ops_start: + rkvdec_free_rcb(ctx); + + return ret; } static void rkvdec_queue_cleanup(struct vb2_queue *vq, u32 state) @@ -820,6 +1039,8 @@ static void rkvdec_stop_streaming(struct vb2_queue *q) if (desc->ops->stop) desc->ops->stop(ctx); + + rkvdec_free_rcb(ctx); } rkvdec_queue_cleanup(q, VB2_BUF_STATE_ERROR); @@ -914,6 +1135,29 @@ void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx) writel(reg, rkvdec->regs + RKVDEC_REG_QOS_CTRL); } +void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len) +{ +#ifdef CONFIG_ARM64 + __iowrite32_copy(dst, src, len / 4); +#else + memcpy_toio(dst, src, len); +#endif +} + +void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold) +{ + /* Set watchdog at 2 times the hardware timeout threshold */ + u32 watchdog_time; + unsigned long axi_rate = clk_get_rate(rkvdec->axi_clk); + + if (axi_rate) + watchdog_time = 2 * div_u64(1000 * (u64)timeout_threshold, axi_rate); + else + watchdog_time = 2000; + + schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(watchdog_time)); +} + static void rkvdec_device_run(void *priv) { struct rkvdec_ctx *ctx = priv; @@ -1005,21 +1249,19 @@ static int rkvdec_add_ctrls(struct rkvdec_ctx *ctx, static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx) { + const struct rkvdec_variant *variant = ctx->dev->variant; unsigned int i, nctrls = 0; int ret; - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) - if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) - nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls; + for (i = 0; i < variant->num_coded_fmts; i++) + nctrls += variant->coded_fmts[i].ctrls->num_ctrls; v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls); - for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) { - if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) { - ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls); - if (ret) - goto err_free_handler; - } + for (i = 0; i < variant->num_coded_fmts; i++) { + ret = rkvdec_add_ctrls(ctx, variant->coded_fmts[i].ctrls); + if (ret) + goto err_free_handler; } ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); @@ -1192,10 +1434,9 @@ static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec) } } -static irqreturn_t rkvdec_irq_handler(int irq, void *priv) +static irqreturn_t rk3399_irq_handler(struct rkvdec_ctx *ctx) { - struct rkvdec_dev *rkvdec = priv; - struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); + struct rkvdec_dev *rkvdec = ctx->dev; enum vb2_buffer_state state; u32 status; @@ -1216,6 +1457,145 @@ static irqreturn_t rkvdec_irq_handler(int irq, void *priv) return IRQ_HANDLED; } +static irqreturn_t vdpu381_irq_handler(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + enum vb2_buffer_state state; + bool need_reset = 0; + u32 status; + + status = readl(rkvdec->regs + VDPU381_REG_STA_INT); + writel(0, rkvdec->regs + VDPU381_REG_STA_INT); + + if (status & VDPU381_STA_INT_DEC_RDY_STA) { + state = VB2_BUF_STATE_DONE; + } else { + state = VB2_BUF_STATE_ERROR; + if (status & (VDPU381_STA_INT_SOFTRESET_RDY | + VDPU381_STA_INT_TIMEOUT | + VDPU381_STA_INT_ERROR)) + rkvdec_iommu_restore(rkvdec); + } + + if (need_reset) + rkvdec_iommu_restore(rkvdec); + + if (cancel_delayed_work(&rkvdec->watchdog_work)) + rkvdec_job_finish(ctx, state); + + return IRQ_HANDLED; +} + +static irqreturn_t vdpu383_irq_handler(struct rkvdec_ctx *ctx) +{ + struct rkvdec_dev *rkvdec = ctx->dev; + enum vb2_buffer_state state; + bool need_reset = 0; + u32 status; + + status = readl(rkvdec->link + VDPU383_LINK_STA_INT); + writel(FIELD_PREP_WM16(VDPU383_STA_INT_ALL, 0), rkvdec->link + VDPU383_LINK_STA_INT); + /* On vdpu383, the interrupts must be disabled */ + writel(FIELD_PREP_WM16(VDPU383_INT_EN_IRQ | VDPU383_INT_EN_LINE_IRQ, 0), + rkvdec->link + VDPU383_LINK_INT_EN); + + if (status & VDPU383_STA_INT_DEC_RDY_STA) { + state = VB2_BUF_STATE_DONE; + } else { + state = VB2_BUF_STATE_ERROR; + rkvdec_iommu_restore(rkvdec); + } + + if (need_reset) + rkvdec_iommu_restore(rkvdec); + + if (cancel_delayed_work(&rkvdec->watchdog_work)) + rkvdec_job_finish(ctx, state); + + return IRQ_HANDLED; +} + +static irqreturn_t rkvdec_irq_handler(int irq, void *priv) +{ + struct rkvdec_dev *rkvdec = priv; + struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev); + const struct rkvdec_variant *variant = rkvdec->variant; + + return variant->ops->irq_handler(ctx); +} + +/* + * Flip one or more matrices along their main diagonal and flatten them + * before writing it to the memory. + * Convert: + * ABCD AEIM + * EFGH => BFJN => AEIMBFJNCGKODHLP + * IJKL CGKO + * MNOP DHLP + */ +static void transpose_and_flatten_matrices(u8 *output, const u8 *input, + int matrices, int row_length) +{ + int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value; + + matrix_size = row_length * row_length; + for (i = 0; i < matrices; i++) { + row = 0; + x_offset = 0; + matrix_offset = i * matrix_size; + for (j = 0; j < matrix_size; j++) { + y_offset = j - (row * row_length); + rot_index = y_offset * row_length + x_offset; + new_value = *(input + i * matrix_size + j); + output[matrix_offset + rot_index] = new_value; + if ((j + 1) % row_length == 0) { + row += 1; + x_offset += 1; + } + } + } +} + +/* + * VDPU383 needs a specific order: + * The 8x8 flatten matrix is based on 4x4 blocks. + * Each 4x4 block is written separately in order. + * + * Base data => Transposed VDPU383 transposed + * + * ABCDEFGH AIQYaiqy AIQYBJRZ + * IJKLMNOP BJRZbjrz CKS0DLT1 + * QRSTUVWX CKS0cks6 aiqybjrz + * YZ012345 => DLT1dlt7 cks6dlt7 + * abcdefgh EMU2emu8 EMU2FNV3 + * ijklmnop FNV3fnv9 GOW4HPX5 + * qrstuvwx GOW4gow# emu8fnv9 + * yz6789#$ HPX5hpx$ gow#hpx$ + * + * As the function reads block of 4x4 it can be used for both 4x4 and 8x8 matrices. + * + */ +static void vdpu383_flatten_matrices(u8 *output, const u8 *input, int matrices, int row_length) +{ + u8 block; + int i, j, matrix_offset, matrix_size, new_value, input_idx, line_offset, block_offset; + + matrix_size = row_length * row_length; + for (i = 0; i < matrices; i++) { + matrix_offset = i * matrix_size; + for (j = 0; j < matrix_size; j++) { + block = j / 16; + line_offset = (j % 16) / 4; + block_offset = (block & 1) * 32 + (block & 2) * 2; + input_idx = ((j % 4) * row_length) + line_offset + block_offset; + + new_value = *(input + i * matrix_size + input_idx); + + output[matrix_offset + j] = new_value; + } + } +} + static void rkvdec_watchdog_func(struct work_struct *work) { struct rkvdec_dev *rkvdec; @@ -1227,29 +1607,136 @@ static void rkvdec_watchdog_func(struct work_struct *work) if (ctx) { dev_err(rkvdec->dev, "Frame processing timed out!\n"); writel(RKVDEC_IRQ_DIS, rkvdec->regs + RKVDEC_REG_INTERRUPT); - writel(0, rkvdec->regs + RKVDEC_REG_SYSCTRL); rkvdec_job_finish(ctx, VB2_BUF_STATE_ERROR); } } +/* + * Some SoCs, like RK3588 have multiple identical VDPU cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the first core can be used to cluster all cores together. + */ +static int rkvdec_disable_multicore(struct rkvdec_dev *rkvdec) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_first_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(rkvdec->dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; + + is_first_core = (rkvdec->dev->of_node == node); + + of_node_put(node); + + if (!is_first_core) { + dev_info(rkvdec->dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; + } + + return 0; +} + +static const struct rkvdec_variant_ops rk3399_variant_ops = { + .irq_handler = rk3399_irq_handler, + .colmv_size = rkvdec_colmv_size, + .flatten_matrices = transpose_and_flatten_matrices, +}; + static const struct rkvdec_variant rk3288_rkvdec_variant = { .num_regs = 68, - .capabilities = RKVDEC_CAPABILITY_HEVC, + .coded_fmts = rk3288_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(rk3288_coded_fmts), + .ops = &rk3399_variant_ops, + .has_single_reg_region = true, }; static const struct rkvdec_variant rk3328_rkvdec_variant = { .num_regs = 109, - .capabilities = RKVDEC_CAPABILITY_HEVC | - RKVDEC_CAPABILITY_H264 | - RKVDEC_CAPABILITY_VP9, + .coded_fmts = rkvdec_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts), + .ops = &rk3399_variant_ops, + .has_single_reg_region = true, .quirks = RKVDEC_QUIRK_DISABLE_QOS, }; static const struct rkvdec_variant rk3399_rkvdec_variant = { .num_regs = 78, - .capabilities = RKVDEC_CAPABILITY_HEVC | - RKVDEC_CAPABILITY_H264 | - RKVDEC_CAPABILITY_VP9, + .coded_fmts = rkvdec_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(rkvdec_coded_fmts), + .ops = &rk3399_variant_ops, + .has_single_reg_region = true, +}; + +static const struct rcb_size_info vdpu381_rcb_sizes[] = { + {6, PIC_WIDTH}, // intrar + {1, PIC_WIDTH}, // transdr (Is actually 0.4*pic_width) + {1, PIC_HEIGHT}, // transdc (Is actually 0.1*pic_height) + {3, PIC_WIDTH}, // streamdr + {6, PIC_WIDTH}, // interr + {3, PIC_HEIGHT}, // interc + {22, PIC_WIDTH}, // dblkr + {6, PIC_WIDTH}, // saor + {11, PIC_WIDTH}, // fbcr + {67, PIC_HEIGHT}, // filtc col +}; + +static const struct rkvdec_variant_ops vdpu381_variant_ops = { + .irq_handler = vdpu381_irq_handler, + .colmv_size = rkvdec_colmv_size, + .flatten_matrices = transpose_and_flatten_matrices, +}; + +static const struct rkvdec_variant vdpu381_variant = { + .coded_fmts = vdpu381_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(vdpu381_coded_fmts), + .rcb_sizes = vdpu381_rcb_sizes, + .num_rcb_sizes = ARRAY_SIZE(vdpu381_rcb_sizes), + .ops = &vdpu381_variant_ops, +}; + +static const struct rcb_size_info vdpu383_rcb_sizes[] = { + {6, PIC_WIDTH}, // streamd + {6, PIC_WIDTH}, // streamd_tile + {12, PIC_WIDTH}, // inter + {12, PIC_WIDTH}, // inter_tile + {16, PIC_WIDTH}, // intra + {10, PIC_WIDTH}, // intra_tile + {120, PIC_WIDTH}, // filterd + {120, PIC_WIDTH}, // filterd_protect + {120, PIC_WIDTH}, // filterd_tile_row + {180, PIC_HEIGHT}, // filterd_tile_col +}; + +static const struct rkvdec_variant_ops vdpu383_variant_ops = { + .irq_handler = vdpu383_irq_handler, + .colmv_size = vdpu383_colmv_size, + .flatten_matrices = vdpu383_flatten_matrices, +}; + +static const struct rkvdec_variant vdpu383_variant = { + .coded_fmts = vdpu383_coded_fmts, + .num_coded_fmts = ARRAY_SIZE(vdpu383_coded_fmts), + .rcb_sizes = vdpu383_rcb_sizes, + .num_rcb_sizes = ARRAY_SIZE(vdpu383_rcb_sizes), + .ops = &vdpu383_variant_ops, }; static const struct of_device_id of_rkvdec_match[] = { @@ -1265,19 +1752,22 @@ static const struct of_device_id of_rkvdec_match[] = { .compatible = "rockchip,rk3399-vdec", .data = &rk3399_rkvdec_variant, }, + { + .compatible = "rockchip,rk3588-vdec", + .data = &vdpu381_variant, + }, + { + .compatible = "rockchip,rk3576-vdec", + .data = &vdpu383_variant, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_rkvdec_match); -static const char * const rkvdec_clk_names[] = { - "axi", "ahb", "cabac", "core" -}; - static int rkvdec_probe(struct platform_device *pdev) { const struct rkvdec_variant *variant; struct rkvdec_dev *rkvdec; - unsigned int i; int ret, irq; variant = of_device_get_match_data(&pdev->dev); @@ -1294,22 +1784,30 @@ static int rkvdec_probe(struct platform_device *pdev) mutex_init(&rkvdec->vdev_lock); INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func); - rkvdec->clocks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), - sizeof(*rkvdec->clocks), GFP_KERNEL); - if (!rkvdec->clocks) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(rkvdec_clk_names); i++) - rkvdec->clocks[i].id = rkvdec_clk_names[i]; - - ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); + ret = rkvdec_disable_multicore(rkvdec); if (ret) return ret; - rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(rkvdec->regs)) - return PTR_ERR(rkvdec->regs); + ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &rkvdec->clocks); + if (ret < 0) + return ret; + + rkvdec->num_clocks = ret; + rkvdec->axi_clk = devm_clk_get(&pdev->dev, "axi"); + + if (rkvdec->variant->has_single_reg_region) { + rkvdec->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rkvdec->regs)) + return PTR_ERR(rkvdec->regs); + } else { + rkvdec->regs = devm_platform_ioremap_resource_byname(pdev, "function"); + if (IS_ERR(rkvdec->regs)) + return PTR_ERR(rkvdec->regs); + + rkvdec->link = devm_platform_ioremap_resource_byname(pdev, "link"); + if (IS_ERR(rkvdec->link)) + return PTR_ERR(rkvdec->link); + } ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { @@ -1331,6 +1829,10 @@ static int rkvdec_probe(struct platform_device *pdev) return ret; } + rkvdec->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); + if (!rkvdec->sram_pool && rkvdec->variant->num_rcb_sizes > 0) + dev_info(&pdev->dev, "No sram node, RCB will be stored in RAM\n"); + pm_runtime_set_autosuspend_delay(&pdev->dev, 100); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -1339,7 +1841,8 @@ static int rkvdec_probe(struct platform_device *pdev) if (ret) goto err_disable_runtime_pm; - if (iommu_get_domain_for_dev(&pdev->dev)) { + rkvdec->iommu_domain = iommu_get_domain_for_dev(&pdev->dev); + if (rkvdec->iommu_domain) { rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev); if (IS_ERR(rkvdec->empty_domain)) { @@ -1353,6 +1856,10 @@ static int rkvdec_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); + + if (rkvdec->sram_pool) + gen_pool_destroy(rkvdec->sram_pool); + return ret; } @@ -1375,16 +1882,14 @@ static int rkvdec_runtime_resume(struct device *dev) { struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); - return clk_bulk_prepare_enable(ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); + return clk_bulk_prepare_enable(rkvdec->num_clocks, rkvdec->clocks); } static int rkvdec_runtime_suspend(struct device *dev) { struct rkvdec_dev *rkvdec = dev_get_drvdata(dev); - clk_bulk_disable_unprepare(ARRAY_SIZE(rkvdec_clk_names), - rkvdec->clocks); + clk_bulk_disable_unprepare(rkvdec->num_clocks, rkvdec->clocks); return 0; } #endif diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h index 566e06fa2b1e..a24be6638b6b 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h @@ -19,16 +19,18 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#define RKVDEC_CAPABILITY_HEVC BIT(0) -#define RKVDEC_CAPABILITY_H264 BIT(1) -#define RKVDEC_CAPABILITY_VP9 BIT(2) - #define RKVDEC_QUIRK_DISABLE_QOS BIT(0) +#define RKVDEC_1080P_PIXELS (1920 * 1088) +#define RKVDEC_4K_PIXELS (4096 * 2304) +#define RKVDEC_8K_PIXELS (7680 * 4320) + struct rkvdec_ctx; +struct rkvdec_rcb_config; struct rkvdec_ctrl_desc { struct v4l2_ctrl_config cfg; @@ -69,9 +71,20 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf) base.vb.vb2_buf); } +struct rkvdec_variant_ops { + irqreturn_t (*irq_handler)(struct rkvdec_ctx *ctx); + u32 (*colmv_size)(u16 width, u16 height); + void (*flatten_matrices)(u8 *output, const u8 *input, int matrices, int row_length); +}; + struct rkvdec_variant { unsigned int num_regs; - unsigned int capabilities; + const struct rkvdec_coded_fmt_desc *coded_fmts; + size_t num_coded_fmts; + const struct rcb_size_info *rcb_sizes; + size_t num_rcb_sizes; + const struct rkvdec_variant_ops *ops; + bool has_single_reg_region; unsigned int quirks; }; @@ -110,7 +123,6 @@ struct rkvdec_coded_fmt_desc { unsigned int num_decoded_fmts; const struct rkvdec_decoded_fmt_desc *decoded_fmts; u32 subsystem_flags; - unsigned int capability; }; struct rkvdec_dev { @@ -120,9 +132,14 @@ struct rkvdec_dev { struct v4l2_m2m_dev *m2m_dev; struct device *dev; struct clk_bulk_data *clocks; + unsigned int num_clocks; + struct clk *axi_clk; void __iomem *regs; + void __iomem *link; struct mutex vdev_lock; /* serializes ioctls */ struct delayed_work watchdog_work; + struct gen_pool *sram_pool; + struct iommu_domain *iommu_domain; struct iommu_domain *empty_domain; const struct rkvdec_variant *variant; }; @@ -135,7 +152,11 @@ struct rkvdec_ctx { struct v4l2_ctrl_handler ctrl_hdl; struct rkvdec_dev *dev; enum rkvdec_image_fmt image_fmt; + struct rkvdec_rcb_config *rcb_config; + u32 colmv_offset; void *priv; + u8 has_sps_st_rps: 1; + u8 has_sps_lt_rps: 1; }; static inline struct rkvdec_ctx *file_to_rkvdec_ctx(struct file *filp) @@ -143,19 +164,36 @@ static inline struct rkvdec_ctx *file_to_rkvdec_ctx(struct file *filp) return container_of(file_to_v4l2_fh(filp), struct rkvdec_ctx, fh); } +enum rkvdec_alloc_type { + RKVDEC_ALLOC_DMA = 0, + RKVDEC_ALLOC_SRAM = 1, +}; + struct rkvdec_aux_buf { void *cpu; dma_addr_t dma; size_t size; + enum rkvdec_alloc_type type; }; void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run); +void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len); +void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold); void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx); +/* RKVDEC ops */ extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops; extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops; extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops; +/* VDPU381 ops */ +extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops; +extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops; + +/* VDPU383 ops */ +extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops; +extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops; + #endif /* RKVDEC_H_ */ diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index 1c1b6b48918e..19e6b187be22 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -57,6 +57,7 @@ struct dcmipp_bytecap_pix_map { static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565), + PIXMAP_MBUS_PFMT(RGB888_1X24, RGB24), PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV), PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), @@ -66,6 +67,9 @@ static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), PIXMAP_MBUS_PFMT(VYUY8_1X16, VYUY), PIXMAP_MBUS_PFMT(Y8_1X8, GREY), + PIXMAP_MBUS_PFMT(Y10_1X10, Y10), + PIXMAP_MBUS_PFMT(Y12_1X12, Y12), + PIXMAP_MBUS_PFMT(Y14_1X14, Y14), PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8), PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8), PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8), @@ -143,7 +147,6 @@ struct dcmipp_bytecap_device { void __iomem *regs; - u32 cmier; u32 cmsr2; struct { @@ -439,8 +442,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, dcmipp_start_capture(vcap, vcap->next); /* Enable interruptions */ - vcap->cmier |= DCMIPP_CMIER_P0ALL; - reg_set(vcap, DCMIPP_CMIER, vcap->cmier); + reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL); vcap->state = DCMIPP_RUNNING; @@ -496,7 +498,7 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) media_pipeline_stop(vcap->vdev.entity.pads); /* Disable interruptions */ - reg_clear(vcap, DCMIPP_CMIER, vcap->cmier); + reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL); /* Stop capture */ reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); @@ -512,6 +514,9 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) /* Disable pipe */ reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN); + /* Clear any pending interrupts */ + reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_P0ALL); + spin_lock_irq(&vcap->irqlock); /* Return all queued buffers to vb2 in ERROR state */ @@ -742,23 +747,20 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg) struct dcmipp_bytecap_device *vcap = container_of(arg, struct dcmipp_bytecap_device, ved); size_t bytesused = 0; - u32 cmsr2; spin_lock_irq(&vcap->irqlock); - cmsr2 = vcap->cmsr2 & vcap->cmier; - /* * If we have an overrun, a frame-end will probably not be generated, * in that case the active buffer will be recycled as next buffer by * the VSYNC handler */ - if (cmsr2 & DCMIPP_CMSR2_P0OVRF) { + if (vcap->cmsr2 & DCMIPP_CMSR2_P0OVRF) { vcap->count.errors++; vcap->count.overrun++; } - if (cmsr2 & DCMIPP_CMSR2_P0FRAMEF) { + if (vcap->cmsr2 & DCMIPP_CMSR2_P0FRAMEF) { vcap->count.frame++; /* Read captured buffer size */ @@ -766,7 +768,7 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg) dcmipp_bytecap_process_frame(vcap, bytesused); } - if (cmsr2 & DCMIPP_CMSR2_P0VSYNCF) { + if (vcap->cmsr2 & DCMIPP_CMSR2_P0VSYNCF) { vcap->count.vsync++; if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) { vcap->count.underrun++; @@ -797,7 +799,7 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg) container_of(arg, struct dcmipp_bytecap_device, ved); /* Store interrupt status register */ - vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & vcap->cmier; + vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & DCMIPP_CMIER_P0ALL; vcap->count.it++; /* Clear interrupt */ diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c index db76a02a1848..f9e4a3a9ef3f 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -49,6 +49,8 @@ struct dcmipp_byteproc_pix_map { static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), PIXMAP_MBUS_BPP(RGB565_1X16, 2), + PIXMAP_MBUS_BPP(RGB888_3X8, 3), + PIXMAP_MBUS_BPP(RGB888_1X24, 3), PIXMAP_MBUS_BPP(YUYV8_2X8, 2), PIXMAP_MBUS_BPP(YUYV8_1X16, 2), PIXMAP_MBUS_BPP(YVYU8_2X8, 2), @@ -58,6 +60,9 @@ static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { PIXMAP_MBUS_BPP(VYUY8_2X8, 2), PIXMAP_MBUS_BPP(VYUY8_1X16, 2), PIXMAP_MBUS_BPP(Y8_1X8, 1), + PIXMAP_MBUS_BPP(Y10_1X10, 2), + PIXMAP_MBUS_BPP(Y12_1X12, 2), + PIXMAP_MBUS_BPP(Y14_1X14, 2), PIXMAP_MBUS_BPP(SBGGR8_1X8, 1), PIXMAP_MBUS_BPP(SGBRG8_1X8, 1), PIXMAP_MBUS_BPP(SGRBG8_1X8, 1), @@ -126,15 +131,22 @@ static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r, static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r, const struct v4l2_mbus_framefmt *fmt) { + const struct dcmipp_byteproc_pix_map *vpix; + r->top = 0; r->left = 0; /* Compose is not possible for JPEG or Bayer formats */ - if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 || - fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 || - fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 || - fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 || - fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) { + if (fmt->code >= MEDIA_BUS_FMT_SBGGR8_1X8 && + fmt->code <= MEDIA_BUS_FMT_JPEG_1X8) { + r->width = fmt->width; + r->height = fmt->height; + return; + } + + /* Prevent compose on formats which are not 1 or 2 bytes per pixel */ + vpix = dcmipp_byteproc_pix_map_by_code(fmt->code); + if (vpix->bpp != 1 && vpix->bpp != 2) { r->width = fmt->width; r->height = fmt->height; return; @@ -147,7 +159,7 @@ static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r, r->height = fmt->height; /* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */ - if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4)) + if (vpix->bpp == 1 && r->width <= (fmt->width / 4)) r->width = fmt->width / 4; else if (r->width <= (fmt->width / 2)) r->width = fmt->width / 2; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index 1b7bae3266c8..49398d077764 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -526,7 +526,12 @@ static int dcmipp_probe(struct platform_device *pdev) return ret; } - kclk = devm_clk_get(&pdev->dev, "kclk"); + /* + * In case of the DCMIPP has only 1 clock (such as on MP13), the + * clock might not be named. + */ + kclk = devm_clk_get(&pdev->dev, + dcmipp->pipe_cfg->needs_mclk ? "kclk" : NULL); if (IS_ERR(kclk)) return dev_err_probe(&pdev->dev, PTR_ERR(kclk), "Unable to get kclk\n"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c index 7e5311b67d7e..c4bc76909b1c 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c @@ -19,11 +19,15 @@ #define DCMIPP_PRCR_FORMAT_SHIFT 16 #define DCMIPP_PRCR_FORMAT_YUV422 0x1e #define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RGB888 0x24 #define DCMIPP_PRCR_FORMAT_RAW8 0x2a #define DCMIPP_PRCR_FORMAT_RAW10 0x2b #define DCMIPP_PRCR_FORMAT_RAW12 0x2c #define DCMIPP_PRCR_FORMAT_RAW14 0x2d #define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_G10 0x4b +#define DCMIPP_PRCR_FORMAT_G12 0x4c +#define DCMIPP_PRCR_FORMAT_G14 0x4d #define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a #define DCMIPP_PRCR_ESS BIT(4) #define DCMIPP_PRCR_PCKPOL BIT(5) @@ -72,6 +76,9 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565), PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565), PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565), + /* RGB888 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_3X8, RGB888_3X8, RGB888, 0, MIPI_CSI2_DT_RGB888), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_1X24, RGB888_1X24, RGB888, 0, MIPI_CSI2_DT_RGB888), /* YUV422 */ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), @@ -85,6 +92,9 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), /* GREY */ PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(Y10_1X10, Y10_1X10, G10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(Y12_1X12, Y12_1X12, G12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(Y14_1X14, Y14_1X14, G14, 0, MIPI_CSI2_DT_RAW14), /* Raw Bayer */ PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig index 4fd521f78425..e798ec00b189 100644 --- a/drivers/media/platform/synopsys/Kconfig +++ b/drivers/media/platform/synopsys/Kconfig @@ -1,3 +1,21 @@ # SPDX-License-Identifier: GPL-2.0-only source "drivers/media/platform/synopsys/hdmirx/Kconfig" + +config VIDEO_DW_MIPI_CSI2RX + tristate "Synopsys DesignWare MIPI CSI-2 Receiver" + depends on VIDEO_DEV + depends on V4L_PLATFORM_DRIVERS + depends on PM && COMMON_CLK + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + The Synopsys DesignWare MIPI CSI-2 Receiver is a CSI-2 bridge with + one input port and one output port. It receives the data with the + help of an external MIPI PHY (C-PHY or D-PHY) and passes it to e.g., + the Rockchip Video Capture (VICAP) block on recent Rockchip SoCs. + This is a driver for this unit. + + To compile this driver as a module, choose M here: the module + will be called dw-mipi-csi2rx. diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile index 3b12c574dd67..e0232ee23304 100644 --- a/drivers/media/platform/synopsys/Makefile +++ b/drivers/media/platform/synopsys/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += hdmirx/ + +obj-$(CONFIG_VIDEO_DW_MIPI_CSI2RX) += dw-mipi-csi2rx.o diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c new file mode 100644 index 000000000000..170346ae1a59 --- /dev/null +++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c @@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synopsys DesignWare MIPI CSI-2 Receiver Driver + * + * Copyright (C) 2019 Rockchip Electronics Co., Ltd. + * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> + * Copyright (C) 2026 Collabora, Ltd. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/reset.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +#define DW_MIPI_CSI2RX_N_LANES 0x04 +#define DW_MIPI_CSI2RX_RESETN 0x10 +#define DW_MIPI_CSI2RX_PHY_STATE 0x14 +#define DW_MIPI_CSI2RX_ERR1 0x20 +#define DW_MIPI_CSI2RX_ERR2 0x24 +#define DW_MIPI_CSI2RX_MSK1 0x28 +#define DW_MIPI_CSI2RX_MSK2 0x2c +#define DW_MIPI_CSI2RX_CONTROL 0x40 + +#define SW_CPHY_EN(x) ((x) << 0) +#define SW_DSI_EN(x) ((x) << 4) +#define SW_DATATYPE_FS(x) ((x) << 8) +#define SW_DATATYPE_FE(x) ((x) << 14) +#define SW_DATATYPE_LS(x) ((x) << 20) +#define SW_DATATYPE_LE(x) ((x) << 26) + +#define DW_MIPI_CSI2RX_CLKS_MAX 1 + +enum { + DW_MIPI_CSI2RX_PAD_SINK, + DW_MIPI_CSI2RX_PAD_SRC, + DW_MIPI_CSI2RX_PAD_MAX, +}; + +struct dw_mipi_csi2rx_format { + u32 code; + u8 depth; + u8 csi_dt; +}; + +struct dw_mipi_csi2rx_device { + struct device *dev; + + void __iomem *base_addr; + struct clk_bulk_data *clks; + unsigned int clks_num; + struct phy *phy; + struct reset_control *reset; + + const struct dw_mipi_csi2rx_format *formats; + unsigned int formats_num; + + struct media_pad pads[DW_MIPI_CSI2RX_PAD_MAX]; + struct v4l2_async_notifier notifier; + struct v4l2_subdev sd; + + enum v4l2_mbus_type bus_type; + u32 lanes_num; +}; + +static const struct v4l2_mbus_framefmt default_format = { + .width = 3840, + .height = 2160, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, +}; + +static const struct dw_mipi_csi2rx_format formats[] = { + /* YUV formats */ + { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = MIPI_CSI2_DT_YUV422_8B, + }, + /* RGB formats */ + { + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + { + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = MIPI_CSI2_DT_RGB888, + }, + /* Bayer formats */ + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + }, + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = MIPI_CSI2_DT_RAW10, + }, + { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + }, + { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + }, + { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + }, + { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = MIPI_CSI2_DT_RAW12, + }, +}; + +static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd) +{ + return container_of(sd, struct dw_mipi_csi2rx_device, sd); +} + +static inline void dw_mipi_csi2rx_write(struct dw_mipi_csi2rx_device *csi2, + unsigned int addr, u32 val) +{ + writel(val, csi2->base_addr + addr); +} + +static inline u32 dw_mipi_csi2rx_read(struct dw_mipi_csi2rx_device *csi2, + unsigned int addr) +{ + return readl(csi2->base_addr + addr); +} + +static const struct dw_mipi_csi2rx_format * +dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code) +{ + WARN_ON(csi2->formats_num == 0); + + for (unsigned int i = 0; i < csi2->formats_num; i++) { + const struct dw_mipi_csi2rx_format *format = &csi2->formats[i]; + + if (format->code == mbus_code) + return format; + } + + return NULL; +} + +static int dw_mipi_csi2rx_start(struct dw_mipi_csi2rx_device *csi2) +{ + struct media_pad *source_pad; + union phy_configure_opts opts; + u32 lanes = csi2->lanes_num; + u32 control = 0; + s64 link_freq; + int ret; + + if (lanes < 1 || lanes > 4) + return -EINVAL; + + source_pad = media_pad_remote_pad_unique( + &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]); + if (IS_ERR(source_pad)) + return PTR_ERR(source_pad); + + /* set mult and div to 0, thus completely rely on V4L2_CID_LINK_FREQ */ + link_freq = v4l2_get_link_freq(source_pad, 0, 0); + if (link_freq < 0) + return link_freq; + + switch (csi2->bus_type) { + case V4L2_MBUS_CSI2_DPHY: + ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq * 2, + lanes, &opts.mipi_dphy); + if (ret) + return ret; + + ret = phy_set_mode(csi2->phy, PHY_MODE_MIPI_DPHY); + if (ret) + return ret; + + ret = phy_configure(csi2->phy, &opts); + if (ret) + return ret; + + control |= SW_CPHY_EN(0); + break; + + case V4L2_MBUS_CSI2_CPHY: + /* TODO: implement CPHY configuration */ + return -EOPNOTSUPP; + default: + return -EINVAL; + } + + control |= SW_DATATYPE_FS(0x00) | SW_DATATYPE_FE(0x01) | + SW_DATATYPE_LS(0x02) | SW_DATATYPE_LE(0x03); + + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_N_LANES, lanes - 1); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_CONTROL, control); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 1); + + return phy_power_on(csi2->phy); +} + +static void dw_mipi_csi2rx_stop(struct dw_mipi_csi2rx_device *csi2) +{ + phy_power_off(csi2->phy); + + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_RESETN, 0); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK1, ~0); + dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_MSK2, ~0); +} + +static const struct media_entity_operations dw_mipi_csi2rx_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int +dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); + + switch (code->pad) { + case DW_MIPI_CSI2RX_PAD_SRC: + if (code->index) + return -EINVAL; + + code->code = + v4l2_subdev_state_get_format(sd_state, + DW_MIPI_CSI2RX_PAD_SINK)->code; + + return 0; + case DW_MIPI_CSI2RX_PAD_SINK: + if (code->index > csi2->formats_num) + return -EINVAL; + + code->code = csi2->formats[code->index].code; + return 0; + default: + return -EINVAL; + } +} + +static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); + const struct dw_mipi_csi2rx_format *fmt; + struct v4l2_mbus_framefmt *sink, *src; + + /* the format on the source pad always matches the sink pad */ + if (format->pad == DW_MIPI_CSI2RX_PAD_SRC) + return v4l2_subdev_get_fmt(sd, state, format); + + sink = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!sink) + return -EINVAL; + + fmt = dw_mipi_csi2rx_find_format(csi2, format->format.code); + if (!fmt) + format->format = default_format; + + *sink = format->format; + + /* propagate the format to the source pad */ + src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!src) + return -EINVAL; + + *src = *sink; + + return 0; +} + +static int dw_mipi_csi2rx_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, + &default_format); +} + +static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); + struct v4l2_subdev *remote_sd; + struct media_pad *sink_pad, *remote_pad; + struct device *dev = csi2->dev; + u64 mask; + int ret; + + sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK]; + remote_pad = media_pad_remote_pad_first(sink_pad); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK, + DW_MIPI_CSI2RX_PAD_SRC, + &streams_mask); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err; + + ret = dw_mipi_csi2rx_start(csi2); + if (ret) { + dev_err(dev, "failed to enable CSI hardware\n"); + goto err_pm_runtime_put; + } + + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); + if (ret) + goto err_csi_stop; + + return 0; + +err_csi_stop: + dw_mipi_csi2rx_stop(csi2); +err_pm_runtime_put: + pm_runtime_put(dev); +err: + return ret; +} + +static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dw_mipi_csi2rx_device *csi2 = to_csi2(sd); + struct v4l2_subdev *remote_sd; + struct media_pad *sink_pad, *remote_pad; + struct device *dev = csi2->dev; + u64 mask; + int ret; + + sink_pad = &sd->entity.pads[DW_MIPI_CSI2RX_PAD_SINK]; + remote_pad = media_pad_remote_pad_first(sink_pad); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + mask = v4l2_subdev_state_xlate_streams(state, DW_MIPI_CSI2RX_PAD_SINK, + DW_MIPI_CSI2RX_PAD_SRC, + &streams_mask); + + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); + + dw_mipi_csi2rx_stop(csi2); + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_pad_ops dw_mipi_csi2rx_pad_ops = { + .enum_mbus_code = dw_mipi_csi2rx_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dw_mipi_csi2rx_set_fmt, + .set_routing = dw_mipi_csi2rx_set_routing, + .enable_streams = dw_mipi_csi2rx_enable_streams, + .disable_streams = dw_mipi_csi2rx_disable_streams, +}; + +static const struct v4l2_subdev_ops dw_mipi_csi2rx_ops = { + .pad = &dw_mipi_csi2rx_pad_ops, +}; + +static int dw_mipi_csi2rx_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = DW_MIPI_CSI2RX_PAD_SINK, + .sink_stream = 0, + .source_pad = DW_MIPI_CSI2RX_PAD_SRC, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .len_routes = ARRAY_SIZE(routes), + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, + &default_format); +} + +static const struct v4l2_subdev_internal_ops dw_mipi_csi2rx_internal_ops = { + .init_state = dw_mipi_csi2rx_init_state, +}; + +static int dw_mipi_csi2rx_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_connection *asd) +{ + struct dw_mipi_csi2rx_device *csi2 = + container_of(notifier, struct dw_mipi_csi2rx_device, notifier); + struct media_pad *sink_pad = &csi2->pads[DW_MIPI_CSI2RX_PAD_SINK]; + int ret; + + ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad, + MEDIA_LNK_FL_ENABLED); + if (ret) { + dev_err(csi2->dev, "failed to link source pad of %s\n", + sd->name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations dw_mipi_csi2rx_notifier_ops = { + .bound = dw_mipi_csi2rx_notifier_bound, +}; + +static int dw_mipi_csi2rx_register_notifier(struct dw_mipi_csi2rx_device *csi2) +{ + struct v4l2_async_connection *asd; + struct v4l2_async_notifier *ntf = &csi2->notifier; + struct v4l2_fwnode_endpoint vep; + struct v4l2_subdev *sd = &csi2->sd; + struct device *dev = csi2->dev; + int ret; + + struct fwnode_handle *ep __free(fwnode_handle) = + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!ep) + return dev_err_probe(dev, -ENODEV, "failed to get endpoint\n"); + + vep.bus_type = V4L2_MBUS_UNKNOWN; + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + if (ret) + return dev_err_probe(dev, ret, "failed to parse endpoint\n"); + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && + vep.bus_type != V4L2_MBUS_CSI2_CPHY) + return dev_err_probe(dev, -EINVAL, + "invalid bus type of endpoint\n"); + + csi2->bus_type = vep.bus_type; + csi2->lanes_num = vep.bus.mipi_csi2.num_data_lanes; + + v4l2_async_subdev_nf_init(ntf, sd); + ntf->ops = &dw_mipi_csi2rx_notifier_ops; + + asd = v4l2_async_nf_add_fwnode_remote(ntf, ep, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + goto err_nf_cleanup; + } + + ret = v4l2_async_nf_register(ntf); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to register notifier\n"); + goto err_nf_cleanup; + } + + return 0; + +err_nf_cleanup: + v4l2_async_nf_cleanup(ntf); + + return ret; +} + +static int dw_mipi_csi2rx_register(struct dw_mipi_csi2rx_device *csi2) +{ + struct media_pad *pads = csi2->pads; + struct v4l2_subdev *sd = &csi2->sd; + int ret; + + ret = dw_mipi_csi2rx_register_notifier(csi2); + if (ret) + goto err; + + v4l2_subdev_init(sd, &dw_mipi_csi2rx_ops); + sd->dev = csi2->dev; + sd->entity.ops = &dw_mipi_csi2rx_media_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + sd->internal_ops = &dw_mipi_csi2rx_internal_ops; + snprintf(sd->name, sizeof(sd->name), "dw-mipi-csi2rx %s", + dev_name(csi2->dev)); + + pads[DW_MIPI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[DW_MIPI_CSI2RX_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, DW_MIPI_CSI2RX_PAD_MAX, pads); + if (ret) + goto err_notifier_unregister; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_entity_cleanup; + + ret = v4l2_async_register_subdev(sd); + if (ret) { + dev_err(sd->dev, "failed to register CSI-2 subdev\n"); + goto err_subdev_cleanup; + } + + return 0; + +err_subdev_cleanup: + v4l2_subdev_cleanup(sd); +err_entity_cleanup: + media_entity_cleanup(&sd->entity); +err_notifier_unregister: + v4l2_async_nf_unregister(&csi2->notifier); + v4l2_async_nf_cleanup(&csi2->notifier); +err: + return ret; +} + +static void dw_mipi_csi2rx_unregister(struct dw_mipi_csi2rx_device *csi2) +{ + struct v4l2_subdev *sd = &csi2->sd; + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_async_nf_unregister(&csi2->notifier); + v4l2_async_nf_cleanup(&csi2->notifier); +} + +static const struct of_device_id dw_mipi_csi2rx_of_match[] = { + { + .compatible = "rockchip,rk3568-mipi-csi2", + }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_mipi_csi2rx_of_match); + +static int dw_mipi_csi2rx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_mipi_csi2rx_device *csi2; + int ret; + + csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL); + if (!csi2) + return -ENOMEM; + csi2->dev = dev; + dev_set_drvdata(dev, csi2); + + csi2->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi2->base_addr)) + return PTR_ERR(csi2->base_addr); + + ret = devm_clk_bulk_get_all(dev, &csi2->clks); + if (ret != DW_MIPI_CSI2RX_CLKS_MAX) + return dev_err_probe(dev, -ENODEV, "failed to get clocks\n"); + csi2->clks_num = ret; + + csi2->phy = devm_phy_get(dev, NULL); + if (IS_ERR(csi2->phy)) + return dev_err_probe(dev, PTR_ERR(csi2->phy), + "failed to get MIPI CSI-2 PHY\n"); + + csi2->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(csi2->reset)) + return dev_err_probe(dev, PTR_ERR(csi2->reset), + "failed to get reset\n"); + + csi2->formats = formats; + csi2->formats_num = ARRAY_SIZE(formats); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to enable pm runtime\n"); + + ret = phy_init(csi2->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize MIPI CSI-2 PHY\n"); + + ret = dw_mipi_csi2rx_register(csi2); + if (ret) + goto err_phy_exit; + + return 0; + +err_phy_exit: + phy_exit(csi2->phy); + + return ret; +} + +static void dw_mipi_csi2rx_remove(struct platform_device *pdev) +{ + struct dw_mipi_csi2rx_device *csi2 = platform_get_drvdata(pdev); + + dw_mipi_csi2rx_unregister(csi2); + phy_exit(csi2->phy); +} + +static int dw_mipi_csi2rx_runtime_suspend(struct device *dev) +{ + struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(csi2->clks_num, csi2->clks); + + return 0; +} + +static int dw_mipi_csi2rx_runtime_resume(struct device *dev) +{ + struct dw_mipi_csi2rx_device *csi2 = dev_get_drvdata(dev); + int ret; + + reset_control_assert(csi2->reset); + udelay(5); + reset_control_deassert(csi2->reset); + + ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(dw_mipi_csi2rx_pm_ops, + dw_mipi_csi2rx_runtime_suspend, + dw_mipi_csi2rx_runtime_resume, NULL); + +static struct platform_driver dw_mipi_csi2rx_drv = { + .driver = { + .name = "dw-mipi-csi2rx", + .of_match_table = dw_mipi_csi2rx_of_match, + .pm = pm_ptr(&dw_mipi_csi2rx_pm_ops), + }, + .probe = dw_mipi_csi2rx_probe, + .remove = dw_mipi_csi2rx_remove, +}; +module_platform_driver(dw_mipi_csi2rx_drv); + +MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Receiver platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index c3007e09bc9f..9cceffa4ce25 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -132,12 +132,14 @@ struct snps_hdmirx_dev { struct delayed_work delayed_work_hotplug; struct delayed_work delayed_work_res_change; struct hdmirx_cec *cec; + struct mutex phy_rw_lock; /* to protect phy r/w configuration */ struct mutex stream_lock; /* to lock video stream capture */ struct mutex work_lock; /* to lock the critical section of hotplug event */ struct reset_control_bulk_data resets[HDMIRX_NUM_RST]; struct clk_bulk_data *clks; struct regmap *grf; struct regmap *vo1_grf; + struct completion cr_read_done; struct completion cr_write_done; struct completion timer_base_lock; struct completion avi_pkt_rcv; @@ -796,11 +798,50 @@ static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg, return 0; } +static int hdmirx_phy_register_read(struct snps_hdmirx_dev *hdmirx_dev, + u32 phy_reg, u32 *val) +{ + struct device *dev = hdmirx_dev->dev; + u32 status; + + guard(mutex)(&hdmirx_dev->phy_rw_lock); + + reinit_completion(&hdmirx_dev->cr_read_done); + /* clear irq status */ + hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff); + /* en irq */ + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N, + PHYCREG_CR_READ_DONE, PHYCREG_CR_READ_DONE); + /* write phy reg addr */ + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg); + /* config read enable */ + hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_READ_P); + + if (!wait_for_completion_timeout(&hdmirx_dev->cr_read_done, + msecs_to_jiffies(20))) { + dev_err(dev, "%s wait cr read done failed\n", __func__); + return -ETIMEDOUT; + } + + /* read phy reg value */ + status = hdmirx_readl(hdmirx_dev, PHYCREG_STATUS); + if (!(status & PHYCREG_CR_PARA_DATAVALID)) { + dev_err(dev, "%s cr read failed\n", __func__); + return -EINVAL; + } + + *val = status & PHYCREG_CR_PARA_RD_DATA_MASK; + + return 0; +} + static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev, u32 phy_reg, u32 val) { struct device *dev = hdmirx_dev->dev; + guard(mutex)(&hdmirx_dev->phy_rw_lock); + reinit_completion(&hdmirx_dev->cr_write_done); /* clear irq status */ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff); @@ -1814,6 +1855,13 @@ static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev, *handled = true; } + if (status & PHYCREG_CR_READ_DONE) { + hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N, + PHYCREG_CR_READ_DONE, 0); + complete(&hdmirx_dev->cr_read_done); + *handled = true; + } + if (status & TMDSVALID_STABLE_CHG) { process_signal_change(hdmirx_dev); v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__); @@ -2313,18 +2361,51 @@ static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev) hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff); } -static void hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev) +static int hdmirx_detect_broken_interrupt(struct snps_hdmirx_dev *hdmirx_dev) { + int ret; + u32 val; + + enable_irq(hdmirx_dev->hdmi_irq); + + hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3); + + ret = hdmirx_phy_register_read(hdmirx_dev, + HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG, + &val); + + disable_irq(hdmirx_dev->hdmi_irq); + + return ret; +} + +static int hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev) +{ + int ret; + hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0); regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2, (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) | ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16)); + + /* + * RK3588 downstream version of TF-A remaps HDMIRX interrupt and + * requires use of a vendor-specific FW API that we don't support + * in this driver. + */ + ret = hdmirx_detect_broken_interrupt(hdmirx_dev); + if (ret) + dev_err_probe(hdmirx_dev->dev, ret, + "interrupt not functioning, open-source TF-A is required by this driver\n"); + /* * Some interrupts are enabled by default, so we disable * all interrupts and clear interrupts status first. */ hdmirx_disable_all_interrupts(hdmirx_dev); + + return ret; } /* hdmi-4k-300mhz EDID produced by v4l2-ctl tool */ @@ -2606,10 +2687,12 @@ static int hdmirx_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs), "failed to remap regs resource\n"); + mutex_init(&hdmirx_dev->phy_rw_lock); mutex_init(&hdmirx_dev->stream_lock); mutex_init(&hdmirx_dev->work_lock); spin_lock_init(&hdmirx_dev->rst_lock); + init_completion(&hdmirx_dev->cr_read_done); init_completion(&hdmirx_dev->cr_write_done); init_completion(&hdmirx_dev->timer_base_lock); init_completion(&hdmirx_dev->avi_pkt_rcv); @@ -2623,7 +2706,10 @@ static int hdmirx_probe(struct platform_device *pdev) hdmirx_dev->timings = cea640x480; hdmirx_enable(dev); - hdmirx_init(hdmirx_dev); + + ret = hdmirx_init(hdmirx_dev); + if (ret) + goto err_pm; v4l2_dev = &hdmirx_dev->v4l2_dev; strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name)); diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h index b13f58e31944..31b887e94e87 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h @@ -116,6 +116,8 @@ #define PHYCREG_CR_PARA_WRITE_P BIT(1) #define PHYCREG_CR_PARA_READ_P BIT(0) #define PHYCREG_STATUS 0x00f4 +#define PHYCREG_CR_PARA_DATAVALID BIT(24) +#define PHYCREG_CR_PARA_RD_DATA_MASK GENMASK(15, 0) #define MAINUNIT_STATUS 0x0150 #define TMDSVALID_STABLE_ST BIT(1) diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig index 3bc4aa35887e..da33facf4467 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -41,6 +41,19 @@ config VIDEO_TI_CAL_MC default. Note that this behavior can be overridden via module parameter 'mc_api'. +config VIDEO_TI_VIP + tristate "TI Video Input Port" + depends on VIDEO_DEV + depends on SOC_DRA7XX || COMPILE_TEST + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + select VIDEO_TI_VPDMA + select VIDEO_TI_SC + select VIDEO_TI_CSC + help + Driver support for VIP module on certain TI SoC's + VIP = Video Input Port. + # Mem2mem drivers config VIDEO_TI_VPE diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c index 55ee14e8b449..9dbf06ac058d 100644 --- a/drivers/media/platform/ti/omap3isp/ispccdc.c +++ b/drivers/media/platform/ti/omap3isp/ispccdc.c @@ -2675,6 +2675,7 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc) pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccdc_media_ops; + me->function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV; ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c index 1204ee221c9e..d668111b44f4 100644 --- a/drivers/media/platform/ti/omap3isp/ispccp2.c +++ b/drivers/media/platform/ti/omap3isp/ispccp2.c @@ -658,7 +658,7 @@ static void ccp2_try_format(struct isp_ccp2_device *ccp2, fmt->height = clamp_t(u32, fmt->height, ISPCCP2_DAT_SIZE_MIN, ISPCCP2_DAT_SIZE_MAX); - } else if (ccp2->input == CCP2_INPUT_MEMORY) { + } else { fmt->width = clamp_t(u32, fmt->width, ISPCCP2_LCM_HSIZE_COUNT_MIN, ISPCCP2_LCM_HSIZE_COUNT_MAX); @@ -1086,6 +1086,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2) pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &ccp2_media_ops; + me->function = MEDIA_ENT_F_VID_IF_BRIDGE; ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c index ae574e1b6528..f227042b61b6 100644 --- a/drivers/media/platform/ti/omap3isp/ispcsi2.c +++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c @@ -1251,6 +1251,7 @@ static int csi2_init_entities(struct isp_csi2_device *csi2) | MEDIA_PAD_FL_MUST_CONNECT; me->ops = &csi2_media_ops; + me->function = MEDIA_ENT_F_VID_IF_BRIDGE; ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c index e383a57654de..3f3b5bd9cdc7 100644 --- a/drivers/media/platform/ti/omap3isp/isppreview.c +++ b/drivers/media/platform/ti/omap3isp/isppreview.c @@ -1742,22 +1742,17 @@ static void preview_try_format(struct isp_prev_device *prev, switch (pad) { case PREV_PAD_SINK: - /* When reading data from the CCDC, the input size has already - * been mangled by the CCDC output pad so it can be accepted - * as-is. - * - * When reading data from memory, clamp the requested width and - * height. The TRM doesn't specify a minimum input height, make + /* + * Clamp the requested width and height. + * The TRM doesn't specify a minimum input height, make * sure we got enough lines to enable the noise filter and color * filter array interpolation. */ - if (prev->input == PREVIEW_INPUT_MEMORY) { - fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH, - preview_max_out_width(prev)); - fmt->height = clamp_t(u32, fmt->height, - PREV_MIN_IN_HEIGHT, - PREV_MAX_IN_HEIGHT); - } + fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH, + preview_max_out_width(prev)); + fmt->height = clamp_t(u32, fmt->height, + PREV_MIN_IN_HEIGHT, + PREV_MAX_IN_HEIGHT); fmt->colorspace = V4L2_COLORSPACE_SRGB; @@ -1796,7 +1791,7 @@ static void preview_try_format(struct isp_prev_device *prev, fmt->width = crop->width; fmt->height = crop->height; - fmt->colorspace = V4L2_COLORSPACE_JPEG; + fmt->colorspace = V4L2_COLORSPACE_SRGB; break; } @@ -2277,7 +2272,7 @@ static int preview_init_entities(struct isp_prev_device *prev) strscpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name)); sd->grp_id = 1 << 16; /* group ID for isp subdevs */ v4l2_set_subdevdata(sd, prev); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&prev->ctrls, 2); v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS, @@ -2294,6 +2289,7 @@ static int preview_init_entities(struct isp_prev_device *prev) pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &preview_media_ops; + me->function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; ret = media_entity_pads_init(me, PREV_PADS_NUM, pads); if (ret < 0) goto error_handler_free; diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c index 87d821b02e5c..ad0127f5b5cb 100644 --- a/drivers/media/platform/ti/omap3isp/ispresizer.c +++ b/drivers/media/platform/ti/omap3isp/ispresizer.c @@ -1405,7 +1405,7 @@ static void resizer_try_format(struct isp_res_device *res, break; } - fmt->colorspace = V4L2_COLORSPACE_JPEG; + fmt->colorspace = V4L2_COLORSPACE_SRGB; fmt->field = V4L2_FIELD_NONE; } @@ -1738,6 +1738,7 @@ static int resizer_init_entities(struct isp_res_device *res) pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; me->ops = &resizer_media_ops; + me->function = MEDIA_ENT_F_PROC_VIDEO_SCALER; ret = media_entity_pads_init(me, RESZ_PADS_NUM, pads); if (ret < 0) return ret; diff --git a/drivers/media/platform/ti/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c index 07bd62a93d99..64bc71d830c4 100644 --- a/drivers/media/platform/ti/omap3isp/ispstat.c +++ b/drivers/media/platform/ti/omap3isp/ispstat.c @@ -1037,6 +1037,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; me->ops = NULL; + me->function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS; return media_entity_pads_init(me, 1, &stat->pad); } diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index 0e7f0bf2b346..86cb27b6ca4e 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -35,6 +35,10 @@ /* * NOTE: When adding new media bus codes, always remember to add * corresponding in-memory formats to the table below!!! + * + * If there are multiple entries with the same pixelformat but + * different media bus codes, then keep those together. Otherwise + * isp_video_enum_format() cannot detect duplicate pixelformats. */ static struct isp_format_info formats[] = { { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, @@ -97,12 +101,12 @@ static struct isp_format_info formats[] = { { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, 0, V4L2_PIX_FMT_UYVY, 16, 2, }, - { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, - MEDIA_BUS_FMT_YUYV8_1X16, 0, - V4L2_PIX_FMT_YUYV, 16, 2, }, { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, 0, V4L2_PIX_FMT_UYVY, 8, 2, }, + { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_YUYV8_1X16, 0, + V4L2_PIX_FMT_YUYV, 16, 2, }, { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, 0, V4L2_PIX_FMT_YUYV, 8, 2, }, @@ -148,12 +152,12 @@ static unsigned int isp_video_mbus_to_pix(const struct isp_video *video, pix->width = mbus->width; pix->height = mbus->height; - for (i = 0; i < ARRAY_SIZE(formats); ++i) { + for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { if (formats[i].code == mbus->code) break; } - if (WARN_ON(i == ARRAY_SIZE(formats))) + if (WARN_ON(i == ARRAY_SIZE(formats) - 1)) return 0; min_bpl = pix->width * formats[i].bpp; @@ -191,7 +195,7 @@ static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, /* Skip the last format in the loop so that it will be selected if no * match is found. */ - for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { + for (i = 0; i < ARRAY_SIZE(formats) - 2; ++i) { if (formats[i].pixelformat == pix->pixelformat) break; } @@ -325,6 +329,13 @@ static int isp_video_queue_setup(struct vb2_queue *queue, struct isp_video_fh *vfh = vb2_get_drv_priv(queue); struct isp_video *video = vfh->video; + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + if (sizes[0] < vfh->format.fmt.pix.sizeimage) + return -EINVAL; + return 0; + } *num_planes = 1; sizes[0] = vfh->format.fmt.pix.sizeimage; @@ -340,6 +351,7 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(buf); struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue); + unsigned int size = vfh->format.fmt.pix.sizeimage; struct isp_buffer *buffer = to_isp_buffer(vbuf); struct isp_video *video = vfh->video; dma_addr_t addr; @@ -360,8 +372,13 @@ static int isp_video_buffer_prepare(struct vb2_buffer *buf) return -EINVAL; } - vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, - vfh->format.fmt.pix.sizeimage); + if (vb2_plane_size(&buffer->vb.vb2_buf, 0) < size) { + dev_dbg(video->isp->dev, + "data will not fit into plane (%lu < %u)\n", + vb2_plane_size(&buffer->vb.vb2_buf, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, size); buffer->dma = addr; return 0; @@ -645,16 +662,41 @@ isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) strscpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); strscpy(cap->card, video->video.name, sizeof(cap->card)); - strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; - + | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_IO_MC; return 0; } static int +isp_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct isp_video *video = video_drvdata(file); + unsigned int i, j; + + if (f->type != video->type) + return -EINVAL; + + for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) { + /* Weed out duplicate pixelformats with different mbus codes */ + if (!f->mbus_code && i && + formats[i - 1].pixelformat == formats[i].pixelformat) + continue; + if (f->mbus_code && formats[i].code != f->mbus_code) + continue; + + if (j == f->index) { + f->pixelformat = formats[i].pixelformat; + return 0; + } + j++; + } + + return -EINVAL; +} + +static int isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) { struct isp_video_fh *vfh = file_to_isp_video_fh(file); @@ -671,11 +713,15 @@ isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) } static int -isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) { - struct isp_video_fh *vfh = file_to_isp_video_fh(file); struct isp_video *video = video_drvdata(file); - struct v4l2_mbus_framefmt fmt; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev *subdev; + u32 pad; + int ret; if (format->type != video->type) return -EINVAL; @@ -715,32 +761,11 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) break; } - /* Fill the bytesperline and sizeimage fields by converting to media bus - * format and back to pixel format. - */ - isp_video_pix_to_mbus(&format->fmt.pix, &fmt); - isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); - - mutex_lock(&video->mutex); - vfh->format = *format; - mutex_unlock(&video->mutex); - - return 0; -} - -static int -isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) -{ - struct isp_video *video = video_drvdata(file); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_subdev *subdev; - u32 pad; - int ret; - - if (format->type != video->type) - return -EINVAL; + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); + isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); + return 0; + } subdev = isp_video_remote_subdev(video, &pad); if (subdev == NULL) @@ -758,6 +783,24 @@ isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) } static int +isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct isp_video_fh *vfh = file_to_isp_video_fh(file); + struct isp_video *video = video_drvdata(file); + int ret; + + ret = isp_video_try_format(file, fh, format); + if (ret) + return ret; + + mutex_lock(&video->mutex); + vfh->format = *format; + mutex_unlock(&video->mutex); + + return 0; +} + +static int isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); @@ -885,7 +928,10 @@ isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) if (a->parm.output.timeperframe.denominator == 0) a->parm.output.timeperframe.denominator = 1; + if (a->parm.output.timeperframe.numerator == 0) + a->parm.output.timeperframe.numerator = 1; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; vfh->timeperframe = a->parm.output.timeperframe; return 0; @@ -906,6 +952,20 @@ isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) } static int +isp_video_create_bufs(struct file *file, void *fh, struct v4l2_create_buffers *p) +{ + struct isp_video_fh *vfh = file_to_isp_video_fh(file); + struct isp_video *video = video_drvdata(file); + int ret; + + mutex_lock(&video->queue_lock); + ret = vb2_create_bufs(&vfh->queue, p); + mutex_unlock(&video->queue_lock); + + return ret; +} + +static int isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct isp_video_fh *vfh = file_to_isp_video_fh(file); @@ -920,6 +980,20 @@ isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) } static int +isp_video_prepare_buf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct isp_video_fh *vfh = file_to_isp_video_fh(file); + struct isp_video *video = video_drvdata(file); + int ret; + + mutex_lock(&video->queue_lock); + ret = vb2_prepare_buf(&vfh->queue, video->video.v4l2_dev->mdev, b); + mutex_unlock(&video->queue_lock); + + return ret; +} + +static int isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct isp_video_fh *vfh = file_to_isp_video_fh(file); @@ -1260,9 +1334,11 @@ isp_video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_querycap = isp_video_querycap, + .vidioc_enum_fmt_vid_cap = isp_video_enum_format, .vidioc_g_fmt_vid_cap = isp_video_get_format, .vidioc_s_fmt_vid_cap = isp_video_set_format, .vidioc_try_fmt_vid_cap = isp_video_try_format, + .vidioc_enum_fmt_vid_out = isp_video_enum_format, .vidioc_g_fmt_vid_out = isp_video_get_format, .vidioc_s_fmt_vid_out = isp_video_set_format, .vidioc_try_fmt_vid_out = isp_video_try_format, @@ -1271,7 +1347,9 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_g_parm = isp_video_get_param, .vidioc_s_parm = isp_video_set_param, .vidioc_reqbufs = isp_video_reqbufs, + .vidioc_create_bufs = isp_video_create_bufs, .vidioc_querybuf = isp_video_querybuf, + .vidioc_prepare_buf = isp_video_prepare_buf, .vidioc_qbuf = isp_video_qbuf, .vidioc_dqbuf = isp_video_dqbuf, .vidioc_streamon = isp_video_streamon, @@ -1288,6 +1366,7 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { static int isp_video_open(struct file *file) { struct isp_video *video = video_drvdata(file); + struct v4l2_mbus_framefmt fmt; struct isp_video_fh *handle; struct vb2_queue *queue; int ret = 0; @@ -1330,6 +1409,14 @@ static int isp_video_open(struct file *file) memset(&handle->format, 0, sizeof(handle->format)); handle->format.type = video->type; + handle->format.fmt.pix.width = 720; + handle->format.fmt.pix.height = 480; + handle->format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + handle->format.fmt.pix.field = V4L2_FIELD_NONE; + handle->format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + isp_video_pix_to_mbus(&handle->format.fmt.pix, &fmt); + isp_video_mbus_to_pix(video, &fmt, &handle->format.fmt.pix); + handle->timeperframe.numerator = 1; handle->timeperframe.denominator = 1; handle->video = video; @@ -1449,12 +1536,15 @@ int omap3isp_video_init(struct isp_video *video, const char *name) video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &isp_video_ioctl_ops; - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING; - else + | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + v4l2_disable_ioctl(&video->video, VIDIOC_S_PARM); + v4l2_disable_ioctl(&video->video, VIDIOC_G_PARM); + } else { video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT - | V4L2_CAP_STREAMING; + | V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + } video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; diff --git a/drivers/media/platform/ti/vpe/Makefile b/drivers/media/platform/ti/vpe/Makefile index 3fadfe084f87..fbb0dec5a30e 100644 --- a/drivers/media/platform/ti/vpe/Makefile +++ b/drivers/media/platform/ti/vpe/Makefile @@ -3,10 +3,12 @@ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o obj-$(CONFIG_VIDEO_TI_VPDMA) += ti-vpdma.o obj-$(CONFIG_VIDEO_TI_SC) += ti-sc.o obj-$(CONFIG_VIDEO_TI_CSC) += ti-csc.o +obj-$(CONFIG_VIDEO_TI_VIP) += ti-vip.o ti-vpe-y := vpe.o ti-vpdma-y := vpdma.o ti-sc-y := sc.o ti-csc-y := csc.o +ti-vip-y := vip.o ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c new file mode 100644 index 000000000000..d4236ac6d867 --- /dev/null +++ b/drivers/media/platform/ti/vpe/vip.c @@ -0,0 +1,3673 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI VIP capture driver + * + * Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com/ + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Yemike Abhilash Chandra, <y-abhilashchandra@ti.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/pm_runtime.h> +#include <linux/sched.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <linux/pinctrl/consumer.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> + +#include "vip.h" + +#define VIP_MODULE_NAME "vip" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-8)"); + +/* + * Minimum and maximum frame sizes + */ +#define MIN_W 128 +#define MIN_H 128 +#define MAX_W 2048 +#define MAX_H 1536 + +/* + * Required alignments + */ +#define S_ALIGN 0 /* multiple of 1 */ +#define H_ALIGN 1 /* multiple of 2 */ +#define W_ALIGN 1 /* multiple of 2 */ + +/* + * Need a descriptor entry for each of up to 15 outputs, + * and up to 2 control transfers. + */ +#define VIP_DESC_LIST_SIZE (17 * sizeof(struct vpdma_dtd)) + +/* + * port flag bits + */ +#define FLAG_INTERLACED BIT(4) +#define FLAG_MULT_PORT BIT(6) +#define FLAG_MULT_ANC BIT(7) + +#define VIP_VPDMA_FIFO_SIZE 2 +#define VIP_DROPQ_SIZE 3 + +/* + * Define indices into the srce_info tables + */ + +#define VIP_SRCE_MULT_PORT 0 +#define VIP_SRCE_MULT_ANC 1 +#define VIP_SRCE_LUMA 2 +#define VIP_SRCE_CHROMA 3 +#define VIP_SRCE_RGB 4 + +#define reg_read(dev, offset) ioread32((dev)->base + (offset)) +#define reg_write(dev, offset, val) iowrite32((val), (dev)->base + (offset)) + +#define GET_OFFSET_TOP(port, obj, reg) \ + ((obj)->base - (port)->dev->base + (reg)) + +#define VIP_SET_MMR_ADB_HDR(port, hdr, regs, offset_a) \ + VPDMA_SET_MMR_ADB_HDR((port)->mmr_adb, vip_mmr_adb, hdr, regs, offset_a) + +/* + * These represent the module resets bit for slice 1 + * Upon detecting slice2 we simply left shift by 1 + */ +#define VIP_DP_RST BIT(16) +#define VIP_CSC_RST BIT(20) +#define VIP_SC_RST BIT(22) + +#define VIP_PARSER_PORT(p) (VIP_PARSER_PORTA_0 + ((p) * 0x8U)) +#define VIP_PARSER_CROP_H_PORT(p) \ + (VIP_PARSER_PORTA_EXTRA4 + ((p) * 0x10U)) +#define VIP_PARSER_CROP_V_PORT(p) \ + (VIP_PARSER_PORTA_EXTRA5 + ((p) * 0x10U)) +#define VIP_PARSER_STOP_IMM_PORT(p) (VIP_PARSER_PORTA_EXTRA6 + ((p) * 0x4U)) + +#define PARSER_IRQ_MASK (VIP_PORTA_OUTPUT_FIFO_YUV | \ + VIP_PORTB_OUTPUT_FIFO_YUV) + +/* + * The srce_info structure contains per-srce data. + */ +struct vip_srce_info { + u8 base_channel; /* the VPDMA channel number */ + u8 vb_index; /* input frame f, f-1, f-2 index */ + u8 vb_part; /* identifies section of co-planar formats */ +}; + +static struct vip_srce_info srce_info[5] = { + [VIP_SRCE_MULT_PORT] = { + .base_channel = VIP1_CHAN_NUM_MULT_PORT_A_SRC0, + .vb_index = 0, + .vb_part = VIP_CHROMA, + }, + [VIP_SRCE_MULT_ANC] = { + .base_channel = VIP1_CHAN_NUM_MULT_ANC_A_SRC0, + .vb_index = 0, + .vb_part = VIP_LUMA, + }, + [VIP_SRCE_LUMA] = { + .base_channel = VIP1_CHAN_NUM_PORT_A_LUMA, + .vb_index = 1, + .vb_part = VIP_LUMA, + }, + [VIP_SRCE_CHROMA] = { + .base_channel = VIP1_CHAN_NUM_PORT_A_CHROMA, + .vb_index = 1, + .vb_part = VIP_CHROMA, + }, + [VIP_SRCE_RGB] = { + .base_channel = VIP1_CHAN_NUM_PORT_A_RGB, + .vb_part = VIP_LUMA, + }, +}; + +static struct vip_fmt vip_formats[VIP_MAX_ACTIVE_FMT] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 1, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], + &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], + }, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], + }, + }, + { + .fourcc = V4L2_PIX_FMT_BGR32, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB24, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], + }, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .coplanar = 0, + .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], + }, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .coplanar = 0, + .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8], + }, + }, + { + /* V4L2 currently only defines one 16 bit variant */ + .fourcc = V4L2_PIX_FMT_SBGGR16, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .coplanar = 0, + .vpdma_fmt = { &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW16], + }, + }, +}; + +/* + * DMA address/data block for the shadow registers + */ +struct vip_mmr_adb { + struct vpdma_adb_hdr sc_hdr0; + u32 sc_regs0[7]; + u32 sc_pad0[1]; + struct vpdma_adb_hdr sc_hdr8; + u32 sc_regs8[6]; + u32 sc_pad8[2]; + struct vpdma_adb_hdr sc_hdr17; + u32 sc_regs17[9]; + u32 sc_pad17[3]; + struct vpdma_adb_hdr csc_hdr; + u32 csc_regs[6]; + u32 csc_pad[2]; +}; + +/* + * Function prototype declarations + */ +static int alloc_port(struct vip_dev *, int); +static void free_port(struct vip_port *); +static int vip_setup_parser(struct vip_port *port); +static int vip_setup_scaler(struct vip_stream *stream); +static void vip_enable_parser(struct vip_port *port, bool on); +static void vip_reset_parser(struct vip_port *port, bool on); +static void vip_parser_stop_imm(struct vip_port *port, bool on); +static void stop_dma(struct vip_stream *stream, bool clear_list); +static int vip_load_vpdma_list_fifo(struct vip_stream *stream); +static inline bool is_scaler_available(struct vip_port *port); +static inline bool allocate_scaler(struct vip_port *port); +static inline void free_scaler(struct vip_port *port); +static bool is_csc_available(struct vip_port *port); +static bool allocate_csc(struct vip_port *port, + enum vip_csc_state csc_direction); +static void free_csc(struct vip_port *port); + +/* initialize v4l2_format_info member in vip_formats array */ +static void vip_init_format_info(struct device *dev) +{ + struct vip_fmt *fmt; + int i; + + for (i = 0; i < ARRAY_SIZE(vip_formats); i++) { + fmt = &vip_formats[i]; + fmt->finfo = v4l2_format_info(fmt->fourcc); + } +} + +/* Print Four-character-code (FOURCC) */ +static char *fourcc_to_str(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +/* + * Find our format description corresponding to the passed v4l2_format + */ +static struct vip_fmt *find_port_format_by_pix(struct vip_port *port, + u32 pixelformat) +{ + struct vip_fmt *fmt; + unsigned int index; + + for (index = 0; index < port->num_active_fmt; index++) { + fmt = port->active_fmt[index]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static struct vip_fmt *find_port_format_by_code(struct vip_port *port, + u32 code) +{ + struct vip_fmt *fmt; + unsigned int index; + + for (index = 0; index < port->num_active_fmt; index++) { + fmt = port->active_fmt[index]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +inline struct vip_port *notifier_to_vip_port(struct v4l2_async_notifier *n) +{ + return container_of(n, struct vip_port, notifier); +} + +static bool vip_is_mbuscode_yuv(u32 code) +{ + return ((code & 0xff00) == 0x2000); +} + +static bool vip_is_mbuscode_rgb(u32 code) +{ + return ((code & 0xff00) == 0x1000); +} + +static bool vip_is_mbuscode_raw(u32 code) +{ + return ((code & 0xff00) == 0x3000); +} + +/* + * This is not an accurate conversion but it is only used to + * assess if color conversion is needed. + */ +static u32 vip_mbus_code_to_fourcc(u32 code) +{ + if (vip_is_mbuscode_rgb(code)) + return V4L2_PIX_FMT_RGB24; + + if (vip_is_mbuscode_yuv(code)) + return V4L2_PIX_FMT_UYVY; + + return V4L2_PIX_FMT_SBGGR8; +} + +static enum vip_csc_state +vip_csc_direction(u32 src_code, const struct v4l2_format_info *dfinfo) +{ + if (vip_is_mbuscode_yuv(src_code) && v4l2_is_format_rgb(dfinfo)) + return VIP_CSC_Y2R; + else if (vip_is_mbuscode_rgb(src_code) && v4l2_is_format_yuv(dfinfo)) + return VIP_CSC_R2Y; + else + return VIP_CSC_NA; +} + +/* + * Insert a masked field into a 32-bit field + */ +static void insert_field(u32 *valp, u32 field, u32 mask, int shift) +{ + u32 val = *valp; + + val &= ~(mask << shift); + val |= (field & mask) << shift; + *valp = val; +} + +/* + * Set the headers for all of the address/data block structures. + */ +static void init_adb_hdrs(struct vip_port *port) +{ + VIP_SET_MMR_ADB_HDR(port, sc_hdr0, sc_regs0, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC0)); + VIP_SET_MMR_ADB_HDR(port, sc_hdr8, sc_regs8, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC8)); + VIP_SET_MMR_ADB_HDR(port, sc_hdr17, sc_regs17, + GET_OFFSET_TOP(port, port->dev->sc, CFG_SC17)); + VIP_SET_MMR_ADB_HDR(port, csc_hdr, csc_regs, + GET_OFFSET_TOP(port, port->dev->csc, CSC_CSC00)); + +}; + +static void vip_module_toggle(struct vip_dev *dev, uint32_t module, bool on) +{ + u32 val = 0; + + val = reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id == VIP_SLICE2) + module <<= 1; + + if (on) + val |= module; + else + val &= ~module; + + reg_write(dev, VIP_CLK_RESET, val); +} + +/* + * Enable or disable the VIP clocks + */ +static void vip_set_clock_enable(struct vip_dev *dev, bool on) +{ + u32 val = 0; + + val = reg_read(dev, VIP_CLK_ENABLE); + if (on) { + val |= VIP_VPDMA_CLK_ENABLE; + if (dev->slice_id == VIP_SLICE1) + val |= VIP_VIP1_DATA_PATH_CLK_ENABLE; + else + val |= VIP_VIP2_DATA_PATH_CLK_ENABLE; + } else { + if (dev->slice_id == VIP_SLICE1) + val &= ~VIP_VIP1_DATA_PATH_CLK_ENABLE; + else + val &= ~VIP_VIP2_DATA_PATH_CLK_ENABLE; + + /* Both VIP are disabled then shutdown VPDMA also */ + if (!(val & (VIP_VIP1_DATA_PATH_CLK_ENABLE | + VIP_VIP2_DATA_PATH_CLK_ENABLE))) + val = 0; + } + + reg_write(dev, VIP_CLK_ENABLE, val); +} + +/* This helper function is used to enable the clock early on to + * enable vpdma firmware loading before the slice device are created + */ +static void vip_shared_set_clock_enable(struct vip_shared *shared, bool on) +{ + u32 val = 0; + + val = VIP_VIP1_DATA_PATH_CLK_ENABLE | VIP_VPDMA_CLK_ENABLE; + + reg_write(shared, VIP_CLK_ENABLE, val); +} + +static void vip_top_reset(struct vip_dev *dev) +{ + u32 val = 0; + + val = reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id == VIP_SLICE1) + insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP1_DATA_PATH_RESET_SHIFT); + else + insert_field(&val, 1, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP2_DATA_PATH_RESET_SHIFT); + + reg_write(dev, VIP_CLK_RESET, val); + + usleep_range(200, 250); + + val = reg_read(dev, VIP_CLK_RESET); + + if (dev->slice_id == VIP_SLICE1) + insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP1_DATA_PATH_RESET_SHIFT); + else + insert_field(&val, 0, VIP_DATA_PATH_CLK_RESET_MASK, + VIP_VIP2_DATA_PATH_RESET_SHIFT); + reg_write(dev, VIP_CLK_RESET, val); +} + +static void vip_top_vpdma_reset(struct vip_shared *shared) +{ + u32 val; + + val = reg_read(shared, VIP_CLK_RESET); + insert_field(&val, 1, VIP_VPDMA_CLK_RESET_MASK, + VIP_VPDMA_CLK_RESET_SHIFT); + reg_write(shared, VIP_CLK_RESET, val); + + usleep_range(200, 250); + + val = reg_read(shared, VIP_CLK_RESET); + insert_field(&val, 0, VIP_VPDMA_CLK_RESET_MASK, + VIP_VPDMA_CLK_RESET_SHIFT); + reg_write(shared, VIP_CLK_RESET, val); +} + +static void vip_set_pclk_invert(struct vip_port *port) +{ + struct vip_ctrl_module *ctrl = port->dev->syscon; + struct vip_dev *dev = port->dev; + u32 index; + /* + * When the VIP parser is configured to so that the pixel clock + * is to be sampled at falling edge, the pixel clock needs to be + * inverted before it is given to the VIP module. This is done + * by setting a bit in the CTRL_CORE_SMA_SW1 register. + */ + + index = 2 * port->dev->slice_id + port->port_id; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: slice%d:port%d -> index: %d\n", __func__, + port->dev->slice_id, port->port_id, index); + + if (ctrl->syscon_pol) + regmap_update_bits(ctrl->syscon_pol, + ctrl->syscon_offset, + ctrl->syscon_bit_field[index], + ctrl->syscon_bit_field[index]); +} + +static void vip_set_data_interface(struct vip_port *port, + enum data_interface_modes mode) +{ + u32 val = 0; + + insert_field(&val, mode, VIP_DATA_INTERFACE_MODE_MASK, + VIP_DATA_INTERFACE_MODE_SHFT); + + reg_write(port->dev->parser, VIP_PARSER_MAIN_CFG, val); +} + +static void vip_set_slice_path(struct vip_dev *dev, + enum data_path_select data_path, u32 path_val) +{ + u32 val = 0; + int data_path_reg; + + data_path_reg = VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id; + + switch (data_path) { + case ALL_FIELDS_DATA_SELECT: + val |= path_val; + break; + case VIP_CSC_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_CSC_SRC_SELECT_MASK, + VIP_CSC_SRC_SELECT_SHFT); + break; + case VIP_SC_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_SC_SRC_SELECT_MASK, + VIP_SC_SRC_SELECT_SHFT); + break; + case VIP_RGB_SRC_DATA_SELECT: + val |= (path_val) ? VIP_RGB_SRC_SELECT : 0; + break; + case VIP_RGB_OUT_LO_DATA_SELECT: + val |= (path_val) ? VIP_RGB_OUT_LO_SRC_SELECT : 0; + break; + case VIP_RGB_OUT_HI_DATA_SELECT: + val |= (path_val) ? VIP_RGB_OUT_HI_SRC_SELECT : 0; + break; + case VIP_CHR_DS_1_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_DS1_SRC_SELECT_MASK, + VIP_DS1_SRC_SELECT_SHFT); + break; + case VIP_CHR_DS_2_SRC_DATA_SELECT: + insert_field(&val, path_val, VIP_DS2_SRC_SELECT_MASK, + VIP_DS2_SRC_SELECT_SHFT); + break; + case VIP_MULTI_CHANNEL_DATA_SELECT: + val |= (path_val) ? VIP_MULTI_CHANNEL_SELECT : 0; + break; + case VIP_CHR_DS_1_DATA_BYPASS: + val |= (path_val) ? VIP_DS1_BYPASS : 0; + break; + case VIP_CHR_DS_2_DATA_BYPASS: + val |= (path_val) ? VIP_DS2_BYPASS : 0; + break; + default: + v4l2_err(&dev->v4l2_dev, "%s: data_path 0x%x is not valid\n", + __func__, data_path); + return; + } + insert_field(&val, data_path, VIP_DATAPATH_SELECT_MASK, + VIP_DATAPATH_SELECT_SHFT); + reg_write(dev, data_path_reg, val); + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: DATA_PATH_SELECT(%08X): %08X\n", __func__, + data_path_reg, reg_read(dev, data_path_reg)); +} + +/* + * Return the vip_stream structure for a given struct file + */ +static inline struct vip_stream *file2stream(struct file *file) +{ + return video_drvdata(file); +} + +/* + * Append a destination descriptor to the current descriptor list, + * setting up dma to the given srce. + */ +static int add_out_dtd(struct vip_stream *stream, int srce_type) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vip_srce_info *sinfo = &srce_info[srce_type]; + struct v4l2_rect *c_rect = &port->c_rect; + struct vip_fmt *fmt = port->fmt; + int channel, plane = 0; + int max_width, max_height; + dma_addr_t dma_addr = 0; + u32 flags; + u32 width = stream->width; + + channel = sinfo->base_channel; + + switch (srce_type) { + case VIP_SRCE_MULT_PORT: + case VIP_SRCE_MULT_ANC: + if (port->port_id == VIP_PORTB) + channel += VIP_CHAN_MULT_PORTB_OFFSET; + channel += stream->stream_id; + flags = 0; + break; + case VIP_SRCE_CHROMA: + plane = 1; + fallthrough; + case VIP_SRCE_LUMA: + if (port->port_id == VIP_PORTB) { + if (port->scaler && !port->fmt->coplanar) + /* + * In this case Port A Chroma channel + * is used to carry Port B scaled YUV422 + */ + channel += 1; + else + channel += VIP_CHAN_YUV_PORTB_OFFSET; + } + flags = port->flags; + break; + case VIP_SRCE_RGB: + if (port->port_id == VIP_PORTB || + (port->port_id == VIP_PORTA && + port->csc == VIP_CSC_NA && + v4l2_is_format_rgb(port->fmt->finfo))) + /* + * RGB sensor only connect to Y_LO + * channel i.e. port B channel. + */ + channel += VIP_CHAN_RGB_PORTB_OFFSET; + flags = port->flags; + break; + default: + v4l2_err(&dev->v4l2_dev, "%s: srce_type 0x%x is not valid\n", + __func__, srce_type); + return -1; + } + + if (dev->slice_id == VIP_SLICE2) + channel += VIP_CHAN_VIP2_OFFSET; + + if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) { + /* + * Special case since we are faking a YUV422 16bit format + * to have the vpdma perform the needed byte swap + * we need to adjust the pixel width accordingly + * otherwise the parser will attempt to collect more pixels + * then available and the vpdma transfer will exceed the + * allocated frame buffer. + */ + width >>= 1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting width to %d\n", + __func__, width); + } + + /* + * Use VPDMA_MAX_SIZE1 or VPDMA_MAX_SIZE2 register for slice0/1 + */ + + if (dev->slice_id == VIP_SLICE1) { + vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE1, + width, stream->height); + + max_width = MAX_OUT_WIDTH_REG1; + max_height = MAX_OUT_HEIGHT_REG1; + } else { + vpdma_set_max_size(dev->shared->vpdma, VPDMA_MAX_SIZE2, + width, stream->height); + + max_width = MAX_OUT_WIDTH_REG2; + max_height = MAX_OUT_HEIGHT_REG2; + } + + /* + * Mark this channel to be cleared while cleaning up resources + * This will make sure that an abort descriptor for this channel + * would be submitted to VPDMA causing any ongoing transaction to be + * aborted and cleanup the VPDMA FSM for this channel + */ + stream->vpdma_channels[channel] = 1; + + vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width, + stream->bytesperline, c_rect, + fmt->vpdma_fmt[plane], dma_addr, + max_width, max_height, channel, flags); + return 0; +} + +/* + * add_stream_dtds - prepares and starts DMA for pending transfers + */ +static void add_stream_dtds(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + int srce_type; + + if (port->flags & FLAG_MULT_PORT) + srce_type = VIP_SRCE_MULT_PORT; + else if (port->flags & FLAG_MULT_ANC) + srce_type = VIP_SRCE_MULT_ANC; + else if (v4l2_is_format_rgb(port->fmt->finfo)) + srce_type = VIP_SRCE_RGB; + else + srce_type = VIP_SRCE_LUMA; + + add_out_dtd(stream, srce_type); + + if (srce_type == VIP_SRCE_LUMA && port->fmt->coplanar) + add_out_dtd(stream, VIP_SRCE_CHROMA); +} + +static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser = dev->parser; + u32 reg_addr = VIP_INT0_ENABLE0_SET + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val = (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Enable Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_MASK, (u32)~PARSER_IRQ_MASK); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_enable_list_complete_irq(dev->shared->vpdma, + irq_num, list_num, true); +} + +static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser = dev->parser; + u32 reg_addr = VIP_INT0_ENABLE0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val = (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Disable all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_MASK, 0xffffffff); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_enable_list_complete_irq(dev->shared->vpdma, + irq_num, list_num, false); +} + +static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num) +{ + struct vip_parser_data *parser = dev->parser; + u32 reg_addr = VIP_INT0_STATUS0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + u32 irq_val = (1 << (list_num * 2)) | + (VIP_VIP1_PARSER_INT << (irq_num * 1)); + + /* Clear all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_CLR, 0xffffffff); + reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0); + + reg_write(dev->shared, reg_addr, irq_val); + + vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id); +} + +static void populate_desc_list(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + + stream->desc_next = stream->desc_list.buf.addr; + add_stream_dtds(stream); + + vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); +} + +/* + * start_dma - adds descriptors to the dma list and submits them. + * Should be called after a new vb is queued and on a vpdma list + * completion interrupt. + */ +static void start_dma(struct vip_stream *stream, struct vip_buffer *buf) +{ + struct vip_dev *dev = stream->port->dev; + struct vpdma_data *vpdma = dev->shared->vpdma; + int list_num = stream->list_num; + dma_addr_t dma_addr; + int drop_data; + + if (vpdma_list_busy(vpdma, list_num)) { + v4l2_err(&dev->v4l2_dev, "vpdma list busy, cannot post\n"); + return; /* nothing to do */ + } + + if (buf) { + dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + drop_data = 0; + v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: vb2 buf idx:%d, dma_addr:%pad\n", + __func__, buf->vb.vb2_buf.index, &dma_addr); + } else { + dma_addr = 0; + drop_data = 1; + v4l2_dbg(4, debug, &dev->v4l2_dev, "%s: dropped\n", __func__); + } + + vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list, + dma_addr, stream->write_desc, drop_data, 0); + + if (stream->port->fmt->coplanar) { + dma_addr += stream->bytesperline * stream->height; + vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list, + dma_addr, stream->write_desc + 1, + drop_data, 1); + } + + vpdma_submit_descs(dev->shared->vpdma, + &stream->desc_list, stream->list_num); +} + +static void vip_schedule_next_buffer(struct vip_stream *stream) +{ + struct vip_dev *dev = stream->port->dev; + struct vip_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&stream->vidq)) { + v4l2_dbg(4, debug, &dev->v4l2_dev, "Dropping frame\n"); + if (list_empty(&stream->dropq)) { + v4l2_err(&dev->v4l2_dev, "No dropq buffer left!"); + spin_unlock_irqrestore(&dev->slock, flags); + return; + } + buf = list_entry(stream->dropq.next, + struct vip_buffer, list); + + buf->drop = true; + list_move_tail(&buf->list, &stream->post_bufs); + buf = NULL; + } else { + buf = list_entry(stream->vidq.next, + struct vip_buffer, list); + buf->drop = false; + list_move_tail(&buf->list, &stream->post_bufs); + v4l2_dbg(4, debug, &dev->v4l2_dev, "added next buffer\n"); + } + + spin_unlock_irqrestore(&dev->slock, flags); + start_dma(stream, buf); +} + +static void vip_process_buffer_complete(struct vip_stream *stream) +{ + struct vip_dev *dev = stream->port->dev; + struct vip_buffer *buf; + struct vb2_v4l2_buffer *vb = NULL; + unsigned long flags, fld; + + buf = list_first_entry_or_null(&stream->post_bufs, struct vip_buffer, list); + + if (stream->port->flags & FLAG_INTERLACED) { + vpdma_unmap_desc_buf(dev->shared->vpdma, + &stream->desc_list.buf); + + fld = dtd_get_field(stream->write_desc); + stream->field = fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; + + vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + } + + if (buf) { + vb = &buf->vb; + vb->sequence = stream->sequence; + vb->vb2_buf.timestamp = ktime_get_ns(); + vb->field = V4L2_FIELD_NONE; + + if (buf->drop) { + spin_lock_irqsave(&dev->slock, flags); + list_move_tail(&buf->list, &stream->dropq); + spin_unlock_irqrestore(&dev->slock, flags); + } else { + spin_lock_irqsave(&dev->slock, flags); + list_del(&buf->list); + spin_unlock_irqrestore(&dev->slock, flags); + vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE); + } + } else { + v4l2_err(&dev->v4l2_dev, "%s: buf is null!!!\n", __func__); + return; + } + + stream->sequence++; +} + +static int vip_reset_vpdma(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vip_buffer *buf; + unsigned long flags; + + stop_dma(stream, false); + + spin_lock_irqsave(&dev->slock, flags); + /* requeue all active buffers in the opposite order */ + while (!list_empty(&stream->post_bufs)) { + buf = list_last_entry(&stream->post_bufs, + struct vip_buffer, list); + list_del(&buf->list); + if (buf->drop == 1) { + list_add_tail(&buf->list, &stream->dropq); + v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing drop buffer on dropq\n"); + } else { + list_add(&buf->list, &stream->vidq); + v4l2_dbg(4, debug, &dev->v4l2_dev, "requeueing vb2 buf idx:%d on vidq\n", + buf->vb.vb2_buf.index); + } + } + spin_unlock_irqrestore(&dev->slock, flags); + + /* Make sure the desc_list is unmapped */ + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + + return 0; +} + +static void vip_overflow_recovery_work(struct work_struct *work) +{ + struct vip_stream *stream = container_of(work, struct vip_stream, + recovery_work); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + + v4l2_err(&dev->v4l2_dev, "%s: Port %c\n", __func__, + port->port_id == VIP_PORTA ? 'A' : 'B'); + + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + + /* 1. Set VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMMEDIATELY */ + /* 2. Set VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMMEDIATELY */ + vip_parser_stop_imm(port, 1); + + /* 3. Clear VIP_PORT_A[8] ENABLE */ + /* + * 4. Set VIP_PORT_A[7] CLR_ASYNC_FIFO_RD + * Set VIP_PORT_A[6] CLR_ASYNC_FIFO_WR + */ + vip_enable_parser(port, false); + + /* 5. Set VIP_PORT_A[23] SW_RESET */ + vip_reset_parser(port, 1); + + /* + * 6. Reset other VIP modules + * For each module used downstream of VIP_PARSER, write 1 to the + * bit location of the VIP_CLKC_RST register which is connected + * to VIP_PARSER + */ + vip_module_toggle(dev, VIP_DP_RST, true); + + usleep_range(200, 250); + + /* + * 7. Abort VPDMA channels + * Write to list attribute to stop list 0 + * Write to list address register location of abort list + * Write to list attribute register list 0 and size of abort list + */ + vip_reset_vpdma(stream); + + /* 8. Clear VIP_PORT_A[23] SW_RESET */ + vip_reset_parser(port, 0); + + /* + * 9. Un-reset other VIP modules + * For each module used downstream of VIP_PARSER, write 0 to + * the bit location of the VIP_CLKC_RST register which is + * connected to VIP_PARSER + */ + vip_module_toggle(dev, VIP_DP_RST, false); + + /* 10. (Delay) */ + /* 11. SC coeff downloaded (if VIP_SCALER is being used) */ + vip_setup_scaler(stream); + + /* 12. (Delay) */ + /* the above are not needed here yet */ + + populate_desc_list(stream); + stream->num_recovery++; + if (stream->num_recovery < 5) { + /* Reload the vpdma */ + vip_load_vpdma_list_fifo(stream); + + enable_irqs(dev, dev->slice_id, stream->list_num); + vip_schedule_next_buffer(stream); + + /* 13. Clear VIP_XTRA6_PORT_A[31:16] YUV_SRCNUM_STOP_IMM */ + /* 14. Clear VIP_XTRA6_PORT_A[15:0] ANC_SRCNUM_STOP_IMM */ + + vip_parser_stop_imm(port, 0); + + /* 15. Set VIP_PORT_A[8] ENABLE */ + /* + * 16. Clear VIP_PORT_A[7] CLR_ASYNC_FIFO_RD + * Clear VIP_PORT_A[6] CLR_ASYNC_FIFO_WR + */ + vip_enable_parser(port, true); + } else { + v4l2_err(&dev->v4l2_dev, "%s: num_recovery limit exceeded leaving disabled\n", + __func__); + } +} + +static void handle_parser_irqs(struct vip_dev *dev) +{ + struct vip_parser_data *parser = dev->parser; + struct vip_port *porta = dev->ports[VIP_PORTA]; + struct vip_port *portb = dev->ports[VIP_PORTB]; + struct vip_stream *stream = NULL; + u32 vip_irq_stat = reg_read(parser, VIP_PARSER_FIQ_STATUS); + int i; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: FIQ_STATUS: 0x%08x\n", __func__, vip_irq_stat); + + /* Clear all Parser Interrupt */ + reg_write(parser, VIP_PARSER_FIQ_CLR, vip_irq_stat); + reg_write(parser, VIP_PARSER_FIQ_CLR, 0x0); + + #ifdef DEBUG + if (vip_irq_stat & VIP_PORTA_VDET) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_VDET\n"); + if (vip_irq_stat & VIP_PORTB_VDET) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_VDET\n"); + if (vip_irq_stat & VIP_PORTA_ASYNC_FIFO_OF) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_ASYNC_FIFO_OF\n"); + if (vip_irq_stat & VIP_PORTB_ASYNC_FIFO_OF) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_ASYNC_FIFO_OF\n"); + if (vip_irq_stat & VIP_PORTA_OUTPUT_FIFO_YUV) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_YUV\n"); + if (vip_irq_stat & VIP_PORTA_OUTPUT_FIFO_ANC) + v4l2_err(&dev->v4l2_dev, "VIP_PORTA_OUTPUT_FIFO_ANC\n"); + if (vip_irq_stat & VIP_PORTB_OUTPUT_FIFO_YUV) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_YUV\n"); + if (vip_irq_stat & VIP_PORTB_OUTPUT_FIFO_ANC) + v4l2_err(&dev->v4l2_dev, "VIP_PORTB_OUTPUT_FIFO_ANC\n"); + if (vip_irq_stat & VIP_PORTA_CONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CONN\n"); + if (vip_irq_stat & VIP_PORTA_DISCONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_DISCONN\n"); + if (vip_irq_stat & VIP_PORTB_CONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CONN\n"); + if (vip_irq_stat & VIP_PORTB_DISCONN) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_DISCONN\n"); + if (vip_irq_stat & VIP_PORTA_SRC0_SIZE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_SRC0_SIZE\n"); + if (vip_irq_stat & VIP_PORTB_SRC0_SIZE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_SRC0_SIZE\n"); + if (vip_irq_stat & VIP_PORTA_YUV_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_YUV_PROTO_VIOLATION\n"); + if (vip_irq_stat & VIP_PORTA_ANC_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_ANC_PROTO_VIOLATION\n"); + if (vip_irq_stat & VIP_PORTB_YUV_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_YUV_PROTO_VIOLATION\n"); + if (vip_irq_stat & VIP_PORTB_ANC_PROTO_VIOLATION) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_ANC_PROTO_VIOLATION\n"); + if (vip_irq_stat & VIP_PORTA_CFG_DISABLE_COMPLETE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTA_CFG_DISABLE_COMPLETE\n"); + if (vip_irq_stat & VIP_PORTB_CFG_DISABLE_COMPLETE) + v4l2_dbg(3, debug, &dev->v4l2_dev, "VIP_PORTB_CFG_DISABLE_COMPLETE\n"); + #endif + + if (vip_irq_stat & (VIP_PORTA_ASYNC_FIFO_OF | + VIP_PORTA_OUTPUT_FIFO_YUV | + VIP_PORTA_OUTPUT_FIFO_ANC)) { + for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) { + if (porta->cap_streams[i] && + porta->cap_streams[i]->port->port_id == + porta->port_id) { + stream = porta->cap_streams[i]; + break; + } + } + if (stream) { + disable_irqs(dev, dev->slice_id, + stream->list_num); + schedule_work(&stream->recovery_work); + return; + } + } + if (vip_irq_stat & (VIP_PORTB_ASYNC_FIFO_OF | + VIP_PORTB_OUTPUT_FIFO_YUV | + VIP_PORTB_OUTPUT_FIFO_ANC)) { + for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) { + if (portb->cap_streams[i] && + portb->cap_streams[i]->port->port_id == + portb->port_id) { + stream = portb->cap_streams[i]; + break; + } + } + if (stream) { + disable_irqs(dev, dev->slice_id, + stream->list_num); + schedule_work(&stream->recovery_work); + return; + } + } +} + +static irqreturn_t vip_irq(int irq_vip, void *data) +{ + struct vip_dev *dev = (struct vip_dev *)data; + struct vpdma_data *vpdma; + struct vip_stream *stream; + int list_num; + int irq_num = dev->slice_id; + u32 irqst, irqst_saved, reg_addr; + + if (!dev->shared) + return IRQ_HANDLED; + + vpdma = dev->shared->vpdma; + reg_addr = VIP_INT0_STATUS0 + + VIP_INTC_INTX_OFFSET * irq_num; + irqst_saved = reg_read(dev->shared, reg_addr); + irqst = irqst_saved; + + v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n", + irq_vip, irq_num, irqst); + if (irqst) { + if (irqst & (VIP_VIP1_PARSER_INT << (irq_num * 1))) { + irqst &= ~(VIP_VIP1_PARSER_INT << (irq_num * 1)); + handle_parser_irqs(dev); + } + + for (list_num = 0; irqst && (list_num < 8); list_num++) { + /* Check for LIST_COMPLETE IRQ */ + if (!(irqst & (1 << list_num * 2))) + continue; + + v4l2_dbg(8, debug, &dev->v4l2_dev, "IRQ %d: handling LIST%d_COMPLETE\n", + irq_num, list_num); + + stream = vpdma_hwlist_get_priv(vpdma, list_num); + if (!stream || stream->list_num != list_num) { + v4l2_err(&dev->v4l2_dev, "IRQ occurred for unused list"); + continue; + } + + vpdma_clear_list_stat(vpdma, irq_num, list_num); + + vip_process_buffer_complete(stream); + + vip_schedule_next_buffer(stream); + + irqst &= ~((1 << list_num * 2)); + } + } + + /* Acknowledge that we are done with all interrupts */ + reg_write(dev->shared, VIP_INTC_E0I, 1 << irq_num); + + /* Clear handled events from status register */ + reg_addr = VIP_INT0_STATUS0_CLR + + VIP_INTC_INTX_OFFSET * irq_num; + reg_write(dev->shared, reg_addr, irqst_saved); + + return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int vip_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, VIP_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card)); + return 0; +} + +static int vip_enuminput(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vip_stream *stream = file2stream(file); + + if (inp->index) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = stream->vfd->tvnorms; + sprintf(inp->name, "Camera %u", stream->vfd->num); + + return 0; +} + +static int vip_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vip_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + + *std = stream->vfd->tvnorms; + v4l2_subdev_call(port->subdev, video, querystd, std); + return 0; +} + +static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + + *std = stream->vfd->tvnorms; + v4l2_subdev_call(port->subdev, video, g_std, std); + + return 0; +} + +static int vip_s_std(struct file *file, void *fh, v4l2_std_id std) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + + if (stream->vfd->tvnorms == std) + return 0; + + if (!(std & stream->vfd->tvnorms)) + return -EINVAL; + + if (vb2_is_busy(&stream->vb_vidq)) + return -EBUSY; + + v4l2_subdev_call(port->subdev, video, s_std, std); + return 0; +} + +static int vip_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_fmt *fmt; + + if (f->index >= port->num_active_fmt) + return -EINVAL; + + fmt = port->active_fmt[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int vip_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vip_fmt *fmt; + int ret; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + + fmt = find_port_format_by_pix(port, f->pixel_format); + if (!fmt) + return -EINVAL; + + fse.index = f->index; + fse.code = fmt->code; + ret = v4l2_subdev_call(port->subdev, pad, enum_frame_size, NULL, &fse); + if (ret) + return -EINVAL; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + f->type = V4L2_FRMSIZE_TYPE_DISCRETE; + f->discrete.width = fse.max_width; + f->discrete.height = fse.max_height; + + return 0; +} + +static int vip_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = f->index, + .width = f->width, + .height = f->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt = find_port_format_by_pix(port, f->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code = fmt->code; + ret = v4l2_subdev_call(port->subdev, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + f->type = V4L2_FRMIVAL_TYPE_DISCRETE; + f->discrete = fie.interval; + + return 0; +} + +static int vip_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vip_stream *stream = video_drvdata(file); + struct vip_port *port = stream->port; + + return v4l2_g_parm_cap(video_devdata(file), port->subdev, a); +} + +static int vip_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct vip_stream *stream = video_drvdata(file); + struct vip_port *port = stream->port; + + return v4l2_s_parm_cap(video_devdata(file), port->subdev, a); +} + +static int vip_calc_format_size(struct vip_port *port, + struct vip_fmt *fmt, + struct v4l2_format *f) +{ + enum v4l2_field *field; + unsigned int stride; + struct vip_dev *dev = port->dev; + + if (!fmt) { + v4l2_dbg(2, debug, &dev->v4l2_dev, + "no vip_fmt format provided!\n"); + return -EINVAL; + } + + field = &f->fmt.pix.field; + if (*field == V4L2_FIELD_ANY) + *field = V4L2_FIELD_NONE; + else if (V4L2_FIELD_NONE != *field && V4L2_FIELD_ALTERNATE != *field) + return -EINVAL; + + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, W_ALIGN, + &f->fmt.pix.height, MIN_H, MAX_H, H_ALIGN, + S_ALIGN); + + stride = f->fmt.pix.width * (fmt->vpdma_fmt[0]->depth >> 3); + f->fmt.pix.bytesperline = ALIGN(stride, VPDMA_STRIDE_ALIGN); + + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + if (fmt->coplanar) { + f->fmt.pix.sizeimage += f->fmt.pix.height * + f->fmt.pix.bytesperline * + fmt->vpdma_fmt[VIP_CHROMA]->depth >> 3; + } + + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "calc_format_size: fourcc:%s size: %dx%d bpl:%d img_size:%d\n", + fourcc_to_str(f->fmt.pix.pixelformat), + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static inline bool vip_is_size_dma_aligned(u32 bpp, u32 width) +{ + return ((width * bpp) == ALIGN(width * bpp, VPDMA_STRIDE_ALIGN)); +} + +static int vip_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vip_fmt *fmt; + u32 best_width, best_height, largest_width, largest_height; + int ret, found; + enum vip_csc_state csc_direction; + struct v4l2_subdev_frame_size_enum fse = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + + fmt = find_port_format_by_pix(port, f->fmt.pix.pixelformat); + if (!fmt) { + /* Just get the first one enumerated */ + fmt = port->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + csc_direction = vip_csc_direction(fmt->code, fmt->finfo); + if (csc_direction != VIP_CSC_NA) { + if (!is_csc_available(port)) { + v4l2_dbg(2, debug, &dev->v4l2_dev, + "CSC not available for Fourcc format (0x%08x).\n", + f->fmt.pix.pixelformat); + + /* Just get the first one enumerated */ + fmt = port->active_fmt[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + /* re-evaluate the csc_direction here */ + csc_direction = vip_csc_direction(fmt->code, + fmt->finfo); + } else { + v4l2_dbg(3, debug, &dev->v4l2_dev, "CSC active on Port %c: going %s\n", + port->port_id == VIP_PORTA ? 'A' : 'B', + (csc_direction == VIP_CSC_Y2R) ? "Y2R" : "R2Y"); + } + } + + /* + * Given that sensors might support multiple mbus code we need + * to use the one that matches the requested pixel format + */ + port->try_mbus_framefmt = port->mbus_framefmt; + port->try_mbus_framefmt.code = fmt->code; + + /* check for/find a valid width/height */ + ret = 0; + found = false; + best_width = 0; + best_height = 0; + largest_width = 0; + largest_height = 0; + + fse.code = fmt->code; + for (fse.index = 0; ; fse.index++) { + u32 bpp = fmt->vpdma_fmt[0]->depth >> 3; + + ret = v4l2_subdev_call(port->subdev, pad, + enum_frame_size, NULL, &fse); + if (ret) + break; + + if (!vip_is_size_dma_aligned(bpp, fse.max_width)) + continue; + + if (fse.max_width >= largest_width && + fse.max_height >= largest_height) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new larger: %dx%d\n", + fse.index, fse.max_width, fse.max_height); + largest_width = fse.max_width; + largest_height = fse.max_height; + } + + if (fse.max_width >= f->fmt.pix.width && + fse.max_height >= f->fmt.pix.height) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found at least larger: %dx%d\n", + fse.index, fse.max_width, fse.max_height); + + if (!best_width || + ((abs(best_width - f->fmt.pix.width) >= + abs(fse.max_width - f->fmt.pix.width)) && + (abs(best_height - f->fmt.pix.height) >= + abs(fse.max_height - f->fmt.pix.height)))) { + best_width = fse.max_width; + best_height = fse.max_height; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found new best: %dx%d\n", + fse.index, fse.max_width, + fse.max_height); + } + } + + if (f->fmt.pix.width == fse.max_width && + f->fmt.pix.height == fse.max_height) { + found = true; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct match: %dx%d\n", + fse.index, fse.max_width, + fse.max_height); + break; + } + + if (f->fmt.pix.width >= fse.min_width && + f->fmt.pix.width <= fse.max_width && + f->fmt.pix.height >= fse.min_height && + f->fmt.pix.height <= fse.max_height) { + found = true; + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt loop:%d found direct range match: %dx%d\n", + fse.index, fse.max_width, + fse.max_height); + break; + } + } + + if (found) { + port->try_mbus_framefmt.width = f->fmt.pix.width; + port->try_mbus_framefmt.height = f->fmt.pix.height; + /* No need to check for scaling */ + goto calc_size; + } else if (f->fmt.pix.width > largest_width) { + port->try_mbus_framefmt.width = largest_width; + port->try_mbus_framefmt.height = largest_height; + } else if (best_width) { + port->try_mbus_framefmt.width = best_width; + port->try_mbus_framefmt.height = best_height; + } else { + /* use existing values as default */ + } + + v4l2_dbg(3, debug, &dev->v4l2_dev, "try_fmt best subdev size: %dx%d\n", + port->try_mbus_framefmt.width, + port->try_mbus_framefmt.height); + + if (is_scaler_available(port) && + csc_direction != VIP_CSC_Y2R && + !vip_is_mbuscode_raw(fmt->code) && + f->fmt.pix.height <= port->try_mbus_framefmt.height && + port->try_mbus_framefmt.height <= SC_MAX_PIXEL_HEIGHT && + port->try_mbus_framefmt.width <= SC_MAX_PIXEL_WIDTH) { + /* + * Scaler is only accessible if the dst colorspace is YUV. + * As the input to the scaler must be in YUV mode only. + * + * Scaling up is allowed only horizontally. + */ + unsigned int hratio, vratio, width_align, height_align; + u32 bpp = fmt->vpdma_fmt[0]->depth >> 3; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler active on Port %c: requesting %dx%d\n", + port->port_id == VIP_PORTA ? 'A' : 'B', + f->fmt.pix.width, f->fmt.pix.height); + + /* Just make sure everything is properly aligned */ + width_align = ALIGN(f->fmt.pix.width * bpp, VPDMA_STRIDE_ALIGN); + width_align /= bpp; + height_align = ALIGN(f->fmt.pix.height, 2); + + f->fmt.pix.width = width_align; + f->fmt.pix.height = height_align; + + hratio = f->fmt.pix.width * 1000 / + port->try_mbus_framefmt.width; + vratio = f->fmt.pix.height * 1000 / + port->try_mbus_framefmt.height; + if (hratio < 125) { + f->fmt.pix.width = port->try_mbus_framefmt.width / 8; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Horizontal scaling ratio out of range adjusting -> %d\n", + f->fmt.pix.width); + } + + if (vratio < 188) { + f->fmt.pix.height = port->try_mbus_framefmt.height / 4; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Vertical scaling ratio out of range adjusting -> %d\n", + f->fmt.pix.height); + } + v4l2_dbg(3, debug, &dev->v4l2_dev, "Scaler: got %dx%d\n", + f->fmt.pix.width, f->fmt.pix.height); + } else { + /* use existing values as default */ + f->fmt.pix.width = port->try_mbus_framefmt.width; + f->fmt.pix.height = port->try_mbus_framefmt.height; + } + +calc_size: + /* That we have a fmt calculate imagesize and bytesperline */ + return vip_calc_format_size(port, fmt, f); +} + +static int vip_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + + /* Use last known values or defaults */ + f->fmt.pix.width = stream->width; + f->fmt.pix.height = stream->height; + f->fmt.pix.pixelformat = port->fmt->fourcc; + f->fmt.pix.field = stream->sup_field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + f->fmt.pix.bytesperline = stream->bytesperline; + f->fmt.pix.sizeimage = stream->sizeimage; + + return 0; +} + +static int vip_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct v4l2_subdev_format sfmt; + struct v4l2_mbus_framefmt *mf; + enum vip_csc_state csc_direction; + int ret; + + ret = vip_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + if (vb2_is_busy(&stream->vb_vidq)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + /* + * Check if we need the scaler or not + * + * Since on previous S_FMT call the scaler might have been + * allocated if it is not needed in this instance we will + * attempt to free it just in case. + * + * free_scaler() is harmless unless the current port + * allocated it. + */ + if (f->fmt.pix.width == port->try_mbus_framefmt.width && + f->fmt.pix.height == port->try_mbus_framefmt.height) + free_scaler(port); + else + allocate_scaler(port); + + port->fmt = find_port_format_by_pix(port, + f->fmt.pix.pixelformat); + stream->width = f->fmt.pix.width; + stream->height = f->fmt.pix.height; + stream->bytesperline = f->fmt.pix.bytesperline; + stream->sizeimage = f->fmt.pix.sizeimage; + stream->sup_field = f->fmt.pix.field; + stream->field = f->fmt.pix.field; + + port->c_rect.left = 0; + port->c_rect.top = 0; + port->c_rect.width = stream->width; + port->c_rect.height = stream->height; + + /* + * Check if we need the csc unit or not + * + * Since on previous S_FMT call, the csc might have been + * allocated if it is not needed in this instance we will + * attempt to free it just in case. + * + * free_csc() is harmless unless the current port + * allocated it. + */ + csc_direction = vip_csc_direction(port->fmt->code, port->fmt->finfo); + if (csc_direction == VIP_CSC_NA) + free_csc(port); + else + allocate_csc(port, csc_direction); + + if (stream->sup_field == V4L2_FIELD_ALTERNATE) + port->flags |= FLAG_INTERLACED; + else + port->flags &= ~FLAG_INTERLACED; + + memset(&sfmt, 0, sizeof(sfmt)); + mf = &sfmt.format; + v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code); + /* Make sure to use the subdev size found in the try_fmt */ + mf->width = port->try_mbus_framefmt.width; + mf->height = port->try_mbus_framefmt.height; + + sfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sfmt.pad = 0; + ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt); + if (ret) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "set_fmt failed in subdev\n"); + return ret; + } + + /* Save it */ + port->mbus_framefmt = *mf; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "s_fmt subdev fmt mbus_code: %04X size: %dx%d\n", + port->mbus_framefmt.code, + port->mbus_framefmt.width, port->mbus_framefmt.height); + + return 0; +} + +static void vip_unset_csc_y2r(struct vip_dev *dev, struct vip_port *port) +{ + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } +} + +static void vip_unset_csc_r2y(struct vip_dev *dev, struct vip_port *port) +{ + if (port->port_id != VIP_PORTA) { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + return; + } + + if (port->scaler && port->fmt->coplanar) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else if (port->scaler) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else if (port->fmt->coplanar) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } +} + +static void vip_unset_rgb(struct vip_dev *dev, struct vip_port *port) +{ + if (port->port_id != VIP_PORTA) { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + return; + } + + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); +} + +static void vip_unset_yuv(struct vip_dev *dev, struct vip_port *port) +{ + if (port->scaler && port->fmt->coplanar) { + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + } + } else if (port->scaler) { + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } + } else if (port->fmt->coplanar) { + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + } else { + /* + * We undo all data path setting except for the multi + * stream case. + * Because we cannot disrupt other on-going capture if only + * one stream is terminated the other might still be going + */ + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } +} + +/* + * Does the exact opposite of set_fmt_params + * It makes sure the DataPath register is sane after tear down + */ +static void unset_fmt_params(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + + stream->sequence = 0; + stream->field = V4L2_FIELD_TOP; + + /* Undo CSC Y2R routing */ + if (port->csc == VIP_CSC_Y2R) { + vip_unset_csc_y2r(dev, port); + /* Undo CSC R2Y routing */ + } else if (port->csc == VIP_CSC_R2Y) { + vip_unset_csc_r2y(dev, port); + /* Undo RGB output routing (no CSC) */ + } else if (v4l2_is_format_rgb(port->fmt->finfo)) { + vip_unset_rgb(dev, port); + /* Undo YUV routing with no CSC */ + } else { + vip_unset_yuv(dev, port); + } +} + +static void vip_config_csc_y2r(struct vip_dev *dev, struct vip_port *port) +{ + port->flags &= ~FLAG_MULT_PORT; + + /* Set alpha component in background color */ + vpdma_set_bg_color(dev->shared->vpdma, + (struct vpdma_data_format *) + port->fmt->vpdma_fmt[0], + 0xff); + + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_SRC_DATA_SELECT, 1); + } else { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1); + } +} + +static void vip_config_csc_r2y(struct vip_dev *dev, struct vip_port *port) +{ + if (port->port_id != VIP_PORTA) { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + return; + } + + port->flags &= ~FLAG_MULT_PORT; + + if (port->scaler && port->fmt->coplanar) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else if (port->scaler) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else if (port->fmt->coplanar) { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_CSC_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } +} + +static void vip_config_rgb(struct vip_dev *dev, struct vip_port *port) +{ + if (port->port_id != VIP_PORTA) { + v4l2_err(&dev->v4l2_dev, "RGB sensor can only be on Port A\n"); + return; + } + + port->flags &= ~FLAG_MULT_PORT; + + /* Set alpha component in background color */ + vpdma_set_bg_color(dev->shared->vpdma, + (struct vpdma_data_format *) + port->fmt->vpdma_fmt[0], + 0xff); + + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 1); +} + +static void vip_config_yuv(struct vip_dev *dev, struct vip_port *port) +{ + if (port->scaler && port->fmt->coplanar) { + port->flags &= ~FLAG_MULT_PORT; + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + } + } else if (port->scaler) { + port->flags &= ~FLAG_MULT_PORT; + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 2); + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_SC_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } + } else if (port->fmt->coplanar) { + port->flags &= ~FLAG_MULT_PORT; + if (port->port_id == VIP_PORTA) { + vip_set_slice_path(dev, VIP_CHR_DS_1_SRC_DATA_SELECT, 3); + vip_set_slice_path(dev, VIP_CHR_DS_1_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_HI_DATA_SELECT, 0); + } else { + vip_set_slice_path(dev, VIP_CHR_DS_2_SRC_DATA_SELECT, 4); + vip_set_slice_path(dev, VIP_CHR_DS_2_DATA_BYPASS, 0); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 0); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } + } else { + port->flags |= FLAG_MULT_PORT; + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT, 0); + } +} + +/* + * Set the registers that are modified when the video format changes. + */ +static void set_fmt_params(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + + stream->sequence = 0; + stream->field = V4L2_FIELD_TOP; + + /* YUV input, RGB output using CSC (Y2R) */ + if (port->csc == VIP_CSC_Y2R) { + vip_config_csc_y2r(dev, port); + /* RGB input, YUV output using CSC (R2Y) */ + } else if (port->csc == VIP_CSC_R2Y) { + vip_config_csc_r2y(dev, port); + /* RGB output without CSC */ + } else if (v4l2_is_format_rgb(port->fmt->finfo)) { + vip_config_rgb(dev, port); + /* YUV output without CSC */ + } else { + vip_config_yuv(dev, port); + } +} + +static int vip_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + struct vip_stream *stream = file2stream(file); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = stream->width; + s->r.height = stream->height; + return 0; + + case V4L2_SEL_TGT_CROP: + s->r = stream->port->c_rect; + return 0; + } + + return -EINVAL; +} + +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int vip_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct vip_stream *stream = file2stream(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct v4l2_rect r = s->r; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (vb2_is_busy(&stream->vb_vidq)) + return -EBUSY; + + v4l_bound_align_image(&r.width, 0, stream->width, 0, + &r.height, 0, stream->height, 0, 0); + + r.left = clamp_t(unsigned int, r.left, 0, stream->width - r.width); + r.top = clamp_t(unsigned int, r.top, 0, stream->height - r.height); + + if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + return -ERANGE; + + s->r = r; + stream->port->c_rect = r; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "cropped (%d,%d)/%dx%d of %dx%d\n", + r.left, r.top, r.width, r.height, + stream->width, stream->height); + + return 0; +} + +static const struct v4l2_ioctl_ops vip_ioctl_ops = { + .vidioc_querycap = vip_querycap, + .vidioc_enum_input = vip_enuminput, + .vidioc_g_input = vip_g_input, + .vidioc_s_input = vip_s_input, + + .vidioc_querystd = vip_querystd, + .vidioc_g_std = vip_g_std, + .vidioc_s_std = vip_s_std, + + .vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vip_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vip_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vip_s_fmt_vid_cap, + + .vidioc_enum_frameintervals = vip_enum_frameintervals, + .vidioc_enum_framesizes = vip_enum_framesizes, + .vidioc_s_parm = vip_s_parm, + .vidioc_g_parm = vip_g_parm, + .vidioc_g_selection = vip_g_selection, + .vidioc_s_selection = vip_s_selection, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Videobuf operations + */ +static int vip_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct vip_stream *stream = vb2_get_drv_priv(vq); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + unsigned int size = stream->sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "get %d buffer(s) of size %d each.\n", + *nbuffers, sizes[0]); + + return 0; +} + +static int vip_buf_prepare(struct vb2_buffer *vb) +{ + struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + + if (vb2_plane_size(vb, 0) < stream->sizeimage) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)stream->sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, stream->sizeimage); + + return 0; +} + +static void vip_buf_queue(struct vb2_buffer *vb) +{ + struct vip_stream *stream = vb2_get_drv_priv(vb->vb2_queue); + struct vip_dev *dev = stream->port->dev; + struct vip_buffer *buf = container_of(vb, struct vip_buffer, + vb.vb2_buf); + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &stream->vidq); + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void return_buffers(struct vb2_queue *vq, int state) +{ + struct vip_stream *stream = vb2_get_drv_priv(vq); + struct vip_dev *dev = stream->port->dev; + struct vip_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&dev->slock, flags); + + /* release all active buffers */ + while (!list_empty(&stream->post_bufs)) { + buf = list_entry(stream->post_bufs.next, + struct vip_buffer, list); + list_del(&buf->list); + if (buf->drop == 1) + list_add_tail(&buf->list, &stream->dropq); + else + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + while (!list_empty(&stream->vidq)) { + buf = list_entry(stream->vidq.next, struct vip_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + INIT_LIST_HEAD(&stream->post_bufs); + INIT_LIST_HEAD(&stream->vidq); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +static int vip_setup_scaler(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct sc_data *sc = dev->sc; + struct csc_data *csc = dev->csc; + struct vpdma_data *vpdma = dev->shared->vpdma; + struct vip_mmr_adb *mmr_adb = port->mmr_adb.addr; + int list_num = stream->list_num; + int timeout = 500; + struct v4l2_format dst_f; + struct v4l2_format src_f; + + memset(&src_f, 0, sizeof(src_f)); + src_f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_fill_pix_format(&src_f.fmt.pix, &port->mbus_framefmt); + src_f.fmt.pix.pixelformat = vip_mbus_code_to_fourcc(port->fmt->code); + + dst_f = src_f; + dst_f.fmt.pix.pixelformat = port->fmt->fourcc; + dst_f.fmt.pix.width = stream->width; + dst_f.fmt.pix.height = stream->height; + + /* if scaler not associated with this port then skip */ + if (port->scaler) { + sc_set_hs_coeffs(sc, port->sc_coeff_h.addr, + port->mbus_framefmt.width, + port->c_rect.width); + sc_set_vs_coeffs(sc, port->sc_coeff_v.addr, + port->mbus_framefmt.height, + port->c_rect.height); + sc_config_scaler(sc, &mmr_adb->sc_regs0[0], + &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], + port->mbus_framefmt.width, + port->mbus_framefmt.height, + port->c_rect.width, + port->c_rect.height); + port->load_mmrs = true; + } + + /* if csc not associated with this port then skip */ + if (port->csc) { + csc_set_coeff(csc, &mmr_adb->csc_regs[0], + &src_f, &dst_f); + + port->load_mmrs = true; + } + + /* If coeff are already loaded then skip */ + if (!sc->load_coeff_v && !sc->load_coeff_h && !port->load_mmrs) + return 0; + + if (vpdma_list_busy(vpdma, list_num)) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: List %d is busy\n", + __func__, list_num); + } + + /* Make sure we start with a clean list */ + vpdma_reset_desc_list(&stream->desc_list); + + /* config descriptors */ + if (port->load_mmrs) { + vpdma_map_desc_buf(vpdma, &port->mmr_adb); + vpdma_add_cfd_adb(&stream->desc_list, CFD_MMR_CLIENT, + &port->mmr_adb); + + port->load_mmrs = false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added mmr_adb config desc\n"); + } + + if (sc->loaded_coeff_h != port->sc_coeff_h.dma_addr || + sc->load_coeff_h) { + vpdma_map_desc_buf(vpdma, &port->sc_coeff_h); + vpdma_add_cfd_block(&stream->desc_list, + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, + &port->sc_coeff_h, 0); + + sc->loaded_coeff_h = port->sc_coeff_h.dma_addr; + sc->load_coeff_h = false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_h config desc\n"); + } + + if (sc->loaded_coeff_v != port->sc_coeff_v.dma_addr || + sc->load_coeff_v) { + vpdma_map_desc_buf(vpdma, &port->sc_coeff_v); + vpdma_add_cfd_block(&stream->desc_list, + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, + &port->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + + sc->loaded_coeff_v = port->sc_coeff_v.dma_addr; + sc->load_coeff_v = false; + v4l2_dbg(3, debug, &dev->v4l2_dev, "Added sc_coeff_v config desc\n"); + } + v4l2_dbg(3, debug, stream, "CFD_SC_CLIENT %d slice_id: %d\n", + VIP_SLICE1_CFD_SC_CLIENT + dev->slice_id, dev->slice_id); + + vpdma_map_desc_buf(vpdma, &stream->desc_list.buf); + v4l2_dbg(3, debug, &dev->v4l2_dev, "Submitting desc on list# %d\n", list_num); + vpdma_submit_descs(vpdma, &stream->desc_list, list_num); + + while (vpdma_list_busy(vpdma, list_num) && timeout--) + usleep_range(1000, 1100); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->mmr_adb); + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_h); + vpdma_unmap_desc_buf(dev->shared->vpdma, &port->sc_coeff_v); + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + + vpdma_reset_desc_list(&stream->desc_list); + + if (timeout <= 0) { + v4l2_err(&dev->v4l2_dev, "Timed out setting up scaler through VPDMA list\n"); + return -EBUSY; + } + + return 0; +} + +static int vip_load_vpdma_list_fifo(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vpdma_data *vpdma = dev->shared->vpdma; + int list_num = stream->list_num; + struct vip_buffer *buf; + unsigned long flags; + int timeout, i; + + if (vpdma_list_busy(dev->shared->vpdma, stream->list_num)) + return -EBUSY; + + for (i = 0; i < VIP_VPDMA_FIFO_SIZE; i++) { + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&stream->vidq)) { + v4l2_err(&dev->v4l2_dev, "No buffer left!"); + spin_unlock_irqrestore(&dev->slock, flags); + return -EINVAL; + } + + buf = list_entry(stream->vidq.next, + struct vip_buffer, list); + buf->drop = false; + + list_move_tail(&buf->list, &stream->post_bufs); + spin_unlock_irqrestore(&dev->slock, flags); + + v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: start_dma vb2 buf idx:%d\n", + __func__, buf->vb.vb2_buf.index); + start_dma(stream, buf); + + timeout = 500; + while (vpdma_list_busy(vpdma, list_num) && timeout--) + usleep_range(1000, 1100); + + if (timeout <= 0) { + v4l2_err(&dev->v4l2_dev, "Timed out loading VPDMA list fifo\n"); + return -EBUSY; + } + } + return 0; +} + +static int vip_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vip_stream *stream = vb2_get_drv_priv(vq); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + int ret; + + ret = vip_setup_scaler(stream); + if (ret) + goto err; + + /* + * Make sure the scaler is configured before the datapath is + * enabled. The scaler can only load the coefficient + * parameters when it is idle. If the scaler path is enabled + * and video data is being received then the VPDMA transfer will + * stall indefinitely. + */ + set_fmt_params(stream); + ret = vip_setup_parser(port); + if (ret) + goto err; + + if (port->subdev) { + ret = v4l2_subdev_call(port->subdev, video, s_stream, 1); + if (ret) { + v4l2_err(&dev->v4l2_dev, "stream on failed in subdev\n"); + goto err; + } + } + + stream->sequence = 0; + stream->field = V4L2_FIELD_TOP; + populate_desc_list(stream); + + ret = vip_load_vpdma_list_fifo(stream); + if (ret) + goto err; + + stream->num_recovery = 0; + + clear_irqs(dev, dev->slice_id, stream->list_num); + enable_irqs(dev, dev->slice_id, stream->list_num); + vip_schedule_next_buffer(stream); + vip_parser_stop_imm(port, false); + vip_enable_parser(port, true); + + return 0; + +err: + return_buffers(vq, VB2_BUF_STATE_QUEUED); + return ret; +} + +/* + * Abort streaming and wait for last buffer + */ +static void vip_stop_streaming(struct vb2_queue *vq) +{ + struct vip_stream *stream = vb2_get_drv_priv(vq); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + int ret; + + vip_parser_stop_imm(port, true); + vip_enable_parser(port, false); + unset_fmt_params(stream); + + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + + if (port->subdev) { + ret = v4l2_subdev_call(port->subdev, video, s_stream, 0); + if (ret) + v4l2_err(&dev->v4l2_dev, "Failed to stop subdev stream"); + } + + stop_dma(stream, true); + + return_buffers(vq, VB2_BUF_STATE_ERROR); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + vpdma_reset_desc_list(&stream->desc_list); +} + +static const struct vb2_ops vip_video_qops = { + .queue_setup = vip_queue_setup, + .buf_prepare = vip_buf_prepare, + .buf_queue = vip_buf_queue, + .start_streaming = vip_start_streaming, + .stop_streaming = vip_stop_streaming, +}; + +static int vip_init_dev(struct vip_dev *dev) +{ + if (dev->num_ports != 0) + goto done; + + vip_set_clock_enable(dev, 1); + vip_module_toggle(dev, VIP_SC_RST, false); + vip_module_toggle(dev, VIP_CSC_RST, false); +done: + dev->num_ports++; + + return 0; +} + +static inline bool is_scaler_available(struct vip_port *port) +{ + if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL) + if (port->dev->sc_assigned == VIP_NOT_ASSIGNED || + port->dev->sc_assigned == port->port_id) + return true; + return false; +} + +static inline bool allocate_scaler(struct vip_port *port) +{ + if (is_scaler_available(port)) { + if (port->dev->sc_assigned == VIP_NOT_ASSIGNED || + port->dev->sc_assigned == port->port_id) { + port->dev->sc_assigned = port->port_id; + port->scaler = true; + return true; + } + } + return false; +} + +static inline void free_scaler(struct vip_port *port) +{ + if (port->dev->sc_assigned == port->port_id) { + port->dev->sc_assigned = VIP_NOT_ASSIGNED; + port->scaler = false; + } +} + +static bool is_csc_available(struct vip_port *port) +{ + if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL) + if (port->dev->csc_assigned == VIP_NOT_ASSIGNED || + port->dev->csc_assigned == port->port_id) + return true; + return false; +} + +static bool allocate_csc(struct vip_port *port, + enum vip_csc_state csc_direction) +{ + struct vip_dev *dev = port->dev; + /* Is CSC needed? */ + if (csc_direction != VIP_CSC_NA) { + if (is_csc_available(port)) { + port->dev->csc_assigned = port->port_id; + port->csc = csc_direction; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc allocated: dir: %d\n", + __func__, csc_direction); + return true; + } + } + return false; +} + +static void free_csc(struct vip_port *port) +{ + struct vip_dev *dev = port->dev; + + if (port->dev->csc_assigned == port->port_id) { + port->dev->csc_assigned = VIP_NOT_ASSIGNED; + port->csc = VIP_CSC_NA; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: csc freed\n", + __func__); + } +} + +static int vip_init_port(struct vip_port *port) +{ + struct vip_dev *dev = port->dev; + int ret; + struct vip_fmt *fmt; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + + if (port->num_streams != 0) + goto done; + + ret = vip_init_dev(port->dev); + if (ret) + goto done; + + /* Get subdevice current frame format */ + ret = v4l2_subdev_call(port->subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) + v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port get_fmt failed in subdev: (%d)\n", + ret); + + /* try to find one that matches */ + fmt = find_port_format_by_code(port, mbus_fmt->code); + if (!fmt) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "subdev default mbus_fmt %04x is not matched.\n", + mbus_fmt->code); + /* if all else fails just pick the first one */ + fmt = port->active_fmt[0]; + + mbus_fmt->code = fmt->code; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + ret = v4l2_subdev_call(port->subdev, pad, set_fmt, + NULL, &sd_fmt); + if (ret) + v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port set_fmt failed in subdev: (%d)\n", + ret); + } + + /* Assign current format */ + port->fmt = fmt; + port->mbus_framefmt = *mbus_fmt; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: g_mbus_fmt subdev mbus_code: %04X fourcc:%s size: %dx%d\n", + __func__, fmt->code, + fourcc_to_str(fmt->fourcc), + mbus_fmt->width, mbus_fmt->height); + + if (mbus_fmt->field == V4L2_FIELD_ALTERNATE) + port->flags |= FLAG_INTERLACED; + else + port->flags &= ~FLAG_INTERLACED; + + port->c_rect.left = 0; + port->c_rect.top = 0; + port->c_rect.width = mbus_fmt->width; + port->c_rect.height = mbus_fmt->height; + + ret = vpdma_alloc_desc_buf(&port->sc_coeff_h, SC_COEF_SRAM_SIZE); + if (ret != 0) + return ret; + + ret = vpdma_alloc_desc_buf(&port->sc_coeff_v, SC_COEF_SRAM_SIZE); + if (ret != 0) + goto free_sc_h; + + ret = vpdma_alloc_desc_buf(&port->mmr_adb, sizeof(struct vip_mmr_adb)); + if (ret != 0) + goto free_sc_v; + + init_adb_hdrs(port); + + vip_enable_parser(port, false); +done: + port->num_streams++; + return 0; + +free_sc_v: + vpdma_free_desc_buf(&port->sc_coeff_v); +free_sc_h: + vpdma_free_desc_buf(&port->sc_coeff_h); + return ret; +} + +static int vip_init_stream(struct vip_stream *stream) +{ + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + struct vip_fmt *fmt; + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_format f; + int ret; + + ret = vip_init_port(port); + if (ret != 0) + return ret; + + fmt = port->fmt; + mbus_fmt = &port->mbus_framefmt; + + memset(&f, 0, sizeof(f)); + + /* Properly calculate the sizeimage and bytesperline values. */ + v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt); + f.fmt.pix.pixelformat = fmt->fourcc; + ret = vip_calc_format_size(port, fmt, &f); + if (ret) + return ret; + + stream->width = f.fmt.pix.width; + stream->height = f.fmt.pix.height; + stream->sup_field = f.fmt.pix.field; + stream->bytesperline = f.fmt.pix.bytesperline; + stream->sizeimage = f.fmt.pix.sizeimage; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream fourcc:%s size: %dx%d bpl:%d img_size:%d\n", + fourcc_to_str(f.fmt.pix.pixelformat), + f.fmt.pix.width, f.fmt.pix.height, + f.fmt.pix.bytesperline, f.fmt.pix.sizeimage); + v4l2_dbg(3, debug, &dev->v4l2_dev, "init_stream vpdma data type: 0x%02X\n", + port->fmt->vpdma_fmt[0]->data_type); + + ret = vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE, + VPDMA_LIST_TYPE_NORMAL); + + if (ret != 0) + return ret; + + stream->write_desc = (struct vpdma_dtd *)stream->desc_list.buf.addr + + 15; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n", + __func__, &stream); + + return 0; +} + +static void vip_release_dev(struct vip_dev *dev) +{ + /* + * On last close, disable clocks to conserve power + */ + + if (--dev->num_ports == 0) { + /* reset the scaler module */ + vip_module_toggle(dev, VIP_SC_RST, true); + vip_module_toggle(dev, VIP_CSC_RST, true); + vip_set_clock_enable(dev, 0); + } +} + +static int vip_set_crop_parser(struct vip_port *port) +{ + struct vip_dev *dev = port->dev; + struct vip_parser_data *parser = dev->parser; + u32 hcrop = 0, vcrop = 0; + u32 width = port->mbus_framefmt.width; + + if (port->fmt->vpdma_fmt[0] == &vpdma_raw_fmts[VPDMA_DATA_FMT_RAW8]) { + /* + * Special case since we are faking a YUV422 16bit format + * to have the vpdma perform the needed byte swap + * we need to adjust the pixel width accordingly + * otherwise the parser will attempt to collect more pixels + * then available and the vpdma transfer will exceed the + * allocated frame buffer. + */ + width >>= 1; + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: 8 bit raw detected, adjusting width to %d\n", + __func__, width); + } + + /* + * Set Parser Crop parameters to source size otherwise + * scaler and colorspace converter will yield garbage. + */ + hcrop = VIP_ACT_BYPASS; + insert_field(&hcrop, 0, VIP_ACT_SKIP_NUMPIX_MASK, + VIP_ACT_SKIP_NUMPIX_SHFT); + insert_field(&hcrop, width, + VIP_ACT_USE_NUMPIX_MASK, VIP_ACT_USE_NUMPIX_SHFT); + reg_write(parser, VIP_PARSER_CROP_H_PORT(port->port_id), hcrop); + + insert_field(&vcrop, 0, VIP_ACT_SKIP_NUMLINES_MASK, + VIP_ACT_SKIP_NUMLINES_SHFT); + insert_field(&vcrop, port->mbus_framefmt.height, + VIP_ACT_USE_NUMLINES_MASK, VIP_ACT_USE_NUMLINES_SHFT); + reg_write(parser, VIP_PARSER_CROP_V_PORT(port->port_id), vcrop); + + return 0; +} + +static int vip_setup_parser(struct vip_port *port) +{ + struct vip_dev *dev = port->dev; + struct vip_parser_data *parser = dev->parser; + struct v4l2_fwnode_endpoint *endpoint = &port->endpoint; + int iface, sync_type; + u32 flags = 0, config0; + + /* Reset the port */ + vip_reset_parser(port, true); + usleep_range(200, 250); + vip_reset_parser(port, false); + + config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (endpoint->bus_type == V4L2_MBUS_BT656) { + flags = endpoint->bus.parallel.flags; + iface = DUAL_8B_INTERFACE; + + /* + * Ideally, this should come from subdev + * port->fmt can be anything once CSC is enabled + */ + if (vip_is_mbuscode_rgb(port->fmt->code)) + sync_type = EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444; + else + sync_type = EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422; + + } else if (endpoint->bus_type == V4L2_MBUS_PARALLEL) { + flags = endpoint->bus.parallel.flags; + + switch (endpoint->bus.parallel.bus_width) { + case 24: + iface = SINGLE_24B_INTERFACE; + break; + case 16: + iface = SINGLE_16B_INTERFACE; + break; + case 8: + default: + iface = DUAL_8B_INTERFACE; + } + + if (vip_is_mbuscode_rgb(port->fmt->code)) + sync_type = DISCRETE_SYNC_SINGLE_RGB_24B; + else + sync_type = DISCRETE_SYNC_SINGLE_YUV422; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + config0 |= VIP_HSYNC_POLARITY; + else if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + config0 &= ~VIP_HSYNC_POLARITY; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + config0 |= VIP_VSYNC_POLARITY; + else if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + config0 &= ~VIP_VSYNC_POLARITY; + + config0 &= ~VIP_USE_ACTVID_HSYNC_ONLY; + config0 |= VIP_ACTVID_POLARITY; + config0 |= VIP_DISCRETE_BASIC_MODE; + + } else { + v4l2_err(&dev->v4l2_dev, "Device doesn't support CSI2"); + return -EINVAL; + } + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) { + vip_set_pclk_invert(port); + config0 |= VIP_PIXCLK_EDGE_POLARITY; + } else { + config0 &= ~VIP_PIXCLK_EDGE_POLARITY; + } + + config0 |= ((sync_type & VIP_SYNC_TYPE_MASK) << VIP_SYNC_TYPE_SHFT); + + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); + + vip_set_data_interface(port, iface); + vip_set_crop_parser(port); + + return 0; +} + +static void vip_enable_parser(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev = port->dev; + struct vip_parser_data *parser = dev->parser; + + config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (on) { + config0 |= VIP_PORT_ENABLE; + config0 &= ~(VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR); + } else { + config0 &= ~VIP_PORT_ENABLE; + config0 |= (VIP_ASYNC_FIFO_RD | VIP_ASYNC_FIFO_WR); + } + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); +} + +static void vip_reset_parser(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev = port->dev; + struct vip_parser_data *parser = dev->parser; + + config0 = reg_read(parser, VIP_PARSER_PORT(port->port_id)); + + if (on) + config0 |= VIP_SW_RESET; + else + config0 &= ~VIP_SW_RESET; + + reg_write(parser, VIP_PARSER_PORT(port->port_id), config0); +} + +static void vip_parser_stop_imm(struct vip_port *port, bool on) +{ + u32 config0; + struct vip_dev *dev = port->dev; + struct vip_parser_data *parser = dev->parser; + + config0 = reg_read(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id)); + + if (on) + config0 = 0xffffffff; + else + config0 = 0; + + reg_write(parser, VIP_PARSER_STOP_IMM_PORT(port->port_id), config0); +} + +static void vip_release_stream(struct vip_stream *stream) +{ + struct vip_dev *dev = stream->port->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: stream instance %pa\n", + __func__, &stream); + + vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf); + vpdma_free_desc_buf(&stream->desc_list.buf); + vpdma_free_desc_list(&stream->desc_list); +} + +static void vip_release_port(struct vip_port *port) +{ + struct vip_dev *dev = port->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: port instance %pa\n", + __func__, &port); + + vpdma_free_desc_buf(&port->mmr_adb); + vpdma_free_desc_buf(&port->sc_coeff_h); + vpdma_free_desc_buf(&port->sc_coeff_v); +} + +static void stop_dma(struct vip_stream *stream, bool clear_list) +{ + struct vip_dev *dev = stream->port->dev; + int ch, size = 0; + + /* Create a list of channels to be cleared */ + for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) { + if (stream->vpdma_channels[ch] == 1) { + stream->vpdma_channels_to_abort[size++] = ch; + v4l2_dbg(2, debug, &dev->v4l2_dev, "Clear channel no: %d\n", ch); + } + } + + /* Clear all the used channels for the list */ + vpdma_list_cleanup(dev->shared->vpdma, stream->list_num, + stream->vpdma_channels_to_abort, size); + + if (clear_list) + for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) + stream->vpdma_channels[ch] = 0; +} + +static int vip_open(struct file *file) +{ + struct vip_stream *stream = video_drvdata(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + int ret = 0; + + mutex_lock(&dev->mutex); + + ret = v4l2_fh_open(file); + if (ret) { + v4l2_err(&dev->v4l2_dev, "v4l2_fh_open failed\n"); + goto unlock; + } + + /* + * If this is the first open file. + * Then initialize hw module. + */ + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (vip_init_stream(stream)) + ret = -ENODEV; +unlock: + mutex_unlock(&dev->mutex); + return ret; +} + +static int vip_release(struct file *file) +{ + struct vip_stream *stream = video_drvdata(file); + struct vip_port *port = stream->port; + struct vip_dev *dev = port->dev; + bool fh_singular; + int ret; + + mutex_lock(&dev->mutex); + + /* Save the singular status before we call the clean-up helper */ + fh_singular = v4l2_fh_is_singular_file(file); + + /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + + free_csc(port); + free_scaler(port); + + /* + * If this is the last open file. + * Then de-initialize hw module. + */ + if (fh_singular) { + vip_release_stream(stream); + + if (--port->num_streams == 0) { + vip_release_port(port); + vip_release_dev(port->dev); + } + } + + mutex_unlock(&dev->mutex); + + return ret; +} + +/* + * File operations + */ +static const struct v4l2_file_operations vip_fops = { + .owner = THIS_MODULE, + .open = vip_open, + .release = vip_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +static struct video_device vip_videodev = { + .name = VIP_MODULE_NAME, + .fops = &vip_fops, + .ioctl_ops = &vip_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, + .device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE, +}; + +static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type) +{ + struct vip_stream *stream; + struct vip_dev *dev = port->dev; + struct vb2_queue *q; + struct video_device *vfd; + struct vip_buffer *buf; + struct list_head *pos, *tmp; + int ret, i; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + stream->port = port; + stream->stream_id = stream_id; + stream->vfl_type = vfl_type; + port->cap_streams[stream_id] = stream; + + stream->list_num = vpdma_hwlist_alloc(dev->shared->vpdma, stream); + if (stream->list_num < 0) { + v4l2_err(&dev->v4l2_dev, "Could not get VPDMA hwlist"); + ret = -ENODEV; + goto do_free_stream; + } + + INIT_LIST_HEAD(&stream->post_bufs); + + /* + * Initialize queue + */ + q = &stream->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->drv_priv = stream; + q->buf_struct_size = sizeof(struct vip_buffer); + q->ops = &vip_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->mutex; + q->min_queued_buffers = 2; + q->dev = dev->v4l2_dev.dev; + + ret = vb2_queue_init(q); + if (ret) + goto do_free_hwlist; + + INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work); + + INIT_LIST_HEAD(&stream->vidq); + + /* Allocate/populate Drop queue entries */ + INIT_LIST_HEAD(&stream->dropq); + for (i = 0; i < VIP_DROPQ_SIZE; i++) { + buf = kzalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) { + ret = -ENOMEM; + goto do_free_dropq; + } + buf->drop = true; + list_add(&buf->list, &stream->dropq); + } + + vfd = video_device_alloc(); + if (!vfd) { + ret = -ENOMEM; + goto do_free_dropq; + } + *vfd = vip_videodev; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->queue = q; + + vfd->lock = &dev->mutex; + video_set_drvdata(vfd, stream); + stream->vfd = vfd; + + ret = video_register_device(vfd, vfl_type, -1); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto do_free_vfd; + } + + v4l2_info(&dev->v4l2_dev, "device registered as %s\n", + video_device_node_name(vfd)); + return 0; + +do_free_vfd: + video_device_release(vfd); +do_free_dropq: + list_for_each_safe(pos, tmp, &stream->dropq) { + buf = list_entry(pos, + struct vip_buffer, list); + v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n"); + list_del(pos); + kfree(buf); + } +do_free_hwlist: + vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); +do_free_stream: + kfree(stream); + return ret; +} + +static void free_stream(struct vip_stream *stream) +{ + struct vip_dev *dev; + struct vip_buffer *buf; + struct list_head *pos, *q; + + if (!stream) + return; + + dev = stream->port->dev; + /* Free up the Drop queue */ + list_for_each_safe(pos, q, &stream->dropq) { + buf = list_entry(pos, + struct vip_buffer, list); + v4l2_dbg(1, debug, &dev->v4l2_dev, "dropq buffer\n"); + list_del(pos); + kfree(buf); + } + + video_unregister_device(stream->vfd); + vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); + stream->port->cap_streams[stream->stream_id] = NULL; + kfree(stream); +} + +static int get_subdev_active_format(struct vip_port *port, + struct v4l2_subdev *subdev) +{ + struct vip_fmt *fmt; + struct vip_dev *dev = port->dev; + struct v4l2_subdev_mbus_code_enum mbus_code; + int ret = 0; + unsigned int k, i, j; + enum vip_csc_state csc; + + /* Enumerate sub device formats and enable all matching local formats */ + port->num_active_fmt = 0; + for (k = 0, i = 0; (ret != -EINVAL); k++) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = k; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + continue; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "subdev %s: code: %04x idx: %d\n", + subdev->name, mbus_code.code, k); + + for (j = 0; j < ARRAY_SIZE(vip_formats); j++) { + fmt = &vip_formats[j]; + if (mbus_code.code != fmt->code) + continue; + + /* + * When the port is configured for BT656 + * then none of the downstream unit can be used. + * So here we need to skip all format requiring + * either CSC or CHR_DS + */ + csc = vip_csc_direction(fmt->code, fmt->finfo); + if (port->endpoint.bus_type == V4L2_MBUS_BT656 && + (csc != VIP_CSC_NA || fmt->coplanar)) + continue; + + port->active_fmt[i] = fmt; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "matched fourcc: %s: code: %04x idx: %d\n", + fourcc_to_str(fmt->fourcc), fmt->code, i); + port->num_active_fmt = ++i; + } + } + + if (i == 0) { + v4l2_err(&dev->v4l2_dev, "No suitable format reported by subdev %s\n", + subdev->name); + return -EINVAL; + } + return 0; +} + +static int alloc_port(struct vip_dev *dev, int id) +{ + struct vip_port *port; + + if (dev->ports[id]) + return -EINVAL; + + port = devm_kzalloc(&dev->pdev->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + dev->ports[id] = port; + port->dev = dev; + port->port_id = id; + port->num_streams = 0; + return 0; +} + +static void free_port(struct vip_port *port) +{ + if (!port) + return; + + v4l2_async_nf_unregister(&port->notifier); + v4l2_async_nf_cleanup(&port->notifier); + free_stream(port->cap_streams[0]); +} + +static int get_field(u32 value, u32 mask, int shift) +{ + return (value & (mask << shift)) >> shift; +} + +static int vip_probe_complete(struct platform_device *pdev); +static void vip_vpdma_fw_cb(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "VPDMA firmware loaded\n"); + + if (pdev->dev.of_node) + vip_probe_complete(pdev); +} + +static int vip_create_streams(struct vip_port *port, + struct v4l2_subdev *subdev) +{ + int i; + + for (i = 0; i < VIP_CAP_STREAMS_PER_PORT; i++) + free_stream(port->cap_streams[i]); + + if (get_subdev_active_format(port, subdev)) + return -ENODEV; + + port->subdev = subdev; + + if (port->endpoint.bus_type == V4L2_MBUS_PARALLEL) { + port->flags |= FLAG_MULT_PORT; + port->num_streams_configured = 1; + alloc_stream(port, 0, VFL_TYPE_VIDEO); + } else if (port->endpoint.bus_type == V4L2_MBUS_BT656) { + port->flags |= FLAG_MULT_PORT; + port->num_streams_configured = 1; + alloc_stream(port, 0, VFL_TYPE_VIDEO); + } + return 0; +} + +static int vip_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct vip_port *port = notifier_to_vip_port(notifier); + int ret; + + if (port->subdev) { + v4l2_info(&port->dev->v4l2_dev, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + v4l2_info(&port->dev->v4l2_dev, "Port %c: Using subdev %s for capture\n", + port->port_id == VIP_PORTA ? 'A' : 'B', subdev->name); + + ret = vip_create_streams(port, subdev); + if (ret) + return ret; + + return 0; +} + +static int vip_async_complete(struct v4l2_async_notifier *notifier) +{ + return 0; +} + +static const struct v4l2_async_notifier_operations vip_async_ops = { + .bound = vip_async_bound, + .complete = vip_async_complete, +}; + +static struct fwnode_handle * +fwnode_graph_get_next_endpoint_by_regs(const struct fwnode_handle *fwnode, + int port_reg, int reg) +{ + return of_fwnode_handle(of_graph_get_endpoint_by_regs(to_of_node(fwnode), + port_reg, reg)); +} + +static int vip_register_subdev_notify(struct vip_port *port, + struct fwnode_handle *ep) +{ + struct v4l2_async_notifier *notifier = &port->notifier; + struct fwnode_handle *subdev; + struct v4l2_fwnode_endpoint *vep; + struct v4l2_async_connection *asd; + int ret; + struct vip_dev *dev = port->dev; + + vep = &port->endpoint; + + subdev = fwnode_graph_get_remote_port_parent(ep); + if (!subdev) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "can't get remote parent\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, vep); + if (ret) { + v4l2_dbg(3, debug, &dev->v4l2_dev, "Failed to parse endpoint:\n"); + fwnode_handle_put(subdev); + return -EINVAL; + } + + v4l2_async_nf_init(notifier, &port->dev->shared->v4l2_dev); + + asd = v4l2_async_nf_add_fwnode(notifier, subdev, struct v4l2_async_connection); + if (IS_ERR(asd)) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "Error adding asd\n"); + fwnode_handle_put(subdev); + v4l2_async_nf_cleanup(notifier); + return -EINVAL; + } + + notifier->ops = &vip_async_ops; + ret = v4l2_async_nf_register(notifier); + if (ret) { + v4l2_dbg(1, debug, &dev->v4l2_dev, "Error registering async notifier\n"); + v4l2_async_nf_cleanup(notifier); + ret = -EINVAL; + } + + return ret; +} + +static int vip_endpoint_scan(struct platform_device *pdev) +{ + struct device_node *parent = pdev->dev.of_node; + struct device_node *ep = NULL; + int count = 0, p; + + for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { + ep = of_graph_get_endpoint_by_regs(parent, p, 0); + if (!ep) + continue; + + count++; + of_node_put(ep); + } + + return count; +} + +static int vip_probe_complete(struct platform_device *pdev) +{ + struct vip_shared *shared = platform_get_drvdata(pdev); + struct vip_ctrl_module *ctrl = NULL; + struct vip_port *port; + struct vip_dev *dev; + struct device_node *parent = pdev->dev.of_node; + struct fwnode_handle *ep = NULL; + unsigned int syscon_args[5]; + int ret, i, slice_id, port_id, p; + + /* Allocate ctrl before using it */ + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->syscon_pol = syscon_regmap_lookup_by_phandle_args(parent, "ti,ctrl-module", + 5, syscon_args); + + if (IS_ERR(ctrl->syscon_pol)) + return dev_err_probe(&pdev->dev, PTR_ERR(ctrl->syscon_pol), + "Failed to get ti,ctrl-module\n"); + + ctrl->syscon_offset = syscon_args[0]; + + for (i = 0; i < ARRAY_SIZE(ctrl->syscon_bit_field); i++) + ctrl->syscon_bit_field[i] = syscon_args[i + 1]; + + for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) { + ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent), + p, 0); + if (!ep) + continue; + + switch (p) { + case 0: + slice_id = VIP_SLICE1; + port_id = VIP_PORTA; + break; + case 1: + slice_id = VIP_SLICE2; + port_id = VIP_PORTA; + break; + case 2: + slice_id = VIP_SLICE1; + port_id = VIP_PORTB; + break; + case 3: + slice_id = VIP_SLICE2; + port_id = VIP_PORTB; + break; + default: + dev_err(&pdev->dev, "Unknown port reg=<%d>\n", p); + continue; + } + + ret = alloc_port(shared->devs[slice_id], port_id); + if (ret < 0) + continue; + + dev = shared->devs[slice_id]; + dev->syscon = ctrl; + port = dev->ports[port_id]; + + vip_register_subdev_notify(port, ep); + fwnode_handle_put(ep); + } + return 0; +} + +static int vip_probe_slice(struct platform_device *pdev, int slice) +{ + struct vip_shared *shared = platform_get_drvdata(pdev); + struct vip_dev *dev; + struct vip_parser_data *parser; + struct sc_data *sc; + struct csc_data *csc; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->irq = platform_get_irq(pdev, slice); + if (dev->irq < 0) + return dev->irq; + + ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq, + 0, VIP_MODULE_NAME, dev); + if (ret < 0) + return -ENOMEM; + + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + dev->slice_id = slice; + dev->pdev = pdev; + dev->base = shared->base; + dev->v4l2_dev = shared->v4l2_dev; + + dev->shared = shared; + shared->devs[slice] = dev; + + vip_top_reset(dev); + vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT, 1); + + parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL); + if (!parser) + return PTR_ERR_OR_ZERO(parser); + + parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER); + if (IS_ERR(parser->base)) + return PTR_ERR(parser->base); + + parser->pdev = pdev; + dev->parser = parser; + + dev->sc_assigned = VIP_NOT_ASSIGNED; + sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL); + if (!sc) + return PTR_ERR_OR_ZERO(sc); + + sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC); + if (IS_ERR(sc->base)) + return PTR_ERR(sc->base); + + sc->pdev = pdev; + dev->sc = sc; + + dev->csc_assigned = VIP_NOT_ASSIGNED; + csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL); + if (!csc) + return PTR_ERR_OR_ZERO(csc); + + csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC); + if (IS_ERR(csc->base)) + return PTR_ERR(csc->base); + + csc->pdev = pdev; + dev->csc = csc; + + return 0; +} + +static int vip_probe(struct platform_device *pdev) +{ + struct vip_shared *shared; + int ret, slice = VIP_SLICE1; + u32 tmp, pid; + + /* If there are no endpoint defined there is nothing to do */ + if (!vip_endpoint_scan(pdev)) { + dev_err(&pdev->dev, "%s: No sensor", __func__); + return -ENODEV; + } + + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "32-bit consistent DMA enable failed\n"); + return ret; + } + + shared = devm_kzalloc(&pdev->dev, sizeof(*shared), GFP_KERNEL); + if (!shared) + return -ENOMEM; + + shared->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(shared->base)) + return PTR_ERR(shared->base); + + vip_init_format_info(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_runtime_disable; + + /* Make sure H/W module has the right functionality */ + pid = reg_read(shared, VIP_PID); + tmp = get_field(pid, VIP_PID_FUNC_MASK, VIP_PID_FUNC_SHIFT); + + if (tmp != VIP_PID_FUNC) { + dev_info(&pdev->dev, "vip: unexpected PID function: 0x%x\n", + tmp); + ret = -ENODEV; + goto err_runtime_put; + } + + ret = v4l2_device_register(&pdev->dev, &shared->v4l2_dev); + if (ret) + goto err_runtime_put; + + /* enable clocks, so the firmware will load properly */ + vip_shared_set_clock_enable(shared, 1); + vip_top_vpdma_reset(shared); + + platform_set_drvdata(pdev, shared); + + v4l2_ctrl_handler_init(&shared->ctrl_handler, 11); + shared->v4l2_dev.ctrl_handler = &shared->ctrl_handler; + + for (slice = VIP_SLICE1; slice < VIP_NUM_SLICES; slice++) { + ret = vip_probe_slice(pdev, slice); + if (ret) { + dev_err(&pdev->dev, "Creating slice failed"); + goto err_dev_unreg; + } + } + + shared->vpdma = &shared->vpdma_data; + + shared->vpdma->pdev = pdev; + shared->vpdma->cb = vip_vpdma_fw_cb; + spin_lock_init(&shared->vpdma->lock); + + shared->vpdma->base = shared->base + VIP_VPDMA_BASE; + if (!shared->vpdma->base) { + dev_err(&pdev->dev, "failed to ioremap\n"); + ret = -ENOMEM; + goto err_dev_unreg; + } + + ret = vpdma_load_firmware(shared->vpdma); + if (ret) { + dev_err(&pdev->dev, "Creating VPDMA failed"); + goto err_dev_unreg; + } + + return 0; + +err_dev_unreg: + v4l2_ctrl_handler_free(&shared->ctrl_handler); + v4l2_device_unregister(&shared->v4l2_dev); +err_runtime_put: + pm_runtime_put_sync(&pdev->dev); +err_runtime_disable: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void vip_remove(struct platform_device *pdev) +{ + struct vip_shared *shared = platform_get_drvdata(pdev); + struct vip_dev *dev; + int slice; + + for (slice = 0; slice < VIP_NUM_SLICES; slice++) { + dev = shared->devs[slice]; + if (!dev) + continue; + + free_port(dev->ports[VIP_PORTA]); + free_port(dev->ports[VIP_PORTB]); + } + + v4l2_ctrl_handler_free(&shared->ctrl_handler); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); +} + +#if defined(CONFIG_OF) +static const struct of_device_id vip_of_match[] = { + { + .compatible = "ti,dra7-vip", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, vip_of_match); +#endif + +static struct platform_driver vip_pdrv = { + .probe = vip_probe, + .remove = vip_remove, + .driver = { + .name = VIP_MODULE_NAME, + .of_match_table = of_match_ptr(vip_of_match), + }, +}; + +module_platform_driver(vip_pdrv); + +MODULE_DESCRIPTION("TI VIP driver"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/ti/vpe/vip.h b/drivers/media/platform/ti/vpe/vip.h new file mode 100644 index 000000000000..20525369955d --- /dev/null +++ b/drivers/media/platform/ti/vpe/vip.h @@ -0,0 +1,717 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TI VIP capture driver + * + * Copyright (C) 2025 Texas Instruments Incorpated - http://www.ti.com/ + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Yemike Abhilash Chandra, <y-abhilashchandra@ti.com> + */ + +#ifndef __TI_VIP_H +#define __TI_VIP_H + +#include <linux/videodev2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-memops.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-async.h> + +#include "vpdma.h" +#include "vpdma_priv.h" +#include "sc.h" +#include "csc.h" + +#define VIP_INSTANCE1 1 +#define VIP_INSTANCE2 2 +#define VIP_INSTANCE3 3 + +#define VIP_SLICE1 0 +#define VIP_SLICE2 1 +#define VIP_NUM_SLICES 2 + +/* + * Additional client identifiers used for VPDMA configuration descriptors + */ +#define VIP_SLICE1_CFD_SC_CLIENT 7 +#define VIP_SLICE2_CFD_SC_CLIENT 8 + +#define VIP_PORTA 0 +#define VIP_PORTB 1 +#define VIP_NUM_PORTS 2 + +#define VIP_MAX_PLANES 2 +#define VIP_LUMA 0 +#define VIP_CHROMA 1 + +#define VIP_CAP_STREAMS_PER_PORT 16 +#define VIP_VBI_STREAMS_PER_PORT 16 + +#define VIP_MAX_SUBDEV 5 + +#define VPDMA_FIRMWARE "vpdma-1b8.bin" + +/* + * This value needs to be at least as large as the number of entry in + * vip_formats[]. + * When vip_formats[] is modified make sure to adjust this value also. + */ +#define VIP_MAX_ACTIVE_FMT 16 +/* + * Colorspace conversion unit can be in one of 3 modes: + * NA - Not Available on this port + * Y2R - Needed for YUV to RGB on this port + * R2Y - Needed for RGB to YUV on this port + */ +enum vip_csc_state { + VIP_CSC_NA = 0, + VIP_CSC_Y2R, + VIP_CSC_R2Y, +}; + +/* buffer for one video frame */ +struct vip_buffer { + /* common v4l buffer stuff */ + struct vb2_v4l2_buffer vb; + struct list_head list; + bool drop; +}; + +/* + * struct vip_fmt - VIP media bus format information + * @fourcc: V4L2 pixel format FCC identifier + * @code: V4L2 media bus format code + * @colorspace: V4L2 colorspace identifier + * @coplanar: 1 if unpacked Luma and Chroma, 0 otherwise (packed/interleaved) + * @vpdma_fmt: VPDMA data format per plane. + * @finfo: Cache v4l2_format_info for associated fourcc + */ +struct vip_fmt { + u32 fourcc; + u32 code; + u32 colorspace; + u8 coplanar; + const struct vpdma_data_format *vpdma_fmt[VIP_MAX_PLANES]; + const struct v4l2_format_info *finfo; +}; + +/* + * The vip_parser_data structures contains the memory mapped + * info to access the parser registers. + */ +struct vip_parser_data { + void __iomem *base; + + struct platform_device *pdev; +}; + +/* + * The vip_shared structure contains data that is shared by both + * the VIP1 and VIP2 slices. + */ +struct vip_shared { + struct list_head list; + void __iomem *base; + struct vpdma_data vpdma_data; + struct vpdma_data *vpdma; + struct v4l2_device v4l2_dev; + struct vip_dev *devs[VIP_NUM_SLICES]; + struct v4l2_ctrl_handler ctrl_handler; +}; + +struct vip_ctrl_module { + struct regmap *syscon_pol; + u32 syscon_offset; + u32 syscon_bit_field[4]; +}; + +/* + * There are two vip_dev structure, one for each vip slice: VIP1 & VIP2. + */ +struct vip_dev { + struct v4l2_device v4l2_dev; + struct platform_device *pdev; + struct vip_shared *shared; + struct vip_ctrl_module *syscon; + int instance_id; + int slice_id; + int num_ports; /* count of open ports */ + struct mutex mutex; + /* protects access to stream buffer queues */ + spinlock_t slock; + + int irq; + void __iomem *base; + + struct vip_port *ports[VIP_NUM_PORTS]; + + char name[16]; + /* parser data handle */ + struct vip_parser_data *parser; + /* scaler data handle */ + struct sc_data *sc; + /* scaler port assignation */ + int sc_assigned; + /* csc data handle */ + struct csc_data *csc; + /* csc port assignation */ + int csc_assigned; +}; + +/* + * There are two vip_port structures for each vip_dev, one for port A + * and one for port B. + */ +struct vip_port { + struct vip_dev *dev; + int port_id; + + unsigned int flags; + struct v4l2_rect c_rect; /* crop rectangle */ + struct v4l2_mbus_framefmt mbus_framefmt; + struct v4l2_mbus_framefmt try_mbus_framefmt; + + char name[16]; + struct vip_fmt *fmt; /* current format info */ + /* Number of channels/streams configured */ + int num_streams_configured; + int num_streams; /* count of open streams */ + struct vip_stream *cap_streams[VIP_CAP_STREAMS_PER_PORT]; + + struct v4l2_async_notifier notifier; + struct v4l2_subdev *subdev; + struct v4l2_fwnode_endpoint endpoint; + struct vip_fmt *active_fmt[VIP_MAX_ACTIVE_FMT]; + int num_active_fmt; + /* have new shadow reg values */ + bool load_mmrs; + /* shadow reg addr/data block */ + struct vpdma_buf mmr_adb; + /* h coeff buffer */ + struct vpdma_buf sc_coeff_h; + /* v coeff buffer */ + struct vpdma_buf sc_coeff_v; + /* Show if scaler resource is available on this port */ + bool scaler; + /* Show the csc resource state on this port */ + enum vip_csc_state csc; +}; + +/* + * When handling multiplexed video, there can be multiple streams for each + * port. The vip_stream structure holds per-stream data. + */ +struct vip_stream { + struct video_device *vfd; + struct vip_port *port; + int stream_id; + int list_num; + int vfl_type; + char name[16]; + struct work_struct recovery_work; + int num_recovery; + enum v4l2_field field; /* current field */ + unsigned int sequence; /* current frame/field seq */ + enum v4l2_field sup_field; /* supported field value */ + unsigned int width; /* frame width */ + unsigned int height; /* frame height */ + unsigned int bytesperline; /* bytes per line in memory */ + unsigned int sizeimage; /* image size in memory */ + struct list_head vidq; /* incoming vip_bufs queue */ + struct list_head dropq; /* drop vip_bufs queue */ + struct list_head post_bufs; /* vip_bufs to be DMAed */ + /* Maintain a list of used channels - Needed for VPDMA cleanup */ + int vpdma_channels[VPDMA_MAX_CHANNELS]; + int vpdma_channels_to_abort[VPDMA_MAX_CHANNELS]; + struct vpdma_desc_list desc_list; /* DMA descriptor list */ + struct vpdma_dtd *write_desc; + /* next unused desc_list addr */ + void *desc_next; + struct vb2_queue vb_vidq; +}; + +/* + * VIP Enumerations + */ +enum data_path_select { + ALL_FIELDS_DATA_SELECT = 0, + VIP_CSC_SRC_DATA_SELECT, + VIP_SC_SRC_DATA_SELECT, + VIP_RGB_SRC_DATA_SELECT, + VIP_RGB_OUT_LO_DATA_SELECT, + VIP_RGB_OUT_HI_DATA_SELECT, + VIP_CHR_DS_1_SRC_DATA_SELECT, + VIP_CHR_DS_2_SRC_DATA_SELECT, + VIP_MULTI_CHANNEL_DATA_SELECT, + VIP_CHR_DS_1_DATA_BYPASS, + VIP_CHR_DS_2_DATA_BYPASS, +}; + +enum data_interface_modes { + SINGLE_24B_INTERFACE = 0, + SINGLE_16B_INTERFACE = 1, + DUAL_8B_INTERFACE = 2, +}; + +enum sync_types { + EMBEDDED_SYNC_SINGLE_YUV422 = 0, + EMBEDDED_SYNC_2X_MULTIPLEXED_YUV422 = 1, + EMBEDDED_SYNC_4X_MULTIPLEXED_YUV422 = 2, + EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422 = 3, + DISCRETE_SYNC_SINGLE_YUV422 = 4, + EMBEDDED_SYNC_SINGLE_RGB_OR_YUV444 = 5, + DISCRETE_SYNC_SINGLE_RGB_24B = 10, +}; + +#define VIP_NOT_ASSIGNED -1 + +/* + * Register offsets and field selectors + */ +#define VIP_PID_FUNC 0xf02 + +#define VIP_PID 0x0000 +#define VIP_PID_MINOR_MASK 0x3f +#define VIP_PID_MINOR_SHIFT 0 +#define VIP_PID_CUSTOM_MASK 0x03 +#define VIP_PID_CUSTOM_SHIFT 6 +#define VIP_PID_MAJOR_MASK 0x07 +#define VIP_PID_MAJOR_SHIFT 8 +#define VIP_PID_RTL_MASK 0x1f +#define VIP_PID_RTL_SHIFT 11 +#define VIP_PID_FUNC_MASK 0xfff +#define VIP_PID_FUNC_SHIFT 16 +#define VIP_PID_SCHEME_MASK 0x03 +#define VIP_PID_SCHEME_SHIFT 30 + +#define VIP_SYSCONFIG 0x0010 +#define VIP_SYSCONFIG_IDLE_MASK 0x03 +#define VIP_SYSCONFIG_IDLE_SHIFT 2 +#define VIP_SYSCONFIG_STANDBY_MASK 0x03 +#define VIP_SYSCONFIG_STANDBY_SHIFT 4 +#define VIP_FORCE_IDLE_MODE 0 +#define VIP_NO_IDLE_MODE 1 +#define VIP_SMART_IDLE_MODE 2 +#define VIP_SMART_IDLE_WAKEUP_MODE 3 +#define VIP_FORCE_STANDBY_MODE 0 +#define VIP_NO_STANDBY_MODE 1 +#define VIP_SMART_STANDBY_MODE 2 +#define VIP_SMART_STANDBY_WAKEUP_MODE 3 + +#define VIP_INTC_INTX_OFFSET 0x0020 + +#define VIP_INT0_STATUS0_RAW_SET 0x0020 +#define VIP_INT0_STATUS0_RAW VIP_INT0_STATUS0_RAW_SET +#define VIP_INT0_STATUS0_CLR 0x0028 +#define VIP_INT0_STATUS0 VIP_INT0_STATUS0_CLR +#define VIP_INT0_ENABLE0_SET 0x0030 +#define VIP_INT0_ENABLE0 VIP_INT0_ENABLE0_SET +#define VIP_INT0_ENABLE0_CLR 0x0038 +#define VIP_INT0_LIST0_COMPLETE BIT(0) +#define VIP_INT0_LIST0_NOTIFY BIT(1) +#define VIP_INT0_LIST1_COMPLETE BIT(2) +#define VIP_INT0_LIST1_NOTIFY BIT(3) +#define VIP_INT0_LIST2_COMPLETE BIT(4) +#define VIP_INT0_LIST2_NOTIFY BIT(5) +#define VIP_INT0_LIST3_COMPLETE BIT(6) +#define VIP_INT0_LIST3_NOTIFY BIT(7) +#define VIP_INT0_LIST4_COMPLETE BIT(8) +#define VIP_INT0_LIST4_NOTIFY BIT(9) +#define VIP_INT0_LIST5_COMPLETE BIT(10) +#define VIP_INT0_LIST5_NOTIFY BIT(11) +#define VIP_INT0_LIST6_COMPLETE BIT(12) +#define VIP_INT0_LIST6_NOTIFY BIT(13) +#define VIP_INT0_LIST7_COMPLETE BIT(14) +#define VIP_INT0_LIST7_NOTIFY BIT(15) +#define VIP_INT0_DESCRIPTOR BIT(16) +#define VIP_VIP1_PARSER_INT BIT(20) +#define VIP_VIP2_PARSER_INT BIT(21) + +#define VIP_INT0_STATUS1_RAW_SET 0x0024 +#define VIP_INT0_STATUS1_RAW VIP_INT0_STATUS0_RAW_SET +#define VIP_INT0_STATUS1_CLR 0x002c +#define VIP_INT0_STATUS1 VIP_INT0_STATUS0_CLR +#define VIP_INT0_ENABLE1_SET 0x0034 +#define VIP_INT0_ENABLE1 VIP_INT0_ENABLE0_SET +#define VIP_INT0_ENABLE1_CLR 0x003c +#define VIP_INT0_ENABLE1_STAT 0x004c +#define VIP_INT0_CHANNEL_GROUP0 BIT(0) +#define VIP_INT0_CHANNEL_GROUP1 BIT(1) +#define VIP_INT0_CHANNEL_GROUP2 BIT(2) +#define VIP_INT0_CHANNEL_GROUP3 BIT(3) +#define VIP_INT0_CHANNEL_GROUP4 BIT(4) +#define VIP_INT0_CHANNEL_GROUP5 BIT(5) +#define VIP_INT0_CLIENT BIT(7) +#define VIP_VIP1_DS1_UV_ERROR_INT BIT(22) +#define VIP_VIP1_DS2_UV_ERROR_INT BIT(23) +#define VIP_VIP2_DS1_UV_ERROR_INT BIT(24) +#define VIP_VIP2_DS2_UV_ERROR_INT BIT(25) + +#define VIP_INTC_E0I 0x00a0 + +#define VIP_CLK_ENABLE 0x0100 +#define VIP_VPDMA_CLK_ENABLE BIT(0) +#define VIP_VIP1_DATA_PATH_CLK_ENABLE BIT(16) +#define VIP_VIP2_DATA_PATH_CLK_ENABLE BIT(17) + +#define VIP_CLK_RESET 0x0104 +#define VIP_VPDMA_RESET BIT(0) +#define VIP_VPDMA_CLK_RESET_MASK 0x1 +#define VIP_VPDMA_CLK_RESET_SHIFT 0 +#define VIP_DATA_PATH_CLK_RESET_MASK 0x1 +#define VIP_VIP1_DATA_PATH_RESET_SHIFT 16 +#define VIP_VIP2_DATA_PATH_RESET_SHIFT 17 +#define VIP_VIP1_DATA_PATH_RESET BIT(16) +#define VIP_VIP2_DATA_PATH_RESET BIT(17) +#define VIP_VIP1_PARSER_RESET BIT(18) +#define VIP_VIP2_PARSER_RESET BIT(19) +#define VIP_VIP1_CSC_RESET BIT(20) +#define VIP_VIP2_CSC_RESET BIT(21) +#define VIP_VIP1_SC_RESET BIT(22) +#define VIP_VIP2_SC_RESET BIT(23) +#define VIP_VIP1_DS1_RESET BIT(25) +#define VIP_VIP2_DS1_RESET BIT(26) +#define VIP_VIP1_DS2_RESET BIT(27) +#define VIP_VIP2_DS2_RESET BIT(28) +#define VIP_MAIN_RESET BIT(31) + +#define VIP_VIP1_DATA_PATH_SELECT 0x010c +#define VIP_VIP2_DATA_PATH_SELECT 0x0110 +#define VIP_CSC_SRC_SELECT_MASK 0x07 +#define VIP_CSC_SRC_SELECT_SHFT 0 +#define VIP_SC_SRC_SELECT_MASK 0x07 +#define VIP_SC_SRC_SELECT_SHFT 3 +#define VIP_RGB_SRC_SELECT BIT(6) +#define VIP_RGB_OUT_LO_SRC_SELECT BIT(7) +#define VIP_RGB_OUT_HI_SRC_SELECT BIT(8) +#define VIP_DS1_SRC_SELECT_MASK 0x07 +#define VIP_DS1_SRC_SELECT_SHFT 9 +#define VIP_DS2_SRC_SELECT_MASK 0x07 +#define VIP_DS2_SRC_SELECT_SHFT 12 +#define VIP_MULTI_CHANNEL_SELECT BIT(15) +#define VIP_DS1_BYPASS BIT(16) +#define VIP_DS2_BYPASS BIT(17) +#define VIP_TESTPORT_B_SELECT BIT(26) +#define VIP_TESTPORT_A_SELECT BIT(27) +#define VIP_DATAPATH_SELECT_MASK 0x0f +#define VIP_DATAPATH_SELECT_SHFT 28 + +#define VIP_PARSER_MAIN_CFG 0x0000 +#define VIP_DATA_INTERFACE_MODE_MASK 0x03 +#define VIP_DATA_INTERFACE_MODE_SHFT 0 +#define VIP_CLIP_BLANK BIT(4) +#define VIP_CLIP_ACTIVE BIT(5) + +#define VIP_SLICE0_PARSER 0x5500 +#define VIP_SLICE1_PARSER 0x5a00 +#define VIP_PARSER_PORTA_0 0x0004 +#define VIP_PARSER_PORTB_0 0x000c +#define VIP_SYNC_TYPE_MASK 0x0f +#define VIP_SYNC_TYPE_SHFT 0 +#define VIP_CTRL_CHANNEL_SEL_MASK 0x03 +#define VIP_CTRL_CHANNEL_SEL_SHFT 4 +#define VIP_ASYNC_FIFO_WR BIT(6) +#define VIP_ASYNC_FIFO_RD BIT(7) +#define VIP_PORT_ENABLE BIT(8) +#define VIP_FID_POLARITY BIT(9) +#define VIP_PIXCLK_EDGE_POLARITY BIT(10) +#define VIP_HSYNC_POLARITY BIT(11) +#define VIP_VSYNC_POLARITY BIT(12) +#define VIP_ACTVID_POLARITY BIT(13) +#define VIP_FID_DETECT_MODE BIT(14) +#define VIP_USE_ACTVID_HSYNC_ONLY BIT(15) +#define VIP_FID_SKEW_PRECOUNT_MASK 0x3f +#define VIP_FID_SKEW_PRECOUNT_SHFT 16 +#define VIP_DISCRETE_BASIC_MODE BIT(22) +#define VIP_SW_RESET BIT(23) +#define VIP_FID_SKEW_POSTCOUNT_MASK 0x3f +#define VIP_FID_SKEW_POSTCOUNT_SHFT 24 +#define VIP_ANALYZER_2X4X_SRCNUM_POS BIT(30) +#define VIP_ANALYZER_FVH_ERR_COR_EN BIT(31) + +#define VIP_PARSER_PORTA_1 0x0008 +#define VIP_PARSER_PORTB_1 0x0010 +#define VIP_SRC0_NUMLINES_MASK 0x0fff +#define VIP_SRC0_NUMLINES_SHFT 0 +#define VIP_ANC_CHAN_SEL_8B_MASK 0x03 +#define VIP_ANC_CHAN_SEL_8B_SHFT 13 +#define VIP_SRC0_NUMPIX_MASK 0x0fff +#define VIP_SRC0_NUMPIX_SHFT 16 +#define VIP_REPACK_SEL_MASK 0x07 +#define VIP_REPACK_SEL_SHFT 28 + +#define VIP_PARSER_FIQ_MASK 0x0014 +#define VIP_PARSER_FIQ_CLR 0x0018 +#define VIP_PARSER_FIQ_STATUS 0x001c +#define VIP_PORTA_VDET BIT(0) +#define VIP_PORTB_VDET BIT(1) +#define VIP_PORTA_ASYNC_FIFO_OF BIT(2) +#define VIP_PORTB_ASYNC_FIFO_OF BIT(3) +#define VIP_PORTA_OUTPUT_FIFO_YUV BIT(4) +#define VIP_PORTA_OUTPUT_FIFO_ANC BIT(6) +#define VIP_PORTB_OUTPUT_FIFO_YUV BIT(7) +#define VIP_PORTB_OUTPUT_FIFO_ANC BIT(9) +#define VIP_PORTA_CONN BIT(10) +#define VIP_PORTA_DISCONN BIT(11) +#define VIP_PORTB_CONN BIT(12) +#define VIP_PORTB_DISCONN BIT(13) +#define VIP_PORTA_SRC0_SIZE BIT(14) +#define VIP_PORTB_SRC0_SIZE BIT(15) +#define VIP_PORTA_YUV_PROTO_VIOLATION BIT(16) +#define VIP_PORTA_ANC_PROTO_VIOLATION BIT(17) +#define VIP_PORTB_YUV_PROTO_VIOLATION BIT(18) +#define VIP_PORTB_ANC_PROTO_VIOLATION BIT(19) +#define VIP_PORTA_CFG_DISABLE_COMPLETE BIT(20) +#define VIP_PORTB_CFG_DISABLE_COMPLETE BIT(21) + +#define VIP_PARSER_PORTA_SOURCE_FID 0x0020 +#define VIP_PARSER_PORTA_ENCODER_FID 0x0024 +#define VIP_PARSER_PORTB_SOURCE_FID 0x0028 +#define VIP_PARSER_PORTB_ENCODER_FID 0x002c + +#define VIP_PARSER_PORTA_SRC0_SIZE 0x0030 +#define VIP_PARSER_PORTB_SRC0_SIZE 0x0070 +#define VIP_SOURCE_HEIGHT_MASK 0x0fff +#define VIP_SOURCE_HEIGHT_SHFT 0 +#define VIP_SOURCE_WIDTH_MASK 0x0fff +#define VIP_SOURCE_WIDTH_SHFT 16 + +#define VIP_PARSER_PORTA_VDET_VEC 0x00b0 +#define VIP_PARSER_PORTB_VDET_VEC 0x00b4 + +#define VIP_PARSER_PORTA_EXTRA2 0x00b8 +#define VIP_PARSER_PORTB_EXTRA2 0x00c8 +#define VIP_ANC_SKIP_NUMPIX_MASK 0x0fff +#define VIP_ANC_SKIP_NUMPIX_SHFT 0 +#define VIP_ANC_BYPASS BIT(15) +#define VIP_ANC_USE_NUMPIX_MASK 0x0fff +#define VIP_ANC_USE_NUMPIX_SHFT 16 +#define VIP_ANC_TARGET_SRCNUM_MASK 0x0f +#define VIP_ANC_TARGET_SRCNUM_SHFT 28 + +#define VIP_PARSER_PORTA_EXTRA3 0x00bc +#define VIP_PARSER_PORTB_EXTRA3 0x00cc +#define VIP_ANC_SKIP_NUMLINES_MASK 0x0fff +#define VIP_ANC_SKIP_NUMLINES_SHFT 0 +#define VIP_ANC_USE_NUMLINES_MASK 0x0fff +#define VIP_ANC_USE_NUMLINES_SHFT 16 + +#define VIP_PARSER_PORTA_EXTRA4 0x00c0 +#define VIP_PARSER_PORTB_EXTRA4 0x00d0 +#define VIP_ACT_SKIP_NUMPIX_MASK 0x0fff +#define VIP_ACT_SKIP_NUMPIX_SHFT 0 +#define VIP_ACT_BYPASS BIT(15) +#define VIP_ACT_USE_NUMPIX_MASK 0x0fff +#define VIP_ACT_USE_NUMPIX_SHFT 16 +#define VIP_ACT_TARGET_SRCNUM_MASK 0x0f +#define VIP_ACT_TARGET_SRCNUM_SHFT 28 + +#define VIP_PARSER_PORTA_EXTRA5 0x00c4 +#define VIP_PARSER_PORTB_EXTRA5 0x00d4 +#define VIP_ACT_SKIP_NUMLINES_MASK 0x0fff +#define VIP_ACT_SKIP_NUMLINES_SHFT 0 +#define VIP_ACT_USE_NUMLINES_MASK 0x0fff +#define VIP_ACT_USE_NUMLINES_SHFT 16 + +#define VIP_PARSER_PORTA_EXTRA6 0x00d8 +#define VIP_PARSER_PORTB_EXTRA6 0x00dc +#define VIP_ANC_SRCNUM_STOP_IMM_SHFT 0 +#define VIP_YUV_SRCNUM_STOP_IMM_SHFT 16 + +#define VIP_SLICE0_CSC 0x5700 +#define VIP_SLICE1_CSC 0x5c00 +#define VIP_CSC_CSC00 0x0200 +#define VIP_CSC_A0_MASK 0x1fff +#define VIP_CSC_A0_SHFT 0 +#define VIP_CSC_B0_MASK 0x1fff +#define VIP_CSC_B0_SHFT 16 + +#define VIP_CSC_CSC01 0x0204 +#define VIP_CSC_C0_MASK 0x1fff +#define VIP_CSC_C0_SHFT 0 +#define VIP_CSC_A1_MASK 0x1fff +#define VIP_CSC_A1_SHFT 16 + +#define VIP_CSC_CSC02 0x0208 +#define VIP_CSC_B1_MASK 0x1fff +#define VIP_CSC_B1_SHFT 0 +#define VIP_CSC_C1_MASK 0x1fff +#define VIP_CSC_C1_SHFT 16 + +#define VIP_CSC_CSC03 0x020c +#define VIP_CSC_A2_MASK 0x1fff +#define VIP_CSC_A2_SHFT 0 +#define VIP_CSC_B2_MASK 0x1fff +#define VIP_CSC_B2_SHFT 16 + +#define VIP_CSC_CSC04 0x0210 +#define VIP_CSC_C2_MASK 0x1fff +#define VIP_CSC_C2_SHFT 0 +#define VIP_CSC_D0_MASK 0x0fff +#define VIP_CSC_D0_SHFT 16 + +#define VIP_CSC_CSC05 0x0214 +#define VIP_CSC_D1_MASK 0x0fff +#define VIP_CSC_D1_SHFT 0 +#define VIP_CSC_D2_MASK 0x0fff +#define VIP_CSC_D2_SHFT 16 +#define VIP_CSC_BYPASS BIT(28) + +#define VIP_SLICE0_SC 0x5800 +#define VIP_SLICE1_SC 0x5d00 +#define VIP_SC_MP_SC0 0x0300 +#define VIP_INTERLACE_O BIT(0) +#define VIP_LINEAR BIT(1) +#define VIP_SC_BYPASS BIT(2) +#define VIP_INVT_FID BIT(3) +#define VIP_USE_RAV BIT(4) +#define VIP_ENABLE_EV BIT(5) +#define VIP_AUTH_HS BIT(6) +#define VIP_DCM_2X BIT(7) +#define VIP_DCM_4X BIT(8) +#define VIP_HP_BYPASS BIT(9) +#define VIP_INTERLACE_I BIT(10) +#define VIP_ENABLE_SIN2_VER_INTP BIT(11) +#define VIP_Y_PK_EN BIT(14) +#define VIP_TRIM BIT(15) +#define VIP_SELFGEN_FID BIT(16) + +#define VIP_SC_MP_SC1 0x0304 +#define VIP_ROW_ACC_INC_MASK 0x07ffffff +#define VIP_ROW_ACC_INC_SHFT 0 + +#define VIP_SC_MP_SC2 0x0308 +#define VIP_ROW_ACC_OFFSET_MASK 0x0fffffff +#define VIP_ROW_ACC_OFFSET_SHFT 0 + +#define VIP_SC_MP_SC3 0x030c +#define VIP_ROW_ACC_OFFSET_B_MASK 0x0fffffff +#define VIP_ROW_ACC_OFFSET_B_SHFT 0 + +#define VIP_SC_MP_SC4 0x0310 +#define VIP_TAR_H_MASK 0x07ff +#define VIP_TAR_H_SHFT 0 +#define VIP_TAR_W_MASK 0x07ff +#define VIP_TAR_W_SHFT 12 +#define VIP_LIN_ACC_INC_U_MASK 0x07 +#define VIP_LIN_ACC_INC_U_SHFT 24 +#define VIP_NLIN_ACC_INIT_U_MASK 0x07 +#define VIP_NLIN_ACC_INIT_U_SHFT 28 + +#define VIP_SC_MP_SC5 0x0314 +#define VIP_SRC_H_MASK 0x03ff +#define VIP_SRC_H_SHFT 0 +#define VIP_SRC_W_MASK 0x07ff +#define VIP_SRC_W_SHFT 12 +#define VIP_NLIN_ACC_INC_U_MASK 0x07 +#define VIP_NLIN_ACC_INC_U_SHFT 24 + +#define VIP_SC_MP_SC6 0x0318 +#define VIP_ROW_ACC_INIT_RAV_MASK 0x03ff +#define VIP_ROW_ACC_INIT_RAV_SHFT 0 +#define VIP_ROW_ACC_INIT_RAV_B_MASK 0x03ff +#define VIP_ROW_ACC_INIT_RAV_B_SHFT 10 + +#define VIP_SC_MP_SC8 0x0320 +#define VIP_NLIN_LEFT_MASK 0x07ff +#define VIP_NLIN_LEFT_SHFT 0 +#define VIP_NLIN_RIGHT_MASK 0x07ff +#define VIP_NLIN_RIGHT_SHFT 12 + +#define VIP_SC_MP_SC9 0x0324 +#define VIP_LIN_ACC_INC VIP_SC_MP_SC9 + +#define VIP_SC_MP_SC10 0x0328 +#define VIP_NLIN_ACC_INIT VIP_SC_MP_SC10 + +#define VIP_SC_MP_SC11 0x032c +#define VIP_NLIN_ACC_INC VIP_SC_MP_SC11 + +#define VIP_SC_MP_SC12 0x0330 +#define VIP_COL_ACC_OFFSET_MASK 0x01ffffff +#define VIP_COL_ACC_OFFSET_SHFT 0 + +#define VIP_SC_MP_SC13 0x0334 +#define VIP_SC_FACTOR_RAV_MASK 0x03ff +#define VIP_SC_FACTOR_RAV_SHFT 0 +#define VIP_CHROMA_INTP_THR_MASK 0x03ff +#define VIP_CHROMA_INTP_THR_SHFT 12 +#define VIP_DELTA_CHROMA_THR_MASK 0x0f +#define VIP_DELTA_CHROMA_THR_SHFT 24 + +#define VIP_SC_MP_SC17 0x0344 +#define VIP_EV_THR_MASK 0x03ff +#define VIP_EV_THR_SHFT 12 +#define VIP_DELTA_LUMA_THR_MASK 0x0f +#define VIP_DELTA_LUMA_THR_SHFT 24 +#define VIP_DELTA_EV_THR_MASK 0x0f +#define VIP_DELTA_EV_THR_SHFT 28 + +#define VIP_SC_MP_SC18 0x0348 +#define VIP_HS_FACTOR_MASK 0x03ff +#define VIP_HS_FACTOR_SHFT 0 +#define VIP_CONF_DEFAULT_MASK 0x01ff +#define VIP_CONF_DEFAULT_SHFT 16 + +#define VIP_SC_MP_SC19 0x034c +#define VIP_HPF_COEFF0_MASK 0xff +#define VIP_HPF_COEFF0_SHFT 0 +#define VIP_HPF_COEFF1_MASK 0xff +#define VIP_HPF_COEFF1_SHFT 8 +#define VIP_HPF_COEFF2_MASK 0xff +#define VIP_HPF_COEFF2_SHFT 16 +#define VIP_HPF_COEFF3_MASK 0xff +#define VIP_HPF_COEFF3_SHFT 23 + +#define VIP_SC_MP_SC20 0x0350 +#define VIP_HPF_COEFF4_MASK 0xff +#define VIP_HPF_COEFF4_SHFT 0 +#define VIP_HPF_COEFF5_MASK 0xff +#define VIP_HPF_COEFF5_SHFT 8 +#define VIP_HPF_NORM_SHFT_MASK 0x07 +#define VIP_HPF_NORM_SHFT_SHFT 16 +#define VIP_NL_LIMIT_MASK 0x1ff +#define VIP_NL_LIMIT_SHFT 20 + +#define VIP_SC_MP_SC21 0x0354 +#define VIP_NL_LO_THR_MASK 0x01ff +#define VIP_NL_LO_THR_SHFT 0 +#define VIP_NL_LO_SLOPE_MASK 0xff +#define VIP_NL_LO_SLOPE_SHFT 16 + +#define VIP_SC_MP_SC22 0x0358 +#define VIP_NL_HI_THR_MASK 0x01ff +#define VIP_NL_HI_THR_SHFT 0 +#define VIP_NL_HI_SLOPE_SH_MASK 0x07 +#define VIP_NL_HI_SLOPE_SH_SHFT 16 + +#define VIP_SC_MP_SC23 0x035c +#define VIP_GRADIENT_THR_MASK 0x07ff +#define VIP_GRADIENT_THR_SHFT 0 +#define VIP_GRADIENT_THR_RANGE_MASK 0x0f +#define VIP_GRADIENT_THR_RANGE_SHFT 12 +#define VIP_MIN_GY_THR_MASK 0xff +#define VIP_MIN_GY_THR_SHFT 16 +#define VIP_MIN_GY_THR_RANGE_MASK 0x0f +#define VIP_MIN_GY_THR_RANGE_SHFT 28 + +#define VIP_SC_MP_SC24 0x0360 +#define VIP_ORG_H_MASK 0x07ff +#define VIP_ORG_H_SHFT 0 +#define VIP_ORG_W_MASK 0x07ff +#define VIP_ORG_W_SHFT 16 + +#define VIP_SC_MP_SC25 0x0364 +#define VIP_OFF_H_MASK 0x07ff +#define VIP_OFF_H_SHFT 0 +#define VIP_OFF_W_MASK 0x07ff +#define VIP_OFF_W_SHFT 16 + +#define VIP_VPDMA_BASE 0xd000 + +#endif diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index bb8a8bd7980c..573aa83f62eb 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -552,6 +552,54 @@ EXPORT_SYMBOL(vpdma_submit_descs); static void dump_dtd(struct vpdma_dtd *dtd); +/** + * vpdma_update_dma_addr() - update DMA address in a descriptor + * @vpdma: VPDMA device context + * @list: vpdma desc list to which we add this descriptor + * @dma_addr: new DMA address to program into the descriptor + * @write_dtd: descriptor pointer used to compute write-back address + * @drop: if true, set the drop bit in the write descriptor + * @idx: index of the descriptor in the list to update + * + * Updates dma addresses of the descriptor at @idx in @list. + * This allows reusing an existing descriptor list with a new buffer + * address, instead of rebuilding the list, which is needed when + * multiple clients share the same VPDMA engine. The list buffer is + * unmapped before the update and remapped after. + */ +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, + dma_addr_t dma_addr, + void *write_dtd, int drop, int idx) +{ + struct vpdma_dtd *dtd = list->buf.addr; + dma_addr_t write_desc_addr; + int offset; + + dtd += idx; + vpdma_unmap_desc_buf(vpdma, &list->buf); + + dtd->start_addr = dma_addr; + + /* Calculate write address from the offset of write_dtd from start + * of the list->buf + */ + offset = (void *)write_dtd - list->buf.addr; + write_desc_addr = list->buf.dma_addr + offset; + + if (drop) + dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, + 1, 1, 0); + else + dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr, + 1, 0, 0); + + vpdma_map_desc_buf(vpdma, &list->buf); + + dump_dtd(dtd); +} +EXPORT_SYMBOL_GPL(vpdma_update_dma_addr); + void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr, u32 width, u32 height) { @@ -1087,7 +1135,7 @@ rel_fw: release_firmware(f); } -static int vpdma_load_firmware(struct vpdma_data *vpdma) +int vpdma_load_firmware(struct vpdma_data *vpdma) { int r; struct device *dev = &vpdma->pdev->dev; @@ -1104,6 +1152,7 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma) return 0; } +EXPORT_SYMBOL_GPL(vpdma_load_firmware); int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, void (*cb)(struct platform_device *pdev)) diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h index e4d7941c6207..1fc53fb33497 100644 --- a/drivers/media/platform/ti/vpe/vpdma.h +++ b/drivers/media/platform/ti/vpe/vpdma.h @@ -222,6 +222,9 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list); int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list, int list_num); bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num); +void vpdma_update_dma_addr(struct vpdma_data *vpdma, + struct vpdma_desc_list *list, dma_addr_t dma_addr, + void *write_dtd, int drop, int idx); /* VPDMA hardware list funcs */ int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv); @@ -278,4 +281,7 @@ void vpdma_dump_regs(struct vpdma_data *vpdma); int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma, void (*cb)(struct platform_device *pdev)); +/* load vpdma firmware*/ +int vpdma_load_firmware(struct vpdma_data *vpdma); + #endif diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h index e0fdc4535b2d..0353de154a1e 100644 --- a/drivers/media/platform/verisilicon/hantro.h +++ b/drivers/media/platform/verisilicon/hantro.h @@ -77,6 +77,7 @@ struct hantro_irq { * @double_buffer: core needs double buffering * @legacy_regs: core uses legacy register set * @late_postproc: postproc must be set up at the end of the job + * @shared_devices: an array of device ids that cannot run concurrently */ struct hantro_variant { unsigned int enc_offset; @@ -101,6 +102,7 @@ struct hantro_variant { unsigned int double_buffer : 1; unsigned int legacy_regs : 1; unsigned int late_postproc : 1; + const struct of_device_id *shared_devices; }; /** diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 60b95b5d8565..94f58f4e4a4e 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -13,6 +13,7 @@ #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> @@ -1035,6 +1036,41 @@ static int hantro_disable_multicore(struct hantro_dev *vpu) return 0; } +static struct v4l2_m2m_dev *hantro_get_v4l2_m2m_dev(struct hantro_dev *vpu) +{ + struct device_node *node; + struct hantro_dev *shared_vpu; + + if (!vpu->variant || !vpu->variant->shared_devices) + goto init_new_m2m_dev; + + for_each_matching_node(node, vpu->variant->shared_devices) { + struct platform_device *pdev; + struct v4l2_m2m_dev *m2m_dev; + + pdev = of_find_device_by_node(node); + if (!pdev) + continue; + + shared_vpu = platform_get_drvdata(pdev); + if (IS_ERR_OR_NULL(shared_vpu) || shared_vpu == vpu) { + platform_device_put(pdev); + continue; + } + + v4l2_m2m_get(shared_vpu->m2m_dev); + m2m_dev = shared_vpu->m2m_dev; + platform_device_put(pdev); + + of_node_put(node); + + return m2m_dev; + } + +init_new_m2m_dev: + return v4l2_m2m_init(&vpu_m2m_ops); +} + static int hantro_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1186,7 +1222,7 @@ static int hantro_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, vpu); - vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops); + vpu->m2m_dev = hantro_get_v4l2_m2m_dev(vpu); if (IS_ERR(vpu->m2m_dev)) { v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(vpu->m2m_dev); @@ -1225,7 +1261,7 @@ err_rm_enc_func: hantro_remove_enc_func(vpu); err_m2m_rel: media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); + v4l2_m2m_put(vpu->m2m_dev); err_v4l2_unreg: v4l2_device_unregister(&vpu->v4l2_dev); err_clk_unprepare: @@ -1248,7 +1284,7 @@ static void hantro_remove(struct platform_device *pdev) hantro_remove_dec_func(vpu); hantro_remove_enc_func(vpu); media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); + v4l2_m2m_put(vpu->m2m_dev); v4l2_device_unregister(&vpu->v4l2_dev); clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); reset_control_assert(vpu->resets); diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c index 5be0e2e76882..6f8e43b7f157 100644 --- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c +++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c @@ -343,6 +343,12 @@ const struct hantro_variant imx8mq_vpu_variant = { .num_regs = ARRAY_SIZE(imx8mq_reg_names) }; +static const struct of_device_id imx8mq_vpu_shared_resources[] __initconst = { + { .compatible = "nxp,imx8mq-vpu-g1", }, + { .compatible = "nxp,imx8mq-vpu-g2", }, + { /* sentinel */ } +}; + const struct hantro_variant imx8mq_vpu_g1_variant = { .dec_fmts = imx8m_vpu_dec_fmts, .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), @@ -356,6 +362,7 @@ const struct hantro_variant imx8mq_vpu_g1_variant = { .num_irqs = ARRAY_SIZE(imx8mq_irqs), .clk_names = imx8mq_g1_clk_names, .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), + .shared_devices = imx8mq_vpu_shared_resources, }; const struct hantro_variant imx8mq_vpu_g2_variant = { @@ -371,6 +378,7 @@ const struct hantro_variant imx8mq_vpu_g2_variant = { .num_irqs = ARRAY_SIZE(imx8mq_g2_irqs), .clk_names = imx8mq_g2_clk_names, .num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names), + .shared_devices = imx8mq_vpu_shared_resources, }; const struct hantro_variant imx8mm_vpu_g1_variant = { diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index e4703bb6be7c..e4e21ad37323 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -72,6 +72,14 @@ : AV1_DIV_ROUND_UP_POW2((_value_), (_n_))); \ }) +enum rockchip_av1_tx_mode { + ROCKCHIP_AV1_TX_MODE_ONLY_4X4 = 0, + ROCKCHIP_AV1_TX_MODE_8X8 = 1, + ROCKCHIP_AV1_TX_MODE_16x16 = 2, + ROCKCHIP_AV1_TX_MODE_32x32 = 3, + ROCKCHIP_AV1_TX_MODE_SELECT = 4, +}; + struct rockchip_av1_film_grain { u8 scaling_lut_y[256]; u8 scaling_lut_cb[256]; @@ -373,12 +381,12 @@ int rockchip_vpu981_av1_dec_init(struct hantro_ctx *ctx) return -ENOMEM; av1_dec->global_model.size = GLOBAL_MODEL_SIZE; - av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_MAX_TILES, + av1_dec->tile_info.cpu = dma_alloc_coherent(vpu->dev, AV1_TILE_INFO_SIZE, &av1_dec->tile_info.dma, GFP_KERNEL); if (!av1_dec->tile_info.cpu) return -ENOMEM; - av1_dec->tile_info.size = AV1_MAX_TILES; + av1_dec->tile_info.size = AV1_TILE_INFO_SIZE; av1_dec->film_grain.cpu = dma_alloc_coherent(vpu->dev, ALIGN(sizeof(struct rockchip_av1_film_grain), 2048), @@ -1396,8 +1404,16 @@ static void rockchip_vpu981_av1_dec_set_cdef(struct hantro_ctx *ctx) u16 luma_sec_strength = 0; u32 chroma_pri_strength = 0; u16 chroma_sec_strength = 0; + bool enable_cdef; int i; + enable_cdef = !(cdef->bits == 0 && + cdef->damping_minus_3 == 0 && + cdef->y_pri_strength[0] == 0 && + cdef->y_sec_strength[0] == 0 && + cdef->uv_pri_strength[0] == 0 && + cdef->uv_sec_strength[0] == 0); + hantro_reg_write(vpu, &av1_enable_cdef, enable_cdef); hantro_reg_write(vpu, &av1_cdef_bits, cdef->bits); hantro_reg_write(vpu, &av1_cdef_damping, cdef->damping_minus_3); @@ -1927,11 +1943,26 @@ static void rockchip_vpu981_av1_dec_set_reference_frames(struct hantro_ctx *ctx) rockchip_vpu981_av1_dec_set_other_frames(ctx); } +static int rockchip_vpu981_av1_get_hardware_tx_mode(enum v4l2_av1_tx_mode tx_mode) +{ + switch (tx_mode) { + case V4L2_AV1_TX_MODE_ONLY_4X4: + return ROCKCHIP_AV1_TX_MODE_ONLY_4X4; + case V4L2_AV1_TX_MODE_LARGEST: + return ROCKCHIP_AV1_TX_MODE_32x32; + case V4L2_AV1_TX_MODE_SELECT: + return ROCKCHIP_AV1_TX_MODE_SELECT; + } + + return ROCKCHIP_AV1_TX_MODE_32x32; +} + static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx) { struct hantro_dev *vpu = ctx->dev; struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec; struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls; + int tx_mode; hantro_reg_write(vpu, &av1_skip_mode, !!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT)); @@ -1953,8 +1984,6 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx) !!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_SHOW_FRAME)); hantro_reg_write(vpu, &av1_switchable_motion_mode, !!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE)); - hantro_reg_write(vpu, &av1_enable_cdef, - !!(ctrls->sequence->flags & V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF)); hantro_reg_write(vpu, &av1_allow_masked_compound, !!(ctrls->sequence->flags & V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND)); @@ -1989,7 +2018,7 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx) !!(ctrls->frame->quantization.flags & V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT)); - hantro_reg_write(vpu, &av1_idr_pic_e, !ctrls->frame->frame_type); + hantro_reg_write(vpu, &av1_idr_pic_e, IS_INTRA(ctrls->frame->frame_type)); hantro_reg_write(vpu, &av1_quant_base_qindex, ctrls->frame->quantization.base_q_idx); hantro_reg_write(vpu, &av1_bit_depth_y_minus8, ctx->bit_depth - 8); hantro_reg_write(vpu, &av1_bit_depth_c_minus8, ctx->bit_depth - 8); @@ -1999,7 +2028,9 @@ static void rockchip_vpu981_av1_dec_set_parameters(struct hantro_ctx *ctx) !!(ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV)); hantro_reg_write(vpu, &av1_comp_pred_mode, (ctrls->frame->flags & V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT) ? 2 : 0); - hantro_reg_write(vpu, &av1_transform_mode, (ctrls->frame->tx_mode == 1) ? 3 : 4); + + tx_mode = rockchip_vpu981_av1_get_hardware_tx_mode(ctrls->frame->tx_mode); + hantro_reg_write(vpu, &av1_transform_mode, tx_mode); hantro_reg_write(vpu, &av1_max_cb_size, (ctrls->sequence->flags & V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK) ? 7 : 6); diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index f3b57f0cb1ec..c133305fd019 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -338,7 +338,6 @@ static int usb_keene_probe(struct usb_interface *intf, if (hdl->error) { retval = hdl->error; - v4l2_ctrl_handler_free(hdl); goto err_v4l2; } retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); @@ -384,6 +383,7 @@ static int usb_keene_probe(struct usb_interface *intf, err_vdev: v4l2_device_unregister(&radio->v4l2_dev); err_v4l2: + v4l2_ctrl_handler_free(&radio->hdl); kfree(radio->buffer); kfree(radio); err: diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index a7ab668ce70b..be846f711969 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -448,8 +448,10 @@ static void device_run(void *priv) ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; spin_unlock(ctx->lock); - if (ctx->is_stateless && src_req) + if (ctx->is_stateless && src_req) { v4l2_ctrl_request_complete(src_req, &ctx->hdl); + media_request_manual_complete(src_req); + } if (ctx->is_enc) v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx); @@ -1518,8 +1520,12 @@ static void vicodec_return_bufs(struct vb2_queue *q, u32 state) vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); if (vbuf == NULL) return; - v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, - &ctx->hdl); + if (ctx->is_stateless && V4L2_TYPE_IS_OUTPUT(q->type)) { + struct media_request *req = vbuf->vb2_buf.req_obj.req; + + v4l2_ctrl_request_complete(req, &ctx->hdl); + media_request_manual_complete(req); + } spin_lock(ctx->lock); v4l2_m2m_buf_done(vbuf, state); spin_unlock(ctx->lock); @@ -1672,6 +1678,7 @@ static void vicodec_buf_request_complete(struct vb2_buffer *vb) struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl); + media_request_manual_complete(vb->req_obj.req); } @@ -2002,6 +2009,12 @@ static int vicodec_request_validate(struct media_request *req) return vb2_request_validate(req); } +static void vicodec_request_queue(struct media_request *req) +{ + media_request_mark_manual_completion(req); + v4l2_m2m_request_queue(req); +} + static const struct v4l2_file_operations vicodec_fops = { .owner = THIS_MODULE, .open = vicodec_open, @@ -2022,7 +2035,7 @@ static const struct video_device vicodec_videodev = { static const struct media_device_ops vicodec_m2m_media_ops = { .req_validate = vicodec_request_validate, - .req_queue = v4l2_m2m_request_queue, + .req_queue = vicodec_request_queue, }; static const struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/test-drivers/visl/visl-dec.c b/drivers/media/test-drivers/visl/visl-dec.c index d90b79de8384..6bbf93757047 100644 --- a/drivers/media/test-drivers/visl/visl-dec.c +++ b/drivers/media/test-drivers/visl/visl-dec.c @@ -547,6 +547,9 @@ static void visl_trace_ctrls(struct visl_ctx *ctx, struct visl_run *run) trace_v4l2_hevc_dpb_entry(&run->hevc.dpram->dpb[i]); trace_v4l2_hevc_pred_weight_table(&run->hevc.spram->pred_weight_table); + trace_v4l2_ctrl_hevc_ext_sps_lt_rps(run->hevc.rps_lt); + trace_v4l2_ctrl_hevc_ext_sps_st_rps(run->hevc.rps_st); + break; case VISL_CODEC_AV1: trace_v4l2_ctrl_av1_sequence(run->av1.seq); @@ -611,6 +614,10 @@ void visl_device_run(void *priv) run.hevc.spram = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SLICE_PARAMS); run.hevc.sm = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); run.hevc.dpram = visl_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); + run.hevc.rps_lt = visl_find_control_data(ctx, + V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS); + run.hevc.rps_st = visl_find_control_data(ctx, + V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS); break; case VISL_CODEC_AV1: run.av1.seq = visl_find_control_data(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE); diff --git a/drivers/media/test-drivers/visl/visl-dec.h b/drivers/media/test-drivers/visl/visl-dec.h index c2c2ef3a8798..6e7562e555bb 100644 --- a/drivers/media/test-drivers/visl/visl-dec.h +++ b/drivers/media/test-drivers/visl/visl-dec.h @@ -7,6 +7,7 @@ #ifndef _VISL_DEC_H_ #define _VISL_DEC_H_ +#include "linux/v4l2-controls.h" #include "visl.h" struct visl_fwht_run { @@ -43,6 +44,8 @@ struct visl_hevc_run { const struct v4l2_ctrl_hevc_slice_params *spram; const struct v4l2_ctrl_hevc_scaling_matrix *sm; const struct v4l2_ctrl_hevc_decode_params *dpram; + const struct v4l2_ctrl_hevc_ext_sps_lt_rps *rps_lt; + const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_st; }; struct visl_av1_run { diff --git a/drivers/media/test-drivers/visl/visl-trace-hevc.h b/drivers/media/test-drivers/visl/visl-trace-hevc.h index 837b8ec12e97..963914c463db 100644 --- a/drivers/media/test-drivers/visl/visl-trace-hevc.h +++ b/drivers/media/test-drivers/visl/visl-trace-hevc.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0+ */ +#include "linux/v4l2-controls.h" #if !defined(_VISL_TRACE_HEVC_H_) || defined(TRACE_HEADER_MULTI_READ) #define _VISL_TRACE_HEVC_H_ @@ -343,6 +344,54 @@ DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_decode_params_tmpl, )) ); +DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_ext_sps_lt_rps_tmpl, + TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_lt_rps *lt), + TP_ARGS(lt), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_hevc_ext_sps_lt_rps, lt)), + TP_fast_assign(__entry->lt = *lt), + TP_printk("\nflags %s\n" + "lt_ref_pic_poc_lsb_sps %x\n", + __print_flags(__entry->lt.flags, "|", + {V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT, "USED_LT"} + ), + __entry->lt.lt_ref_pic_poc_lsb_sps + ) +) + +DECLARE_EVENT_CLASS(v4l2_ctrl_hevc_ext_sps_st_rps_tmpl, + TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_st_rps *st), + TP_ARGS(st), + TP_STRUCT__entry(__field_struct(struct v4l2_ctrl_hevc_ext_sps_st_rps, st)), + TP_fast_assign(__entry->st = *st), + TP_printk("\nflags %s\n" + "delta_idx_minus1: %u\n" + "delta_rps_sign: %u\n" + "abs_delta_rps_minus1: %u\n" + "num_negative_pics: %u\n" + "num_positive_pics: %u\n" + "used_by_curr_pic: %08x\n" + "use_delta_flag: %08x\n" + "delta_poc_s0_minus1: %s\n" + "delta_poc_s1_minus1: %s\n", + __print_flags(__entry->st.flags, "|", + {V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED, "INTER_REF_PIC_SET_PRED"} + ), + __entry->st.delta_idx_minus1, + __entry->st.delta_rps_sign, + __entry->st.abs_delta_rps_minus1, + __entry->st.num_negative_pics, + __entry->st.num_positive_pics, + __entry->st.used_by_curr_pic, + __entry->st.use_delta_flag, + __print_array(__entry->st.delta_poc_s0_minus1, + ARRAY_SIZE(__entry->st.delta_poc_s0_minus1), + sizeof(__entry->st.delta_poc_s0_minus1[0])), + __print_array(__entry->st.delta_poc_s1_minus1, + ARRAY_SIZE(__entry->st.delta_poc_s1_minus1), + sizeof(__entry->st.delta_poc_s1_minus1[0])) + ) +) + DECLARE_EVENT_CLASS(v4l2_hevc_dpb_entry_tmpl, TP_PROTO(const struct v4l2_hevc_dpb_entry *e), @@ -391,6 +440,16 @@ DEFINE_EVENT(v4l2_ctrl_hevc_decode_params_tmpl, v4l2_ctrl_hevc_decode_params, TP_ARGS(d) ); +DEFINE_EVENT(v4l2_ctrl_hevc_ext_sps_lt_rps_tmpl, v4l2_ctrl_hevc_ext_sps_lt_rps, + TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_lt_rps *lt), + TP_ARGS(lt) +); + +DEFINE_EVENT(v4l2_ctrl_hevc_ext_sps_st_rps_tmpl, v4l2_ctrl_hevc_ext_sps_st_rps, + TP_PROTO(const struct v4l2_ctrl_hevc_ext_sps_st_rps *st), + TP_ARGS(st) +); + DEFINE_EVENT(v4l2_hevc_dpb_entry_tmpl, v4l2_hevc_dpb_entry, TP_PROTO(const struct v4l2_hevc_dpb_entry *e), TP_ARGS(e) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index b32bb906a9de..5807734ae26c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -3709,6 +3709,11 @@ status); "Failed to submit read-control URB status=%d", status); hdw->ctl_read_pend_flag = 0; + if (hdw->ctl_write_pend_flag) { + usb_unlink_urb(hdw->ctl_write_urb); + while (hdw->ctl_write_pend_flag) + wait_for_completion(&hdw->ctl_done); + } goto done; } } diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 2905505c240c..f0f6f8454d9c 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -483,6 +483,7 @@ static int uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, u8 query, return 0; case UVC_GET_MIN: + /* Not used, we use -UVC_GET_MAX */ case UVC_GET_MAX: case UVC_GET_RES: case UVC_GET_DEF: @@ -526,8 +527,7 @@ static int uvc_ctrl_get_rel_speed(struct uvc_control_mapping *mapping, *out = (sign == 0) ? 0 : (sign > 0 ? value : -value); return 0; case UVC_GET_MIN: - *out = -value; - return 0; + /* Not used, we use -UVC_GET_MAX */ case UVC_GET_MAX: case UVC_GET_RES: case UVC_GET_DEF: @@ -1432,7 +1432,7 @@ static bool uvc_ctrl_is_readable(u32 which, struct uvc_control *ctrl, * auto_exposure=1, exposure_time_absolute=251. */ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, - const struct v4l2_ext_controls *ctrls, + const struct v4l2_ext_controls *ctrls, u32 which, unsigned long ioctl) { struct uvc_control_mapping *master_map = NULL; @@ -1442,14 +1442,24 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, s32 val; int ret; int i; + /* + * There is no need to check the ioctl, all the ioctls except + * VIDIOC_G_EXT_CTRLS use which=V4L2_CTRL_WHICH_CUR_VAL. + */ + bool is_which_min_max = which == V4L2_CTRL_WHICH_MIN_VAL || + which == V4L2_CTRL_WHICH_MAX_VAL; if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0) - return -EACCES; + return is_which_min_max ? -EINVAL : -EACCES; ctrl = uvc_find_control(chain, v4l2_id, &mapping); if (!ctrl) return -EINVAL; + if ((!(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) || + !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX)) && is_which_min_max) + return -EINVAL; + if (ioctl == VIDIOC_G_EXT_CTRLS) return uvc_ctrl_is_readable(ctrls->which, ctrl, mapping); @@ -1517,6 +1527,17 @@ static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl, return ~0; } +static bool uvc_ctrl_is_relative_ptz(__u32 ctrl_id) +{ + switch (ctrl_id) { + case V4L2_CID_ZOOM_CONTINUOUS: + case V4L2_CID_PAN_SPEED: + case V4L2_CID_TILT_SPEED: + return true; + } + return false; +} + /* * Maximum retry count to avoid spurious errors with controls. Increasing this * value does no seem to produce better results in the tested hardware. @@ -1576,18 +1597,32 @@ static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain, break; } - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) - v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, UVC_GET_MIN, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - else - v4l2_ctrl->minimum = 0; - if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) v4l2_ctrl->maximum = uvc_mapping_get_s32(mapping, UVC_GET_MAX, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); else v4l2_ctrl->maximum = 0; + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) { + /* + * For relative PTZ controls, UVC_GET_MIN for + * b(Pan|Tilt|Zoom)Speed returns the minimum speed of the + * movement in direction specified in the sign field. + * See in USB Device Class Definition for Video Devices: + * 4.2.2.1.13 Zoom (Relative) Control + * 4.2.2.1.15 PanTilt (Relative) Control + * + * For minimum value, use maximum speed but in negative direction. + */ + if (uvc_ctrl_is_relative_ptz(v4l2_ctrl->id)) + v4l2_ctrl->minimum = -v4l2_ctrl->maximum; + else + v4l2_ctrl->minimum = uvc_mapping_get_s32(mapping, + UVC_GET_MIN, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + } else { + v4l2_ctrl->minimum = 0; + } + if (ctrl->info.flags & UVC_CTRL_FLAG_GET_RES) v4l2_ctrl->step = uvc_mapping_get_s32(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); @@ -2449,6 +2484,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, u32 which, static int uvc_ctrl_clamp(struct uvc_video_chain *chain, struct uvc_control *ctrl, + u32 v4l2_id, struct uvc_control_mapping *mapping, s32 *value_in_out) { @@ -2466,10 +2502,24 @@ static int uvc_ctrl_clamp(struct uvc_video_chain *chain, return ret; } - min = uvc_mapping_get_s32(mapping, UVC_GET_MIN, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); max = uvc_mapping_get_s32(mapping, UVC_GET_MAX, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + /* + * For relative PTZ controls, UVC_GET_MIN for + * b(Pan|Tilt|Zoom)Speed returns the minimum speed of the + * movement in direction specified in the sign field. + * See in USB Device Class Definition for Video Devices: + * 4.2.2.1.13 Zoom (Relative) Control + * 4.2.2.1.15 PanTilt (Relative) Control + * + * For minimum value, use maximum speed but in negative direction. + */ + if (uvc_ctrl_is_relative_ptz(v4l2_id)) + min = -max; + else + min = uvc_mapping_get_s32(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + step = uvc_mapping_get_s32(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); if (step == 0) @@ -2583,7 +2633,7 @@ int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl) if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) return -EACCES; - ret = uvc_ctrl_clamp(chain, ctrl, mapping, &xctrl->value); + ret = uvc_ctrl_clamp(chain, ctrl, xctrl->id, mapping, &xctrl->value); if (ret) return ret; /* @@ -2929,8 +2979,7 @@ int uvc_ctrl_restore_values(struct uvc_device *dev) if (!ctrl->initialized || !ctrl->modified || (ctrl->info.flags & UVC_CTRL_FLAG_RESTORE) == 0) continue; - dev_dbg(&dev->intf->dev, - "restoring control %pUl/%u/%u\n", + uvc_dbg(dev, CONTROL, "restoring control %pUl/%u/%u\n", ctrl->info.entity, ctrl->info.index, ctrl->info.selector); ctrl->dirty = 1; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index ee4f54d68349..aa3e8d295e0f 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -165,28 +165,17 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, return NULL; } -static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) +static struct uvc_streaming *uvc_stream_for_terminal(struct uvc_device *dev, + struct uvc_entity *term) { - struct uvc_streaming *stream, *last_stream; - unsigned int count = 0; + u16 id = UVC_HARDWARE_ENTITY_ID(term->id); + struct uvc_streaming *stream; list_for_each_entry(stream, &dev->streams, list) { - count += 1; - last_stream = stream; if (stream->header.bTerminalLink == id) return stream; } - /* - * If the streaming entity is referenced by an invalid ID, notify the - * user and use heuristics to guess the correct entity. - */ - if (count == 1 && id == UVC_INVALID_ENTITY_ID) { - dev_warn(&dev->intf->dev, - "UVC non compliance: Invalid USB header. The streaming entity has an invalid ID, guessing the correct one."); - return last_stream; - } - return NULL; } @@ -823,10 +812,12 @@ static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type, } /* Per UVC 1.1+ spec 3.7.2, the ID is unique. */ - if (uvc_entity_by_id(dev, id)) { - dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", id); + if (uvc_entity_by_id(dev, UVC_HARDWARE_ENTITY_ID(id))) + dev_err(&dev->intf->dev, "Found multiple Units with ID %u\n", + UVC_HARDWARE_ENTITY_ID(id)); + + if (uvc_entity_by_id(dev, id)) id = UVC_INVALID_ENTITY_ID; - } extra_size = roundup(extra_size, sizeof(*entity->pads)); if (num_pads) @@ -982,6 +973,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, struct usb_host_interface *alts = dev->intf->cur_altsetting; unsigned int i, n, p, len; const char *type_name; + unsigned int id; u16 type; switch (buffer[2]) { @@ -1120,8 +1112,28 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } + id = buffer[3]; + + /* + * Some devices, such as the Grandstream GUV3100, exhibit entity + * ID collisions between units and streaming output terminals. + * Move streaming output terminals to their own ID namespace by + * setting bit UVC_TERM_OUTPUT (15), above the ID's 8-bit value. + * The bit is ignored in uvc_stream_for_terminal() when looking + * up the streaming interface for the terminal. + * + * This hack is safe to enable unconditionally, as the ID is not + * used for any other purpose (streaming output terminals have + * no controls and are never referenced as sources in UVC + * descriptors). Other types output terminals can have controls, + * so limit usage of this separate namespace to streaming output + * terminals. + */ + if (type & UVC_TT_STREAMING) + id |= UVC_TERM_OUTPUT; + term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT, - buffer[3], 1, 0); + id, 1, 0); if (IS_ERR(term)) return PTR_ERR(term); @@ -2118,8 +2130,8 @@ static int uvc_register_terms(struct uvc_device *dev, if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) continue; - stream = uvc_stream_by_id(dev, term->id); - if (stream == NULL) { + stream = uvc_stream_for_terminal(dev, term); + if (!stream) { dev_info(&dev->intf->dev, "No streaming interface found for terminal %u.", term->id); diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 790184c9843d..8b8f44b4a045 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -177,18 +177,20 @@ static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count) ret = uvc_pm_get(stream->dev); if (ret) - return ret; + goto err_buffers; queue->buf_used = 0; ret = uvc_video_start_streaming(stream); - if (ret == 0) - return 0; + if (ret) + goto err_pm; - uvc_pm_put(stream->dev); + return 0; +err_pm: + uvc_pm_put(stream->dev); +err_buffers: uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED); - return ret; } @@ -339,7 +341,7 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue) * the device has been disconnected. */ static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue, - struct uvc_buffer *buf) + struct uvc_buffer *buf) { buf->error = 0; buf->state = UVC_BUF_STATE_QUEUED; diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 9e4a251eca88..30c160daed8c 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -765,14 +765,15 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *priv, static int uvc_ctrl_check_access(struct uvc_video_chain *chain, struct v4l2_ext_controls *ctrls, - unsigned long ioctl) + u32 which, unsigned long ioctl) { struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; int ret = 0; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl); + ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, which, + ioctl); if (ret) break; } @@ -806,7 +807,7 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *priv, which = V4L2_CTRL_WHICH_CUR_VAL; } - ret = uvc_ctrl_check_access(chain, ctrls, VIDIOC_G_EXT_CTRLS); + ret = uvc_ctrl_check_access(chain, ctrls, which, VIDIOC_G_EXT_CTRLS); if (ret < 0) return ret; @@ -840,7 +841,8 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle, if (!ctrls->count) return 0; - ret = uvc_ctrl_check_access(chain, ctrls, ioctl); + ret = uvc_ctrl_check_access(chain, ctrls, V4L2_CTRL_WHICH_CUR_VAL, + ioctl); if (ret < 0) return ret; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 2094e059d7d3..59eb95a4b70c 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1771,12 +1771,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream) } static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream, - struct uvc_urb *uvc_urb, gfp_t gfp_flags) + struct uvc_urb *uvc_urb, unsigned int size, + gfp_t gfp_flags) { struct usb_device *udev = stream->dev->udev; - uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size, - gfp_flags, &uvc_urb->dma, + uvc_urb->buffer = usb_alloc_noncoherent(udev, size, gfp_flags, + &uvc_urb->dma, uvc_stream_dir(stream), &uvc_urb->sgt); return !!uvc_urb->buffer; @@ -1812,13 +1813,14 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, npackets = UVC_MAX_PACKETS; /* Retry allocations until one succeed. */ - for (; npackets > 1; npackets /= 2) { - stream->urb_size = psize * npackets; + for (; npackets > 0; npackets /= 2) { + unsigned int urb_size = psize * npackets; for (i = 0; i < UVC_URBS; ++i) { struct uvc_urb *uvc_urb = &stream->uvc_urb[i]; - if (!uvc_alloc_urb_buffer(stream, uvc_urb, gfp_flags)) { + if (!uvc_alloc_urb_buffer(stream, uvc_urb, urb_size, + gfp_flags)) { uvc_free_urb_buffers(stream); break; } @@ -1830,6 +1832,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, uvc_dbg(stream->dev, VIDEO, "Allocated %u URB buffers of %ux%u bytes each\n", UVC_URBS, npackets, psize); + stream->urb_size = urb_size; return npackets; } } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index ed7bad31f75c..8480d65ecb85 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -41,7 +41,8 @@ #define UVC_EXT_GPIO_UNIT 0x7ffe #define UVC_EXT_GPIO_UNIT_ID 0x100 -#define UVC_INVALID_ENTITY_ID 0xffff +#define UVC_HARDWARE_ENTITY_ID(id) ((id) & 0xff) +#define UVC_INVALID_ENTITY_ID 0xffff /* ------------------------------------------------------------------------ * Driver specific constants. @@ -786,7 +787,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, u32 which, struct v4l2_ext_control *xctrl); int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl); int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id, - const struct v4l2_ext_controls *ctrls, + const struct v4l2_ext_controls *ctrls, u32 which, unsigned long ioctl); int uvc_xu_ctrl_query(struct uvc_video_chain *chain, diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ee884a8221fb..1c08bba9ecb9 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -343,7 +343,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, struct v4l2_subdev *sd, struct v4l2_async_connection *asc) { - struct v4l2_async_notifier *subdev_notifier; bool registered = false; int ret; @@ -389,6 +388,25 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n", dev_name(sd->dev), ret); + return 0; + +err_call_unbind: + v4l2_async_nf_call_unbind(notifier, sd, asc); + list_del(&asc->asc_subdev_entry); + +err_unregister_subdev: + if (registered) + v4l2_device_unregister_subdev(sd); + + return ret; +} + +static int +v4l2_async_nf_try_subdev_notifier(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd) +{ + struct v4l2_async_notifier *subdev_notifier; + /* * See if the sub-device has a notifier. If not, return here. */ @@ -404,16 +422,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, subdev_notifier->parent = notifier; return v4l2_async_nf_try_all_subdevs(subdev_notifier); - -err_call_unbind: - v4l2_async_nf_call_unbind(notifier, sd, asc); - list_del(&asc->asc_subdev_entry); - -err_unregister_subdev: - if (registered) - v4l2_device_unregister_subdev(sd); - - return ret; } /* Test all async sub-devices in a notifier for a match. */ @@ -445,6 +453,10 @@ again: if (ret < 0) return ret; + ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); + if (ret < 0) + return ret; + /* * v4l2_async_match_notify() may lead to registering a * new notifier and thus changing the async subdevs @@ -829,7 +841,11 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asc); if (ret) - goto err_unbind; + goto err_unlock; + + ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); + if (ret) + goto err_unbind_one; ret = v4l2_async_nf_try_complete(notifier); if (ret) @@ -853,9 +869,10 @@ err_unbind: if (subdev_notifier) v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - if (asc) - v4l2_async_unbind_subdev_one(notifier, asc); +err_unbind_one: + v4l2_async_unbind_subdev_one(notifier, asc); +err_unlock: mutex_unlock(&list_lock); sd->owner = NULL; diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 209bc05883bb..79a157975f70 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -424,6 +424,12 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: pr_cont("HEVC_SLICE_PARAMS"); break; + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: + pr_cont("HEVC_EXT_SPS_ST_RPS"); + break; + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS: + pr_cont("HEVC_EXT_SPS_LT_RPS"); + break; case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: pr_cont("HEVC_SCALING_MATRIX"); break; @@ -961,6 +967,8 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, 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_ext_sps_lt_rps *p_hevc_lt_rps; + struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering; @@ -1254,6 +1262,20 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: break; + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: + p_hevc_st_rps = p; + + if (p_hevc_st_rps->flags & ~V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED) + return -EINVAL; + break; + + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS: + p_hevc_lt_rps = p; + + if (p_hevc_lt_rps->flags & ~V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_HDR10_CLL_INFO: break; @@ -2006,6 +2028,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS: elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params); break; + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS: + elem_size = sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps); + break; + case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS: + elem_size = sizeof(struct v4l2_ctrl_hevc_ext_sps_lt_rps); + break; case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX: elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix); break; @@ -2780,7 +2808,8 @@ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl, orientation_ctrl = V4L2_CAMERA_ORIENTATION_EXTERNAL; break; default: - return -EINVAL; + hdl->error = -EINVAL; + return hdl->error; } if (!v4l2_ctrl_new_std_menu(hdl, ctrl_ops, V4L2_CID_CAMERA_ORIENTATION, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index ad41f65374e2..551426c4cd01 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -1135,6 +1135,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_FAULT: return "Faults"; case V4L2_CID_FLASH_CHARGE: return "Charge"; case V4L2_CID_FLASH_READY: return "Ready to Strobe"; + case V4L2_CID_FLASH_DURATION: return "Strobe Duration"; + case V4L2_CID_FLASH_STROBE_OE: return "Strobe Output Enable"; /* JPEG encoder controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -1233,6 +1235,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_STATELESS_HEVC_DECODE_MODE: return "HEVC Decode Mode"; case V4L2_CID_STATELESS_HEVC_START_CODE: return "HEVC Start Code"; case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: return "HEVC Entry Point Offsets"; + case V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS: return "HEVC Short Term Ref Sets"; + case V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS: return "HEVC Long Term Ref Sets"; case V4L2_CID_STATELESS_AV1_SEQUENCE: return "AV1 Sequence Parameters"; case V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY: return "AV1 Tile Group Entry"; case V4L2_CID_STATELESS_AV1_FRAME: return "AV1 Frame Parameters"; @@ -1281,6 +1285,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FLASH_STROBE_STATUS: case V4L2_CID_FLASH_CHARGE: case V4L2_CID_FLASH_READY: + case V4L2_CID_FLASH_STROBE_OE: case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: @@ -1578,6 +1583,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_U32; *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY; break; + case V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS: + *type = V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS; + *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY; + break; + case V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS: + *type = V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS; + *flags |= V4L2_CTRL_FLAG_DYNAMIC_ARRAY; + break; case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR: *type = V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR; break; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index cb153ce42c45..22ec702a4567 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -465,6 +465,9 @@ static int __v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, enum v4l2_mbus_type mbus_type; int rval; + if (!fwnode) + return -EINVAL; + pr_debug("===== begin parsing endpoint %pfw\n", fwnode); fwnode_property_read_u32(fwnode, "bus-type", &bus_type); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 98512ea4cc5b..37d33d4a363d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1544,6 +1544,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break; case V4L2_PIX_FMT_AJPG: descr = "Aspeed JPEG"; break; case V4L2_PIX_FMT_AV1_FRAME: descr = "AV1 Frame"; break; + case V4L2_PIX_FMT_AV1: descr = "AV1 OBU Stream"; break; case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break; case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break; case V4L2_PIX_FMT_HEXTILE: descr = "Hextile Compressed Format"; break; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index fec93c1a9231..b661f483dad3 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -90,6 +90,7 @@ static const char * const m2m_entity_name[] = { * @job_work: worker to run queued jobs. * @job_queue_flags: flags of the queue status, %QUEUE_PAUSED. * @m2m_ops: driver callbacks + * @kref: device reference count */ struct v4l2_m2m_dev { struct v4l2_m2m_ctx *curr_ctx; @@ -109,6 +110,8 @@ struct v4l2_m2m_dev { unsigned long job_queue_flags; const struct v4l2_m2m_ops *m2m_ops; + + struct kref kref; }; static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, @@ -1034,8 +1037,6 @@ static int v4l2_m2m_register_entity(struct media_device *mdev, { struct media_entity *entity; struct media_pad *pads; - char *name; - unsigned int len; int num_pads; int ret; @@ -1068,12 +1069,10 @@ static int v4l2_m2m_register_entity(struct media_device *mdev, entity->info.dev.major = VIDEO_MAJOR; entity->info.dev.minor = vdev->minor; } - len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]); - name = kmalloc(len, GFP_KERNEL); - if (!name) + entity->name = kasprintf(GFP_KERNEL, "%s-%s", vdev->name, + m2m_entity_name[type]); + if (!entity->name) return -ENOMEM; - snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]); - entity->name = name; entity->function = function; ret = media_entity_pads_init(entity, num_pads, pads); @@ -1200,6 +1199,7 @@ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) INIT_LIST_HEAD(&m2m_dev->job_queue); spin_lock_init(&m2m_dev->job_spinlock); INIT_WORK(&m2m_dev->job_work, v4l2_m2m_device_run_work); + kref_init(&m2m_dev->kref); return m2m_dev; } @@ -1211,6 +1211,25 @@ void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev) } EXPORT_SYMBOL_GPL(v4l2_m2m_release); +void v4l2_m2m_get(struct v4l2_m2m_dev *m2m_dev) +{ + kref_get(&m2m_dev->kref); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_get); + +static void v4l2_m2m_release_from_kref(struct kref *kref) +{ + struct v4l2_m2m_dev *m2m_dev = container_of(kref, struct v4l2_m2m_dev, kref); + + v4l2_m2m_release(m2m_dev); +} + +void v4l2_m2m_put(struct v4l2_m2m_dev *m2m_dev) +{ + kref_put(&m2m_dev->kref, v4l2_m2m_release_from_kref); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_put); + struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, void *drv_priv, int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 25e66bf18f5f..66842b975f91 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2112,7 +2112,7 @@ int v4l2_subdev_routing_validate(struct v4l2_subdev *sd, { u32 *remote_pads = NULL; unsigned int i, j; - int ret = -EINVAL; + int ret = -ENXIO; if (disallow & (V4L2_SUBDEV_ROUTING_NO_STREAM_MIX | V4L2_SUBDEV_ROUTING_NO_MULTIPLEXING)) { diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index 3a4eb4f6d3be..a3cd9d3e9ce7 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -2961,11 +2961,8 @@ int atomisp_set_parameters(struct video_device *vdev, * per-frame setting only works for the main output frame. */ param = kvzalloc(sizeof(*param), GFP_KERNEL); - if (!param) { - dev_err(asd->isp->dev, "%s: failed to alloc params buffer\n", - __func__); + if (!param) return -ENOMEM; - } css_param = ¶m->params; } diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c index 6a10d3545278..e422cca55022 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/fc/fc_1.0/ia_css_formats.host.c @@ -35,7 +35,8 @@ ia_css_formats_dump( const struct sh_css_isp_formats_params *formats, unsigned int level) { - if (!formats) return; + if (!formats) + return; ia_css_debug_dtrace(level, "\t%-32s = %d\n", "video_full_range_flag", formats->video_full_range_flag); } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index bf8e6dca40e5..49ce295771e4 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -312,7 +312,7 @@ int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) pr_err("%s(): timeout waiting for MSGSTATE %04x\n", __func__, stat & flags); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } return 0; } @@ -343,7 +343,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) av7110->arm_errors++; return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } if (FW_VERSION(av7110->arm_app) <= 0x261f) @@ -359,7 +359,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) pr_err("%s(): timeout waiting for HANDSHAKE_REG\n", __func__); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } #endif @@ -405,7 +405,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) av7110->arm_errors++; return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } } @@ -433,7 +433,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) __func__, (buf[0] >> 8) & 0xff); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); @@ -559,7 +559,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, return -ETIMEDOUT; } #ifdef _NOHANDSHAKE - msleep(1); + usleep_range(1000, 2000); #endif } @@ -574,7 +574,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } #endif @@ -719,7 +719,7 @@ static int FlushText(struct av7110 *av7110) mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } mutex_unlock(&av7110->dcomlock); return 0; @@ -745,7 +745,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } #ifndef _NOHANDSHAKE start = jiffies; @@ -758,7 +758,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } - msleep(1); + usleep_range(1000, 2000); } #endif for (i = 0; i < length / 2; i++) diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index 0c813860f5b2..93bf47a62e38 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -496,7 +496,7 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) dprintk("delay = %i usec\n", check_count * 10); break; } - udelay(10); + usleep_range(10, 20); } if (valid) break; diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index dd8c7b3233bc..211f67fb92b5 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -23,65 +23,65 @@ * there must be 5 pads: 1 input pad from sensor, and * the 4 virtual channel output pads */ -#define CSI2_SINK_PAD 0 -#define CSI2_NUM_SINK_PADS 1 -#define CSI2_NUM_SRC_PADS 4 -#define CSI2_NUM_PADS 5 +#define CSI2_SINK_PAD 0 +#define CSI2_NUM_SINK_PADS 1 +#define CSI2_NUM_SRC_PADS 4 +#define CSI2_NUM_PADS 5 /* * The default maximum bit-rate per lane in Mbps, if the * source subdev does not provide V4L2_CID_LINK_FREQ. */ -#define CSI2_DEFAULT_MAX_MBPS 849 +#define CSI2_DEFAULT_MAX_MBPS 849 struct csi2_dev { - struct device *dev; - struct v4l2_subdev sd; + struct device *dev; + struct v4l2_subdev sd; struct v4l2_async_notifier notifier; - struct media_pad pad[CSI2_NUM_PADS]; - struct clk *dphy_clk; - struct clk *pllref_clk; - struct clk *pix_clk; /* what is this? */ - void __iomem *base; + struct media_pad pad[CSI2_NUM_PADS]; + struct clk *dphy_clk; + struct clk *pllref_clk; + struct clk *pix_clk; /* what is this? */ + void __iomem *base; - struct v4l2_subdev *remote; - unsigned int remote_pad; - unsigned short data_lanes; + struct v4l2_subdev *remote; + unsigned int remote_pad; + unsigned short data_lanes; /* lock to protect all members below */ struct mutex lock; struct v4l2_mbus_framefmt format_mbus; - int stream_count; - struct v4l2_subdev *src_sd; - bool sink_linked[CSI2_NUM_SRC_PADS]; + int stream_count; + struct v4l2_subdev *src_sd; + bool sink_linked[CSI2_NUM_SRC_PADS]; }; #define DEVICE_NAME "imx6-mipi-csi2" /* Register offsets */ -#define CSI2_VERSION 0x000 -#define CSI2_N_LANES 0x004 -#define CSI2_PHY_SHUTDOWNZ 0x008 -#define CSI2_DPHY_RSTZ 0x00c -#define CSI2_RESETN 0x010 -#define CSI2_PHY_STATE 0x014 -#define PHY_STOPSTATEDATA_BIT 4 -#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n)) -#define PHY_RXCLKACTIVEHS BIT(8) -#define PHY_RXULPSCLKNOT BIT(9) -#define PHY_STOPSTATECLK BIT(10) -#define CSI2_DATA_IDS_1 0x018 -#define CSI2_DATA_IDS_2 0x01c -#define CSI2_ERR1 0x020 -#define CSI2_ERR2 0x024 -#define CSI2_MSK1 0x028 -#define CSI2_MSK2 0x02c -#define CSI2_PHY_TST_CTRL0 0x030 +#define CSI2_VERSION 0x000 +#define CSI2_N_LANES 0x004 +#define CSI2_PHY_SHUTDOWNZ 0x008 +#define CSI2_DPHY_RSTZ 0x00c +#define CSI2_RESETN 0x010 +#define CSI2_PHY_STATE 0x014 +#define PHY_STOPSTATEDATA_BIT 4 +#define PHY_STOPSTATEDATA(n) BIT(PHY_STOPSTATEDATA_BIT + (n)) +#define PHY_RXCLKACTIVEHS BIT(8) +#define PHY_RXULPSCLKNOT BIT(9) +#define PHY_STOPSTATECLK BIT(10) +#define CSI2_DATA_IDS_1 0x018 +#define CSI2_DATA_IDS_2 0x01c +#define CSI2_ERR1 0x020 +#define CSI2_ERR2 0x024 +#define CSI2_MSK1 0x028 +#define CSI2_MSK2 0x02c +#define CSI2_PHY_TST_CTRL0 0x030 #define PHY_TESTCLR BIT(0) #define PHY_TESTCLK BIT(1) -#define CSI2_PHY_TST_CTRL1 0x034 +#define CSI2_PHY_TST_CTRL1 0x034 #define PHY_TESTEN BIT(16) /* * i.MX CSI2IPU Gasket registers follow. The CSI2IPU gasket is @@ -106,13 +106,13 @@ static inline struct csi2_dev *notifier_to_dev(struct v4l2_async_notifier *n) * reference manual is as follows: * * 1. Deassert presetn signal (global reset). - * It's not clear what this "global reset" signal is (maybe APB - * global reset), but in any case this step would be probably - * be carried out during driver load in csi2_probe(). + * It's not clear what this "global reset" signal is (maybe APB + * global reset), but in any case this step would be probably + * be carried out during driver load in csi2_probe(). * * 2. Configure MIPI Camera Sensor to put all Tx lanes in LP-11 state. - * This must be carried out by the MIPI sensor's s_power(ON) subdev - * op. + * This must be carried out by the MIPI sensor's s_power(ON) subdev + * op. * * 3. D-PHY initialization. * 4. CSI2 Controller programming (Set N_LANES, deassert PHY_SHUTDOWNZ, @@ -719,7 +719,6 @@ err_parse: static int csi2_probe(struct platform_device *pdev) { struct csi2_dev *csi2; - struct resource *res; int i, ret; csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL); @@ -767,22 +766,18 @@ static int csi2_probe(struct platform_device *pdev) return PTR_ERR(csi2->pix_clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - v4l2_err(&csi2->sd, "failed to get platform resources\n"); - return -ENODEV; - } - - csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE); - if (!csi2->base) - return -ENOMEM; + csi2->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi2->base)) + return PTR_ERR(csi2->base); - mutex_init(&csi2->lock); + ret = devm_mutex_init(&pdev->dev, &csi2->lock); + if (ret) + return ret; ret = clk_prepare_enable(csi2->pllref_clk); if (ret) { v4l2_err(&csi2->sd, "failed to enable pllref_clk\n"); - goto rmmutex; + return ret; } ret = clk_prepare_enable(csi2->dphy_clk); @@ -805,8 +800,6 @@ clean_notifier: clk_disable_unprepare(csi2->dphy_clk); pllref_off: clk_disable_unprepare(csi2->pllref_clk); -rmmutex: - mutex_destroy(&csi2->lock); return ret; } @@ -820,7 +813,6 @@ static void csi2_remove(struct platform_device *pdev) v4l2_async_unregister_subdev(sd); clk_disable_unprepare(csi2->dphy_clk); clk_disable_unprepare(csi2->pllref_clk); - mutex_destroy(&csi2->lock); media_entity_cleanup(&sd->entity); } diff --git a/drivers/staging/media/ipu7/ipu7-buttress.c b/drivers/staging/media/ipu7/ipu7-buttress.c index e5707f5e300b..40c6c8473357 100644 --- a/drivers/staging/media/ipu7/ipu7-buttress.c +++ b/drivers/staging/media/ipu7/ipu7-buttress.c @@ -342,14 +342,23 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) u32 disable_irqs = 0; u32 irq_status; unsigned int i; + int active; - pm_runtime_get_noresume(dev); + active = pm_runtime_get_if_active(dev); + if (active <= 0) + return IRQ_NONE; pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); + if (pb_local_irq == 0xffffffff) { + dev_warn_once(dev, "invalid PB irq status\n"); + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } + if (pb_local_irq & ~BIT(0)) { dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, pb_local_irq); @@ -370,6 +379,12 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) return IRQ_NONE; } + if (irq_status == 0xffffffff) { + dev_warn_once(dev, "invalid irq status 0x%08x\n", irq_status); + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } + do { writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c index 2d5717883518..3f15af3b4c79 100644 --- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c @@ -124,6 +124,7 @@ static const struct cdr_fbk_cap_prog_params table7[] = { { 1350, 1589, 4 }, { 1590, 1949, 5 }, { 1950, 2499, 6 }, + { 2500, 3500, 7 }, { } }; @@ -838,9 +839,10 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, dwc_phy_write_mask(isys, id, reg + 0x400 * i, reset_thresh, 9, 11); + /* Tuning ITMINRX to 2 for CPHY */ reg = CORE_DIG_CLANE_0_RW_LP_0; for (i = 0; i < trios; i++) - dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); reg = CORE_DIG_CLANE_0_RW_LP_2; for (i = 0; i < trios; i++) @@ -860,7 +862,11 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, for (i = 0; i < (lanes + 1); i++) { reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); - dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); + /* Set GMODE to 2 when CPHY >= 1.5Gsps */ + if (mbps >= 1500) + dwc_phy_write_mask(isys, id, reg, 2U, 3, 4); + else + dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); @@ -930,8 +936,9 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, 7, 12, 14); dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, 0, 8, 10); + /* resistance tuning: 1 for 45ohm, 0 for 50ohm */ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, - 0, 8, 8); + 1, 8, 8); if (aggregation) phy_mode = isys->csi2[0].phy_mode; diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c index ded1986eb8ba..ea35cce4830a 100644 --- a/drivers/staging/media/ipu7/ipu7-mmu.c +++ b/drivers/staging/media/ipu7/ipu7-mmu.c @@ -231,7 +231,7 @@ static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); - for (i = 0; i < ISP_L1PT_PTES; i++) + for (i = 0; i < ISP_L2PT_PTES; i++) pt[i] = mmu_info->dummy_page_pteval; return pt; diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index 5cddc09c72bf..fa5a1867626f 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2620,7 +2620,7 @@ out_ipu_bus_del_devices: if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) ipu7_mmu_cleanup(isp->isys->mmu); if (!IS_ERR_OR_NULL(isp->psys)) - pm_runtime_put(&isp->psys->auxdev.dev); + pm_runtime_put_sync(&isp->psys->auxdev.dev); ipu7_bus_del_devices(pdev); release_firmware(isp->cpd_fw); buttress_exit: @@ -2684,6 +2684,10 @@ static void ipu7_pci_reset_done(struct pci_dev *pdev) */ static int ipu7_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); + + synchronize_irq(pdev->irq); + return 0; } diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c index 604185c00a1a..3c3f6e3fd1ec 100644 --- a/drivers/staging/media/tegra-video/csi.c +++ b/drivers/staging/media/tegra-video/csi.c @@ -835,10 +835,6 @@ static void tegra_csi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#if defined(CONFIG_ARCH_TEGRA_210_SOC) -extern const struct tegra_csi_soc tegra210_csi_soc; -#endif - static const struct of_device_id tegra_csi_of_id_table[] = { #if defined(CONFIG_ARCH_TEGRA_210_SOC) { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc }, diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h index 3e6e5ee1bb1e..609c5952e050 100644 --- a/drivers/staging/media/tegra-video/csi.h +++ b/drivers/staging/media/tegra-video/csi.h @@ -130,6 +130,10 @@ struct tegra_csi_soc { unsigned int tpg_frmrate_table_size; }; +#if defined(CONFIG_ARCH_TEGRA_210_SOC) +extern const struct tegra_csi_soc tegra210_csi_soc; +#endif + /** * struct tegra_csi - NVIDIA Tegra CSI device structure * diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index c9276ff76157..14b327afe045 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -438,7 +438,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, .target = V4L2_SEL_TGT_CROP_BOUNDS, }; struct v4l2_rect *try_crop; - int ret; + int ret = 0; subdev = tegra_channel_get_remote_source_subdev(chan); if (!subdev) @@ -482,8 +482,10 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, } else { ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); - if (ret) - return -EINVAL; + if (ret) { + ret = -EINVAL; + goto out_free; + } try_crop->width = sdsel.r.width; try_crop->height = sdsel.r.height; @@ -495,14 +497,15 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan, ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt); if (ret < 0) - return ret; + goto out_free; v4l2_fill_pix_format(pix, &fmt.format); chan->vi->ops->vi_fmt_align(pix, fmtinfo->bpp); +out_free: __v4l2_subdev_state_free(sd_state); - return 0; + return ret; } static int tegra_channel_try_format(struct file *file, void *fh, diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 5ec717f3afd5..80cd3b113125 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -263,10 +263,6 @@ static void tegra_vip_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -#if defined(CONFIG_ARCH_TEGRA_2x_SOC) -extern const struct tegra_vip_soc tegra20_vip_soc; -#endif - static const struct of_device_id tegra_vip_of_id_table[] = { #if defined(CONFIG_ARCH_TEGRA_2x_SOC) { .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc }, diff --git a/drivers/staging/media/tegra-video/vip.h b/drivers/staging/media/tegra-video/vip.h index 32ceaaccbba2..fdded00447e4 100644 --- a/drivers/staging/media/tegra-video/vip.h +++ b/drivers/staging/media/tegra-video/vip.h @@ -50,6 +50,10 @@ struct tegra_vip_soc { const struct tegra_vip_ops *ops; }; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +extern const struct tegra_vip_soc tegra20_vip_soc; +#endif + /** * struct tegra_vip - NVIDIA Tegra VIP device structure * diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h index 22e0dab0809e..ea92ac623a45 100644 --- a/include/linux/usb/uvc.h +++ b/include/linux/usb/uvc.h @@ -10,6 +10,14 @@ /* ------------------------------------------------------------------------ * GUIDs + * + * The GUID returned by lsusb can be converted to this format with the + * following python snippet: + * + * import uuid + * id = "{01234567-89ab-cdef-0123-456789abcdef}" + * le = uuid.UUID(id).bytes_le + * print("{" + ", ".join([f"0x{b:02x}" for b in le]) + "}") */ #define UVC_GUID_UVC_CAMERA \ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ diff --git a/include/media/dvb_vb2.h b/include/media/dvb_vb2.h index 8cb88452cd6c..8932396d2c99 100644 --- a/include/media/dvb_vb2.h +++ b/include/media/dvb_vb2.h @@ -72,8 +72,6 @@ struct dvb_buffer { /** * struct dvb_vb2_ctx - control struct for VB2 handler * @vb_q: pointer to &struct vb2_queue with videobuf2 queue. - * @mutex: mutex to serialize vb2 operations. Used by - * vb2 core %wait_prepare and %wait_finish operations. * @slock: spin lock used to protect buffer filling at dvb_vb2.c. * @dvb_q: List of buffers that are not filled yet. * @buf: Pointer to the buffer that are currently being filled. @@ -96,7 +94,6 @@ struct dvb_buffer { */ struct dvb_vb2_ctx { struct vb2_queue vb_q; - struct mutex mutex; spinlock_t slock; struct list_head dvb_q; struct dvb_buffer *buf; @@ -114,8 +111,8 @@ struct dvb_vb2_ctx { }; #ifndef CONFIG_DVB_MMAP -static inline int dvb_vb2_init(struct dvb_vb2_ctx *ctx, - const char *name, int non_blocking) +static inline int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, + struct mutex *mutex, int non_blocking) { return 0; }; @@ -124,7 +121,7 @@ static inline int dvb_vb2_release(struct dvb_vb2_ctx *ctx) return 0; }; #define dvb_vb2_is_streaming(ctx) (0) -#define dvb_vb2_fill_buffer(ctx, file, wait, flags) (0) +#define dvb_vb2_fill_buffer(ctx, file, wait, flags, flush) (0) static inline __poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx, struct file *file, @@ -138,10 +135,12 @@ static inline __poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx, * * @ctx: control struct for VB2 handler * @name: name for the VB2 handler + * @mutex: pointer to the mutex that serializes vb2 ioctls * @non_blocking: * if not zero, it means that the device is at non-blocking mode */ -int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int non_blocking); +int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, + struct mutex *mutex, int non_blocking); /** * dvb_vb2_release - Releases the VB2 handler allocated resources and @@ -166,10 +165,12 @@ int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx); * @buffer_flags: * pointer to buffer flags as defined by &enum dmx_buffer_flags. * can be NULL. + * @flush: flush the buffer, even if it isn't full. */ int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx, const unsigned char *src, int len, - enum dmx_buffer_flags *buffer_flags); + enum dmx_buffer_flags *buffer_flags, + bool flush); /** * dvb_vb2_poll - Wrapper to vb2_core_streamon() for Digital TV diff --git a/include/media/media-device.h b/include/media/media-device.h index 53d2a16a70b0..749c327e3c58 100644 --- a/include/media/media-device.h +++ b/include/media/media-device.h @@ -11,6 +11,7 @@ #ifndef _MEDIA_DEVICE_H #define _MEDIA_DEVICE_H +#include <linux/atomic.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/pci.h> @@ -106,6 +107,9 @@ struct media_device_ops { * @ops: Operation handler callbacks * @req_queue_mutex: Serialise the MEDIA_REQUEST_IOC_QUEUE ioctl w.r.t. * other operations that stop or start streaming. + * @num_requests: number of associated requests + * @num_request_objects: number of associated request objects + * @media_dir: DebugFS media directory * @request_id: Used to generate unique request IDs * * This structure represents an abstract high-level media device. It allows easy @@ -179,6 +183,11 @@ struct media_device { const struct media_device_ops *ops; struct mutex req_queue_mutex; + atomic_t num_requests; + atomic_t num_request_objects; + + /* debugfs */ + struct dentry *media_dir; atomic_t request_id; }; diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h index d27c1c646c28..dbcabeffcb57 100644 --- a/include/media/media-devnode.h +++ b/include/media/media-devnode.h @@ -20,9 +20,13 @@ #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> +#include <linux/debugfs.h> struct media_device; +/* debugfs top-level media directory */ +extern struct dentry *media_debugfs_root; + /* * Flag to mark the media_devnode struct as registered. Drivers must not touch * this flag directly, it will be set and cleared by media_devnode_register and diff --git a/include/media/media-request.h b/include/media/media-request.h index bb500b2f9da4..43ed18c11b51 100644 --- a/include/media/media-request.h +++ b/include/media/media-request.h @@ -56,6 +56,9 @@ struct media_request_object; * @access_count: count the number of request accesses that are in progress * @objects: List of @struct media_request_object request objects * @num_incomplete_objects: The number of incomplete objects in the request + * @manual_completion: if true, then the request won't be marked as completed + * when @num_incomplete_objects reaches 0. Call media_request_manual_complete() + * to complete the request after @num_incomplete_objects == 0. * @poll_wait: Wait queue for poll * @lock: Serializes access to this struct */ @@ -68,6 +71,7 @@ struct media_request { unsigned int access_count; struct list_head objects; unsigned int num_incomplete_objects; + bool manual_completion; wait_queue_head_t poll_wait; spinlock_t lock; }; @@ -218,6 +222,38 @@ media_request_get_by_fd(struct media_device *mdev, int request_fd); int media_request_alloc(struct media_device *mdev, int *alloc_fd); +/** + * media_request_mark_manual_completion - Enable manual completion + * + * @req: The request + * + * Mark that the request has to be manually completed by calling + * media_request_manual_complete(). + * + * This function shall be called in the req_queue callback. + */ +static inline void +media_request_mark_manual_completion(struct media_request *req) +{ + req->manual_completion = true; +} + +/** + * media_request_manual_complete - Mark the request as completed + * + * @req: The request + * + * This function completes a request that was marked for manual completion by an + * earlier call to media_request_mark_manual_completion(). The request's + * @manual_completion field is reset to false. + * + * All objects contained in the request must have been completed previously. It + * is an error to call this function otherwise. If such an error occurred, the + * function will WARN and the object completion will be delayed until + * @num_incomplete_objects is 0. + */ +void media_request_manual_complete(struct media_request *req); + #else static inline void media_request_get(struct media_request *req) @@ -256,6 +292,7 @@ struct media_request_object_ops { * struct media_request_object - An opaque object that belongs to a media * request * + * @mdev: Media device this object belongs to * @ops: object's operations * @priv: object's priv pointer * @req: the request this object belongs to (can be NULL) @@ -267,6 +304,7 @@ struct media_request_object_ops { * another struct that contains the actual data for this request object. */ struct media_request_object { + struct media_device *mdev; const struct media_request_object_ops *ops; void *priv; struct media_request *req; @@ -336,7 +374,7 @@ void media_request_object_init(struct media_request_object *obj); * @req: The media request * @ops: The object ops for this object * @priv: A driver-specific priv pointer associated with this object - * @is_buffer: Set to true if the object a buffer object. + * @is_buffer: Set to true if the object is a buffer object. * @obj: The object * * Bind this object to the request and set the ops and priv values of diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 31fc1bee3797..327976b14d50 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -1581,6 +1581,9 @@ int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd); * not overwritten. Callers should register the controls they want to handle * themselves before calling this function. * + * This function will set the control handler's error field on failure, just as + * other functions adding controls to the handler. + * * Return: 0 on success, a negative error code on failure. */ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl, diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index f7c57c776589..cd82e70ccbaa 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -182,7 +182,7 @@ enum v4l2_fwnode_bus_type { /** * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle + * @fwnode: pointer to the endpoint's fwnode handle (may be NULL) * @vep: pointer to the V4L2 fwnode data structure * * This function parses the V4L2 fwnode endpoint specific parameters from the @@ -218,7 +218,7 @@ enum v4l2_fwnode_bus_type { * * Return: %0 on success or a negative error code on failure: * %-ENOMEM on memory allocation failure - * %-EINVAL on parsing failure + * %-EINVAL on parsing failure, including @fwnode == NULL * %-ENXIO on mismatching bus types */ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, @@ -236,7 +236,7 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); /** * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle + * @fwnode: pointer to the endpoint's fwnode handle (may be NULL) * @vep: pointer to the V4L2 fwnode data structure * * This function parses the V4L2 fwnode endpoint specific parameters from the @@ -276,7 +276,7 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); * * Return: %0 on success or a negative error code on failure: * %-ENOMEM on memory allocation failure - * %-EINVAL on parsing failure + * %-EINVAL on parsing failure, including @fwnode == NULL * %-ENXIO on mismatching bus types */ int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode, diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index bf6a09a04dcf..31de25d792b9 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -548,6 +548,27 @@ v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); /** + * v4l2_m2m_get() - take a reference to the m2m_dev structure + * + * @m2m_dev: opaque pointer to the internal data to handle M2M context + * + * This is used to share the M2M device across multiple devices. This + * can be used to avoid scheduling two hardware nodes concurrently. + */ +void v4l2_m2m_get(struct v4l2_m2m_dev *m2m_dev); + +/** + * v4l2_m2m_put() - remove a reference to the m2m_dev structure + * + * @m2m_dev: opaque pointer to the internal data to handle M2M context + * + * Once the M2M device has no more references, v4l2_m2m_release() will be + * called automatically. Users of this method should never call + * v4l2_m2m_release() directly. See v4l2_m2m_get() for more details. + */ +void v4l2_m2m_put(struct v4l2_m2m_dev *m2m_dev); + +/** * v4l2_m2m_ctx_init() - allocate and initialize a m2m context * * @m2m_dev: opaque pointer to the internal data to handle M2M context diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 9b02aeba4108..4424d481d7f7 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -351,13 +351,6 @@ struct vb2_buffer { * \*num_buffers are being allocated additionally to * the buffers already allocated. If either \*num_planes * or the requested sizes are invalid callback must return %-EINVAL. - * @wait_prepare: release any locks taken while calling vb2 functions; - * it is called before an ioctl needs to wait for a new - * buffer to arrive; required to avoid a deadlock in - * blocking access type. - * @wait_finish: reacquire all locks released in the previous callback; - * required to continue operation after sleeping while - * waiting for a new buffer to arrive. * @buf_out_validate: called when the output buffer is prepared or queued * to a request; drivers can use this to validate * userspace-provided information; this is required only @@ -436,9 +429,6 @@ struct vb2_ops { unsigned int *num_buffers, unsigned int *num_planes, unsigned int sizes[], struct device *alloc_devs[]); - void (*wait_prepare)(struct vb2_queue *q); - void (*wait_finish)(struct vb2_queue *q); - int (*buf_out_validate)(struct vb2_buffer *vb); int (*buf_init)(struct vb2_buffer *vb); int (*buf_prepare)(struct vb2_buffer *vb); @@ -521,10 +511,10 @@ struct vb2_buf_ops { * @non_coherent_mem: when set queue will attempt to allocate buffers using * non-coherent memory. * @lock: pointer to a mutex that protects the &struct vb2_queue. The - * driver can set this to a mutex to let the v4l2 core serialize - * the queuing ioctls. If the driver wants to handle locking - * itself, then this should be set to NULL. This lock is not used - * by the videobuf2 core API. + * driver must set this to a mutex to let the v4l2 core serialize + * the queuing ioctls. This lock is used when waiting for a new + * buffer to arrive: the lock is released, we wait for the new + * buffer, and then retaken. * @owner: The filehandle that 'owns' the buffers, i.e. the filehandle * that called reqbufs, create_buffers or started fileio. * This field is not used by the videobuf2 core API, but it allows @@ -680,8 +670,6 @@ struct vb2_queue { * called. Used to check for unbalanced ops. */ u32 cnt_queue_setup; - u32 cnt_wait_prepare; - u32 cnt_wait_finish; u32 cnt_prepare_streaming; u32 cnt_start_streaming; u32 cnt_stop_streaming; @@ -766,8 +754,7 @@ void vb2_discard_done(struct vb2_queue *q); * @q: pointer to &struct vb2_queue with videobuf2 queue. * * This function will wait until all buffers that have been given to the driver - * by &vb2_ops->buf_queue are given back to vb2 with vb2_buffer_done(). It - * doesn't call &vb2_ops->wait_prepare/&vb2_ops->wait_finish pair. + * by &vb2_ops->buf_queue are given back to vb2 with vb2_buffer_done(). * It is intended to be called with all locks taken, for example from * &vb2_ops->stop_streaming callback. */ diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 77ce8238ab30..71d2864fb235 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -367,24 +367,6 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, */ void vb2_video_unregister_device(struct video_device *vdev); -/** - * vb2_ops_wait_prepare - helper function to lock a struct &vb2_queue - * - * @vq: pointer to &struct vb2_queue - * - * ..note:: only use if vq->lock is non-NULL. - */ -void vb2_ops_wait_prepare(struct vb2_queue *vq); - -/** - * vb2_ops_wait_finish - helper function to unlock a struct &vb2_queue - * - * @vq: pointer to &struct vb2_queue - * - * ..note:: only use if vq->lock is non-NULL. - */ -void vb2_ops_wait_finish(struct vb2_queue *vq); - struct media_request; int vb2_request_validate(struct media_request *req); void vb2_request_queue(struct media_request *req); diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index f84ed133a6c9..68dd0c4e47b2 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -1192,6 +1192,8 @@ enum v4l2_flash_strobe_source { #define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11) #define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12) +#define V4L2_CID_FLASH_DURATION (V4L2_CID_FLASH_CLASS_BASE + 13) +#define V4L2_CID_FLASH_STROBE_OE (V4L2_CID_FLASH_CLASS_BASE + 14) /* JPEG-class control IDs */ @@ -2099,6 +2101,8 @@ struct v4l2_ctrl_mpeg2_quantisation { #define V4L2_CID_STATELESS_HEVC_DECODE_MODE (V4L2_CID_CODEC_STATELESS_BASE + 405) #define V4L2_CID_STATELESS_HEVC_START_CODE (V4L2_CID_CODEC_STATELESS_BASE + 406) #define V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS (V4L2_CID_CODEC_STATELESS_BASE + 407) +#define V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS (V4L2_CID_CODEC_STATELESS_BASE + 408) +#define V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS (V4L2_CID_CODEC_STATELESS_BASE + 409) enum v4l2_stateless_hevc_decode_mode { V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED, @@ -2554,6 +2558,65 @@ struct v4l2_ctrl_hevc_scaling_matrix { __u8 scaling_list_dc_coef_32x32[2]; }; +#define V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED 0x1 + +/* + * struct v4l2_ctrl_hevc_ext_sps_st_rps - HEVC short term RPS parameters + * + * Dynamic size 1-dimension array for short term RPS. The number of elements + * is v4l2_ctrl_hevc_sps::num_short_term_ref_pic_sets. It can contain up to 65 elements. + * + * @delta_idx_minus1: Specifies the delta compare to the index. See details in section 7.4.8 + * "Short-term reference picture set semantics" of the specification. + * @delta_rps_sign: Sign of the delta as specified in section 7.4.8 "Short-term reference picture + * set semantics" of the specification. + * @abs_delta_rps_minus1: Absolute delta RPS as specified in section 7.4.8 "Short-term reference + * picture set semantics" of the specification. + * @num_negative_pics: Number of short-term RPS entries that have picture order count values less + * than the picture order count value of the current picture. + * @num_positive_pics: Number of short-term RPS entries that have picture order count values + * greater than the picture order count value of the current picture. + * @used_by_curr_pic: Bit j specifies if short-term RPS j is used by the current picture. + * @use_delta_flag: Bit j equals to 1 specifies that the j-th entry in the source candidate + * short-term RPS is included in this candidate short-term RPS. + * @delta_poc_s0_minus1: Specifies the negative picture order count delta for the i-th entry in + * the short-term RPS. See details in section 7.4.8 "Short-term reference + * picture set semantics" of the specification. + * @delta_poc_s1_minus1: Specifies the positive picture order count delta for the i-th entry in + * the short-term RPS. See details in section 7.4.8 "Short-term reference + * picture set semantics" of the specification. + * @flags: See V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_{} + */ +struct v4l2_ctrl_hevc_ext_sps_st_rps { + __u8 delta_idx_minus1; + __u8 delta_rps_sign; + __u8 num_negative_pics; + __u8 num_positive_pics; + __u32 used_by_curr_pic; + __u32 use_delta_flag; + __u16 abs_delta_rps_minus1; + __u16 delta_poc_s0_minus1[16]; + __u16 delta_poc_s1_minus1[16]; + __u16 flags; +}; + +#define V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT 0x1 + +/* + * struct v4l2_ctrl_hevc_ext_sps_lt_rps - HEVC long term RPS parameters + * + * Dynamic size 1-dimension array for long term RPS. The number of elements + * is v4l2_ctrl_hevc_sps::num_long_term_ref_pics_sps. It can contain up to 65 elements. + * + * @lt_ref_pic_poc_lsb_sps: picture order count modulo MaxPicOrderCntLsb of the i-th candidate + * long-term reference picture. + * @flags: See V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_{} + */ +struct v4l2_ctrl_hevc_ext_sps_lt_rps { + __u16 lt_ref_pic_poc_lsb_sps; + __u16 flags; +}; + /* Stateless VP9 controls */ #define V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED 0x1 diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index add08188f068..eda4492e40dc 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -775,6 +775,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_H264_SLICE v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ #define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */ #define V4L2_PIX_FMT_AV1_FRAME v4l2_fourcc('A', 'V', '1', 'F') /* AV1 parsed frame */ +#define V4L2_PIX_FMT_AV1 v4l2_fourcc('A', 'V', '0', '1') /* AV1 */ #define V4L2_PIX_FMT_SPK v4l2_fourcc('S', 'P', 'K', '0') /* Sorenson Spark */ #define V4L2_PIX_FMT_RV30 v4l2_fourcc('R', 'V', '3', '0') /* RealVideo 8 */ #define V4L2_PIX_FMT_RV40 v4l2_fourcc('R', 'V', '4', '0') /* RealVideo 9 & 10 */ @@ -1985,6 +1986,8 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS = 0x0272, V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX = 0x0273, V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS = 0x0274, + V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS = 0x0275, + V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS = 0x0276, V4L2_CTRL_TYPE_AV1_SEQUENCE = 0x280, V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY = 0x281, |
