diff options
343 files changed, 14879 insertions, 3012 deletions
@@ -244,6 +244,7 @@ Santosh Shilimkar <ssantosh@kernel.org> Santosh Shilimkar <santosh.shilimkar@oracle.org> Sascha Hauer <s.hauer@pengutronix.de> S.Çağlar Onur <caglar@pardus.org.tr> +Sakari Ailus <sakari.ailus@linux.intel.com> <sakari.ailus@iki.fi> Sean Nyekjaer <sean@geanix.com> <sean.nyekjaer@prevas.dk> Sebastian Reichel <sre@kernel.org> <sre@debian.org> Sebastian Reichel <sre@kernel.org> <sebastian.reichel@collabora.co.uk> diff --git a/Documentation/devicetree/bindings/display/connector/analog-tv-connector.txt b/Documentation/devicetree/bindings/display/connector/analog-tv-connector.txt index 0c0970c210ab..883bcb2604c7 100644 --- a/Documentation/devicetree/bindings/display/connector/analog-tv-connector.txt +++ b/Documentation/devicetree/bindings/display/connector/analog-tv-connector.txt @@ -6,16 +6,22 @@ Required properties: Optional properties: - label: a symbolic name for the connector +- sdtv-standards: limit the supported TV standards on a connector to the given + ones. If not specified all TV standards are allowed. + Possible TV standards are defined in + include/dt-bindings/display/sdtv-standards.h. Required nodes: - Video port for TV input Example ------- +#include <dt-bindings/display/sdtv-standards.h> tv: connector { compatible = "composite-video-connector"; label = "tv"; + sdtv-standards = <(SDTV_STD_PAL | SDTV_STD_NTSC)>; port { tv_connector_in: endpoint { diff --git a/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml b/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml new file mode 100644 index 000000000000..75196d11da58 --- /dev/null +++ b/Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/allwinner,sun8i-a83t-de2-rotate.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A83T DE2 Rotate Device Tree Bindings + +maintainers: + - Jernej Skrabec <jernej.skrabec@siol.net> + - Chen-Yu Tsai <wens@csie.org> + - Maxime Ripard <mripard@kernel.org> + +description: |- + The Allwinner A83T and A64 have a rotation core used for + rotating and flipping images. + +properties: + compatible: + oneOf: + - const: allwinner,sun8i-a83t-de2-rotate + - items: + - const: allwinner,sun50i-a64-de2-rotate + - const: allwinner,sun8i-a83t-de2-rotate + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Rotate interface clock + - description: Rotate module clock + + clock-names: + items: + - const: bus + - const: mod + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/sun8i-de2.h> + #include <dt-bindings/reset/sun8i-de2.h> + + rotate: rotate@1020000 { + compatible = "allwinner,sun8i-a83t-de2-rotate"; + reg = <0x1020000 0x10000>; + interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&display_clocks CLK_BUS_ROT>, + <&display_clocks CLK_ROT>; + clock-names = "bus", + "mod"; + resets = <&display_clocks RST_ROT>; + }; + +... diff --git a/Documentation/devicetree/bindings/media/allwinner,sun8i-h3-deinterlace.yaml b/Documentation/devicetree/bindings/media/allwinner,sun8i-h3-deinterlace.yaml index 2e40f700e84f..8707df613f6c 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun8i-h3-deinterlace.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun8i-h3-deinterlace.yaml @@ -17,7 +17,11 @@ description: |- properties: compatible: - const: allwinner,sun8i-h3-deinterlace + oneOf: + - const: allwinner,sun8i-h3-deinterlace + - items: + - const: allwinner,sun50i-a64-deinterlace + - const: allwinner,sun8i-h3-deinterlace reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt index ce2894506e1f..d2ca32512272 100644 --- a/Documentation/devicetree/bindings/media/aspeed-video.txt +++ b/Documentation/devicetree/bindings/media/aspeed-video.txt @@ -1,11 +1,12 @@ * Device tree bindings for Aspeed Video Engine -The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can +The Video Engine (VE) embedded in the Aspeed AST2400/2500/2600 SOCs can capture and compress video data from digital or analog sources. Required properties: - compatible: "aspeed,ast2400-video-engine" or - "aspeed,ast2500-video-engine" + "aspeed,ast2500-video-engine" or + "aspeed,ast2600-video-engine" - reg: contains the offset and length of the VE memory region - clocks: clock specifiers for the syscon clocks associated with the VE (ordering must match the clock-names property) diff --git a/Documentation/devicetree/bindings/media/i2c/imx219.yaml b/Documentation/devicetree/bindings/media/i2c/imx219.yaml new file mode 100644 index 000000000000..32d6b693274f --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/imx219.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor + +maintainers: + - Dave Stevenson <dave.stevenson@raspberrypi.com> + +description: |- + The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor + with an active array size of 3280H x 2464V. It is programmable through + I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet. + Image data is sent through MIPI CSI-2, which is configured as either 2 or + 4 data lanes. + +properties: + compatible: + const: sony,imx219 + + reg: + description: I2C device address + maxItems: 1 + + clocks: + maxItems: 1 + + VDIG-supply: + description: + Digital I/O voltage supply, 1.8 volts + + VANA-supply: + description: + Analog voltage supply, 2.8 volts + + VDDL-supply: + description: + Digital core voltage supply, 1.2 volts + + reset-gpios: + description: |- + Reference to the GPIO connected to the xclr pin, if any. + Must be released (set high) after all supplies are applied. + + # See ../video-interfaces.txt for more details + port: + type: object + properties: + endpoint: + type: object + properties: + data-lanes: + description: |- + The sensor supports either two-lane, or four-lane operation. + If this property is omitted four-lane operation is assumed. + For two-lane operation the property must be set to <1 2>. + items: + - const: 1 + - const: 2 + + clock-noncontinuous: + type: boolean + description: |- + MIPI CSI-2 clock is non-continuous if this property is present, + otherwise it's continuous. + + link-frequencies: + allOf: + - $ref: /schemas/types.yaml#/definitions/uint64-array + description: + Allowed data bus frequencies. + + required: + - link-frequencies + +required: + - compatible + - reg + - clocks + - VANA-supply + - VDIG-supply + - VDDL-supply + - port + +additionalProperties: false + +examples: + - | + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + imx219: sensor@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&imx219_clk>; + VANA-supply = <&imx219_vana>; /* 2.8v */ + VDIG-supply = <&imx219_vdig>; /* 1.8v */ + VDDL-supply = <&imx219_vddl>; /* 1.2v */ + + port { + imx219_0: endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <456000000>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/tvp5150.txt b/Documentation/devicetree/bindings/media/i2c/tvp5150.txt index 8c0fc1a26bf0..6c88ce858d08 100644 --- a/Documentation/devicetree/bindings/media/i2c/tvp5150.txt +++ b/Documentation/devicetree/bindings/media/i2c/tvp5150.txt @@ -5,38 +5,150 @@ The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats. Required Properties: -- compatible: value must be "ti,tvp5150" -- reg: I2C slave address +==================== +- compatible: Value must be "ti,tvp5150". +- reg: I2C slave address. Optional Properties: -- pdn-gpios: phandle for the GPIO connected to the PDN pin, if any. -- reset-gpios: phandle for the GPIO connected to the RESETB pin, if any. +==================== +- pdn-gpios: Phandle for the GPIO connected to the PDN pin, if any. +- reset-gpios: Phandle for the GPIO connected to the RESETB pin, if any. -The device node must contain one 'port' child node for its digital output -video port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. +The device node must contain one 'port' child node per device physical input +and output port, in accordance with the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes +are numbered as follows -Required Endpoint Properties for parallel synchronization: + Name Type Port + -------------------------------------- + AIP1A sink 0 + AIP1B sink 1 + Y-OUT src 2 -- hsync-active: active state of the HSYNC signal. Must be <1> (HIGH). -- vsync-active: active state of the VSYNC signal. Must be <1> (HIGH). -- field-even-active: field signal level during the even field data - transmission. Must be <0>. +The device node must contain at least one sink port and the src port. Each input +port must be linked to an endpoint defined in [1]. The port/connector layout is +as follows -If none of hsync-active, vsync-active and field-even-active is specified, -the endpoint is assumed to use embedded BT.656 synchronization. +tvp-5150 port@0 (AIP1A) + endpoint@0 -----------> Comp0-Con port + endpoint@1 ------+----> Svideo-Con port +tvp-5150 port@1 (AIP1B) | + endpoint@1 ------+ + endpoint@0 -----------> Comp1-Con port +tvp-5150 port@2 + endpoint (video bitstream output at YOUT[0-7] parallel bus) -Example: +Required Endpoint Properties for parallel synchronization on output port: +========================================================================= + +- hsync-active: Active state of the HSYNC signal. Must be <1> (HIGH). +- vsync-active: Active state of the VSYNC signal. Must be <1> (HIGH). +- field-even-active: Field signal level during the even field data + transmission. Must be <0>. + +Note: Do not specify any of these properties if you want to use the embedded + BT.656 synchronization. + +Optional Connector Properties: +============================== + +- sdtv-standards: Set the possible signals to which the hardware tries to lock + instead of using the autodetection mechnism. Please look at + [1] for more information. + +[1] Documentation/devicetree/bindings/display/connector/analog-tv-connector.txt. + +Example - three input sources: +#include <dt-bindings/display/sdtv-standards.h> + +comp_connector_0 { + compatible = "composite-video-connector"; + label = "Composite0"; + sdtv-standards = <SDTV_STD_PAL_M>; /* limit to pal-m signals */ + + port { + composite0_to_tvp5150: endpoint { + remote-endpoint = <&tvp5150_to_composite0>; + }; + }; +}; + +comp_connector_1 { + compatible = "composite-video-connector"; + label = "Composite1"; + sdtv-standards = <SDTV_STD_NTSC_M>; /* limit to ntsc-m signals */ + + port { + composite1_to_tvp5150: endpoint { + remote-endpoint = <&tvp5150_to_composite1>; + }; + }; +}; + +svideo_connector { + compatible = "svideo-connector"; + label = "S-Video"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + svideo_luma_to_tvp5150: endpoint@0 { + reg = <0>; + remote-endpoint = <&tvp5150_to_svideo_luma>; + }; + + svideo_chroma_to_tvp5150: endpoint@1 { + reg = <1>; + remote-endpoint = <&tvp5150_to_svideo_chroma>; + }; + }; +}; &i2c2 { - ... tvp5150@5c { compatible = "ti,tvp5150"; reg = <0x5c>; pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>; reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + tvp5150_to_composite0: endpoint@0 { + reg = <0>; + remote-endpoint = <&composite0_to_tvp5150>; + }; + + tvp5150_to_svideo_luma: endpoint@1 { + reg = <1>; + remote-endpoint = <&svideo_luma_to_tvp5150>; + }; + }; + + port@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + tvp5150_to_composite1: endpoint@0 { + reg = <0>; + remote-endpoint = <&composite1_to_tvp5150>; + }; + + tvp5150_to_svideo_chroma: endpoint@1 { + reg = <1>; + remote-endpoint = <&svideo_chroma_to_tvp5150>; + }; + }; + + port@2 { + reg = <2>; - port { tvp5150_1: endpoint { remote-endpoint = <&ccdc_ep>; }; diff --git a/Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml b/Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml new file mode 100644 index 000000000000..a2d1cd77c1e2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/nxp,imx8mq-vpu.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Hantro G1/G2 VPU codecs implemented on i.MX8MQ SoCs + +maintainers: + - Philipp Zabel <p.zabel@pengutronix.de> + +description: + Hantro G1/G2 video decode accelerators present on i.MX8MQ SoCs. + +properties: + compatible: + const: nxp,imx8mq-vpu + + reg: + maxItems: 3 + + reg-names: + items: + - const: g1 + - const: g2 + - const: ctrl + + interrupts: + maxItems: 2 + + interrupt-names: + items: + - const: g1 + - const: g2 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: g1 + - const: g2 + - const: bus + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - clocks + - clock-names + +examples: + - | + #include <dt-bindings/clock/imx8mq-clock.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + vpu: video-codec@38300000 { + compatible = "nxp,imx8mq-vpu"; + reg = <0x38300000 0x10000>, + <0x38310000 0x10000>, + <0x38320000 0x10000>; + reg-names = "g1", "g2", "ctrl"; + interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "g1", "g2"; + clocks = <&clk IMX8MQ_CLK_VPU_G1_ROOT>, + <&clk IMX8MQ_CLK_VPU_G2_ROOT>, + <&clk IMX8MQ_CLK_VPU_DEC_ROOT>; + clock-names = "g1", "g2", "bus"; + power-domains = <&pgc_vpu>; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml new file mode 100644 index 000000000000..f9606df02d70 --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8916-venus.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,msm8916-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov <stanimir.varbanov@linaro.org> + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,msm8916-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: core + - const: iface + - const: bus + + iommus: + maxItems: 1 + + memory-region: + maxItems: 1 + + video-decoder: + type: object + + properties: + compatible: + const: "venus-decoder" + + required: + - compatible + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: "venus-encoder" + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,gcc-msm8916.h> + + video-codec@1d00000 { + compatible = "qcom,msm8916-venus"; + reg = <0x01d00000 0xff000>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, + <&gcc GCC_VENUS0_AHB_CLK>, + <&gcc GCC_VENUS0_AXI_CLK>; + clock-names = "core", "iface", "bus"; + power-domains = <&gcc VENUS_GDSC>; + iommus = <&apps_iommu 5>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + }; + + video-encoder { + compatible = "venus-encoder"; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml new file mode 100644 index 000000000000..fa0dc6c47f1d --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml @@ -0,0 +1,172 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,msm8996-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov <stanimir.varbanov@linaro.org> + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,msm8996-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: mbus + + iommus: + maxItems: 20 + + memory-region: + maxItems: 1 + + video-decoder: + type: object + + properties: + compatible: + const: venus-decoder + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: venus-encoder + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,mmcc-msm8996.h> + + video-codec@c00000 { + compatible = "qcom,msm8996-venus"; + reg = <0x00c00000 0xff000>; + interrupts = <GIC_SPI 287 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mmcc VIDEO_CORE_CLK>, + <&mmcc VIDEO_AHB_CLK>, + <&mmcc VIDEO_AXI_CLK>, + <&mmcc VIDEO_MAXI_CLK>; + clock-names = "core", "iface", "bus", "mbus"; + power-domains = <&mmcc VENUS_GDSC>; + iommus = <&venus_smmu 0x00>, + <&venus_smmu 0x01>, + <&venus_smmu 0x0a>, + <&venus_smmu 0x07>, + <&venus_smmu 0x0e>, + <&venus_smmu 0x0f>, + <&venus_smmu 0x08>, + <&venus_smmu 0x09>, + <&venus_smmu 0x0b>, + <&venus_smmu 0x0c>, + <&venus_smmu 0x0d>, + <&venus_smmu 0x10>, + <&venus_smmu 0x11>, + <&venus_smmu 0x21>, + <&venus_smmu 0x28>, + <&venus_smmu 0x29>, + <&venus_smmu 0x2b>, + <&venus_smmu 0x2c>, + <&venus_smmu 0x2d>, + <&venus_smmu 0x31>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + clocks = <&mmcc VIDEO_SUBCORE0_CLK>; + clock-names = "core"; + power-domains = <&mmcc VENUS_CORE0_GDSC>; + }; + + video-encoder { + compatible = "venus-encoder"; + clocks = <&mmcc VIDEO_SUBCORE1_CLK>; + clock-names = "core"; + power-domains = <&mmcc VENUS_CORE1_GDSC>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml new file mode 100644 index 000000000000..764affa4877e --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sc7180-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov <stanimir.varbanov@linaro.org> + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sc7180-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + + clocks: + maxItems: 5 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec0_bus + + iommus: + maxItems: 1 + + memory-region: + maxItems: 1 + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: video-mem + - const: cpu-cfg + + video-decoder: + type: object + + properties: + compatible: + const: venus-decoder + + required: + - compatible + + additionalProperties: false + + video-encoder: + type: object + + properties: + compatible: + const: venus-encoder + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + - clocks + - clock-names + - iommus + - memory-region + - video-decoder + - video-encoder + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,videocc-sc7180.h> + + venus: video-codec@aa00000 { + compatible = "qcom,sc7180-venus"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&videocc VENUS_GDSC>, + <&videocc VCODEC0_GDSC>; + power-domain-names = "venus", "vcodec0"; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; + clock-names = "core", "iface", "bus", + "vcodec0_core", "vcodec0_bus"; + iommus = <&apps_smmu 0x0c00 0x60>; + memory-region = <&venus_mem>; + + video-decoder { + compatible = "venus-decoder"; + }; + + video-encoder { + compatible = "venus-encoder"; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml new file mode 100644 index 000000000000..8552f4ab907e --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-venus-v2.yaml @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sdm845-venus-v2.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov <stanimir.varbanov@linaro.org> + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sdm845-venus-v2 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 3 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: vcodec1 + + clocks: + maxItems: 7 + + clock-names: + items: + - const: core + - const: iface + - const: bus + - const: vcodec0_core + - const: vcodec0_bus + - const: vcodec1_core + - const: vcodec1_bus + + iommus: + maxItems: 2 + + memory-region: + maxItems: 1 + + video-core0: + type: object + + properties: + compatible: + const: venus-decoder + + required: + - compatible + + additionalProperties: false + + video-core1: + type: object + + properties: + compatible: + const: venus-encoder + + required: + - compatible + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - power-domain-names + - clocks + - clock-names + - iommus + - memory-region + - video-core0 + - video-core1 + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,videocc-sdm845.h> + + video-codec@aa00000 { + compatible = "qcom,sdm845-venus-v2"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>, + <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; + clock-names = "core", "iface", "bus", + "vcodec0_core", "vcodec0_bus", + "vcodec1_core", "vcodec1_bus"; + power-domains = <&videocc VENUS_GDSC>, + <&videocc VCODEC0_GDSC>, + <&videocc VCODEC1_GDSC>; + power-domain-names = "venus", "vcodec0", "vcodec1"; + iommus = <&apps_smmu 0x10a0 0x8>, + <&apps_smmu 0x10b0 0x0>; + memory-region = <&venus_mem>; + + video-core0 { + compatible = "venus-decoder"; + }; + + video-core1 { + compatible = "venus-encoder"; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml new file mode 100644 index 000000000000..05cabe4e893a --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,sdm845-venus.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/media/qcom,sdm845-venus.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm Venus video encode and decode accelerators + +maintainers: + - Stanimir Varbanov <stanimir.varbanov@linaro.org> + +description: | + The Venus IP is a video encode and decode accelerator present + on Qualcomm platforms + +properties: + compatible: + const: qcom,sdm845-venus + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + + clocks: + maxItems: 3 + + clock-names: + items: + - const: core + - const: iface + - const: bus + + iommus: + maxItems: 2 + + memory-region: + maxItems: 1 + + video-core0: + type: object + + properties: + compatible: + const: venus-decoder + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: bus + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-core1: + type: object + + properties: + compatible: + const: venus-encoder + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: bus + + power-domains: + maxItems: 1 + + required: + - compatible + - clocks + - clock-names + - power-domains + + additionalProperties: false + + video-firmware: + type: object + + description: | + Firmware subnode is needed when the platform does not + have TrustZone. + + properties: + iommus: + maxItems: 1 + + required: + - iommus + +required: + - compatible + - reg + - interrupts + - power-domains + - clocks + - clock-names + - iommus + - memory-region + - video-core0 + - video-core1 + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/qcom,videocc-sdm845.h> + + video-codec@aa00000 { + compatible = "qcom,sdm845-venus"; + reg = <0 0x0aa00000 0 0xff000>; + interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&videocc VIDEO_CC_VENUS_CTL_CORE_CLK>, + <&videocc VIDEO_CC_VENUS_AHB_CLK>, + <&videocc VIDEO_CC_VENUS_CTL_AXI_CLK>; + clock-names = "core", "iface", "bus"; + power-domains = <&videocc VENUS_GDSC>; + iommus = <&apps_smmu 0x10a0 0x8>, + <&apps_smmu 0x10b0 0x0>; + memory-region = <&venus_mem>; + + video-core0 { + compatible = "venus-decoder"; + clocks = <&videocc VIDEO_CC_VCODEC0_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC0_AXI_CLK>; + clock-names = "core", "bus"; + power-domains = <&videocc VCODEC0_GDSC>; + }; + + video-core1 { + compatible = "venus-encoder"; + clocks = <&videocc VIDEO_CC_VCODEC1_CORE_CLK>, + <&videocc VIDEO_CC_VCODEC1_AXI_CLK>; + clock-names = "core", "bus"; + power-domains = <&videocc VCODEC1_GDSC>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/qcom,venus.txt b/Documentation/devicetree/bindings/media/qcom,venus.txt deleted file mode 100644 index b602c4c025e7..000000000000 --- a/Documentation/devicetree/bindings/media/qcom,venus.txt +++ /dev/null @@ -1,120 +0,0 @@ -* Qualcomm Venus video encoder/decoder accelerators - -- compatible: - Usage: required - Value type: <stringlist> - Definition: Value should contain one of: - - "qcom,msm8916-venus" - - "qcom,msm8996-venus" - - "qcom,sdm845-venus" -- reg: - Usage: required - Value type: <prop-encoded-array> - Definition: Register base address and length of the register map. -- interrupts: - Usage: required - Value type: <prop-encoded-array> - Definition: Should contain interrupt line number. -- clocks: - Usage: required - Value type: <prop-encoded-array> - Definition: A List of phandle and clock specifier pairs as listed - in clock-names property. -- clock-names: - Usage: required for msm8916 - Value type: <stringlist> - Definition: Should contain the following entries: - - "core" Core video accelerator clock - - "iface" Video accelerator AHB clock - - "bus" Video accelerator AXI clock -- clock-names: - Usage: required for msm8996 - Value type: <stringlist> - Definition: Should contain the following entries: - - "core" Core video accelerator clock - - "iface" Video accelerator AHB clock - - "bus" Video accelerator AXI clock - - "mbus" Video MAXI clock -- power-domains: - Usage: required - Value type: <prop-encoded-array> - Definition: A phandle and power domain specifier pairs to the - power domain which is responsible for collapsing - and restoring power to the peripheral. -- iommus: - Usage: required - Value type: <prop-encoded-array> - Definition: A list of phandle and IOMMU specifier pairs. -- memory-region: - Usage: required - Value type: <phandle> - Definition: reference to the reserved-memory for the firmware - memory region. - -* Subnodes -The Venus video-codec node must contain two subnodes representing -video-decoder and video-encoder, and one optional firmware subnode. -Firmware subnode is needed when the platform does not have TrustZone. - -Every of video-encoder or video-decoder subnode should have: - -- compatible: - Usage: required - Value type: <stringlist> - Definition: Value should contain "venus-decoder" or "venus-encoder" -- clocks: - Usage: required for msm8996 - Value type: <prop-encoded-array> - Definition: A List of phandle and clock specifier pairs as listed - in clock-names property. -- clock-names: - Usage: required for msm8996 - Value type: <stringlist> - Definition: Should contain the following entries: - - "core" Subcore video accelerator clock - -- power-domains: - Usage: required for msm8996 - Value type: <prop-encoded-array> - Definition: A phandle and power domain specifier pairs to the - power domain which is responsible for collapsing - and restoring power to the subcore. - -The firmware subnode must have: - -- iommus: - Usage: required - Value type: <prop-encoded-array> - Definition: A list of phandle and IOMMU specifier pairs. - -* An Example - video-codec@1d00000 { - compatible = "qcom,msm8916-venus"; - reg = <0x01d00000 0xff000>; - interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>, - <&gcc GCC_VENUS0_AHB_CLK>, - <&gcc GCC_VENUS0_AXI_CLK>; - clock-names = "core", "iface", "bus"; - power-domains = <&gcc VENUS_GDSC>; - iommus = <&apps_iommu 5>; - memory-region = <&venus_mem>; - - video-decoder { - compatible = "venus-decoder"; - clocks = <&mmcc VIDEO_SUBCORE0_CLK>; - clock-names = "core"; - power-domains = <&mmcc VENUS_CORE0_GDSC>; - }; - - video-encoder { - compatible = "venus-encoder"; - clocks = <&mmcc VIDEO_SUBCORE1_CLK>; - clock-names = "core"; - power-domains = <&mmcc VENUS_CORE1_GDSC>; - }; - - video-firmware { - iommus = <&apps_iommu 0x10b2 0x0>; - }; - }; diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index a64ee038d235..b27c9385d490 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -143,6 +143,7 @@ properties: - rc-videomate-k100 - rc-videomate-s350 - rc-videomate-tv-pvr + - rc-videostrong-kii-pro - rc-wetek-hub - rc-wetek-play2 - rc-winfast diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.txt b/Documentation/devicetree/bindings/media/rockchip-rga.txt index fd5276abfad6..c53a8e5133f6 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.txt +++ b/Documentation/devicetree/bindings/media/rockchip-rga.txt @@ -6,8 +6,9 @@ BitBLT, alpha blending and image blur/sharpness. Required properties: - compatible: value should be one of the following - "rockchip,rk3288-rga"; - "rockchip,rk3399-rga"; + "rockchip,rk3228-rga", "rockchip,rk3288-rga": for Rockchip RK3228 + "rockchip,rk3288-rga": for Rockchip RK3288 + "rockchip,rk3399-rga": for Rockchip RK3399 - interrupts: RGA interrupt specifier. diff --git a/Documentation/media/kapi/csi2.rst b/Documentation/media/kapi/csi2.rst index 030a5c41ec75..e111ff7bfd3d 100644 --- a/Documentation/media/kapi/csi2.rst +++ b/Documentation/media/kapi/csi2.rst @@ -74,7 +74,7 @@ Before the receiver driver may enable the CSI-2 transmitter by using the :c:type:`v4l2_subdev_video_ops`->s_stream(), it must have powered the transmitter up by using the :c:type:`v4l2_subdev_core_ops`->s_power() callback. This may take -place either indirectly by using :c:func:`v4l2_pipeline_pm_use` or +place either indirectly by using :c:func:`v4l2_pipeline_pm_get` or directly. Formats diff --git a/Documentation/media/kapi/v4l2-dev.rst b/Documentation/media/kapi/v4l2-dev.rst index 4c5a15c53dbf..63c064837c00 100644 --- a/Documentation/media/kapi/v4l2-dev.rst +++ b/Documentation/media/kapi/v4l2-dev.rst @@ -185,7 +185,7 @@ This will create the character device for you. .. code-block:: c - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -201,7 +201,7 @@ types exist: ========================== ==================== ============================== :c:type:`vfl_devnode_type` Device name Usage ========================== ==================== ============================== -``VFL_TYPE_GRABBER`` ``/dev/videoX`` for video input/output devices +``VFL_TYPE_VIDEO`` ``/dev/videoX`` for video input/output devices ``VFL_TYPE_VBI`` ``/dev/vbiX`` for vertical blank data (i.e. closed captions, teletext) ``VFL_TYPE_RADIO`` ``/dev/radioX`` for radio tuners diff --git a/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst b/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst index a21659d55c6b..6818ddf1495c 100644 --- a/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst +++ b/Documentation/media/uapi/cec/cec-ioc-adap-g-conn-info.rst @@ -44,18 +44,18 @@ is only available if the ``CEC_CAP_CONNECTOR_INFO`` capability is set. .. flat-table:: struct cec_connector_info :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 8 + :widths: 1 1 8 * - __u32 - ``type`` - The type of connector this adapter is associated with. - * - union + * - union { - ``(anonymous)`` - - - * - - - ``struct cec_drm_connector_info`` + * - ``struct cec_drm_connector_info`` - drm - :ref:`cec-drm-connector-info` + * - } + - .. tabularcolumns:: |p{4.4cm}|p{2.5cm}|p{10.6cm}| diff --git a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst index 5e21b1fbfc01..d16b226b1bef 100644 --- a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst +++ b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst @@ -109,35 +109,33 @@ it is guaranteed that the state did change in between the two events. .. flat-table:: struct cec_event :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 8 + :widths: 1 1 8 * - __u64 - ``ts`` - - :cspan:`1`\ Timestamp of the event in ns. + - Timestamp of the event in ns. The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access the same clock from userspace use :c:func:`clock_gettime`. * - __u32 - ``event`` - - :cspan:`1` The CEC event type, see :ref:`cec-events`. + - The CEC event type, see :ref:`cec-events`. * - __u32 - ``flags`` - - :cspan:`1` Event flags, see :ref:`cec-event-flags`. - * - union + - Event flags, see :ref:`cec-event-flags`. + * - union { - (anonymous) - - - - - * - - - struct cec_event_state_change + * - struct cec_event_state_change - ``state_change`` - The new adapter state as sent by the :ref:`CEC_EVENT_STATE_CHANGE <CEC-EVENT-STATE-CHANGE>` event. - * - - - struct cec_event_lost_msgs + * - struct cec_event_lost_msgs - ``lost_msgs`` - The number of lost messages as sent by the :ref:`CEC_EVENT_LOST_MSGS <CEC-EVENT-LOST-MSGS>` event. + * - } + - .. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| diff --git a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst index 6218d9cbdd83..33e2b110145c 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst @@ -64,12 +64,11 @@ id's until they get an error. .. flat-table:: struct media_entity_desc :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 1 8 + :widths: 2 2 1 8 * - __u32 - ``id`` - - - - Entity ID, set by the application. When the ID is or'ed with ``MEDIA_ENT_ID_FLAG_NEXT``, the driver clears the flag and returns the first entity with a larger ID. Do not expect that the ID will @@ -79,79 +78,70 @@ id's until they get an error. * - char - ``name``\ [32] - - - - Entity name as an UTF-8 NULL-terminated string. This name must be unique within the media topology. * - __u32 - ``type`` - - - - Entity type, see :ref:`media-entity-functions` for details. * - __u32 - ``revision`` - - - - Entity revision. Always zero (obsolete) * - __u32 - ``flags`` - - - - Entity flags, see :ref:`media-entity-flag` for details. * - __u32 - ``group_id`` - - - - Entity group ID. Always zero (obsolete) * - __u16 - ``pads`` - - - - Number of pads * - __u16 - ``links`` - - - - Total number of outbound links. Inbound links are not counted in this field. * - __u32 - ``reserved[4]`` - - - - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union + * - union { + - (anonymous) - * - - - struct + * - struct - ``dev`` - - Valid for (sub-)devices that create a single device node. * - - - - __u32 - ``major`` - Device node major number. * - - - - __u32 - ``minor`` - Device node minor number. - * - - - __u8 + * - __u8 - ``raw``\ [184] - - + * - } + - Return Value diff --git a/Documentation/media/uapi/v4l/buffer.rst b/Documentation/media/uapi/v4l/buffer.rst index 9149b57728e5..3112300c2fa0 100644 --- a/Documentation/media/uapi/v4l/buffer.rst +++ b/Documentation/media/uapi/v4l/buffer.rst @@ -172,11 +172,10 @@ struct v4l2_buffer .. flat-table:: struct v4l2_buffer :header-rows: 0 :stub-columns: 0 - :widths: 1 2 1 10 + :widths: 1 2 10 * - __u32 - ``index`` - - - Number of the buffer, set by the application except when calling :ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`, then it is set by the driver. This field can range from zero to the number of buffers @@ -186,14 +185,12 @@ struct v4l2_buffer :ref:`VIDIOC_CREATE_BUFS` minus one. * - __u32 - ``type`` - - - Type of the buffer, same as struct :c:type:`v4l2_format` ``type`` or struct :c:type:`v4l2_requestbuffers` ``type``, set by the application. See :c:type:`v4l2_buf_type` * - __u32 - ``bytesused`` - - - The number of bytes occupied by the data in the buffer. It depends on the negotiated data format and may change with each buffer for compressed variable size data like JPEG images. Drivers must set @@ -205,18 +202,15 @@ struct v4l2_buffer ``planes`` pointer is used instead. * - __u32 - ``flags`` - - - Flags set by the application or driver, see :ref:`buffer-flags`. * - __u32 - ``field`` - - - Indicates the field order of the image in the buffer, see :c:type:`v4l2_field`. This field is not used when the buffer contains VBI data. Drivers must set it when ``type`` refers to a capture stream, applications when it refers to an output stream. * - struct timeval - ``timestamp`` - - - For capture streams this is time when the first data byte was captured, as returned by the :c:func:`clock_gettime()` function for the relevant clock id; see ``V4L2_BUF_FLAG_TIMESTAMP_*`` in @@ -229,7 +223,6 @@ struct v4l2_buffer stream. * - struct :c:type:`v4l2_timecode` - ``timecode`` - - - When the ``V4L2_BUF_FLAG_TIMECODE`` flag is set in ``flags``, this structure contains a frame timecode. In :c:type:`V4L2_FIELD_ALTERNATE <v4l2_field>` mode the top and @@ -239,10 +232,9 @@ struct v4l2_buffer independent of the ``timestamp`` and ``sequence`` fields. * - __u32 - ``sequence`` - - - Set by the driver, counting the frames (not fields!) in sequence. This field is set for both input and output devices. - * - :cspan:`3` + * - :cspan:`2` In :c:type:`V4L2_FIELD_ALTERNATE <v4l2_field>` mode the top and bottom field have the same sequence number. The count starts at @@ -262,13 +254,11 @@ struct v4l2_buffer * - __u32 - ``memory`` - - - This field must be set by applications and/or drivers in accordance with the selected I/O method. See :c:type:`v4l2_memory` - * - union + * - union { - ``m`` - * - - - __u32 + * - __u32 - ``offset`` - For the single-planar API and when ``memory`` is ``V4L2_MEMORY_MMAP`` this is the offset of the buffer from the @@ -276,29 +266,27 @@ struct v4l2_buffer and apart of serving as parameter to the :ref:`mmap() <func-mmap>` function not useful for applications. See :ref:`mmap` for details - * - - - unsigned long + * - unsigned long - ``userptr`` - For the single-planar API and when ``memory`` is ``V4L2_MEMORY_USERPTR`` this is a pointer to the buffer (casted to unsigned long type) in virtual memory, set by the application. See :ref:`userp` for details. - * - - - struct v4l2_plane + * - struct v4l2_plane - ``*planes`` - When using the multi-planar API, contains a userspace pointer to an array of struct :c:type:`v4l2_plane`. The size of the array should be put in the ``length`` field of this struct :c:type:`v4l2_buffer` structure. - * - - - int + * - int - ``fd`` - For the single-plane API and when ``memory`` is ``V4L2_MEMORY_DMABUF`` this is the file descriptor associated with a DMABUF buffer. + * - } + - * - __u32 - ``length`` - - - Size of the buffer (not the payload) in bytes for the single-planar API. This is set by the driver based on the calls to :ref:`VIDIOC_REQBUFS` and/or @@ -308,12 +296,10 @@ struct v4l2_buffer actual number of valid elements in that array. * - __u32 - ``reserved2`` - - - A place holder for future extensions. Drivers and applications must set this to 0. * - __u32 - ``request_fd`` - - - The file descriptor of the request to queue the buffer to. If the flag ``V4L2_BUF_FLAG_REQUEST_FD`` is set, then the buffer will be queued to this request. If the flag is not set, then this field will @@ -344,11 +330,10 @@ struct v4l2_plane .. flat-table:: :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``bytesused`` - - - The number of bytes occupied by data in the plane (its payload). Drivers must set this field when ``type`` refers to a capture stream, applications when it refers to an output stream. If the @@ -362,40 +347,35 @@ struct v4l2_plane which may not be 0. * - __u32 - ``length`` - - - Size in bytes of the plane (not its payload). This is set by the driver based on the calls to :ref:`VIDIOC_REQBUFS` and/or :ref:`VIDIOC_CREATE_BUFS`. - * - union + * - union { - ``m`` - - - - - * - - - __u32 + * - __u32 - ``mem_offset`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_MMAP``, this is the value that should be passed to :ref:`mmap() <func-mmap>`, similar to the ``offset`` field in struct :c:type:`v4l2_buffer`. - * - - - unsigned long + * - unsigned long - ``userptr`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_USERPTR``, this is a userspace pointer to the memory allocated for this plane by an application. - * - - - int + * - int - ``fd`` - When the memory type in the containing struct :c:type:`v4l2_buffer` is ``V4L2_MEMORY_DMABUF``, this is a file descriptor associated with a DMABUF buffer, similar to the ``fd`` field in struct :c:type:`v4l2_buffer`. + * - } + - * - __u32 - ``data_offset`` - - - Offset in bytes to video data in the plane. Drivers must set this field when ``type`` refers to a capture stream, applications when it refers to an output stream. @@ -407,7 +387,6 @@ struct v4l2_plane at offset ``data_offset`` from the start of the plane. * - __u32 - ``reserved[11]`` - - - Reserved for future use. Should be zeroed by drivers and applications. diff --git a/Documentation/media/uapi/v4l/dev-sliced-vbi.rst b/Documentation/media/uapi/v4l/dev-sliced-vbi.rst index e86346f66017..7b2d38dd402a 100644 --- a/Documentation/media/uapi/v4l/dev-sliced-vbi.rst +++ b/Documentation/media/uapi/v4l/dev-sliced-vbi.rst @@ -478,33 +478,30 @@ struct v4l2_mpeg_vbi_fmt_ivtv .. flat-table:: :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u8 - ``magic``\ [4] - - - A "magic" constant from :ref:`v4l2-mpeg-vbi-fmt-ivtv-magic` that indicates this is a valid sliced VBI data payload and also indicates which member of the anonymous union, ``itv0`` or ``ITV0``, to use for the payload data. - * - union + * - union { - (anonymous) - * - - - struct :c:type:`v4l2_mpeg_vbi_itv0` + * - struct :c:type:`v4l2_mpeg_vbi_itv0` - ``itv0`` - The primary form of the sliced VBI data payload that contains anywhere from 1 to 35 lines of sliced VBI data. Line masks are provided in this form of the payload indicating which VBI lines are provided. - * - - - struct :ref:`v4l2_mpeg_vbi_ITV0 <v4l2-mpeg-vbi-itv0-1>` + * - struct :ref:`v4l2_mpeg_vbi_ITV0 <v4l2-mpeg-vbi-itv0-1>` - ``ITV0`` - An alternate form of the sliced VBI data payload used when 36 lines of sliced VBI data are present. No line masks are provided in this form of the payload; all valid line mask bits are implcitly set. - - + * - } + - .. _v4l2-mpeg-vbi-fmt-ivtv-magic: diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst index 28313c0f4e7c..d4fc5f25aa14 100644 --- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst @@ -2028,6 +2028,22 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` - 0x00000004 - The DPB entry is a long term reference frame + * - ``V4L2_H264_DPB_ENTRY_FLAG_FIELD`` + - 0x00000008 + - The DPB entry is a field reference, which means only one of the field + will be used when decoding the new frame/field. When not set the DPB + entry is a frame reference (both fields will be used). Note that this + flag does not say anything about the number of fields contained in the + reference frame, it just describes the one used to decode the new + field/frame + * - ``V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD`` + - 0x00000010 + - The DPB entry is a bottom field reference (only the bottom field of the + reference frame is needed to decode the new frame/field). Only valid if + V4L2_H264_DPB_ENTRY_FLAG_FIELD is set. When + V4L2_H264_DPB_ENTRY_FLAG_FIELD is set but + V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD is not, that means the + DPB entry is a top field reference ``V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (enum)`` Specifies the decoding mode to use. Currently exposes slice-based and diff --git a/Documentation/media/uapi/v4l/pixfmt-bayer.rst b/Documentation/media/uapi/v4l/pixfmt-bayer.rst index cfa2f4e3e114..807ab34ba93b 100644 --- a/Documentation/media/uapi/v4l/pixfmt-bayer.rst +++ b/Documentation/media/uapi/v4l/pixfmt-bayer.rst @@ -34,5 +34,6 @@ orders. See also `the Wikipedia article on Bayer filter pixfmt-srggb10-ipu3 pixfmt-srggb12 pixfmt-srggb12p + pixfmt-srggb14 pixfmt-srggb14p pixfmt-srggb16 diff --git a/Documentation/media/uapi/v4l/pixfmt-srggb14.rst b/Documentation/media/uapi/v4l/pixfmt-srggb14.rst new file mode 100644 index 000000000000..3420d4d1825e --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-srggb14.rst @@ -0,0 +1,82 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _V4L2-PIX-FMT-SRGGB14: +.. _v4l2-pix-fmt-sbggr14: +.. _v4l2-pix-fmt-sgbrg14: +.. _v4l2-pix-fmt-sgrbg14: + + +*************************************************************************************************************************** +V4L2_PIX_FMT_SRGGB14 ('RG14'), V4L2_PIX_FMT_SGRBG14 ('GR14'), V4L2_PIX_FMT_SGBRG14 ('GB14'), V4L2_PIX_FMT_SBGGR14 ('BG14'), +*************************************************************************************************************************** + + +14-bit Bayer formats expanded to 16 bits + + +Description +=========== + +These four pixel formats are raw sRGB / Bayer formats with 14 bits per +colour. Each sample is stored in a 16-bit word, with two unused high +bits filled with zeros. Each n-pixel row contains n/2 green samples +and n/2 blue or red samples, with alternating red and blue rows. Bytes +are stored in memory in little endian order. They are conventionally +described as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an +example of a small V4L2_PIX_FMT_SBGGR14 image: + +**Byte Order.** +Each cell is one byte, the two most significant bits in the high bytes are +zero. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 2 1 1 1 1 1 1 1 1 + + + * - start + 0: + - B\ :sub:`00low` + - B\ :sub:`00high` + - G\ :sub:`01low` + - G\ :sub:`01high` + - B\ :sub:`02low` + - B\ :sub:`02high` + - G\ :sub:`03low` + - G\ :sub:`03high` + * - start + 8: + - G\ :sub:`10low` + - G\ :sub:`10high` + - R\ :sub:`11low` + - R\ :sub:`11high` + - G\ :sub:`12low` + - G\ :sub:`12high` + - R\ :sub:`13low` + - R\ :sub:`13high` + * - start + 16: + - B\ :sub:`20low` + - B\ :sub:`20high` + - G\ :sub:`21low` + - G\ :sub:`21high` + - B\ :sub:`22low` + - B\ :sub:`22high` + - G\ :sub:`23low` + - G\ :sub:`23high` + * - start + 24: + - G\ :sub:`30low` + - G\ :sub:`30high` + - R\ :sub:`31low` + - R\ :sub:`31high` + - G\ :sub:`32low` + - G\ :sub:`32high` + - R\ :sub:`33low` + - R\ :sub:`33high` diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst index db43dda5aafb..054275c0dfc1 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst @@ -100,7 +100,8 @@ describing all planes of that format. * - __u8 - ``flags`` - Flags set by the application or driver, see :ref:`format-flags`. - * - :cspan:`2` union { (anonymous) + * - union { + - (anonymous) * - __u8 - ``ycbcr_enc`` - Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. @@ -113,7 +114,8 @@ describing all planes of that format. This information supplements the ``colorspace`` and must be set by the driver for capture streams and by the application for output streams, see :ref:`colorspaces`. - * - :cspan:`2` } + * - } + - * - __u8 - ``quantization`` - Quantization range, from enum :c:type:`v4l2_quantization`. diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst index a8321c348bf8..a993b861bf75 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst @@ -143,7 +143,6 @@ Single-planar format structure - Flags set by the application or driver, see :ref:`format-flags`. * - union { - (anonymous) - - * - __u32 - ``ycbcr_enc`` - Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. @@ -158,7 +157,6 @@ Single-planar format structure streams, see :ref:`colorspaces`. * - } - - - * - __u32 - ``quantization`` - Quantization range, from enum :c:type:`v4l2_quantization`. diff --git a/Documentation/media/uapi/v4l/pixfmt-y14.rst b/Documentation/media/uapi/v4l/pixfmt-y14.rst new file mode 100644 index 000000000000..5c260f8da088 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-y14.rst @@ -0,0 +1,72 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _V4L2-PIX-FMT-Y14: + +************************* +V4L2_PIX_FMT_Y14 ('Y14 ') +************************* + + +Grey-scale image + + +Description +=========== + +This is a grey-scale image with a depth of 14 bits per pixel. Pixels are +stored in 16-bit words with unused high bits padded with 0. The least +significant byte is stored at lower memory addresses (little-endian). + +**Byte Order.** +Each cell is one byte. + + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - start + 0: + - Y'\ :sub:`00low` + - Y'\ :sub:`00high` + - Y'\ :sub:`01low` + - Y'\ :sub:`01high` + - Y'\ :sub:`02low` + - Y'\ :sub:`02high` + - Y'\ :sub:`03low` + - Y'\ :sub:`03high` + * - start + 8: + - Y'\ :sub:`10low` + - Y'\ :sub:`10high` + - Y'\ :sub:`11low` + - Y'\ :sub:`11high` + - Y'\ :sub:`12low` + - Y'\ :sub:`12high` + - Y'\ :sub:`13low` + - Y'\ :sub:`13high` + * - start + 16: + - Y'\ :sub:`20low` + - Y'\ :sub:`20high` + - Y'\ :sub:`21low` + - Y'\ :sub:`21high` + - Y'\ :sub:`22low` + - Y'\ :sub:`22high` + - Y'\ :sub:`23low` + - Y'\ :sub:`23high` + * - start + 24: + - Y'\ :sub:`30low` + - Y'\ :sub:`30high` + - Y'\ :sub:`31low` + - Y'\ :sub:`31high` + - Y'\ :sub:`32low` + - Y'\ :sub:`32high` + - Y'\ :sub:`33low` + - Y'\ :sub:`33high` diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index 15e11f27b4c8..17bfb2beaa6a 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -5792,6 +5792,43 @@ the following codes. - u\ :sub:`2` - u\ :sub:`1` - u\ :sub:`0` + * .. _MEDIA-BUS-FMT-Y14-1X14: + + - MEDIA_BUS_FMT_Y14_1X14 + - 0x202d + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` * .. _MEDIA-BUS-FMT-UYVY8-1X16: - MEDIA_BUS_FMT_UYVY8_1X16 diff --git a/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst b/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst index a1cf20181cf1..d38031dbe4e4 100644 --- a/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst +++ b/Documentation/media/uapi/v4l/vidioc-dbg-g-chip-info.rst @@ -91,23 +91,23 @@ instructions. .. flat-table:: struct v4l2_dbg_match :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - See :ref:`name-chip-match-types` for a list of possible types. - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``addr`` - Match a chip by this number, interpreted according to the ``type`` field. - * - - - char + * - char - ``name[32]`` - Match a chip by this name, interpreted according to the ``type`` field. Currently unused. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst b/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst index 29e1d4fc4f52..112597c6cad2 100644 --- a/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst +++ b/Documentation/media/uapi/v4l/vidioc-dbg-g-register.rst @@ -100,23 +100,23 @@ instructions. .. flat-table:: struct v4l2_dbg_match :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - See :ref:`chip-match-types` for a list of possible types. - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``addr`` - Match a chip by this number, interpreted according to the ``type`` field. - * - - - char + * - char - ``name[32]`` - Match a chip by this name, interpreted according to the ``type`` field. Currently unused. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst index f1a504836f31..784c5980da8d 100644 --- a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst +++ b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst @@ -77,32 +77,25 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod .. flat-table:: struct v4l2_decoder_cmd :header-rows: 0 :stub-columns: 0 - :widths: 11 24 12 16 106 + :widths: 1 1 1 3 * - __u32 - ``cmd`` - - - - The decoder command, see :ref:`decoder-cmds`. * - __u32 - ``flags`` - - - - Flags to go with the command. If no flags are defined for this command, drivers and applications must set this field to zero. - * - union + * - union { - (anonymous) - - - - - - - * - - - struct + * - struct - ``start`` - - Structure containing additional data for the ``V4L2_DEC_CMD_START`` command. * - - - - __s32 - ``speed`` - Playback speed and direction. The playback speed is defined as @@ -113,7 +106,6 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod of 1 steps just one frame forward, a speed of -1 steps just one frame back. * - - - - __u32 - ``format`` - Format restrictions. This field is set by the driver, not the @@ -124,30 +116,26 @@ introduced in Linux 3.3. They are, however, mandatory for stateful mem2mem decod GOPs, which it can then play in reverse order. So to implement reverse playback the application must feed the decoder the last GOP in the video file, then the GOP before that, etc. etc. - * - - - struct + * - struct - ``stop`` - - Structure containing additional data for the ``V4L2_DEC_CMD_STOP`` command. * - - - - __u64 - ``pts`` - Stop playback at this ``pts`` or immediately if the playback is already past that timestamp. Leave to 0 if you want to stop after the last frame was decoded. - * - - - struct + * - struct - ``raw`` - - - - * - - - - __u32 - ``data``\ [16] - Reserved for future extensions. Drivers and applications must set the array to zero. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-dqevent.rst b/Documentation/media/uapi/v4l/vidioc-dqevent.rst index 42659a3d1705..2f37d255352a 100644 --- a/Documentation/media/uapi/v4l/vidioc-dqevent.rst +++ b/Documentation/media/uapi/v4l/vidioc-dqevent.rst @@ -55,66 +55,54 @@ call. .. flat-table:: struct v4l2_event :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` - - - Type of the event, see :ref:`event-type`. - * - union + * - union { - ``u`` - - - - - * - - - struct :c:type:`v4l2_event_vsync` + * - struct :c:type:`v4l2_event_vsync` - ``vsync`` - Event data for event ``V4L2_EVENT_VSYNC``. - * - - - struct :c:type:`v4l2_event_ctrl` + * - struct :c:type:`v4l2_event_ctrl` - ``ctrl`` - Event data for event ``V4L2_EVENT_CTRL``. - * - - - struct :c:type:`v4l2_event_frame_sync` + * - struct :c:type:`v4l2_event_frame_sync` - ``frame_sync`` - Event data for event ``V4L2_EVENT_FRAME_SYNC``. - * - - - struct :c:type:`v4l2_event_motion_det` + * - struct :c:type:`v4l2_event_motion_det` - ``motion_det`` - Event data for event V4L2_EVENT_MOTION_DET. - * - - - struct :c:type:`v4l2_event_src_change` + * - struct :c:type:`v4l2_event_src_change` - ``src_change`` - Event data for event V4L2_EVENT_SOURCE_CHANGE. - * - - - __u8 + * - __u8 - ``data``\ [64] - Event data. Defined by the event type. The union should be used to define easily accessible type for events. + * - } + - * - __u32 - ``pending`` - - - Number of pending events excluding this one. * - __u32 - ``sequence`` - - - Event sequence number. The sequence number is incremented for every subscribed event that takes place. If sequence numbers are not contiguous it means that events have been lost. * - struct timespec - ``timestamp`` - - - Event timestamp. The timestamp has been taken from the ``CLOCK_MONOTONIC`` clock. To access the same clock outside V4L2, use :c:func:`clock_gettime`. * - u32 - ``id`` - - - The ID associated with the event source. If the event does not have an associated ID (this depends on the event type), then this is 0. * - __u32 - ``reserved``\ [8] - - - Reserved for future extensions. Drivers must set the array to zero. @@ -233,54 +221,45 @@ call. .. flat-table:: struct v4l2_event_ctrl :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``changes`` - - - A bitmask that tells what has changed. See :ref:`ctrl-changes-flags`. * - __u32 - ``type`` - - - The type of the control. See enum :c:type:`v4l2_ctrl_type`. - * - union (anonymous) - - - - - - - * - - - __s32 + * - union { + - (anonymous) + * - __s32 - ``value`` - The 32-bit value of the control for 32-bit control types. This is 0 for string controls since the value of a string cannot be passed using :ref:`VIDIOC_DQEVENT`. - * - - - __s64 + * - __s64 - ``value64`` - The 64-bit value of the control for 64-bit control types. + * - } + - * - __u32 - ``flags`` - - - The control flags. See :ref:`control-flags`. * - __s32 - ``minimum`` - - - The minimum value of the control. See struct :ref:`v4l2_queryctrl <v4l2-queryctrl>`. * - __s32 - ``maximum`` - - - The maximum value of the control. See struct :ref:`v4l2_queryctrl <v4l2-queryctrl>`. * - __s32 - ``step`` - - - The step value of the control. See struct :ref:`v4l2_queryctrl <v4l2-queryctrl>`. * - __s32 - ``default_value`` - - - The default value value of the control. See struct :ref:`v4l2_queryctrl <v4l2-queryctrl>`. diff --git a/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst b/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst index e62d45d37072..1d0acbf14c4f 100644 --- a/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst +++ b/Documentation/media/uapi/v4l/vidioc-dv-timings-cap.rst @@ -112,7 +112,7 @@ that doesn't support them will return an ``EINVAL`` error code. .. flat-table:: struct v4l2_dv_timings_cap :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` @@ -127,16 +127,14 @@ that doesn't support them will return an ``EINVAL`` error code. - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union - - - - - * - - - struct :c:type:`v4l2_bt_timings_cap` + * - union { + - (anonymous) + * - struct :c:type:`v4l2_bt_timings_cap` - ``bt`` - BT.656/1120 timings capabilities of the hardware. - * - - - __u32 + * - __u32 - ``raw_data``\ [32] + * - } - .. tabularcolumns:: |p{7.0cm}|p{10.5cm}| diff --git a/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst b/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst index 2c69f26b165d..563a67cddeca 100644 --- a/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst +++ b/Documentation/media/uapi/v4l/vidioc-enum-frameintervals.rst @@ -138,36 +138,31 @@ application should zero out all members except for the *IN* fields. * - __u32 - ``index`` - - - IN: Index of the given frame interval in the enumeration. * - __u32 - ``pixel_format`` - - - IN: Pixel format for which the frame intervals are enumerated. * - __u32 - ``width`` - - - IN: Frame width for which the frame intervals are enumerated. * - __u32 - ``height`` - - - IN: Frame height for which the frame intervals are enumerated. * - __u32 - ``type`` - - - OUT: Frame interval type the device supports. - * - union - - - - + * - union { + - (anonymous) - OUT: Frame interval with the given index. - * - - - struct :c:type:`v4l2_fract` + * - struct :c:type:`v4l2_fract` - ``discrete`` - Frame interval [s]. - * - - - struct :c:type:`v4l2_frmival_stepwise` + * - struct :c:type:`v4l2_frmival_stepwise` - ``stepwise`` - + * - } + - + - * - __u32 - ``reserved[2]`` - diff --git a/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst b/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst index cf31f548826f..cd97546a7122 100644 --- a/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst +++ b/Documentation/media/uapi/v4l/vidioc-enum-framesizes.rst @@ -155,31 +155,27 @@ application should zero out all members except for the *IN* fields. * - __u32 - ``index`` - - - IN: Index of the given frame size in the enumeration. * - __u32 - ``pixel_format`` - - - IN: Pixel format for which the frame sizes are enumerated. * - __u32 - ``type`` - - - OUT: Frame size type the device supports. - * - union - - - - + * - union { + - (anonymous) - OUT: Frame size with the given index. - * - - - struct :c:type:`v4l2_frmsize_discrete` + * - struct :c:type:`v4l2_frmsize_discrete` - ``discrete`` - - * - - - struct :c:type:`v4l2_frmsize_stepwise` + * - struct :c:type:`v4l2_frmsize_stepwise` - ``stepwise`` - + * - } + - + - * - __u32 - ``reserved[2]`` - - - Reserved space for future use. Must be zeroed by drivers and applications. diff --git a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst index 5c675cbac4cf..e36dd2622857 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-dv-timings.rst @@ -179,23 +179,21 @@ EBUSY .. flat-table:: struct v4l2_dv_timings :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - ``type`` - - - Type of DV timings as listed in :ref:`dv-timing-types`. - * - union - - - - - * - - - struct :c:type:`v4l2_bt_timings` + * - union { + - (anonymous) + * - struct :c:type:`v4l2_bt_timings` - ``bt`` - Timings defined by BT.656/1120 specifications - * - - - __u32 + * - __u32 - ``reserved``\ [32] - + * - } + - .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| diff --git a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst index 271cac18afbb..cdb2a2a512d6 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-ext-ctrls.rst @@ -136,15 +136,13 @@ still cause this situation. .. flat-table:: struct v4l2_ext_control :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``id`` - - - Identifies the control, set by the application. * - __u32 - ``size`` - - - The total size in bytes of the payload of this control. This is normally 0, but for pointer controls this should be set to the size of the memory containing the payload, or that will receive @@ -161,55 +159,48 @@ still cause this situation. *length* of the string may well be much smaller. * - __u32 - ``reserved2``\ [1] - - - Reserved for future extensions. Drivers and applications must set the array to zero. - * - union + * - union { - (anonymous) - * - - - __s32 + * - __s32 - ``value`` - New value or current value. Valid if this control is not of type ``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is not set. - * - - - __s64 + * - __s64 - ``value64`` - New value or current value. Valid if this control is of type ``V4L2_CTRL_TYPE_INTEGER64`` and ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is not set. - * - - - char * + * - char * - ``string`` - A pointer to a string. Valid if this control is of type ``V4L2_CTRL_TYPE_STRING``. - * - - - __u8 * + * - __u8 * - ``p_u8`` - A pointer to a matrix control of unsigned 8-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U8``. - * - - - __u16 * + * - __u16 * - ``p_u16`` - A pointer to a matrix control of unsigned 16-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U16``. - * - - - __u32 * + * - __u32 * - ``p_u32`` - A pointer to a matrix control of unsigned 32-bit values. Valid if this control is of type ``V4L2_CTRL_TYPE_U32``. - * - - - :c:type:`v4l2_area` * + * - :c:type:`v4l2_area` * - ``p_area`` - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is of type ``V4L2_CTRL_TYPE_AREA``. - * - - - void * + * - void * - ``ptr`` - A pointer to a compound type which can be an N-dimensional array and/or a compound type (the control's type is >= ``V4L2_CTRL_COMPOUND_TYPES``). Valid if ``V4L2_CTRL_FLAG_HAS_PAYLOAD`` is set for this control. + * - } + - .. tabularcolumns:: |p{4.0cm}|p{2.2cm}|p{2.1cm}|p{8.2cm}| @@ -221,12 +212,11 @@ still cause this situation. .. flat-table:: struct v4l2_ext_controls :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 - * - union + * - union { - (anonymous) - * - - - __u32 + * - __u32 - ``ctrl_class`` - The control class to which all controls belong, see :ref:`ctrl-class`. Drivers that use a kernel framework for @@ -235,8 +225,7 @@ still cause this situation. support this can be tested by setting ``ctrl_class`` to 0 and calling :ref:`VIDIOC_TRY_EXT_CTRLS <VIDIOC_G_EXT_CTRLS>` with a ``count`` of 0. If that succeeds, then the driver supports this feature. - * - - - __u32 + * - __u32 - ``which`` - Which value of the control to get/set/try. ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of the @@ -261,6 +250,8 @@ still cause this situation. by setting ctrl_class to ``V4L2_CTRL_WHICH_CUR_VAL`` and calling VIDIOC_TRY_EXT_CTRLS with a count of 0. If that fails, then the driver does not support ``V4L2_CTRL_WHICH_CUR_VAL``. + * - } + - * - __u32 - ``count`` - The number of controls in the controls array. May also be zero. diff --git a/Documentation/media/uapi/v4l/vidioc-g-fmt.rst b/Documentation/media/uapi/v4l/vidioc-g-fmt.rst index e35a9caff652..1e69bfc46e8d 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-fmt.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-fmt.rst @@ -103,51 +103,44 @@ The format as returned by :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` must be identical * - __u32 - ``type`` - - - Type of the data stream, see :c:type:`v4l2_buf_type`. - * - union + * - union { - ``fmt`` - * - - - struct :c:type:`v4l2_pix_format` + * - struct :c:type:`v4l2_pix_format` - ``pix`` - Definition of an image format, see :ref:`pixfmt`, used by video capture and output devices. - * - - - struct :c:type:`v4l2_pix_format_mplane` + * - struct :c:type:`v4l2_pix_format_mplane` - ``pix_mp`` - Definition of an image format, see :ref:`pixfmt`, used by video capture and output devices that support the :ref:`multi-planar version of the API <planar-apis>`. - * - - - struct :c:type:`v4l2_window` + * - struct :c:type:`v4l2_window` - ``win`` - Definition of an overlaid image, see :ref:`overlay`, used by video overlay devices. - * - - - struct :c:type:`v4l2_vbi_format` + * - struct :c:type:`v4l2_vbi_format` - ``vbi`` - Raw VBI capture or output parameters. This is discussed in more detail in :ref:`raw-vbi`. Used by raw VBI capture and output devices. - * - - - struct :c:type:`v4l2_sliced_vbi_format` + * - struct :c:type:`v4l2_sliced_vbi_format` - ``sliced`` - Sliced VBI capture or output parameters. See :ref:`sliced` for details. Used by sliced VBI capture and output devices. - * - - - struct :c:type:`v4l2_sdr_format` + * - struct :c:type:`v4l2_sdr_format` - ``sdr`` - Definition of a data format, see :ref:`pixfmt`, used by SDR capture and output devices. - * - - - struct :c:type:`v4l2_meta_format` + * - struct :c:type:`v4l2_meta_format` - ``meta`` - Definition of a metadata format, see :ref:`meta-formats`, used by metadata capture devices. - * - - - __u8 + * - __u8 - ``raw_data``\ [200] - Place holder for future extensions. + * - } + - Return Value diff --git a/Documentation/media/uapi/v4l/vidioc-g-parm.rst b/Documentation/media/uapi/v4l/vidioc-g-parm.rst index d9d5d97848d3..044a459e073f 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-parm.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-parm.rst @@ -69,33 +69,29 @@ union holding separate parameters for input and output devices. .. flat-table:: struct v4l2_streamparm :header-rows: 0 :stub-columns: 0 - :widths: 1 1 1 2 + :widths: 1 1 2 * - __u32 - ``type`` - - - The buffer (stream) type, same as struct :c:type:`v4l2_format` ``type``, set by the application. See :c:type:`v4l2_buf_type`. - * - union + * - union { - ``parm`` - - - - - * - - - struct :c:type:`v4l2_captureparm` + * - struct :c:type:`v4l2_captureparm` - ``capture`` - Parameters for capture devices, used when ``type`` is ``V4L2_BUF_TYPE_VIDEO_CAPTURE`` or ``V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE``. - * - - - struct :c:type:`v4l2_outputparm` + * - struct :c:type:`v4l2_outputparm` - ``output`` - Parameters for output devices, used when ``type`` is ``V4L2_BUF_TYPE_VIDEO_OUTPUT`` or ``V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE``. - * - - - __u8 + * - __u8 - ``raw_data``\ [200] - A place holder for future extensions. + * - } + - diff --git a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst index 6690928e657b..8971f4cfb16e 100644 --- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst @@ -290,34 +290,29 @@ See also the examples in :ref:`control`. .. flat-table:: struct v4l2_querymenu :header-rows: 0 :stub-columns: 0 - :widths: 1 1 2 1 + :widths: 1 1 2 * - __u32 - - - ``id`` - Identifies the control, set by the application from the respective struct :ref:`v4l2_queryctrl <v4l2-queryctrl>` ``id``. * - __u32 - - - ``index`` - Index of the menu item, starting at zero, set by the application. - * - union - - - - - - - * - - - __u8 + * - union { + - (anonymous) + * - __u8 - ``name``\ [32] - Name of the menu item, a NUL-terminated ASCII string. This information is intended for the user. This field is valid for ``V4L2_CTRL_TYPE_MENU`` type controls. - * - - - __s64 + * - __s64 - ``value`` - Value of the integer menu item. This field is valid for ``V4L2_CTRL_TYPE_INTEGER_MENU`` type controls. - * - __u32 + * - } - + * - __u32 - ``reserved`` - Reserved for future extensions. Drivers must set the array to zero. @@ -378,7 +373,7 @@ See also the examples in :ref:`control`. - 0 - 0 - A control which performs an action when set. Drivers must ignore - the value passed with ``VIDIOC_S_CTRL`` and return an ``EINVAL`` error + the value passed with ``VIDIOC_S_CTRL`` and return an ``EACCES`` error code on a ``VIDIOC_G_CTRL`` attempt. * - ``V4L2_CTRL_TYPE_INTEGER64`` - any diff --git a/Documentation/media/uapi/v4l/yuv-formats.rst b/Documentation/media/uapi/v4l/yuv-formats.rst index 867470e5f9e1..3b259e31b7a1 100644 --- a/Documentation/media/uapi/v4l/yuv-formats.rst +++ b/Documentation/media/uapi/v4l/yuv-formats.rst @@ -35,6 +35,7 @@ to brightness information. pixfmt-grey pixfmt-y10 pixfmt-y12 + pixfmt-y14 pixfmt-y10b pixfmt-y10p pixfmt-y16 diff --git a/Documentation/media/v4l-drivers/ipu3.rst b/Documentation/media/v4l-drivers/ipu3.rst index e4904ab44e60..a694f49491f9 100644 --- a/Documentation/media/v4l-drivers/ipu3.rst +++ b/Documentation/media/v4l-drivers/ipu3.rst @@ -311,10 +311,13 @@ Down Scaler and GDC blocks should be configured with the supported resolutions as each hardware block has its own alignment requirement. You must configure the output resolution of the hardware blocks smartly to meet -the hardware requirement along with keeping the maximum field of view. -The intermediate resolutions can be generated by specific tool and this -information can be obtained by looking at the following IPU3 ImgU configuration -table. +the hardware requirement along with keeping the maximum field of view. The +intermediate resolutions can be generated by specific tool - + +https://github.com/intel/intel-ipu3-pipecfg + +This tool can be used to generate intermediate resolutions. More information can +be obtained by looking at the following IPU3 ImgU configuration table. https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master diff --git a/Documentation/media/v4l-drivers/vivid.rst b/Documentation/media/v4l-drivers/vivid.rst index 7082fec4075d..52e57b773f07 100644 --- a/Documentation/media/v4l-drivers/vivid.rst +++ b/Documentation/media/v4l-drivers/vivid.rst @@ -4,9 +4,9 @@ The Virtual Video Test Driver (vivid) ===================================== This driver emulates video4linux hardware of various types: video capture, video -output, vbi capture and output, radio receivers and transmitters and a software -defined radio receiver. In addition a simple framebuffer device is available for -testing capture and output overlays. +output, vbi capture and output, metadata capture and output, radio receivers and +transmitters, touch capture and a software defined radio receiver. In addition a +simple framebuffer device is available for testing capture and output overlays. Up to 64 vivid instances can be created, each with up to 16 inputs and 16 outputs. @@ -36,6 +36,8 @@ This document describes the features implemented by this driver: - Radio receiver and transmitter support, including RDS support - Software defined radio (SDR) support - Capture and output overlay support +- Metadata capture and output support +- Touch capture support These features will be described in more detail below. @@ -69,6 +71,9 @@ all configurable using the following module options: - bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both - bit 12: Radio Transmitter node - bit 16: Framebuffer for testing overlays + - bit 17: Metadata Capture node + - bit 18: Metadata Output node + - bit 19: Touch Capture node So to create four instances, the first two with just one video capture device, the second two with just one video output device you would pass @@ -175,6 +180,21 @@ all configurable using the following module options: give the desired swradioX start number for each SDR capture device. The default is -1 which will just take the first free number. +- meta_cap_nr: + + give the desired videoX start number for each metadata capture device. + The default is -1 which will just take the first free number. + +- meta_out_nr: + + give the desired videoX start number for each metadata output device. + The default is -1 which will just take the first free number. + +- touch_cap_nr: + + give the desired v4l-touchX start number for each touch capture device. + The default is -1 which will just take the first free number. + - ccs_cap_mode: specify the allowed video capture crop/compose/scaling combination @@ -547,6 +567,33 @@ The generated data contains the In-phase and Quadrature components of a 1 kHz tone that has an amplitude of sqrt(2). +Metadata Capture +---------------- + +The Metadata capture generates UVC format metadata. The PTS and SCR are +transmitted based on the values set in vivid contols. + +The Metadata device will only work for the Webcam input, it will give +back an error for all other inputs. + + +Metadata Output +--------------- + +The Metadata output can be used to set brightness, contrast, saturation and hue. + +The Metadata device will only work for the Webcam output, it will give +back an error for all other outputs. + + +Touch Capture +------------- + +The Touch capture generates touch patterns simulating single tap, double tap, +triple tap, move from left to right, zoom in, zoom out, palm press (simulating +a large area being pressed on a touchpad), and simulating 16 simultaneous +touch points. + Controls -------- @@ -1049,6 +1096,16 @@ FM Radio Modulator Controls to pass the RDS blocks to the driver, or "Controls" where the RDS data is Provided by the RDS controls mentioned above. +Metadata Capture Controls +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Generate PTS + + if set, then the generated metadata stream contains Presentation timestamp. + +- Generate SCR + + if set, then the generated metadata stream contains Source Clock information. Video, VBI and RDS Looping -------------------------- diff --git a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt index 66c7c568bd86..9c39ee58ea50 100644 --- a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt +++ b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt @@ -649,7 +649,7 @@ video_device注册 接下来你需要注册视频设备:这会为你创建一个字符设备。 - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -660,7 +660,7 @@ video_device注册 注册哪种设备是根据类型(type)参数。存在以下类型: -VFL_TYPE_GRABBER: 用于视频输入/输出设备的 videoX +VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视) VFL_TYPE_RADIO: 用于广播调谐器的 radioX diff --git a/MAINTAINERS b/MAINTAINERS index 7ec848ae301c..1b37feaf18e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10963,6 +10963,7 @@ F: drivers/media/platform/atmel/atmel-isc.h F: drivers/media/platform/atmel/atmel-isc-base.c F: drivers/media/platform/atmel/atmel-isc-regs.h F: Documentation/devicetree/bindings/media/atmel-isc.txt +F: include/linux/atmel-isc-media.h MICROCHIP ISI DRIVER M: Eugen Hristev <eugen.hristev@microchip.com> @@ -13914,6 +13915,7 @@ L: linux-arm-msm@vger.kernel.org T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/platform/qcom/venus/ +F: Documentation/devicetree/bindings/media/*venus* QUALCOMM WCN36XX WIRELESS DRIVER M: Kalle Valo <kvalo@codeaurora.org> @@ -14328,9 +14330,12 @@ F: Documentation/devicetree/bindings/media/rockchip-rga.txt HANTRO VPU CODEC DRIVER M: Ezequiel Garcia <ezequiel@collabora.com> +M: Philipp Zabel <p.zabel@pengutronix.de> L: linux-media@vger.kernel.org +L: linux-rockchip@lists.infradead.org S: Maintained F: drivers/staging/media/hantro/ +F: Documentation/devicetree/bindings/media/nxp,imx8mq-vpu.yaml F: Documentation/devicetree/bindings/media/rockchip-vpu.txt ROCKER DRIVER @@ -14377,6 +14382,14 @@ F: include/net/rose.h F: include/uapi/linux/rose.h F: net/rose/ +ROTATION DRIVER FOR ALLWINNER A83T +M: Jernej Skrabec <jernej.skrabec@siol.net> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/platform/sunxi/sun8i-rotate/ +F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml + RTL2830 MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> L: linux-media@vger.kernel.org @@ -15347,11 +15360,10 @@ S: Odd Fixes F: drivers/net/ethernet/smsc/smc91x.* SMIA AND SMIA++ IMAGE SENSOR DRIVER -M: Sakari Ailus <sakari.ailus@iki.fi> +M: Sakari Ailus <sakari.ailus@linux.intel.com> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/smiapp/ -F: include/media/i2c/smiapp.h F: drivers/media/i2c/smiapp-pll.c F: drivers/media/i2c/smiapp-pll.h F: include/uapi/linux/smiapp.h @@ -15530,6 +15542,14 @@ S: Maintained F: drivers/media/i2c/imx214.c F: Documentation/devicetree/bindings/media/i2c/sony,imx214.txt +SONY IMX219 SENSOR DRIVER +M: Dave Stevenson <dave.stevenson@raspberrypi.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/imx219.c +F: Documentation/devicetree/bindings/media/i2c/imx219.yaml + SONY IMX258 SENSOR DRIVER M: Sakari Ailus <sakari.ailus@linux.intel.com> L: linux-media@vger.kernel.org @@ -16763,12 +16783,12 @@ F: sound/soc/codecs/twl4030* TI VPE/CAL DRIVERS M: Benoit Parrot <bparrot@ti.com> L: linux-media@vger.kernel.org +S: Maintained W: http://linuxtv.org/ Q: http://patchwork.linuxtv.org/project/linux-media/list/ -S: Maintained -F: drivers/media/platform/ti-vpe/ -F: Documentation/devicetree/bindings/media/ti,vpe.yaml F: Documentation/devicetree/bindings/media/ti,cal.yaml +F: Documentation/devicetree/bindings/media/ti,vpe.yaml +F: drivers/media/platform/ti-vpe/ TI WILINK WIRELESS DRIVERS L: linux-wireless@vger.kernel.org @@ -17460,7 +17480,7 @@ L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: https://linuxtv.org S: Odd Fixes -F: drivers/media/usb/usbvision/ +F: drivers/staging/media/usbvision/ USB WEBCAM GADGET M: Laurent Pinchart <laurent.pinchart@ideasonboard.com> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dts index 2f1f829450a2..6c9cc45fb417 100644 --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dts +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dts @@ -76,3 +76,7 @@ }; }; }; + +&ir { + linux,rc-map-name = "rc-videostrong-kii-pro"; +}; diff --git a/drivers/base/property.c b/drivers/base/property.c index 511f6d7acdfe..5f35c0ccf5e0 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -566,6 +566,7 @@ const char *fwnode_get_name(const struct fwnode_handle *fwnode) { return fwnode_call_ptr_op(fwnode, get_name); } +EXPORT_SYMBOL_GPL(fwnode_get_name); /** * fwnode_get_name_prefix - Return the prefix of node for printing purposes diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index b36a41332867..9dfea5c4b6ab 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -208,9 +208,9 @@ config MEDIA_SUBDRV_AUTOSELECT If unsure say Y. config MEDIA_HIDE_ANCILLARY_SUBDRV - bool - depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT - default y + bool + depends on MEDIA_SUBDRV_AUTOSELECT && !COMPILE_TEST && !EXPERT + default y config MEDIA_ATTACH bool diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c index 4a841bee5cc2..e748cd54b45d 100644 --- a/drivers/media/cec/cec-notifier.c +++ b/drivers/media/cec/cec-notifier.c @@ -23,7 +23,7 @@ struct cec_notifier { struct kref kref; struct device *hdmi_dev; struct cec_connector_info conn_info; - const char *conn_name; + const char *port_name; struct cec_adapter *cec_adap; u16 phys_addr; @@ -32,16 +32,30 @@ struct cec_notifier { static LIST_HEAD(cec_notifiers); static DEFINE_MUTEX(cec_notifiers_lock); -struct cec_notifier * -cec_notifier_get_conn(struct device *hdmi_dev, const char *conn_name) +/** + * cec_notifier_get_conn - find or create a new cec_notifier for the given + * device and connector tuple. + * @hdmi_dev: device that sends the events. + * @port_name: the connector name from which the event occurs + * + * If a notifier for device @dev already exists, then increase the refcount + * and return that notifier. + * + * If it doesn't exist, then allocate a new notifier struct and return a + * pointer to that new struct. + * + * Return NULL if the memory could not be allocated. + */ +static struct cec_notifier * +cec_notifier_get_conn(struct device *hdmi_dev, const char *port_name) { struct cec_notifier *n; mutex_lock(&cec_notifiers_lock); list_for_each_entry(n, &cec_notifiers, head) { if (n->hdmi_dev == hdmi_dev && - (!conn_name || - (n->conn_name && !strcmp(n->conn_name, conn_name)))) { + (!port_name || + (n->port_name && !strcmp(n->port_name, port_name)))) { kref_get(&n->kref); mutex_unlock(&cec_notifiers_lock); return n; @@ -51,9 +65,9 @@ cec_notifier_get_conn(struct device *hdmi_dev, const char *conn_name) if (!n) goto unlock; n->hdmi_dev = hdmi_dev; - if (conn_name) { - n->conn_name = kstrdup(conn_name, GFP_KERNEL); - if (!n->conn_name) { + if (port_name) { + n->port_name = kstrdup(port_name, GFP_KERNEL); + if (!n->port_name) { kfree(n); n = NULL; goto unlock; @@ -68,7 +82,6 @@ unlock: mutex_unlock(&cec_notifiers_lock); return n; } -EXPORT_SYMBOL_GPL(cec_notifier_get_conn); static void cec_notifier_release(struct kref *kref) { @@ -76,7 +89,7 @@ static void cec_notifier_release(struct kref *kref) container_of(kref, struct cec_notifier, kref); list_del(&n->head); - kfree(n->conn_name); + kfree(n->port_name); kfree(n); } @@ -88,10 +101,10 @@ static void cec_notifier_put(struct cec_notifier *n) } struct cec_notifier * -cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, const struct cec_connector_info *conn_info) { - struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, conn_name); + struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, port_name); if (!n) return n; @@ -129,7 +142,7 @@ void cec_notifier_conn_unregister(struct cec_notifier *n) EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister); struct cec_notifier * -cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, struct cec_adapter *adap) { struct cec_notifier *n; @@ -137,7 +150,7 @@ cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name, if (WARN_ON(!adap)) return NULL; - n = cec_notifier_get_conn(hdmi_dev, conn_name); + n = cec_notifier_get_conn(hdmi_dev, port_name); if (!n) return n; diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index aabb830e7468..d6531874faa6 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -97,8 +97,6 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); DEB_EE("q->curr:%p\n", q->curr); - BUG_ON(!q->curr); - /* finish current buffer */ if (NULL == q->curr) { DEB_D("aiii. no current buffer\n"); @@ -296,7 +294,7 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) int res; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: { + case VFL_TYPE_VIDEO: { DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->video_q; @@ -378,7 +376,7 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: /* DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count); @@ -409,7 +407,7 @@ static ssize_t fops_write(struct file *file, const char __user *data, size_t cou int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: return -EINVAL; case VFL_TYPE_VBI: if (fh->dev->ext_vv_data->vbi_fops.write) { @@ -597,7 +595,7 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); vfd->fops = &video_fops; - if (type == VFL_TYPE_GRABBER) + if (type == VFL_TYPE_VIDEO) vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; else vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; @@ -611,7 +609,7 @@ int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; vfd->device_caps |= dev->ext_vv_data->capabilities; - if (type == VFL_TYPE_GRABBER) + if (type == VFL_TYPE_VIDEO) vfd->device_caps &= ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); else diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c index c95d4583498e..8916bb644756 100644 --- a/drivers/media/common/siano/smsdvb-debugfs.c +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -45,88 +45,88 @@ static void smsdvb_print_dvb_stats(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_rf_locked = %d\n", p->is_rf_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_demod_locked = %d\n", p->is_demod_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_external_lna_on = %d\n", p->is_external_lna_on); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "SNR = %d\n", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "ber = %d\n", p->ber); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "FIB_CRC = %d\n", p->FIB_CRC); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "ts_per = %d\n", p->ts_per); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "MFER = %d\n", p->MFER); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "RSSI = %d\n", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "in_band_pwr = %d\n", p->in_band_pwr); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "carrier_offset = %d\n", p->carrier_offset); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "modem_state = %d\n", p->modem_state); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "frequency = %d\n", p->frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "bandwidth = %d\n", p->bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "transmission_mode = %d\n", p->transmission_mode); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "modem_state = %d\n", p->modem_state); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "guard_interval = %d\n", p->guard_interval); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "code_rate = %d\n", p->code_rate); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "lp_code_rate = %d\n", p->lp_code_rate); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "hierarchy = %d\n", p->hierarchy); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", p->constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "burst_size = %d\n", p->burst_size); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "burst_duration = %d\n", p->burst_duration); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "burst_cycle_time = %d\n", p->burst_cycle_time); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "calc_burst_cycle_time = %d\n", p->calc_burst_cycle_time); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_rows = %d\n", p->num_of_rows); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_padd_cols = %d\n", p->num_of_padd_cols); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_punct_cols = %d\n", p->num_of_punct_cols); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "error_ts_packets = %d\n", p->error_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %d\n", p->total_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_valid_mpe_tlbs = %d\n", p->num_of_valid_mpe_tlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_invalid_mpe_tlbs = %d\n", p->num_of_invalid_mpe_tlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_corrected_mpe_tlbs = %d\n", p->num_of_corrected_mpe_tlbs); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_error_count = %d\n", p->ber_error_count); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %d\n", p->ber_bit_count); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "pre_ber = %d\n", p->pre_ber); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "cell_id = %d\n", p->cell_id); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "dvbh_srv_ind_hp = %d\n", p->dvbh_srv_ind_hp); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "dvbh_srv_ind_lp = %d\n", p->dvbh_srv_ind_lp); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_mpe_received = %d\n", p->num_mpe_received); debug_data->stats_count = n; @@ -148,42 +148,42 @@ static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "statistics_type = %d\t", p->statistics_type); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "full_size = %d\n", p->full_size); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_rf_locked = %d\t\t", p->is_rf_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_demod_locked = %d\t", p->is_demod_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_external_lna_on = %d\n", p->is_external_lna_on); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "SNR = %d dB\t\t", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "RSSI = %d dBm\t\t", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "in_band_pwr = %d dBm\n", p->in_band_pwr); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "carrier_offset = %d\t", p->carrier_offset); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "bandwidth = %d\t\t", p->bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "frequency = %d Hz\n", p->frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "transmission_mode = %d\t", p->transmission_mode); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "modem_state = %d\t\t", p->modem_state); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "guard_interval = %d\n", p->guard_interval); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "system_type = %d\t\t", p->system_type); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "partial_reception = %d\t", p->partial_reception); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_layers = %d\n", p->num_of_layers); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "sms_to_host_tx_errors = %d\n", p->sms_to_host_tx_errors); for (i = 0; i < 3; i++) { @@ -191,31 +191,34 @@ static void smsdvb_print_isdb_stats(struct smsdvb_debugfs *debug_data, p->layer_info[i].number_of_segments > 13) continue; - n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", p->layer_info[i].code_rate); - n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", p->layer_info[i].constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", p->layer_info[i].ber); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "\tber_error_count = %-5d\t", p->layer_info[i].ber_error_count); - n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", p->layer_info[i].ber_bit_count); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", p->layer_info[i].ts_per); - n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "\terror_ts_packets = %-5d\t", p->layer_info[i].error_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "total_ts_packets = %-5d\t", p->layer_info[i].total_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tnumber_of_segments = %d\t", p->layer_info[i].number_of_segments); - n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors); } @@ -238,44 +241,44 @@ static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, buf = debug_data->stats_data; - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "statistics_type = %d\t", p->statistics_type); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "full_size = %d\n", p->full_size); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_rf_locked = %d\t\t", p->is_rf_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_demod_locked = %d\t", p->is_demod_locked); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "is_external_lna_on = %d\n", p->is_external_lna_on); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "SNR = %d dB\t\t", p->SNR); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "RSSI = %d dBm\t\t", p->RSSI); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "in_band_pwr = %d dBm\n", p->in_band_pwr); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "carrier_offset = %d\t", p->carrier_offset); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "bandwidth = %d\t\t", p->bandwidth); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "frequency = %d Hz\n", p->frequency); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "transmission_mode = %d\t", p->transmission_mode); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "modem_state = %d\t\t", p->modem_state); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "guard_interval = %d\n", p->guard_interval); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "system_type = %d\t\t", p->system_type); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "partial_reception = %d\t", p->partial_reception); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "num_of_layers = %d\n", p->num_of_layers); - n += snprintf(&buf[n], PAGE_SIZE - n, "segment_number = %d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "segment_number = %d\t", p->segment_number); - n += snprintf(&buf[n], PAGE_SIZE - n, "tune_bw = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "tune_bw = %d\n", p->tune_bw); for (i = 0; i < 3; i++) { @@ -283,31 +286,34 @@ static void smsdvb_print_isdb_stats_ex(struct smsdvb_debugfs *debug_data, p->layer_info[i].number_of_segments > 13) continue; - n += snprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\nLayer %d\n", i); + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tcode_rate = %d\t", p->layer_info[i].code_rate); - n += snprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "constellation = %d\n", p->layer_info[i].constellation); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tber = %-5d\t", p->layer_info[i].ber); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tber_error_count = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "\tber_error_count = %-5d\t", p->layer_info[i].ber_error_count); - n += snprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "ber_bit_count = %-5d\n", p->layer_info[i].ber_bit_count); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tpre_ber = %-5d\t", p->layer_info[i].pre_ber); - n += snprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tts_per = %-5d\n", p->layer_info[i].ts_per); - n += snprintf(&buf[n], PAGE_SIZE - n, "\terror_ts_packets = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "\terror_ts_packets = %-5d\t", p->layer_info[i].error_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, "total_ts_packets = %-5d\t", + n += scnprintf(&buf[n], PAGE_SIZE - n, + "total_ts_packets = %-5d\t", p->layer_info[i].total_ts_packets); - n += snprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "ti_ldepth_i = %d\n", p->layer_info[i].ti_ldepth_i); - n += snprintf(&buf[n], PAGE_SIZE - n, + n += scnprintf(&buf[n], PAGE_SIZE - n, "\tnumber_of_segments = %d\t", p->layer_info[i].number_of_segments); - n += snprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", + n += scnprintf(&buf[n], PAGE_SIZE - n, "tmcc_errors = %d\n", p->layer_info[i].tmcc_errors); } diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 4489744fbbd9..44d65f5be845 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -393,8 +393,8 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory, } } - dprintk(1, "allocated %d buffers, %d plane(s) each\n", - buffer, num_planes); + dprintk(3, "allocated %d buffers, %d plane(s) each\n", + buffer, num_planes); return buffer; } diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index d0c9dffe49e5..d3a3ee5b597b 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -593,8 +593,8 @@ static int vb2_dc_map_dmabuf(void *mem_priv) /* checking if dmabuf is big enough to store contiguous chunk */ contig_size = vb2_dc_get_contiguous_size(sgt); if (contig_size < buf->size) { - pr_err("contiguous chunk is too small %lu/%lu b\n", - contig_size, buf->size); + pr_err("contiguous chunk is too small %lu/%lu\n", + contig_size, buf->size); dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); return -EFAULT; } diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index ac7be872f460..5de016412c42 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2182,7 +2182,7 @@ int drxj_dap_atomic_read_reg32(struct i2c_device_addr *dev_addr, u32 *data, u32 flags) { u8 buf[sizeof(*data)] = { 0 }; - int rc = -EIO; + int rc; u32 word = 0; if (!data) @@ -4229,7 +4229,7 @@ int drxj_dap_scu_atomic_write_reg16(struct i2c_device_addr *dev_addr, u16 data, u32 flags) { u8 buf[2]; - int rc = -EIO; + int rc; buf[0] = (u8) (data & 0xff); buf[1] = (u8) ((data >> 8) & 0xff); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index c96f05ff5f2f..d2c28dcf6b42 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -65,6 +65,92 @@ err: } /* + * m88ds3103b demod has an internal device related to clocking. First the i2c + * gate must be opened, for one transaction, then writes will be allowed. + */ +static int m88ds3103b_dt_write(struct m88ds3103_dev *dev, int reg, int data) +{ + struct i2c_client *client = dev->client; + u8 buf[] = {reg, data}; + u8 val; + int ret; + struct i2c_msg msg = { + .addr = dev->dt_addr, .flags = 0, .buf = buf, .len = 2 + }; + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + + val = 0x11; + ret = regmap_write(dev->regmap, 0x03, val); + if (ret) + dev_dbg(&client->dev, "fail=%d\n", ret); + + ret = i2c_transfer(dev->dt_client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "0x%02x (ret=%i, reg=0x%02x, value=0x%02x)\n", + dev->dt_addr, ret, reg, data); + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; + } + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", + dev->dt_addr, reg, data); + + return 0; +} + +/* + * m88ds3103b demod has an internal device related to clocking. First the i2c + * gate must be opened, for two transactions, then reads will be allowed. + */ +static int m88ds3103b_dt_read(struct m88ds3103_dev *dev, u8 reg) +{ + struct i2c_client *client = dev->client; + int ret; + u8 val; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = dev->dt_addr, + .flags = 0, + .buf = b0, + .len = 1 + }, + { + .addr = dev->dt_addr, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x00); + + val = 0x12; + ret = regmap_write(dev->regmap, 0x03, val); + if (ret) + dev_dbg(&client->dev, "fail=%d\n", ret); + + ret = i2c_transfer(dev->dt_client->adapter, msg, 2); + if (ret != 2) { + dev_err(&client->dev, "0x%02x (ret=%d, reg=0x%02x)\n", + dev->dt_addr, ret, reg); + + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + return -EREMOTEIO; + } + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + dev_dbg(&client->dev, "0x%02x reg 0x%02x, value 0x%02x\n", + dev->dt_addr, reg, b1[0]); + + return b1[0]; +} + +/* * Get the demodulator AGC PWM voltage setting supplied to the tuner. */ int m88ds3103_get_agc_pwm(struct dvb_frontend *fe, u8 *_agc_pwm) @@ -288,6 +374,251 @@ err: return ret; } +static int m88ds3103b_select_mclk(struct m88ds3103_dev *dev) +{ + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + u32 adc_Freq_MHz[3] = {96, 93, 99}; + u8 reg16_list[3] = {96, 92, 100}, reg16, reg15; + u32 offset_MHz[3]; + u32 max_offset = 0; + u32 old_setting = dev->mclk; + u32 tuner_freq_MHz = c->frequency / 1000; + u8 i; + char big_symbol = 0; + + big_symbol = (c->symbol_rate > 45010000) ? 1 : 0; + + if (big_symbol) { + reg16 = 115; + } else { + reg16 = 96; + + /* TODO: IS THIS NECESSARY ? */ + for (i = 0; i < 3; i++) { + offset_MHz[i] = tuner_freq_MHz % adc_Freq_MHz[i]; + + if (offset_MHz[i] > (adc_Freq_MHz[i] / 2)) + offset_MHz[i] = adc_Freq_MHz[i] - offset_MHz[i]; + + if (offset_MHz[i] > max_offset) { + max_offset = offset_MHz[i]; + reg16 = reg16_list[i]; + dev->mclk = adc_Freq_MHz[i] * 1000 * 1000; + + if (big_symbol) + dev->mclk /= 2; + + dev_dbg(&client->dev, "modifying mclk %u -> %u\n", + old_setting, dev->mclk); + } + } + } + + if (dev->mclk == 93000000) + regmap_write(dev->regmap, 0xA0, 0x42); + else if (dev->mclk == 96000000) + regmap_write(dev->regmap, 0xA0, 0x44); + else if (dev->mclk == 99000000) + regmap_write(dev->regmap, 0xA0, 0x46); + else if (dev->mclk == 110250000) + regmap_write(dev->regmap, 0xA0, 0x4E); + else + regmap_write(dev->regmap, 0xA0, 0x44); + + reg15 = m88ds3103b_dt_read(dev, 0x15); + + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + + if (big_symbol) + reg15 |= 0x02; + else + reg15 &= ~0x02; + + m88ds3103b_dt_write(dev, 0x15, reg15); + m88ds3103b_dt_write(dev, 0x16, reg16); + + usleep_range(5000, 5500); + + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, (u8)(big_symbol ? 0x0E : 0x0A)); + + usleep_range(5000, 5500); + + return 0; +} + +static int m88ds3103b_set_mclk(struct m88ds3103_dev *dev, u32 mclk_khz) +{ + u8 reg11 = 0x0A, reg15, reg16, reg1D, reg1E, reg1F, tmp; + u8 sm, f0 = 0, f1 = 0, f2 = 0, f3 = 0; + u16 pll_div_fb, N; + u32 div; + + reg15 = m88ds3103b_dt_read(dev, 0x15); + reg16 = m88ds3103b_dt_read(dev, 0x16); + reg1D = m88ds3103b_dt_read(dev, 0x1D); + + if (dev->cfg->ts_mode != M88DS3103_TS_SERIAL) { + if (reg16 == 92) + tmp = 93; + else if (reg16 == 100) + tmp = 99; + else + tmp = 96; + + mclk_khz *= tmp; + mclk_khz /= 96; + } + + pll_div_fb = (reg15 & 0x01) << 8; + pll_div_fb += reg16; + pll_div_fb += 32; + + div = 9000 * pll_div_fb * 4; + div /= mclk_khz; + + if (dev->cfg->ts_mode == M88DS3103_TS_SERIAL) { + reg11 |= 0x02; + + if (div <= 32) { + N = 2; + + f0 = 0; + f1 = div / N; + f2 = div - f1; + f3 = 0; + } else if (div <= 34) { + N = 3; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = div - f0 - f1; + f3 = 0; + } else if (div <= 64) { + N = 4; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = (div - f0 - f1) / (N - 2); + f3 = div - f0 - f1 - f2; + } else { + N = 4; + + f0 = 16; + f1 = 16; + f2 = 16; + f3 = 16; + } + + if (f0 == 16) + f0 = 0; + else if ((f0 < 8) && (f0 != 0)) + f0 = 8; + + if (f1 == 16) + f1 = 0; + else if ((f1 < 8) && (f1 != 0)) + f1 = 8; + + if (f2 == 16) + f2 = 0; + else if ((f2 < 8) && (f2 != 0)) + f2 = 8; + + if (f3 == 16) + f3 = 0; + else if ((f3 < 8) && (f3 != 0)) + f3 = 8; + } else { + reg11 &= ~0x02; + + if (div <= 32) { + N = 2; + + f0 = 0; + f1 = div / N; + f2 = div - f1; + f3 = 0; + } else if (div <= 48) { + N = 3; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = div - f0 - f1; + f3 = 0; + } else if (div <= 64) { + N = 4; + + f0 = div / N; + f1 = (div - f0) / (N - 1); + f2 = (div - f0 - f1) / (N - 2); + f3 = div - f0 - f1 - f2; + } else { + N = 4; + + f0 = 16; + f1 = 16; + f2 = 16; + f3 = 16; + } + + if (f0 == 16) + f0 = 0; + else if ((f0 < 9) && (f0 != 0)) + f0 = 9; + + if (f1 == 16) + f1 = 0; + else if ((f1 < 9) && (f1 != 0)) + f1 = 9; + + if (f2 == 16) + f2 = 0; + else if ((f2 < 9) && (f2 != 0)) + f2 = 9; + + if (f3 == 16) + f3 = 0; + else if ((f3 < 9) && (f3 != 0)) + f3 = 9; + } + + sm = N - 1; + + /* Write to registers */ + //reg15 &= 0x01; + //reg15 |= (pll_div_fb >> 8) & 0x01; + + //reg16 = pll_div_fb & 0xFF; + + reg1D &= ~0x03; + reg1D |= sm; + reg1D |= 0x80; + + reg1E = ((f3 << 4) + f2) & 0xFF; + reg1F = ((f1 << 4) + f0) & 0xFF; + + m88ds3103b_dt_write(dev, 0x05, 0x40); + m88ds3103b_dt_write(dev, 0x11, 0x08); + m88ds3103b_dt_write(dev, 0x1D, reg1D); + m88ds3103b_dt_write(dev, 0x1E, reg1E); + m88ds3103b_dt_write(dev, 0x1F, reg1F); + + m88ds3103b_dt_write(dev, 0x17, 0xc1); + m88ds3103b_dt_write(dev, 0x17, 0x81); + + usleep_range(5000, 5500); + + m88ds3103b_dt_write(dev, 0x05, 0x00); + m88ds3103b_dt_write(dev, 0x11, 0x0A); + + usleep_range(5000, 5500); + + return 0; +} + static int m88ds3103_set_frontend(struct dvb_frontend *fe) { struct m88ds3103_dev *dev = fe->demodulator_priv; @@ -298,7 +629,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */ u8 buf[3]; u16 u16tmp; - u32 tuner_frequency_khz, target_mclk; + u32 tuner_frequency_khz, target_mclk, u32tmp; s32 s32tmp; static const struct reg_sequence reset_buf[] = { {0x07, 0x80}, {0x07, 0x00} @@ -321,6 +652,20 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) /* Disable demod clock path */ if (dev->chip_id == M88RS6000_CHIP_ID) { + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = regmap_read(dev->regmap, 0xb2, &u32tmp); + if (ret) + goto err; + if (u32tmp == 0x01) { + ret = regmap_write(dev->regmap, 0x00, 0x00); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xb2, 0x00); + if (ret) + goto err; + } + } + ret = regmap_write(dev->regmap, 0x06, 0xe0); if (ret) goto err; @@ -346,7 +691,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) tuner_frequency_khz = c->frequency; } - /* select M88RS6000 demod main mclk and ts mclk from tuner die. */ + /* set M88RS6000/DS3103B demod main mclk and ts mclk from tuner die */ if (dev->chip_id == M88RS6000_CHIP_ID) { if (c->symbol_rate > 45010000) dev->mclk = 110250000; @@ -358,6 +703,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) else target_mclk = 144000000; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_select_mclk(dev); + m88ds3103b_set_mclk(dev, target_mclk / 1000); + } + /* Enable demod clock path */ ret = regmap_write(dev->regmap, 0x06, 0x00); if (ret) @@ -469,12 +819,42 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); if (ret) goto err; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + buf[0] = m88ds3103b_dt_read(dev, 0x15); + buf[1] = m88ds3103b_dt_read(dev, 0x16); + + if (c->symbol_rate > 45010000) { + buf[0] &= ~0x03; + buf[0] |= 0x02; + buf[0] |= ((147 - 32) >> 8) & 0x01; + buf[1] = (147 - 32) & 0xFF; + + dev->mclk = 110250 * 1000; + } else { + buf[0] &= ~0x03; + buf[0] |= ((128 - 32) >> 8) & 0x01; + buf[1] = (128 - 32) & 0xFF; + + dev->mclk = 96000 * 1000; + } + m88ds3103b_dt_write(dev, 0x15, buf[0]); + m88ds3103b_dt_write(dev, 0x16, buf[1]); + + regmap_read(dev->regmap, 0x30, &u32tmp); + u32tmp &= ~0x80; + regmap_write(dev->regmap, 0x30, u32tmp & 0xff); + } + ret = regmap_write(dev->regmap, 0xf1, 0x01); if (ret) goto err; - ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); - if (ret) - goto err; + + if (dev->chiptype != M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); + if (ret) + goto err; + } } switch (dev->cfg->ts_mode) { @@ -488,6 +868,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) break; case M88DS3103_TS_PARALLEL: u8tmp = 0x02; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + u8tmp = 0x01; + u8tmp1 = 0x01; + } break; case M88DS3103_TS_CI: u8tmp = 0x03; @@ -516,6 +900,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) u8tmp1 = 0x3f; u8tmp2 = 0x3f; break; + case M88DS3103_TS_PARALLEL: + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + ret = m88ds3103_update_bits(dev, 0x29, 0x01, u8tmp1); + if (ret) + goto err; + } + /* fall through */ default: u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); u8tmp1 = u16tmp / 2 - 1; @@ -543,6 +934,9 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) else u8tmp = 0x06; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + m88ds3103b_set_mclk(dev, target_mclk / 1000); + ret = regmap_write(dev->regmap, 0xc3, 0x08); if (ret) goto err; @@ -578,6 +972,16 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + /* enable/disable 192M LDPC clock */ + ret = m88ds3103_update_bits(dev, 0x29, 0x10, + (c->delivery_system == SYS_DVBS) ? 0x10 : 0x0); + if (ret) + goto err; + + ret = m88ds3103_update_bits(dev, 0xc9, 0x08, 0x08); + } + dev_dbg(&client->dev, "carrier offset=%d\n", (tuner_frequency_khz - c->frequency)); @@ -642,7 +1046,7 @@ static int m88ds3103_init(struct dvb_frontend *fe) if (utmp) goto warm; - /* global reset, global diseqc reset, golbal fec reset */ + /* global reset, global diseqc reset, global fec reset */ ret = regmap_write(dev->regmap, 0x07, 0xe0); if (ret) goto err; @@ -652,12 +1056,15 @@ static int m88ds3103_init(struct dvb_frontend *fe) /* cold state - try to download firmware */ dev_info(&client->dev, "found a '%s' in cold state\n", - m88ds3103_ops.info.name); + dev->fe.ops.info.name); - if (dev->chip_id == M88RS6000_CHIP_ID) + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + name = M88DS3103B_FIRMWARE; + else if (dev->chip_id == M88RS6000_CHIP_ID) name = M88RS6000_FIRMWARE; else name = M88DS3103_FIRMWARE; + /* request the firmware, this will block and timeout */ ret = request_firmware(&firmware, name, &client->dev); if (ret) { @@ -700,10 +1107,16 @@ static int m88ds3103_init(struct dvb_frontend *fe) } dev_info(&client->dev, "found a '%s' in warm state\n", - m88ds3103_ops.info.name); + dev->fe.ops.info.name); dev_info(&client->dev, "firmware version: %X.%X\n", (utmp >> 4) & 0xf, (utmp >> 0 & 0xf)); + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + m88ds3103b_dt_write(dev, 0x21, 0x92); + m88ds3103b_dt_write(dev, 0x15, 0x6C); + m88ds3103b_dt_write(dev, 0x17, 0xC1); + m88ds3103b_dt_write(dev, 0x17, 0x81); + } warm: /* warm state */ dev->warm = true; @@ -1393,6 +1806,8 @@ static int m88ds3103_probe(struct i2c_client *client, goto err_kfree; dev->chip_id = utmp >> 1; + dev->chiptype = (u8)id->driver_data; + dev_dbg(&client->dev, "chip_id=%02x\n", dev->chip_id); switch (dev->chip_id) { @@ -1459,7 +1874,10 @@ static int m88ds3103_probe(struct i2c_client *client, /* create dvb_frontend */ memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops)); - if (dev->chip_id == M88RS6000_CHIP_ID) + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) + strscpy(dev->fe.ops.info.name, "Montage Technology M88DS3103B", + sizeof(dev->fe.ops.info.name)); + else if (dev->chip_id == M88RS6000_CHIP_ID) strscpy(dev->fe.ops.info.name, "Montage Technology M88RS6000", sizeof(dev->fe.ops.info.name)); if (!pdata->attach_in_use) @@ -1470,6 +1888,26 @@ static int m88ds3103_probe(struct i2c_client *client, /* setup callbacks */ pdata->get_dvb_frontend = m88ds3103_get_dvb_frontend; pdata->get_i2c_adapter = m88ds3103_get_i2c_adapter; + + if (dev->chiptype == M88DS3103_CHIPTYPE_3103B) { + /* enable i2c repeater for tuner */ + m88ds3103_update_bits(dev, 0x11, 0x01, 0x01); + + /* get frontend address */ + ret = regmap_read(dev->regmap, 0x29, &utmp); + if (ret) + goto err_kfree; + dev->dt_addr = ((utmp & 0x80) == 0) ? 0x42 >> 1 : 0x40 >> 1; + dev_err(&client->dev, "dt addr is 0x%02x", dev->dt_addr); + + dev->dt_client = i2c_new_dummy_device(client->adapter, + dev->dt_addr); + if (!dev->dt_client) { + ret = -ENODEV; + goto err_kfree; + } + } + return 0; err_kfree: kfree(dev); @@ -1484,6 +1922,9 @@ static int m88ds3103_remove(struct i2c_client *client) dev_dbg(&client->dev, "\n"); + if (dev->dt_client) + i2c_unregister_device(dev->dt_client); + i2c_mux_del_adapters(dev->muxc); kfree(dev); @@ -1491,7 +1932,9 @@ static int m88ds3103_remove(struct i2c_client *client) } static const struct i2c_device_id m88ds3103_id_table[] = { - {"m88ds3103", 0}, + {"m88ds3103", M88DS3103_CHIPTYPE_3103}, + {"m88rs6000", M88DS3103_CHIPTYPE_RS6000}, + {"m88ds3103b", M88DS3103_CHIPTYPE_3103B}, {} }; MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table); @@ -1513,3 +1956,4 @@ MODULE_DESCRIPTION("Montage Technology M88DS3103 DVB-S/S2 demodulator driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(M88DS3103_FIRMWARE); MODULE_FIRMWARE(M88RS6000_FIRMWARE); +MODULE_FIRMWARE(M88DS3103B_FIRMWARE); diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h index c825032f07ab..aa5306f40201 100644 --- a/drivers/media/dvb-frontends/m88ds3103_priv.h +++ b/drivers/media/dvb-frontends/m88ds3103_priv.h @@ -16,13 +16,20 @@ #include <linux/regmap.h> #include <linux/math64.h> -#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" -#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" +#define M88DS3103B_FIRMWARE "dvb-demod-m88ds3103b.fw" +#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw" +#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw" + #define M88RS6000_CHIP_ID 0x74 #define M88DS3103_CHIP_ID 0x70 +#define M88DS3103_CHIPTYPE_3103 0 +#define M88DS3103_CHIPTYPE_RS6000 1 +#define M88DS3103_CHIPTYPE_3103B 2 + struct m88ds3103_dev { struct i2c_client *client; + struct i2c_client *dt_client; struct regmap_config regmap_config; struct regmap *regmap; struct m88ds3103_config config; @@ -35,10 +42,13 @@ struct m88ds3103_dev { struct i2c_mux_core *muxc; /* auto detect chip id to do different config */ u8 chip_id; + /* chip type to differentiate m88rs6000 from m88ds3103b */ + u8 chiptype; /* main mclk is calculated for M88RS6000 dynamically */ s32 mclk; u64 post_bit_error; u64 post_bit_count; + u8 dt_addr; }; struct m88ds3103_reg_val { diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 1953b00b3e48..685c0ac71819 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -470,10 +470,11 @@ static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status) goto error; if (dev->delivery_system == SYS_DVBS) { - dev->dvbv3_ber = buf[0] << 24 | buf[1] << 16 | - buf[2] << 8 | buf[3] << 0; - dev->post_bit_error += buf[0] << 24 | buf[1] << 16 | - buf[2] << 8 | buf[3] << 0; + u32 bit_error = buf[0] << 24 | buf[1] << 16 | + buf[2] << 8 | buf[3] << 0; + + dev->dvbv3_ber = bit_error; + dev->post_bit_error += bit_error; c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; c->post_bit_error.stat[0].uvalue = dev->post_bit_error; dev->block_error += buf[4] << 8 | buf[5] << 0; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c68e002d26ea..125d596c13dd 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -238,6 +238,7 @@ config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on GPIOLIB || COMPILE_TEST + select REGMAP_I2C select HDMI select V4L2_FWNODE help @@ -379,6 +380,7 @@ config VIDEO_TVP5150 tristate "Texas Instruments TVP5150 video decoder" depends on VIDEO_V4L2 && I2C select V4L2_FWNODE + select REGMAP_I2C help Support for the Texas Instruments TVP5150 video decoder. @@ -584,6 +586,7 @@ config VIDEO_IMX214 tristate "Sony IMX214 sensor support" depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on V4L2_FWNODE + select REGMAP_I2C help This is a Video4Linux2 sensor driver for the Sony IMX214 camera. @@ -591,6 +594,17 @@ config VIDEO_IMX214 To compile this driver as a module, choose M here: the module will be called imx214. +config VIDEO_IMX219 + tristate "Sony IMX219 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX219 camera. + + To compile this driver as a module, choose M here: the + module will be called imx219. + config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -612,6 +626,7 @@ config VIDEO_IMX274 config VIDEO_IMX290 tristate "Sony IMX290 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select REGMAP_I2C select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the Sony @@ -804,6 +819,7 @@ config VIDEO_OV7670 config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" depends on I2C && VIDEO_V4L2 + select REGMAP_I2C help This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c147bb9d28db..77bf7d0b691f 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o +obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 6528e2343fc8..00159daa6fcd 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -749,6 +749,17 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, return ret; } +static int adv7180_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + return adv7180_set_pad_format(sd, cfg, &fmt); +} + static int adv7180_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { @@ -854,6 +865,7 @@ static const struct v4l2_subdev_core_ops adv7180_core_ops = { }; static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { + .init_cfg = adv7180_init_cfg, .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c index adcaaa8c86d1..4175d06ffd47 100644 --- a/drivers/media/i2c/imx214.c +++ b/drivers/media/i2c/imx214.c @@ -803,7 +803,6 @@ err_rpm_put: static int imx214_g_frame_interval(struct v4l2_subdev *subdev, struct v4l2_subdev_frame_interval *fival) { - fival->pad = 0; fival->interval.numerator = 1; fival->interval.denominator = IMX214_FPS; diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c new file mode 100644 index 000000000000..cb03bdec1f9c --- /dev/null +++ b/drivers/media/i2c/imx219.c @@ -0,0 +1,1481 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A V4L2 driver for Sony IMX219 cameras. + * Copyright (C) 2019, Raspberry Pi (Trading) Ltd + * + * Based on Sony imx258 camera driver + * Copyright (C) 2018 Intel Corporation + * + * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver + * Copyright 2018 Qtechnology A/S + * + * Flip handling taken from the Sony IMX319 driver. + * Copyright (C) 2018 Intel Corporation + * + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.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 <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mediabus.h> +#include <asm/unaligned.h> + +#define IMX219_REG_VALUE_08BIT 1 +#define IMX219_REG_VALUE_16BIT 2 + +#define IMX219_REG_MODE_SELECT 0x0100 +#define IMX219_MODE_STANDBY 0x00 +#define IMX219_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX219_REG_CHIP_ID 0x0000 +#define IMX219_CHIP_ID 0x0219 + +/* External clock frequency is 24.0M */ +#define IMX219_XCLK_FREQ 24000000 + +/* Pixel rate is fixed at 182.4M for all the modes */ +#define IMX219_PIXEL_RATE 182400000 + +#define IMX219_DEFAULT_LINK_FREQ 456000000 + +/* V_TIMING internal */ +#define IMX219_REG_VTS 0x0160 +#define IMX219_VTS_15FPS 0x0dc6 +#define IMX219_VTS_30FPS_1080P 0x06e3 +#define IMX219_VTS_30FPS_BINNED 0x06e3 +#define IMX219_VTS_30FPS_640x480 0x06e3 +#define IMX219_VTS_MAX 0xffff + +#define IMX219_VBLANK_MIN 4 + +/*Frame Length Line*/ +#define IMX219_FLL_MIN 0x08a6 +#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_STEP 1 +#define IMX219_FLL_DEFAULT 0x0c98 + +/* HBLANK control - read only */ +#define IMX219_PPL_DEFAULT 3448 + +/* Exposure control */ +#define IMX219_REG_EXPOSURE 0x015a +#define IMX219_EXPOSURE_MIN 4 +#define IMX219_EXPOSURE_STEP 1 +#define IMX219_EXPOSURE_DEFAULT 0x640 +#define IMX219_EXPOSURE_MAX 65535 + +/* Analog gain control */ +#define IMX219_REG_ANALOG_GAIN 0x0157 +#define IMX219_ANA_GAIN_MIN 0 +#define IMX219_ANA_GAIN_MAX 232 +#define IMX219_ANA_GAIN_STEP 1 +#define IMX219_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX219_REG_DIGITAL_GAIN 0x0158 +#define IMX219_DGTL_GAIN_MIN 0x0100 +#define IMX219_DGTL_GAIN_MAX 0x0fff +#define IMX219_DGTL_GAIN_DEFAULT 0x0100 +#define IMX219_DGTL_GAIN_STEP 1 + +#define IMX219_REG_ORIENTATION 0x0172 + +/* Test Pattern Control */ +#define IMX219_REG_TEST_PATTERN 0x0600 +#define IMX219_TEST_PATTERN_DISABLE 0 +#define IMX219_TEST_PATTERN_SOLID_COLOR 1 +#define IMX219_TEST_PATTERN_COLOR_BARS 2 +#define IMX219_TEST_PATTERN_GREY_COLOR 3 +#define IMX219_TEST_PATTERN_PN9 4 + +/* Test pattern colour components */ +#define IMX219_REG_TESTP_RED 0x0602 +#define IMX219_REG_TESTP_GREENR 0x0604 +#define IMX219_REG_TESTP_BLUE 0x0606 +#define IMX219_REG_TESTP_GREENB 0x0608 +#define IMX219_TESTP_COLOUR_MIN 0 +#define IMX219_TESTP_COLOUR_MAX 0x03ff +#define IMX219_TESTP_COLOUR_STEP 1 +#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX +#define IMX219_TESTP_GREENR_DEFAULT 0 +#define IMX219_TESTP_BLUE_DEFAULT 0 +#define IMX219_TESTP_GREENB_DEFAULT 0 + +struct imx219_reg { + u16 address; + u8 val; +}; + +struct imx219_reg_list { + unsigned int num_of_regs; + const struct imx219_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx219_mode { + /* Frame width */ + unsigned int width; + /* Frame height */ + unsigned int height; + + /* V-timing */ + unsigned int vts_def; + + /* Default register values */ + struct imx219_reg_list reg_list; +}; + +/* + * Register sets lifted off the i2C interface from the Raspberry Pi firmware + * driver. + * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. + */ +static const struct imx219_reg mode_3280x2464_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x0c}, + {0x30eb, 0x05}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0164, 0x00}, + {0x0165, 0x00}, + {0x0166, 0x0c}, + {0x0167, 0xcf}, + {0x0168, 0x00}, + {0x0169, 0x00}, + {0x016a, 0x09}, + {0x016b, 0x9f}, + {0x016c, 0x0c}, + {0x016d, 0xd0}, + {0x016e, 0x09}, + {0x016f, 0xa0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x0c}, + {0x0625, 0xd0}, + {0x0626, 0x09}, + {0x0627, 0xa0}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const struct imx219_reg mode_1920_1080_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x05}, + {0x30eb, 0x0c}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0162, 0x0d}, + {0x0163, 0x78}, + {0x0164, 0x02}, + {0x0165, 0xa8}, + {0x0166, 0x0a}, + {0x0167, 0x27}, + {0x0168, 0x02}, + {0x0169, 0xb4}, + {0x016a, 0x06}, + {0x016b, 0xeb}, + {0x016c, 0x07}, + {0x016d, 0x80}, + {0x016e, 0x04}, + {0x016f, 0x38}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x00}, + {0x0175, 0x00}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x07}, + {0x0625, 0x80}, + {0x0626, 0x04}, + {0x0627, 0x38}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const struct imx219_reg mode_1640_1232_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x0c}, + {0x30eb, 0x05}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0164, 0x00}, + {0x0165, 0x00}, + {0x0166, 0x0c}, + {0x0167, 0xcf}, + {0x0168, 0x00}, + {0x0169, 0x00}, + {0x016a, 0x09}, + {0x016b, 0x9f}, + {0x016c, 0x06}, + {0x016d, 0x68}, + {0x016e, 0x04}, + {0x016f, 0xd0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x01}, + {0x0175, 0x01}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x06}, + {0x0625, 0x68}, + {0x0626, 0x04}, + {0x0627, 0xd0}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, + {0x0162, 0x0d}, + {0x0163, 0x78}, +}; + +static const struct imx219_reg mode_640_480_regs[] = { + {0x0100, 0x00}, + {0x30eb, 0x05}, + {0x30eb, 0x0c}, + {0x300a, 0xff}, + {0x300b, 0xff}, + {0x30eb, 0x05}, + {0x30eb, 0x09}, + {0x0114, 0x01}, + {0x0128, 0x00}, + {0x012a, 0x18}, + {0x012b, 0x00}, + {0x0162, 0x0d}, + {0x0163, 0x78}, + {0x0164, 0x03}, + {0x0165, 0xe8}, + {0x0166, 0x08}, + {0x0167, 0xe7}, + {0x0168, 0x02}, + {0x0169, 0xf0}, + {0x016a, 0x06}, + {0x016b, 0xaf}, + {0x016c, 0x02}, + {0x016d, 0x80}, + {0x016e, 0x01}, + {0x016f, 0xe0}, + {0x0170, 0x01}, + {0x0171, 0x01}, + {0x0174, 0x03}, + {0x0175, 0x03}, + {0x0301, 0x05}, + {0x0303, 0x01}, + {0x0304, 0x03}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x39}, + {0x030b, 0x01}, + {0x030c, 0x00}, + {0x030d, 0x72}, + {0x0624, 0x06}, + {0x0625, 0x68}, + {0x0626, 0x04}, + {0x0627, 0xd0}, + {0x455e, 0x00}, + {0x471e, 0x4b}, + {0x4767, 0x0f}, + {0x4750, 0x14}, + {0x4540, 0x00}, + {0x47b4, 0x14}, + {0x4713, 0x30}, + {0x478b, 0x10}, + {0x478f, 0x10}, + {0x4793, 0x10}, + {0x4797, 0x0e}, + {0x479b, 0x0e}, +}; + +static const struct imx219_reg raw8_framefmt_regs[] = { + {0x018c, 0x08}, + {0x018d, 0x08}, + {0x0309, 0x08}, +}; + +static const struct imx219_reg raw10_framefmt_regs[] = { + {0x018c, 0x0a}, + {0x018d, 0x0a}, + {0x0309, 0x0a}, +}; + +static const char * const imx219_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx219_test_pattern_val[] = { + IMX219_TEST_PATTERN_DISABLE, + IMX219_TEST_PATTERN_COLOR_BARS, + IMX219_TEST_PATTERN_SOLID_COLOR, + IMX219_TEST_PATTERN_GREY_COLOR, + IMX219_TEST_PATTERN_PN9, +}; + +/* regulator supplies */ +static const char * const imx219_supply_name[] = { + /* Supplies can be enabled in any order */ + "VANA", /* Analog (2.8V) supply */ + "VDIG", /* Digital Core (1.8V) supply */ + "VDDL", /* IF (1.2V) supply */ +}; + +#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) + +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 codes[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, +}; + +/* + * Initialisation delay between XCLR low->high and the moment when the sensor + * can start capture (i.e. can leave software stanby) must be not less than: + * t4 + max(t5, t6 + <time to initialize the sensor register over I2C>) + * where + * t4 is fixed, and is max 200uS, + * t5 is fixed, and is 6000uS, + * t6 depends on the sensor external clock, and is max 32000 clock periods. + * As per sensor datasheet, the external clock must be from 6MHz to 27MHz. + * So for any acceptable external clock t6 is always within the range of + * 1185 to 5333 uS, and is always less than t5. + * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then + * initialize the sensor over I2C, and then exit the software standby. + * + * This start-up time can be optimized a bit more, if we start the writes + * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor + * initialization over I2C may complete before (t4+t5) expires, and we must + * ensure that capture is not started before (t4+t5). + * + * This delay doesn't account for the power supply startup time. If needed, + * this should be taken care of via the regulator framework. E.g. in the + * case of DT for regulator-fixed one should define the startup-delay-us + * property. + */ +#define IMX219_XCLR_MIN_DELAY_US 6200 +#define IMX219_XCLR_DELAY_RANGE_US 1000 + +/* Mode configs */ +static const struct imx219_mode supported_modes[] = { + { + /* 8MPix 15fps mode */ + .width = 3280, + .height = 2464, + .vts_def = IMX219_VTS_15FPS, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, + }, + }, + { + /* 1080P 30fps cropped */ + .width = 1920, + .height = 1080, + .vts_def = IMX219_VTS_30FPS_1080P, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), + .regs = mode_1920_1080_regs, + }, + }, + { + /* 2x2 binned 30fps mode */ + .width = 1640, + .height = 1232, + .vts_def = IMX219_VTS_30FPS_BINNED, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), + .regs = mode_1640_1232_regs, + }, + }, + { + /* 640x480 30fps mode */ + .width = 640, + .height = 480, + .vts_def = IMX219_VTS_30FPS_640x480, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640_480_regs), + .regs = mode_640_480_regs, + }, + }, +}; + +struct imx219 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_mbus_framefmt fmt; + + struct clk *xclk; /* system clock to IMX219 */ + u32 xclk_freq; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES]; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + /* Current mode */ + const struct imx219_mode *mode; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx219, sd); +} + +/* Read registers up to 2 at a time */ +static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = { reg >> 8, reg & 0xff }; + u8 data_buf[4] = { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 2 at a time */ +static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx219_write_regs(struct imx219 *imx219, + const struct imx219_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Get bayer order based on flip setting. */ +static u32 imx219_get_format_code(struct imx219 *imx219, u32 code) +{ + unsigned int i; + + lockdep_assert_held(&imx219->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == code) + break; + + if (i >= ARRAY_SIZE(codes)) + i = 0; + + i = (i & ~3) | (imx219->vflip->val ? 2 : 0) | + (imx219->hflip->val ? 1 : 0); + + return codes[i]; +} + +static void imx219_set_default_format(struct imx219 *imx219) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = &imx219->fmt; + fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); + fmt->width = supported_modes[0].width; + fmt->height = supported_modes[0].height; + fmt->field = V4L2_FIELD_NONE; +} + +static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx219 *imx219 = to_imx219(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + mutex_lock(&imx219->mutex); + + /* Initialize try_fmt */ + try_fmt->width = supported_modes[0].width; + try_fmt->height = supported_modes[0].height; + try_fmt->code = imx219_get_format_code(imx219, + MEDIA_BUS_FMT_SRGGB10_1X10); + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx219->mutex); + + return 0; +} + +static int imx219_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx219 *imx219 = + container_of(ctrl->handler, struct imx219, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + int ret; + + if (ctrl->id == V4L2_CID_VBLANK) { + int exposure_max, exposure_def; + + /* Update max exposure while meeting expected vblanking */ + exposure_max = imx219->mode->height + ctrl->val - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN, + IMX219_REG_VALUE_08BIT, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN, + IMX219_REG_VALUE_16BIT, + imx219_test_pattern_val[ctrl->val]); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1, + imx219->hflip->val | + imx219->vflip->val << 1); + break; + case V4L2_CID_VBLANK: + ret = imx219_write_reg(imx219, IMX219_REG_VTS, + IMX219_REG_VALUE_16BIT, + imx219->mode->height + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_RED: + ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_GREENR: + ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_BLUE: + ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN_GREENB: + ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB, + IMX219_REG_VALUE_16BIT, ctrl->val); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx219_ctrl_ops = { + .s_ctrl = imx219_set_ctrl, +}; + +static int imx219_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx219 *imx219 = to_imx219(sd); + + if (code->index >= (ARRAY_SIZE(codes) / 4)) + return -EINVAL; + + code->code = imx219_get_format_code(imx219, codes[code->index * 4]); + + return 0; +} + +static int imx219_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx219 *imx219 = to_imx219(sd); + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != imx219_get_format_code(imx219, imx219->fmt.code)) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt) +{ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + fmt->colorspace, + fmt->ycbcr_enc); + fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); +} + +static void imx219_update_pad_format(struct imx219 *imx219, + const struct imx219_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + imx219_reset_colorspace(&fmt->format); +} + +static int __imx219_get_pad_format(struct imx219 *imx219, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad); + /* update the code which could change due to vflip or hflip: */ + try_fmt->code = imx219_get_format_code(imx219, try_fmt->code); + fmt->format = *try_fmt; + } else { + imx219_update_pad_format(imx219, imx219->mode, fmt); + fmt->format.code = imx219_get_format_code(imx219, + imx219->fmt.code); + } + + return 0; +} + +static int imx219_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx219 *imx219 = to_imx219(sd); + int ret; + + mutex_lock(&imx219->mutex); + ret = __imx219_get_pad_format(imx219, cfg, fmt); + mutex_unlock(&imx219->mutex); + + return ret; +} + +static int imx219_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx219 *imx219 = to_imx219(sd); + const struct imx219_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + int exposure_max, exposure_def, hblank; + unsigned int i; + + mutex_lock(&imx219->mutex); + + for (i = 0; i < ARRAY_SIZE(codes); i++) + if (codes[i] == fmt->format.code) + break; + if (i >= ARRAY_SIZE(codes)) + i = 0; + + /* Bayer order varies with flips */ + fmt->format.code = imx219_get_format_code(imx219, codes[i]); + + 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); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else if (imx219->mode != mode || + imx219->fmt.code != fmt->format.code) { + imx219->fmt = fmt->format; + imx219->mode = mode; + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + __v4l2_ctrl_s_ctrl(imx219->vblank, + mode->vts_def - mode->height); + /* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); + /* + * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank + * depends on mode->width only, and is not changeble in any + * way other than changing the mode. + */ + hblank = IMX219_PPL_DEFAULT - mode->width; + __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, + hblank); + } + + mutex_unlock(&imx219->mutex); + + return 0; +} + +static int imx219_set_framefmt(struct imx219 *imx219) +{ + switch (imx219->fmt.code) { + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SBGGR8_1X8: + return imx219_write_regs(imx219, raw8_framefmt_regs, + ARRAY_SIZE(raw8_framefmt_regs)); + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + return imx219_write_regs(imx219, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs)); + } + + return -EINVAL; +} + +static int imx219_start_streaming(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + const struct imx219_reg_list *reg_list; + int ret; + + /* Apply default values of current mode */ + reg_list = &imx219->mode->reg_list; + ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + ret = imx219_set_framefmt(imx219); + if (ret) { + dev_err(&client->dev, "%s failed to set frame format: %d\n", + __func__, ret); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler); + if (ret) + return ret; + + /* set stream on register */ + return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, + IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING); +} + +static void imx219_stop_streaming(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + int ret; + + /* set stream off register */ + ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, + IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); +} + +static int imx219_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx219 *imx219 = to_imx219(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx219->mutex); + if (imx219->streaming == enable) { + mutex_unlock(&imx219->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx219_start_streaming(imx219); + if (ret) + goto err_rpm_put; + } else { + imx219_stop_streaming(imx219); + pm_runtime_put(&client->dev); + } + + imx219->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx219->vflip, enable); + __v4l2_ctrl_grab(imx219->hflip, enable); + + mutex_unlock(&imx219->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx219->mutex); + + return ret; +} + +/* Power/clock management functions */ +static int imx219_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx219 *imx219 = to_imx219(sd); + int ret; + + ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES, + imx219->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + ret = clk_prepare_enable(imx219->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + gpiod_set_value_cansleep(imx219->reset_gpio, 1); + usleep_range(IMX219_XCLR_MIN_DELAY_US, + IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); + + return ret; +} + +static int imx219_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx219 *imx219 = to_imx219(sd); + + gpiod_set_value_cansleep(imx219->reset_gpio, 0); + regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies); + clk_disable_unprepare(imx219->xclk); + + return 0; +} + +static int __maybe_unused imx219_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx219 *imx219 = to_imx219(sd); + + if (imx219->streaming) + imx219_stop_streaming(imx219); + + return 0; +} + +static int __maybe_unused imx219_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx219 *imx219 = to_imx219(sd); + int ret; + + if (imx219->streaming) { + ret = imx219_start_streaming(imx219); + if (ret) + goto error; + } + + return 0; + +error: + imx219_stop_streaming(imx219); + imx219->streaming = 0; + + return ret; +} + +static int imx219_get_regulators(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + unsigned int i; + + for (i = 0; i < IMX219_NUM_SUPPLIES; i++) + imx219->supplies[i].supply = imx219_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + IMX219_NUM_SUPPLIES, + imx219->supplies); +} + +/* Verify chip ID */ +static int imx219_identify_module(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + int ret; + u32 val; + + ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID, + IMX219_REG_VALUE_16BIT, &val); + if (ret) { + dev_err(&client->dev, "failed to read chip id %x\n", + IMX219_CHIP_ID); + return ret; + } + + if (val != IMX219_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + IMX219_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops imx219_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx219_video_ops = { + .s_stream = imx219_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx219_pad_ops = { + .enum_mbus_code = imx219_enum_mbus_code, + .get_fmt = imx219_get_pad_format, + .set_fmt = imx219_set_pad_format, + .enum_frame_size = imx219_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx219_subdev_ops = { + .core = &imx219_core_ops, + .video = &imx219_video_ops, + .pad = &imx219_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx219_internal_ops = { + .open = imx219_open, +}; + +/* Initialize control handlers */ +static int imx219_init_controls(struct imx219 *imx219) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + unsigned int height = imx219->mode->height; + int exposure_max, exposure_def, hblank; + int i, ret; + + ctrl_hdlr = &imx219->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + if (ret) + return ret; + + mutex_init(&imx219->mutex); + ctrl_hdlr->lock = &imx219->mutex; + + /* By default, PIXEL_RATE is read only */ + imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_PIXEL_RATE, + IMX219_PIXEL_RATE, + IMX219_PIXEL_RATE, 1, + IMX219_PIXEL_RATE); + + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VBLANK, IMX219_VBLANK_MIN, + IMX219_VTS_MAX - height, 1, + imx219->mode->vts_def - height); + hblank = IMX219_PPL_DEFAULT - imx219->mode->width; + imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx219->hblank) + imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + exposure_max = imx219->mode->vts_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX219_EXPOSURE_MIN, exposure_max, + IMX219_EXPOSURE_STEP, + exposure_def); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX, + IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX, + IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT); + + imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (imx219->hflip) + imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (imx219->vflip) + imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx219_test_pattern_menu) - 1, + 0, 0, imx219_test_pattern_menu); + for (i = 0; i < 4; i++) { + /* + * The assumption is that + * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 + * V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 + * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 + */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_TEST_PATTERN_RED + i, + IMX219_TESTP_COLOUR_MIN, + IMX219_TESTP_COLOUR_MAX, + IMX219_TESTP_COLOUR_STEP, + IMX219_TESTP_COLOUR_MAX); + /* The "Solid color" pattern is white by default */ + } + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + imx219->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx219->mutex); + + return ret; +} + +static void imx219_free_controls(struct imx219 *imx219) +{ + v4l2_ctrl_handler_free(imx219->sd.ctrl_handler); + mutex_destroy(&imx219->mutex); +} + +static int imx219_check_hwcfg(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) { + dev_err(dev, "only 2 data lanes are currently supported\n"); + goto error_out; + } + + /* Check the link frequency set in device tree */ + if (!ep_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequency property not found in DT\n"); + goto error_out; + } + + if (ep_cfg.nr_of_link_frequencies != 1 || + ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) { + dev_err(dev, "Link frequency not supported: %lld\n", + ep_cfg.link_frequencies[0]); + goto error_out; + } + + ret = 0; + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx219_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx219 *imx219; + int ret; + + imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL); + if (!imx219) + return -ENOMEM; + + v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops); + + /* Check the hardware configuration in device tree */ + if (imx219_check_hwcfg(dev)) + return -EINVAL; + + /* Get system clock (xclk) */ + imx219->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(imx219->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(imx219->xclk); + } + + imx219->xclk_freq = clk_get_rate(imx219->xclk); + if (imx219->xclk_freq != IMX219_XCLK_FREQ) { + dev_err(dev, "xclk frequency not supported: %d Hz\n", + imx219->xclk_freq); + return -EINVAL; + } + + ret = imx219_get_regulators(imx219); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + /* Request optional enable pin */ + imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + /* + * The sensor must be powered for imx219_identify_module() + * to be able to read the CHIP_ID register + */ + ret = imx219_power_on(dev); + if (ret) + return ret; + + ret = imx219_identify_module(imx219); + if (ret) + goto error_power_off; + + /* Set default mode to max resolution */ + imx219->mode = &supported_modes[0]; + + /* sensor doesn't enter LP-11 state upon power up until and unless + * streaming is started, so upon power up switch the modes to: + * streaming -> standby + */ + ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, + IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING); + if (ret < 0) + goto error_power_off; + usleep_range(100, 110); + + /* put sensor back to standby mode */ + ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT, + IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY); + if (ret < 0) + goto error_power_off; + usleep_range(100, 110); + + ret = imx219_init_controls(imx219); + if (ret) + goto error_power_off; + + /* Initialize subdev */ + imx219->sd.internal_ops = &imx219_internal_ops; + imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx219->pad.flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize default format */ + imx219_set_default_format(imx219); + + ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad); + if (ret) { + dev_err(dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&imx219->sd); + if (ret < 0) { + dev_err(dev, "failed to register sensor sub-device: %d\n", ret); + goto error_media_entity; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx219->sd.entity); + +error_handler_free: + imx219_free_controls(imx219); + +error_power_off: + imx219_power_off(dev); + + return ret; +} + +static int imx219_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx219 *imx219 = to_imx219(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx219_free_controls(imx219); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx219_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id imx219_dt_ids[] = { + { .compatible = "sony,imx219" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx219_dt_ids); + +static const struct dev_pm_ops imx219_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume) + SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL) +}; + +static struct i2c_driver imx219_i2c_driver = { + .driver = { + .name = "imx219", + .of_match_table = imx219_dt_ids, + .pm = &imx219_pm_ops, + }, + .probe_new = imx219_probe, + .remove = imx219_remove, +}; + +module_i2c_driver(imx219_i2c_driver); + +MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com"); +MODULE_DESCRIPTION("Sony IMX219 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 1ae252378799..8537cc4ca108 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -63,6 +63,10 @@ #define OV5675_TEST_PATTERN_ENABLE BIT(7) #define OV5675_TEST_PATTERN_BAR_SHIFT 2 +/* Flip Mirror Controls from sensor */ +#define OV5675_REG_FORMAT1 0x3820 +#define OV5675_REG_FORMAT2 0x373d + #define to_ov5675(_sd) container_of(_sd, struct ov5675, sd) enum { @@ -314,21 +318,21 @@ static const struct ov5675_reg mode_1296x972_regs[] = { {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, - {0x3803, 0xf4}, + {0x3803, 0x00}, {0x3804, 0x0a}, {0x3805, 0x3f}, - {0x3806, 0x06}, - {0x3807, 0xb3}, + {0x3806, 0x07}, + {0x3807, 0xb7}, {0x3808, 0x05}, - {0x3809, 0x00}, - {0x380a, 0x02}, - {0x380b, 0xd0}, + {0x3809, 0x10}, + {0x380a, 0x03}, + {0x380b, 0xcc}, {0x380c, 0x02}, {0x380d, 0xee}, {0x380e, 0x07}, - {0x380f, 0xe4}, - {0x3811, 0x10}, - {0x3813, 0x09}, + {0x380f, 0xd0}, + {0x3811, 0x08}, + {0x3813, 0x0d}, {0x3814, 0x03}, {0x3815, 0x01}, {0x3816, 0x03}, @@ -604,6 +608,53 @@ static int ov5675_test_pattern(struct ov5675 *ov5675, u32 pattern) OV5675_REG_VALUE_08BIT, pattern); } +/* + * OV5675 supports keeping the pixel order by mirror and flip function + * The Bayer order isn't affected by the flip controls + */ +static int ov5675_set_ctrl_hflip(struct ov5675 *ov5675, u32 ctrl_val) +{ + int ret; + u32 val; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + return ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val & ~BIT(3) : val); +} + +static int ov5675_set_ctrl_vflip(struct ov5675 *ov5675, u8 ctrl_val) +{ + int ret; + u32 val; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, &val); + if (ret) + return ret; + + ret = ov5675_write_reg(ov5675, OV5675_REG_FORMAT1, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val | BIT(4) | BIT(5) : val); + + if (ret) + return ret; + + ret = ov5675_read_reg(ov5675, OV5675_REG_FORMAT2, + OV5675_REG_VALUE_08BIT, &val); + + if (ret) + return ret; + + return ov5675_write_reg(ov5675, OV5675_REG_FORMAT2, + OV5675_REG_VALUE_08BIT, + ctrl_val ? val | BIT(1) : val); +} + static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov5675 *ov5675 = container_of(ctrl->handler, @@ -654,6 +705,14 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov5675_test_pattern(ov5675, ctrl->val); break; + case V4L2_CID_HFLIP: + ov5675_set_ctrl_hflip(ov5675, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ov5675_set_ctrl_vflip(ov5675, ctrl->val); + break; + default: ret = -EINVAL; break; @@ -722,6 +781,11 @@ static int ov5675_init_controls(struct ov5675 *ov5675) V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov5675_test_pattern_menu) - 1, 0, 0, ov5675_test_pattern_menu); + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (ctrl_hdlr->error) return ctrl_hdlr->error; diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index d6cd15bb699a..cc678d9d2e0d 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -971,16 +971,9 @@ unlock_and_return: return ret; } -/* Calculate the delay in us by clock rate and clock cycles */ -static inline u32 ov5695_cal_delay(u32 cycles) -{ - return DIV_ROUND_UP(cycles, OV5695_XVCLK_FREQ / 1000 / 1000); -} - static int __ov5695_power_on(struct ov5695 *ov5695) { - int ret; - u32 delay_us; + int i, ret; struct device *dev = &ov5695->client->dev; ret = clk_prepare_enable(ov5695->xvclk); @@ -991,21 +984,28 @@ static int __ov5695_power_on(struct ov5695 *ov5695) gpiod_set_value_cansleep(ov5695->reset_gpio, 1); - ret = regulator_bulk_enable(OV5695_NUM_SUPPLIES, ov5695->supplies); - if (ret < 0) { - dev_err(dev, "Failed to enable regulators\n"); - goto disable_clk; + /* + * The hardware requires the regulators to be powered on in order, + * so enable them one by one. + */ + for (i = 0; i < OV5695_NUM_SUPPLIES; i++) { + ret = regulator_enable(ov5695->supplies[i].consumer); + if (ret) { + dev_err(dev, "Failed to enable %s: %d\n", + ov5695->supplies[i].supply, ret); + goto disable_reg_clk; + } } gpiod_set_value_cansleep(ov5695->reset_gpio, 0); - /* 8192 cycles prior to first SCCB transaction */ - delay_us = ov5695_cal_delay(8192); - usleep_range(delay_us, delay_us * 2); + usleep_range(1000, 1200); return 0; -disable_clk: +disable_reg_clk: + for (--i; i >= 0; i--) + regulator_disable(ov5695->supplies[i].consumer); clk_disable_unprepare(ov5695->xvclk); return ret; @@ -1013,9 +1013,22 @@ disable_clk: static void __ov5695_power_off(struct ov5695 *ov5695) { + struct device *dev = &ov5695->client->dev; + int i, ret; + clk_disable_unprepare(ov5695->xvclk); gpiod_set_value_cansleep(ov5695->reset_gpio, 1); - regulator_bulk_disable(OV5695_NUM_SUPPLIES, ov5695->supplies); + + /* + * The hardware requires the regulators to be powered off in order, + * so disable them one by one. + */ + for (i = OV5695_NUM_SUPPLIES - 1; i >= 0; i--) { + ret = regulator_disable(ov5695->supplies[i].consumer); + if (ret) + dev_err(dev, "Failed to disable %s: %d\n", + ov5695->supplies[i].supply, ret); + } } static int __maybe_unused ov5695_runtime_resume(struct device *dev) @@ -1285,7 +1298,7 @@ static int ov5695_probe(struct i2c_client *client, if (clk_get_rate(ov5695->xvclk) != OV5695_XVCLK_FREQ) dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); - ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ov5695->reset_gpio)) { dev_err(dev, "Failed to get reset-gpios\n"); return -EINVAL; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c index 8911660da86f..71cf68a95bb2 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c @@ -547,7 +547,7 @@ int s5c73m3_init_controls(struct s5c73m3 *state) V4L2_CTRL_FLAG_UPDATE; v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false); ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE; - v4l2_ctrl_cluster(6, &ctrls->focus_auto); + v4l2_ctrl_cluster(5, &ctrls->focus_auto); state->sensor_sd.ctrl_handler = hdl; diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index a80d7701b519..5e4f6a2ef78e 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -57,6 +57,45 @@ static const struct smiapp_module_ident smiapp_module_idents[] = { * */ +static u32 smiapp_get_limit(struct smiapp_sensor *sensor, + unsigned int limit) +{ + if (WARN_ON(limit >= SMIAPP_LIMIT_LAST)) + return 1; + + return sensor->limits[limit]; +} + +#define SMIA_LIM(sensor, limit) \ + smiapp_get_limit(sensor, SMIAPP_LIMIT_##limit) + +static int smiapp_read_all_smia_limits(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + u32 val; + + rval = smiapp_read( + sensor, smiapp_reg_limits[i].addr, &val); + if (rval) + return rval; + + sensor->limits[i] = val; + + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", + smiapp_reg_limits[i].addr, + smiapp_reg_limits[i].what, val, val); + } + + if (SMIA_LIM(sensor, SCALER_N_MIN) == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -240,35 +279,35 @@ static int smiapp_pll_try(struct smiapp_sensor *sensor, { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll_limits lim = { - .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], - .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], - .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], - .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], - .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], - .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], - .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], - .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], - - .op.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], - .op.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], - .op.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], - .op.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], - .op.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], - .op.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], - .op.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], - .op.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], - - .vt.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], - .vt.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], - .vt.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], - .vt.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], - .vt.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], - .vt.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], - .vt.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], - .vt.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], - - .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], - .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + .min_pre_pll_clk_div = SMIA_LIM(sensor, MIN_PRE_PLL_CLK_DIV), + .max_pre_pll_clk_div = SMIA_LIM(sensor, MAX_PRE_PLL_CLK_DIV), + .min_pll_ip_freq_hz = SMIA_LIM(sensor, MIN_PLL_IP_FREQ_HZ), + .max_pll_ip_freq_hz = SMIA_LIM(sensor, MAX_PLL_IP_FREQ_HZ), + .min_pll_multiplier = SMIA_LIM(sensor, MIN_PLL_MULTIPLIER), + .max_pll_multiplier = SMIA_LIM(sensor, MAX_PLL_MULTIPLIER), + .min_pll_op_freq_hz = SMIA_LIM(sensor, MIN_PLL_OP_FREQ_HZ), + .max_pll_op_freq_hz = SMIA_LIM(sensor, MAX_PLL_OP_FREQ_HZ), + + .op.min_sys_clk_div = SMIA_LIM(sensor, MIN_OP_SYS_CLK_DIV), + .op.max_sys_clk_div = SMIA_LIM(sensor, MAX_OP_SYS_CLK_DIV), + .op.min_pix_clk_div = SMIA_LIM(sensor, MIN_OP_PIX_CLK_DIV), + .op.max_pix_clk_div = SMIA_LIM(sensor, MAX_OP_PIX_CLK_DIV), + .op.min_sys_clk_freq_hz = SMIA_LIM(sensor, MIN_OP_SYS_CLK_FREQ_HZ), + .op.max_sys_clk_freq_hz = SMIA_LIM(sensor, MAX_OP_SYS_CLK_FREQ_HZ), + .op.min_pix_clk_freq_hz = SMIA_LIM(sensor, MIN_OP_PIX_CLK_FREQ_HZ), + .op.max_pix_clk_freq_hz = SMIA_LIM(sensor, MAX_OP_PIX_CLK_FREQ_HZ), + + .vt.min_sys_clk_div = SMIA_LIM(sensor, MIN_VT_SYS_CLK_DIV), + .vt.max_sys_clk_div = SMIA_LIM(sensor, MAX_VT_SYS_CLK_DIV), + .vt.min_pix_clk_div = SMIA_LIM(sensor, MIN_VT_PIX_CLK_DIV), + .vt.max_pix_clk_div = SMIA_LIM(sensor, MAX_VT_PIX_CLK_DIV), + .vt.min_sys_clk_freq_hz = SMIA_LIM(sensor, MIN_VT_SYS_CLK_FREQ_HZ), + .vt.max_sys_clk_freq_hz = SMIA_LIM(sensor, MAX_VT_SYS_CLK_FREQ_HZ), + .vt.min_pix_clk_freq_hz = SMIA_LIM(sensor, MIN_VT_PIX_CLK_FREQ_HZ), + .vt.max_pix_clk_freq_hz = SMIA_LIM(sensor, MAX_VT_PIX_CLK_FREQ_HZ), + + .min_line_length_pck_bin = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN), + .min_line_length_pck = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK), }; return smiapp_pll_calculate(&client->dev, &lim, pll); @@ -311,7 +350,7 @@ static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + sensor->vblank->val - - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + - SMIA_LIM(sensor, COARSE_INTEGRATION_TIME_MAX_MARGIN); __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, max, ctrl->step, max); } @@ -568,10 +607,10 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) sensor->analog_gain = v4l2_ctrl_new_std( &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], - max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), - sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MIN), + SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MAX), + max(SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_STEP), 1U), + SMIA_LIM(sensor, ANALOGUE_GAIN_CODE_MIN)); /* Exposure limits will be updated soon, use just something here. */ sensor->exposure = v4l2_ctrl_new_std( @@ -677,45 +716,6 @@ static void smiapp_free_controls(struct smiapp_sensor *sensor) v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); } -static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, - unsigned int n) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned int i; - u32 val; - int rval; - - for (i = 0; i < n; i++) { - rval = smiapp_read( - sensor, smiapp_reg_limits[limit[i]].addr, &val); - if (rval) - return rval; - sensor->limits[limit[i]] = val; - dev_dbg(&client->dev, "0x%8.8x \"%s\" = %u, 0x%x\n", - smiapp_reg_limits[limit[i]].addr, - smiapp_reg_limits[limit[i]].what, val, val); - } - - return 0; -} - -static int smiapp_get_all_limits(struct smiapp_sensor *sensor) -{ - unsigned int i; - int rval; - - for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { - rval = smiapp_get_limits(sensor, &i, 1); - if (rval < 0) - return rval; - } - - if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) - smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); - - return 0; -} - static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); @@ -869,21 +869,21 @@ static void smiapp_update_blanking(struct smiapp_sensor *sensor) int min, max; if (sensor->binning_vertical > 1 || sensor->binning_horizontal > 1) { - min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]; - max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN]; - min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]; - max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN]; - min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]; + min_fll = SMIA_LIM(sensor, MIN_FRAME_LENGTH_LINES_BIN); + max_fll = SMIA_LIM(sensor, MAX_FRAME_LENGTH_LINES_BIN); + min_llp = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK_BIN); + max_llp = SMIA_LIM(sensor, MAX_LINE_LENGTH_PCK_BIN); + min_lbp = SMIA_LIM(sensor, MIN_LINE_BLANKING_PCK_BIN); } else { - min_fll = sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES]; - max_fll = sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES]; - min_llp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK]; - max_llp = sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK]; - min_lbp = sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK]; + min_fll = SMIA_LIM(sensor, MIN_FRAME_LENGTH_LINES); + max_fll = SMIA_LIM(sensor, MAX_FRAME_LENGTH_LINES); + min_llp = SMIA_LIM(sensor, MIN_LINE_LENGTH_PCK); + max_llp = SMIA_LIM(sensor, MAX_LINE_LENGTH_PCK); + min_lbp = SMIA_LIM(sensor, MIN_LINE_BLANKING_PCK); } min = max_t(int, - sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + SMIA_LIM(sensor, MIN_FRAME_BLANKING_LINES), min_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); max = max_fll - sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; @@ -961,7 +961,7 @@ static int smiapp_read_nvm_page(struct smiapp_sensor *sensor, u32 p, u8 *nvm, return -ENODATA; } - if (sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + if (SMIA_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & SMIAPP_DATA_TRANSFER_IF_CAPABILITY_POLL) { for (i = 1000; i > 0; i--) { if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) @@ -1416,7 +1416,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) */ /* Digital crop */ - if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + if (SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { rval = smiapp_write( sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, @@ -1444,7 +1444,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) } /* Scaling */ - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + if (SMIA_LIM(sensor, SCALING_CAPABILITY) != SMIAPP_SCALING_CAPABILITY_NONE) { rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, sensor->scaling_mode); @@ -1467,7 +1467,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (rval < 0) goto out; - if ((sensor->limits[SMIAPP_LIMIT_FLASH_MODE_CAPABILITY] & + if ((SMIA_LIM(sensor, FLASH_MODE_CAPABILITY) & (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && sensor->hwcfg->strobe_setup != NULL && @@ -1715,8 +1715,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { if (ssd == sensor->scaler) { sensor->scale_m = - sensor->limits[ - SMIAPP_LIMIT_SCALER_N_MIN]; + SMIA_LIM(sensor, SCALER_N_MIN); sensor->scaling_mode = SMIAPP_SCALING_MODE_NONE; } else if (ssd == sensor->binner) { @@ -1828,12 +1827,12 @@ static int smiapp_set_format(struct v4l2_subdev *subdev, fmt->format.width = clamp(fmt->format.width, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE), + SMIA_LIM(sensor, MAX_X_OUTPUT_SIZE)); fmt->format.height = clamp(fmt->format.height, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], - sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + SMIA_LIM(sensor, MIN_Y_OUTPUT_SIZE), + SMIA_LIM(sensor, MAX_Y_OUTPUT_SIZE)); smiapp_get_crop_compose(subdev, cfg, crops, NULL, fmt->which); @@ -1886,7 +1885,7 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, val -= abs(w - ask_w); val -= abs(h - ask_h); - if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + if (w < SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE)) val -= SCALING_GOODNESS_EXTREME; dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", @@ -1952,7 +1951,7 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, struct i2c_client *client = v4l2_get_subdevdata(subdev); struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); u32 min, max, a, b, max_m; - u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + u32 scale_m = SMIA_LIM(sensor, SCALER_N_MIN); int mode = SMIAPP_SCALING_MODE_HORIZONTAL; u32 try[4]; u32 ntry = 0; @@ -1965,19 +1964,19 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, crops[SMIAPP_PAD_SINK]->height); a = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + * SMIA_LIM(sensor, SCALER_N_MIN) / sel->r.width; b = crops[SMIAPP_PAD_SINK]->height - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + * SMIA_LIM(sensor, SCALER_N_MIN) / sel->r.height; max_m = crops[SMIAPP_PAD_SINK]->width - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] - / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + * SMIA_LIM(sensor, SCALER_N_MIN) + / SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE); - a = clamp(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], - sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); - b = clamp(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], - sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); - max_m = clamp(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN], - sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX]); + a = clamp(a, SMIA_LIM(sensor, SCALER_M_MIN), + SMIA_LIM(sensor, SCALER_M_MAX)); + b = clamp(b, SMIA_LIM(sensor, SCALER_M_MIN), + SMIA_LIM(sensor, SCALER_M_MAX)); + max_m = clamp(max_m, SMIA_LIM(sensor, SCALER_M_MIN), + SMIA_LIM(sensor, SCALER_M_MAX)); dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); @@ -2004,7 +2003,7 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, subdev, crops[SMIAPP_PAD_SINK]->width / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + * SMIA_LIM(sensor, SCALER_N_MIN), sel->r.width, crops[SMIAPP_PAD_SINK]->height, sel->r.height, @@ -2018,18 +2017,18 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, best = this; } - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + if (SMIA_LIM(sensor, SCALING_CAPABILITY) == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) continue; this = scaling_goodness( subdev, crops[SMIAPP_PAD_SINK]->width / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + * SMIA_LIM(sensor, SCALER_N_MIN), sel->r.width, crops[SMIAPP_PAD_SINK]->height / try[i] - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + * SMIA_LIM(sensor, SCALER_N_MIN), sel->r.height, sel->flags); @@ -2043,12 +2042,12 @@ static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, sel->r.width = (crops[SMIAPP_PAD_SINK]->width / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + * SMIA_LIM(sensor, SCALER_N_MIN)) & ~1; if (mode == SMIAPP_SCALING_MODE_BOTH) sel->r.height = (crops[SMIAPP_PAD_SINK]->height / scale_m - * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + * SMIA_LIM(sensor, SCALER_N_MIN)) & ~1; else sel->r.height = crops[SMIAPP_PAD_SINK]->height; @@ -2104,7 +2103,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, return 0; if (ssd == sensor->scaler && sel->pad == SMIAPP_PAD_SINK - && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + && SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) return 0; return -EINVAL; @@ -2120,7 +2119,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, if (ssd == sensor->binner) return 0; if (ssd == sensor->scaler - && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + && SMIA_LIM(sensor, SCALING_CAPABILITY) != SMIAPP_SCALING_CAPABILITY_NONE) return 0; /* Fall through */ @@ -2185,8 +2184,8 @@ static void smiapp_get_native_size(struct smiapp_subdev *ssd, { r->top = 0; r->left = 0; - r->width = ssd->sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; - r->height = ssd->sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + r->width = SMIA_LIM(ssd->sensor, X_ADDR_MAX) + 1; + r->height = SMIA_LIM(ssd->sensor, Y_ADDR_MAX) + 1; } static int __smiapp_get_selection(struct v4l2_subdev *subdev, @@ -2271,10 +2270,10 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev, sel->r.height = SMIAPP_ALIGN_DIM(sel->r.height, sel->flags); sel->r.width = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + SMIA_LIM(sensor, MIN_X_OUTPUT_SIZE), sel->r.width); sel->r.height = max_t(unsigned int, - sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + SMIA_LIM(sensor, MIN_Y_OUTPUT_SIZE), sel->r.height); switch (sel->target) { @@ -2927,7 +2926,7 @@ static int smiapp_probe(struct i2c_client *client) goto out_power_off; } - rval = smiapp_get_all_limits(sensor); + rval = smiapp_read_all_smia_limits(sensor); if (rval) { rval = -ENODEV; goto out_power_off; @@ -2963,7 +2962,7 @@ static int smiapp_probe(struct i2c_client *client) goto out_power_off; } - if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + if (SMIA_LIM(sensor, BINNING_CAPABILITY)) { u32 val; rval = smiapp_read(sensor, @@ -3000,7 +2999,7 @@ static int smiapp_probe(struct i2c_client *client) } if (sensor->minfo.smiapp_version && - sensor->limits[SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY] & + SMIA_LIM(sensor, DATA_TRANSFER_IF_CAPABILITY) & SMIAPP_DATA_TRANSFER_IF_CAPABILITY_SUPPORTED) { if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { dev_err(&client->dev, "sysfs nvm entry failed\n"); @@ -3010,21 +3009,21 @@ static int smiapp_probe(struct i2c_client *client) } /* We consider this as profile 0 sensor if any of these are zero. */ - if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || - !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + if (!SMIA_LIM(sensor, MIN_OP_SYS_CLK_DIV) || + !SMIA_LIM(sensor, MAX_OP_SYS_CLK_DIV) || + !SMIA_LIM(sensor, MIN_OP_PIX_CLK_DIV) || + !SMIA_LIM(sensor, MAX_OP_PIX_CLK_DIV)) { sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; - } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + } else if (SMIA_LIM(sensor, SCALING_CAPABILITY) != SMIAPP_SCALING_CAPABILITY_NONE) { - if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + if (SMIA_LIM(sensor, SCALING_CAPABILITY) == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; else sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; sensor->scaler = &sensor->ssds[sensor->ssds_used]; sensor->ssds_used++; - } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + } else if (SMIA_LIM(sensor, DIGITAL_CROP_CAPABILITY) == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { sensor->scaler = &sensor->ssds[sensor->ssds_used]; sensor->ssds_used++; @@ -3034,13 +3033,13 @@ static int smiapp_probe(struct i2c_client *client) sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; sensor->ssds_used++; - sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scale_m = SMIA_LIM(sensor, SCALER_N_MIN); /* prepare PLL configuration input values */ sensor->pll.bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; sensor->pll.csi2.lanes = sensor->hwcfg->lanes; sensor->pll.ext_clk_freq_hz = sensor->hwcfg->ext_clk; - sensor->pll.scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->pll.scale_n = SMIA_LIM(sensor, SCALER_N_MIN); /* Profile 0 sensors have no separate OP clock branch. */ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index 43505cd0616e..e6f96309786f 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -35,6 +35,10 @@ #define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) #define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 +#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 + #define SMIAPP_DPHY_CTRL_AUTOMATIC 0 /* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ #define SMIAPP_DPHY_CTRL_UI 1 diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index ce8c1d47fbf0..1b58b7c6c839 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -8,6 +8,8 @@ * Contact: Sakari Ailus <sakari.ailus@iki.fi> */ +#include <asm/unaligned.h> + #include <linux/delay.h> #include <linux/i2c.h> @@ -69,18 +71,19 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct i2c_msg msg; - unsigned char data[4]; - u16 offset = reg; + unsigned char data_buf[sizeof(u32)] = { 0 }; + unsigned char offset_buf[sizeof(u16)]; int r; + if (len > sizeof(data_buf)) + return -EINVAL; + msg.addr = client->addr; msg.flags = 0; - msg.len = 2; - msg.buf = data; + msg.len = sizeof(offset_buf); + msg.buf = offset_buf; + put_unaligned_be16(reg, offset_buf); - /* high byte goes out first */ - data[0] = (u8) (offset >> 8); - data[1] = (u8) offset; r = i2c_transfer(client->adapter, &msg, 1); if (r != 1) { if (r >= 0) @@ -90,6 +93,8 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, msg.len = len; msg.flags = I2C_M_RD; + msg.buf = &data_buf[sizeof(data_buf) - len]; + r = i2c_transfer(client->adapter, &msg, 1); if (r != 1) { if (r >= 0) @@ -97,27 +102,12 @@ static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, goto err; } - *val = 0; - /* high byte comes first */ - switch (len) { - case SMIAPP_REG_32BIT: - *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + - data[3]; - break; - case SMIAPP_REG_16BIT: - *val = (data[0] << 8) + data[1]; - break; - case SMIAPP_REG_8BIT: - *val = data[0]; - break; - default: - BUG(); - } + *val = get_unaligned_be32(data_buf); return 0; err: - dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + dev_err(&client->dev, "read from offset 0x%x error %d\n", reg, r); return r; } @@ -158,7 +148,7 @@ static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, && len != SMIAPP_REG_32BIT) return -EINVAL; - if (len == SMIAPP_REG_8BIT || !only8) + if (!only8) rval = ____smiapp_read(sensor, SMIAPP_REG_ADDR(reg), len, val); else rval = ____smiapp_read_8only(sensor, SMIAPP_REG_ADDR(reg), len, @@ -214,13 +204,10 @@ int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) struct i2c_msg msg; unsigned char data[6]; unsigned int retries; - u8 flags = SMIAPP_REG_FLAGS(reg); u8 len = SMIAPP_REG_WIDTH(reg); - u16 offset = SMIAPP_REG_ADDR(reg); int r; - if ((len != SMIAPP_REG_8BIT && len != SMIAPP_REG_16BIT && - len != SMIAPP_REG_32BIT) || flags) + if (len > sizeof(data) - 2) return -EINVAL; msg.addr = client->addr; @@ -228,27 +215,8 @@ int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) msg.len = 2 + len; msg.buf = data; - /* high byte goes out first */ - data[0] = (u8) (reg >> 8); - data[1] = (u8) (reg & 0xff); - - switch (len) { - case SMIAPP_REG_8BIT: - data[2] = val; - break; - case SMIAPP_REG_16BIT: - data[2] = val >> 8; - data[3] = val; - break; - case SMIAPP_REG_32BIT: - data[2] = val >> 24; - data[3] = val >> 16; - data[4] = val >> 8; - data[5] = val; - break; - default: - BUG(); - } + put_unaligned_be16(SMIAPP_REG_ADDR(reg), data); + put_unaligned_be32(val << (8 * (sizeof(val) - len)), data + 2); for (retries = 0; retries < 5; retries++) { /* @@ -269,7 +237,8 @@ int smiapp_write_no_quirk(struct smiapp_sensor *sensor, u32 reg, u32 val) } dev_err(&client->dev, - "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + "wrote 0x%x to offset 0x%x error %d\n", val, + SMIAPP_REG_ADDR(reg), r); return r; } diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 4837d80dc453..6f469934f9e3 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -14,7 +14,6 @@ #include <linux/mutex.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-subdev.h> -#include <media/i2c/smiapp.h> #include "smiapp-pll.h" #include "smiapp-reg.h" @@ -42,6 +41,49 @@ #define SMIAPP_COLOUR_COMPONENTS 4 +#define SMIAPP_NAME "smiapp" + +#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ +#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ + +/* + * Sometimes due to board layout considerations the camera module can be + * mounted rotated. The typical rotation used is 180 degrees which can be + * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. + * FIXME: rotation also changes the bayer pattern. + */ +enum smiapp_module_board_orient { + SMIAPP_MODULE_BOARD_ORIENT_0 = 0, + SMIAPP_MODULE_BOARD_ORIENT_180, +}; + +struct smiapp_flash_strobe_parms { + u8 mode; + u32 strobe_width_high_us; + u16 strobe_delay; + u16 stobe_start_point; + u8 trigger; +}; + +struct smiapp_hwconfig { + /* + * Change the cci address if i2c_addr_alt is set. + * Both default and alternate cci addr need to be present + */ + unsigned short i2c_addr_dfl; /* Default i2c addr */ + unsigned short i2c_addr_alt; /* Alternate i2c addr */ + + uint32_t ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + uint64_t *op_sys_clock; + + enum smiapp_module_board_orient module_board_orient; + + struct smiapp_flash_strobe_parms *strobe_setup; +}; + #include "smiapp-limits.h" struct smiapp_quirk; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index edad49cebcdf..eb39cf5ea089 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -13,12 +13,15 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of_graph.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-mc.h> +#include <media/v4l2-rect.h> #include "tvp5150_reg.h" @@ -31,6 +34,15 @@ #define TVP5150_MBUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 #define TVP5150_FIELD V4L2_FIELD_ALTERNATE #define TVP5150_COLORSPACE V4L2_COLORSPACE_SMPTE170M +#define TVP5150_STD_MASK (V4L2_STD_NTSC | \ + V4L2_STD_NTSC_443 | \ + V4L2_STD_PAL | \ + V4L2_STD_PAL_M | \ + V4L2_STD_PAL_N | \ + V4L2_STD_PAL_Nc | \ + V4L2_STD_SECAM) + +#define TVP5150_MAX_CONNECTORS 3 /* Check dt-bindings for more information */ MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); @@ -44,18 +56,26 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg) enum tvp5150_pads { - TVP5150_PAD_IF_INPUT, + TVP5150_PAD_AIP1A, + TVP5150_PAD_AIP1B, TVP5150_PAD_VID_OUT, TVP5150_NUM_PADS }; +struct tvp5150_connector { + struct v4l2_fwnode_connector base; + struct media_entity ent; + struct media_pad pad; +}; + struct tvp5150 { struct v4l2_subdev sd; -#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pads[TVP5150_NUM_PADS]; - struct media_entity input_ent[TVP5150_INPUT_NUM]; - struct media_pad input_pad[TVP5150_INPUT_NUM]; -#endif + struct tvp5150_connector connectors[TVP5150_MAX_CONNECTORS]; + struct tvp5150_connector *cur_connector; + unsigned int connectors_num; + struct v4l2_ctrl_handler hdl; struct v4l2_rect rect; struct regmap *regmap; @@ -282,9 +302,12 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) break; } - dev_dbg_lvl(sd->dev, 1, debug, "Selecting video route: route input=%i, output=%i => tvp5150 input=%i, opmode=%i\n", - decoder->input, decoder->output, - input, opmode); + dev_dbg_lvl(sd->dev, 1, debug, + "Selecting video route: route input=%s, output=%s => tvp5150 input=0x%02x, opmode=0x%02x\n", + decoder->input == 0 ? "aip1a" : + decoder->input == 2 ? "aip1b" : "svideo", + decoder->output == 0 ? "normal" : "black-frame-gen", + input, opmode); regmap_write(decoder->regmap, TVP5150_OP_MODE_CTL, opmode); regmap_write(decoder->regmap, TVP5150_VD_IN_SRC_SEL_1, input); @@ -773,17 +796,33 @@ static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct tvp5150 *decoder = to_tvp5150(sd); + struct tvp5150_connector *cur_con = decoder->cur_connector; + v4l2_std_id supported_stds; if (decoder->norm == std) return 0; + /* In case of no of-connectors are available no limitations are made */ + if (!decoder->connectors_num) + supported_stds = V4L2_STD_ALL; + else + supported_stds = cur_con->base.connector.analog.sdtv_stds; + + /* + * Check if requested std or group of std's is/are supported by the + * connector. + */ + if ((supported_stds & std) == 0) + return -EINVAL; + /* Change cropping height limits */ if (std & V4L2_STD_525_60) decoder->rect.height = TVP5150_V_MAX_525_60; else decoder->rect.height = TVP5150_V_MAX_OTHERS; - decoder->norm = std; + /* Set only the specific supported std in case of group of std's. */ + decoder->norm = supported_stds & std; return tvp5150_set_std(sd, std); } @@ -986,6 +1025,25 @@ static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop) crop->height = TVP5150_V_MAX_OTHERS; } +static struct v4l2_rect * +tvp5150_get_pad_crop(struct tvp5150 *decoder, + struct v4l2_subdev_pad_config *cfg, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &decoder->rect; + case V4L2_SUBDEV_FORMAT_TRY: +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) + return v4l2_subdev_get_try_crop(&decoder->sd, cfg, pad); +#else + return ERR_PTR(-EINVAL); +#endif + default: + return ERR_PTR(-EINVAL); + } +} + static int tvp5150_fill_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -1010,25 +1068,10 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, return 0; } -static int tvp5150_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_selection *sel) +static unsigned int tvp5150_get_hmax(struct v4l2_subdev *sd) { struct tvp5150 *decoder = to_tvp5150(sd); - struct v4l2_rect rect = sel->r; v4l2_std_id std; - int hmax; - - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || - sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect.left, rect.top, rect.width, rect.height); - - /* tvp5150 has some special limits */ - rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); /* Calculate height based on current standard */ if (decoder->norm == V4L2_STD_ALL) @@ -1036,36 +1079,78 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd, else std = decoder->norm; - if (std & V4L2_STD_525_60) - hmax = TVP5150_V_MAX_525_60; - else - hmax = TVP5150_V_MAX_OTHERS; + return (std & V4L2_STD_525_60) ? + TVP5150_V_MAX_525_60 : TVP5150_V_MAX_OTHERS; +} - /* - * alignments: - * - width = 2 due to UYVY colorspace - * - height, image = no special alignment - */ - v4l_bound_align_image(&rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left, 1, &rect.height, - hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top, 0, 0); +static void tvp5150_set_hw_selection(struct v4l2_subdev *sd, + struct v4l2_rect *rect) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int hmax = tvp5150_get_hmax(sd); - regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect->top); regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, - rect.top + rect.height - hmax); + rect->top + rect->height - hmax); regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB, - rect.left >> TVP5150_CROP_SHIFT); + rect->left >> TVP5150_CROP_SHIFT); regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB, - rect.left | (1 << TVP5150_CROP_SHIFT)); + rect->left | (1 << TVP5150_CROP_SHIFT)); regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB, - (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + (rect->left + rect->width - TVP5150_MAX_CROP_LEFT) >> TVP5150_CROP_SHIFT); regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB, - rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + rect->left + rect->width - TVP5150_MAX_CROP_LEFT); +} + +static int tvp5150_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + struct v4l2_rect *rect = &sel->r; + struct v4l2_rect *crop; + unsigned int hmax; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + dev_dbg_lvl(sd->dev, 1, debug, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect->left, rect->top, rect->width, rect->height); + + /* tvp5150 has some special limits */ + rect->left = clamp(rect->left, 0, TVP5150_MAX_CROP_LEFT); + rect->top = clamp(rect->top, 0, TVP5150_MAX_CROP_TOP); + hmax = tvp5150_get_hmax(sd); + + /* + * alignments: + * - width = 2 due to UYVY colorspace + * - height, image = no special alignment + */ + v4l_bound_align_image(&rect->width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect->left, + TVP5150_H_MAX - rect->left, 1, &rect->height, + hmax - TVP5150_MAX_CROP_TOP - rect->top, + hmax - rect->top, 0, 0); + + if (!IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) && + sel->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + crop = tvp5150_get_pad_crop(decoder, cfg, sel->pad, sel->which); + if (IS_ERR(crop)) + return PTR_ERR(crop); + + /* + * Update output image size if the selection (crop) rectangle size or + * position has been modified. + */ + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && + !v4l2_rect_equal(rect, crop)) + tvp5150_set_hw_selection(sd, rect); - decoder->rect = rect; + *crop = *rect; return 0; } @@ -1075,11 +1160,9 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); + struct v4l2_rect *crop; v4l2_std_id std; - if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: sel->r.left = 0; @@ -1097,7 +1180,11 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd, sel->r.height = TVP5150_V_MAX_OTHERS; return 0; case V4L2_SEL_TGT_CROP: - sel->r = decoder->rect; + crop = tvp5150_get_pad_crop(decoder, cfg, sel->pad, + sel->which); + if (IS_ERR(crop)) + return PTR_ERR(crop); + sel->r = *crop; return 0; default: return -EINVAL; @@ -1170,30 +1257,148 @@ static int tvp5150_enum_frame_size(struct v4l2_subdev *sd, } /**************************************************************************** - Media entity ops + * Media entity ops ****************************************************************************/ +#if defined(CONFIG_MEDIA_CONTROLLER) +static int tvp5150_set_link(struct media_pad *connector_pad, + struct media_pad *tvp5150_pad, u32 flags) +{ + struct media_link *link; + + link = media_entity_find_link(connector_pad, tvp5150_pad); + if (!link) + return -EINVAL; + + link->flags = flags; + link->reverse->flags = link->flags; + + return 0; +} + +static int tvp5150_disable_all_input_links(struct tvp5150 *decoder) +{ + struct media_pad *connector_pad; + unsigned int i; + int err; + + for (i = 0; i < TVP5150_NUM_PADS - 1; i++) { + connector_pad = media_entity_remote_pad(&decoder->pads[i]); + if (!connector_pad) + continue; + + err = tvp5150_set_link(connector_pad, &decoder->pads[i], 0); + if (err) + return err; + } + + return 0; +} + +static int tvp5150_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config); -#ifdef CONFIG_MEDIA_CONTROLLER static int tvp5150_link_setup(struct media_entity *entity, - const struct media_pad *local, + const struct media_pad *tvp5150_pad, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct tvp5150 *decoder = to_tvp5150(sd); - int i; + struct media_pad *other_tvp5150_pad = + &decoder->pads[tvp5150_pad->index ^ 1]; + struct v4l2_fwnode_connector *v4l2c; + bool is_svideo = false; + unsigned int i; + int err; + + /* + * The TVP5150 state is determined by the enabled sink pad link(s). + * Enabling or disabling the source pad link has no effect. + */ + if (tvp5150_pad->flags & MEDIA_PAD_FL_SOURCE) + return 0; - for (i = 0; i < TVP5150_INPUT_NUM; i++) { - if (remote->entity == &decoder->input_ent[i]) + /* Check if the svideo connector should be enabled */ + for (i = 0; i < decoder->connectors_num; i++) { + if (remote->entity == &decoder->connectors[i].ent) { + v4l2c = &decoder->connectors[i].base; + is_svideo = v4l2c->type == V4L2_CONN_SVIDEO; break; + } } - /* Do nothing for entities that are not input connectors */ - if (i == TVP5150_INPUT_NUM) - return 0; + dev_dbg_lvl(sd->dev, 1, debug, "link setup '%s':%d->'%s':%d[%d]", + remote->entity->name, remote->index, + tvp5150_pad->entity->name, tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED); + if (is_svideo) + dev_dbg_lvl(sd->dev, 1, debug, + "link setup '%s':%d->'%s':%d[%d]", + remote->entity->name, remote->index, + other_tvp5150_pad->entity->name, + other_tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED); - decoder->input = i; + /* + * The TVP5150 has an internal mux which allows the following setup: + * + * comp-connector1 --\ + * |---> AIP1A + * / + * svideo-connector -| + * \ + * |---> AIP1B + * comp-connector2 --/ + * + * We can't rely on user space that the current connector gets disabled + * first before enabling the new connector. Disable all active + * connector links to be on the safe side. + */ + err = tvp5150_disable_all_input_links(decoder); + if (err) + return err; + + tvp5150_s_routing(sd, is_svideo ? TVP5150_SVIDEO : tvp5150_pad->index, + flags & MEDIA_LNK_FL_ENABLED ? TVP5150_NORMAL : + TVP5150_BLACK_SCREEN, 0); + + if (flags & MEDIA_LNK_FL_ENABLED) { + struct v4l2_fwnode_connector_analog *v4l2ca; + u32 new_norm; + + /* + * S-Video connector is conneted to both ports AIP1A and AIP1B. + * Both links must be enabled in one-shot regardless which link + * the user requests. + */ + if (is_svideo) { + err = tvp5150_set_link((struct media_pad *)remote, + other_tvp5150_pad, flags); + if (err) + return err; + } - tvp5150_selmux(sd); + if (!decoder->connectors_num) + return 0; + + /* Update the current connector */ + decoder->cur_connector = + container_of(remote, struct tvp5150_connector, pad); + + /* + * Do nothing if the new connector supports the same tv-norms as + * the old one. + */ + v4l2ca = &decoder->cur_connector->base.connector.analog; + new_norm = decoder->norm & v4l2ca->sdtv_stds; + if (decoder->norm == new_norm) + return 0; + + /* + * Fallback to the new connector tv-norms if we can't find any + * common between the current tv-norm and the new one. + */ + tvp5150_s_std(sd, new_norm ? new_norm : v4l2ca->sdtv_stds); + } return 0; } @@ -1202,20 +1407,54 @@ static const struct media_entity_operations tvp5150_sd_media_ops = { .link_setup = tvp5150_link_setup, }; #endif - /**************************************************************************** I2C Command ****************************************************************************/ +static int __maybe_unused tvp5150_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->irq) + /* Disable lock interrupt */ + return regmap_update_bits(decoder->regmap, + TVP5150_INT_ENABLE_REG_A, + TVP5150_INT_A_LOCK, 0); + return 0; +} + +static int __maybe_unused tvp5150_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->irq) + /* Enable lock interrupt */ + return regmap_update_bits(decoder->regmap, + TVP5150_INT_ENABLE_REG_A, + TVP5150_INT_A_LOCK, + TVP5150_INT_A_LOCK); + return 0; +} static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) { struct tvp5150 *decoder = to_tvp5150(sd); - unsigned int mask, val = 0, int_val = 0; + unsigned int mask, val = 0; + int ret; mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | TVP5150_MISC_CTL_CLOCK_OE; if (enable) { + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + tvp5150_enable(sd); /* Enable outputs if decoder is locked */ @@ -1223,15 +1462,13 @@ static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) val = decoder->lock ? decoder->oe : 0; else val = decoder->oe; - int_val = TVP5150_INT_A_LOCK; + v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt); + } else { + pm_runtime_put(sd->dev); } regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val); - if (decoder->irq) - /* Enable / Disable lock interrupt */ - regmap_update_bits(decoder->regmap, TVP5150_INT_ENABLE_REG_A, - TVP5150_INT_A_LOCK, int_val); return 0; } @@ -1342,6 +1579,19 @@ static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regi } #endif +static int tvp5150_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); + default: + return -EINVAL; + } +} + static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { int status = tvp5150_read(sd, 0x88); @@ -1352,40 +1602,96 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) static int tvp5150_registered(struct v4l2_subdev *sd) { -#ifdef CONFIG_MEDIA_CONTROLLER +#if defined(CONFIG_MEDIA_CONTROLLER) struct tvp5150 *decoder = to_tvp5150(sd); - int ret = 0; - int i; - - for (i = 0; i < TVP5150_INPUT_NUM; i++) { - struct media_entity *input = &decoder->input_ent[i]; - struct media_pad *pad = &decoder->input_pad[i]; - - if (!input->name) - continue; + unsigned int i; + int ret; - decoder->input_pad[i].flags = MEDIA_PAD_FL_SOURCE; + /* + * Setup connector pads and links. Enable the link to the first + * available connector per default. + */ + for (i = 0; i < decoder->connectors_num; i++) { + struct media_entity *con = &decoder->connectors[i].ent; + struct media_pad *pad = &decoder->connectors[i].pad; + struct v4l2_fwnode_connector *v4l2c = + &decoder->connectors[i].base; + struct v4l2_connector_link *link = + v4l2_connector_first_link(v4l2c); + unsigned int port = link->fwnode_link.remote_port; + unsigned int flags = i ? 0 : MEDIA_LNK_FL_ENABLED; + bool is_svideo = v4l2c->type == V4L2_CONN_SVIDEO; + + pad->flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(con, 1, pad); + if (ret < 0) + goto err; - ret = media_entity_pads_init(input, 1, pad); + ret = media_device_register_entity(sd->v4l2_dev->mdev, con); if (ret < 0) - return ret; + goto err; - ret = media_device_register_entity(sd->v4l2_dev->mdev, input); + ret = media_create_pad_link(con, 0, &sd->entity, port, flags); if (ret < 0) - return ret; + goto err; - ret = media_create_pad_link(input, 0, &sd->entity, - TVP5150_PAD_IF_INPUT, 0); - if (ret < 0) { - media_device_unregister_entity(input); - return ret; + if (is_svideo) { + /* + * Check tvp5150_link_setup() comments for more + * information. + */ + link = v4l2_connector_last_link(v4l2c); + port = link->fwnode_link.remote_port; + ret = media_create_pad_link(con, 0, &sd->entity, port, + flags); + if (ret < 0) + goto err; + } + + /* Enable default input. */ + if (flags == MEDIA_LNK_FL_ENABLED) { + decoder->input = + is_svideo ? TVP5150_SVIDEO : + port == 0 ? TVP5150_COMPOSITE0 : + TVP5150_COMPOSITE1; + + tvp5150_selmux(sd); + decoder->cur_connector = &decoder->connectors[i]; + tvp5150_s_std(sd, v4l2c->connector.analog.sdtv_stds); } } + + return 0; + +err: + for (i = 0; i < decoder->connectors_num; i++) + media_device_unregister_entity(&decoder->connectors[i].ent); + return ret; #endif return 0; } +static int tvp5150_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int ret; + + ret = pm_runtime_get_sync(sd->dev); + if (ret < 0) { + pm_runtime_put_noidle(sd->dev); + return ret; + } + + return 0; +} + +static int tvp5150_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { @@ -1399,6 +1705,8 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .g_register = tvp5150_g_register, .s_register = tvp5150_s_register, #endif + .subscribe_event = tvp5150_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { @@ -1441,6 +1749,8 @@ static const struct v4l2_subdev_ops tvp5150_ops = { static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { .registered = tvp5150_registered, + .open = tvp5150_open, + .close = tvp5150_close, }; /**************************************************************************** @@ -1591,102 +1901,211 @@ static int tvp5150_init(struct i2c_client *c) return 0; } -static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) +#if defined(CONFIG_MEDIA_CONTROLLER) +static int tvp5150_mc_init(struct tvp5150 *decoder) { - struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; - struct device_node *ep; -#ifdef CONFIG_MEDIA_CONTROLLER - struct device_node *connectors, *child; - struct media_entity *input; - const char *name; - u32 input_type; -#endif - unsigned int flags; - int ret = 0; + struct v4l2_subdev *sd = &decoder->sd; + unsigned int i; - ep = of_graph_get_next_endpoint(np, NULL); - if (!ep) - return -EINVAL; + sd->entity.ops = &tvp5150_sd_media_ops; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); - if (ret) - goto err; + for (i = 0; i < TVP5150_NUM_PADS - 1; i++) { + decoder->pads[i].flags = MEDIA_PAD_FL_SINK; + decoder->pads[i].sig_type = PAD_SIGNAL_ANALOG; + } - flags = bus_cfg.bus.parallel.flags; + decoder->pads[i].flags = MEDIA_PAD_FL_SOURCE; + decoder->pads[i].sig_type = PAD_SIGNAL_DV; - if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL && - !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH && - flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH && - flags & V4L2_MBUS_FIELD_EVEN_LOW)) { - ret = -EINVAL; - goto err; - } + return media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, + decoder->pads); +} - decoder->mbus_type = bus_cfg.bus_type; +#else /* !defined(CONFIG_MEDIA_CONTROLLER) */ -#ifdef CONFIG_MEDIA_CONTROLLER - connectors = of_get_child_by_name(np, "connectors"); +static inline int tvp5150_mc_init(struct tvp5150 *decoder) +{ + return 0; +} +#endif /* defined(CONFIG_MEDIA_CONTROLLER) */ - if (!connectors) - goto err; +static int tvp5150_validate_connectors(struct tvp5150 *decoder) +{ + struct device *dev = decoder->sd.dev; + struct tvp5150_connector *tvpc; + struct v4l2_fwnode_connector *v4l2c; + unsigned int i; + + if (!decoder->connectors_num) { + dev_err(dev, "No valid connector found\n"); + return -ENODEV; + } - for_each_available_child_of_node(connectors, child) { - ret = of_property_read_u32(child, "input", &input_type); - if (ret) { - dev_err(decoder->sd.dev, - "missing type property in node %pOFn\n", - child); - of_node_put(child); - goto err_connector; + for (i = 0; i < decoder->connectors_num; i++) { + struct v4l2_connector_link *link0 = NULL; + struct v4l2_connector_link *link1; + + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + + if (v4l2c->type == V4L2_CONN_COMPOSITE) { + if (v4l2c->nr_of_links != 1) { + dev_err(dev, "Composite: connector needs 1 link\n"); + return -EINVAL; + } + link0 = v4l2_connector_first_link(v4l2c); + if (!link0) { + dev_err(dev, "Composite: invalid first link\n"); + return -EINVAL; + } + if (link0->fwnode_link.remote_id == 1) { + dev_err(dev, "Composite: invalid endpoint id\n"); + return -EINVAL; + } } - if (input_type >= TVP5150_INPUT_NUM) { - ret = -EINVAL; - of_node_put(child); - goto err_connector; + if (v4l2c->type == V4L2_CONN_SVIDEO) { + if (v4l2c->nr_of_links != 2) { + dev_err(dev, "SVideo: connector needs 2 links\n"); + return -EINVAL; + } + link0 = v4l2_connector_first_link(v4l2c); + if (!link0) { + dev_err(dev, "SVideo: invalid first link\n"); + return -EINVAL; + } + link1 = v4l2_connector_last_link(v4l2c); + if (link0->fwnode_link.remote_port == + link1->fwnode_link.remote_port) { + dev_err(dev, "SVideo: invalid link setup\n"); + return -EINVAL; + } } - input = &decoder->input_ent[input_type]; + if (!(v4l2c->connector.analog.sdtv_stds & TVP5150_STD_MASK)) { + dev_err(dev, "Unsupported tv-norm on connector %s\n", + v4l2c->name); + return -EINVAL; + } + } + + return 0; +} + +static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) +{ + struct device *dev = decoder->sd.dev; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_UNKNOWN + }; + struct device_node *ep_np; + struct tvp5150_connector *tvpc; + struct v4l2_fwnode_connector *v4l2c; + unsigned int flags, ep_num; + unsigned int i; + int ret; + + /* At least 1 output and 1 input */ + ep_num = of_graph_get_endpoint_count(np); + if (ep_num < 2 || ep_num > 5) { + dev_err(dev, "At least 1 input and 1 output must be connected to the device.\n"); + return -EINVAL; + } - /* Each input connector can only be defined once */ - if (input->name) { - dev_err(decoder->sd.dev, - "input %s with same type already exists\n", - input->name); - of_node_put(child); - ret = -EINVAL; - goto err_connector; + /* Layout if all connectors are used: + * + * tvp-5150 port@0 (AIP1A) + * endpoint@0 -----------> Comp0-Con port + * endpoint@1 --------+--> Svideo-Con port + * tvp-5150 port@1 (AIP1B) | + * endpoint@1 --------+ + * endpoint@0 -----------> Comp1-Con port + * tvp-5150 port@2 + * endpoint (video bitstream output at YOUT[0-7] parallel bus) + */ + for_each_endpoint_of_node(np, ep_np) { + struct fwnode_handle *ep_fwnode = of_fwnode_handle(ep_np); + unsigned int next_connector = decoder->connectors_num; + struct of_endpoint ep; + + of_graph_parse_endpoint(ep_np, &ep); + if (ep.port > 1 || ep.id > 1) { + dev_dbg(dev, "Ignore connector on port@%u/ep@%u\n", + ep.port, ep.id); + continue; } - switch (input_type) { - case TVP5150_COMPOSITE0: - case TVP5150_COMPOSITE1: - input->function = MEDIA_ENT_F_CONN_COMPOSITE; - break; - case TVP5150_SVIDEO: - input->function = MEDIA_ENT_F_CONN_SVIDEO; - break; + tvpc = &decoder->connectors[next_connector]; + v4l2c = &tvpc->base; + + if (ep.port == 0 || (ep.port == 1 && ep.id == 0)) { + ret = v4l2_fwnode_connector_parse(ep_fwnode, v4l2c); + if (ret) + goto err_put; + ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c); + if (ret) + goto err_put; + decoder->connectors_num++; + } else { + /* Adding the 2nd svideo link */ + for (i = 0; i < TVP5150_MAX_CONNECTORS; i++) { + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + if (v4l2c->type == V4L2_CONN_SVIDEO) + break; + } + + ret = v4l2_fwnode_connector_add_link(ep_fwnode, v4l2c); + if (ret) + goto err_put; } + } - input->flags = MEDIA_ENT_FL_CONNECTOR; + ret = tvp5150_validate_connectors(decoder); + if (ret) + goto err_free; + + for (i = 0; i < decoder->connectors_num; i++) { + tvpc = &decoder->connectors[i]; + v4l2c = &tvpc->base; + tvpc->ent.flags = MEDIA_ENT_FL_CONNECTOR; + tvpc->ent.function = v4l2c->type == V4L2_CONN_SVIDEO ? + MEDIA_ENT_F_CONN_SVIDEO : MEDIA_ENT_F_CONN_COMPOSITE; + tvpc->ent.name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", + v4l2c->name, v4l2c->label ? + v4l2c->label : ""); + } - ret = of_property_read_string(child, "label", &name); - if (ret < 0) { - dev_err(decoder->sd.dev, - "missing label property in node %pOFn\n", - child); - of_node_put(child); - goto err_connector; - } + ep_np = of_graph_get_endpoint_by_regs(np, TVP5150_PAD_VID_OUT, 0); + if (!ep_np) { + dev_err(dev, "Error no output endpoint available\n"); + goto err_free; + } + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_np), &bus_cfg); + of_node_put(ep_np); + if (ret) + goto err_free; - input->name = name; + flags = bus_cfg.bus.parallel.flags; + if (bus_cfg.bus_type == V4L2_MBUS_PARALLEL && + !(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH && + flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH && + flags & V4L2_MBUS_FIELD_EVEN_LOW)) { + ret = -EINVAL; + goto err_free; } -err_connector: - of_node_put(connectors); -#endif -err: - of_node_put(ep); + decoder->mbus_type = bus_cfg.bus_type; + + return 0; + +err_put: + of_node_put(ep_np); +err_free: + for (i = 0; i < TVP5150_MAX_CONNECTORS; i++) + v4l2_fwnode_connector_free(&decoder->connectors[i].base); + return ret; } @@ -1701,6 +2120,7 @@ static int tvp5150_probe(struct i2c_client *c) struct v4l2_subdev *sd; struct device_node *np = c->dev.of_node; struct regmap *map; + unsigned int i; int res; /* Check if the adapter supports the needed features */ @@ -1722,6 +2142,9 @@ static int tvp5150_probe(struct i2c_client *c) core->regmap = map; sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + sd->internal_ops = &tvp5150_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; if (IS_ENABLED(CONFIG_OF) && np) { res = tvp5150_parse_dt(core, np); @@ -1734,30 +2157,29 @@ static int tvp5150_probe(struct i2c_client *c) core->mbus_type = V4L2_MBUS_BT656; } - v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); - sd->internal_ops = &tvp5150_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - -#if defined(CONFIG_MEDIA_CONTROLLER) - core->pads[TVP5150_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; - core->pads[TVP5150_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG; - core->pads[TVP5150_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - core->pads[TVP5150_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; - - sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - - res = media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, core->pads); - if (res < 0) + res = tvp5150_mc_init(core); + if (res) return res; - sd->entity.ops = &tvp5150_sd_media_ops; -#endif - res = tvp5150_detect_version(core); if (res < 0) return res; - core->norm = V4L2_STD_ALL; /* Default is autodetect */ + /* + * Iterate over all available connectors in case they are supported and + * successfully parsed. Fallback to default autodetect in case they + * aren't supported. + */ + for (i = 0; i < core->connectors_num; i++) { + struct v4l2_fwnode_connector *v4l2c; + + v4l2c = &core->connectors[i].base; + core->norm |= v4l2c->connector.analog.sdtv_stds; + } + + if (!core->connectors_num) + core->norm = V4L2_STD_ALL; + core->detected_norm = V4L2_STD_UNKNOWN; core->input = TVP5150_COMPOSITE1; core->enable = true; @@ -1802,6 +2224,11 @@ static int tvp5150_probe(struct i2c_client *c) if (debug > 1) tvp5150_log_status(sd); + + pm_runtime_set_active(&c->dev); + pm_runtime_enable(&c->dev); + pm_runtime_idle(&c->dev); + return 0; err: @@ -1813,18 +2240,32 @@ static int tvp5150_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int i; dev_dbg_lvl(sd->dev, 1, debug, "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); + for (i = 0; i < decoder->connectors_num; i++) + v4l2_fwnode_connector_free(&decoder->connectors[i].base); + for (i = 0; i < decoder->connectors_num; i++) + media_device_unregister_entity(&decoder->connectors[i].ent); v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&decoder->hdl); + pm_runtime_disable(&c->dev); + pm_runtime_set_suspended(&c->dev); + return 0; } /* ----------------------------------------------------------------------- */ +static const struct dev_pm_ops tvp5150_pm_ops = { + SET_RUNTIME_PM_OPS(tvp5150_runtime_suspend, + tvp5150_runtime_resume, + NULL) +}; + static const struct i2c_device_id tvp5150_id[] = { { "tvp5150", 0 }, { } @@ -1843,6 +2284,7 @@ static struct i2c_driver tvp5150_driver = { .driver = { .of_match_table = of_match_ptr(tvp5150_of_match), .name = "tvp5150", + .pm = &tvp5150_pm_ops, }, .probe_new = tvp5150_probe, .remove = tvp5150_remove, diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 078141712c88..0465832a4090 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -255,7 +255,7 @@ static int amg88xx_set_power(struct video_i2c_data *data, bool on) return amg88xx_set_power_off(data); } -#if IS_ENABLED(CONFIG_HWMON) +#if IS_REACHABLE(CONFIG_HWMON) static const u32 amg88xx_temp_config[] = { HWMON_T_INPUT, @@ -858,7 +858,7 @@ static int video_i2c_probe(struct i2c_client *client, } } - ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&data->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) goto error_pm_disable; diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c index 668770e9f609..211279c5fd77 100644 --- a/drivers/media/mc/mc-entity.c +++ b/drivers/media/mc/mc-entity.c @@ -662,9 +662,14 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, struct media_link *link; struct media_link *backlink; - BUG_ON(source == NULL || sink == NULL); - BUG_ON(source_pad >= source->num_pads); - BUG_ON(sink_pad >= sink->num_pads); + if (WARN_ON(!source || !sink) || + WARN_ON(source_pad >= source->num_pads) || + WARN_ON(sink_pad >= sink->num_pads)) + return -EINVAL; + if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE))) + return -EINVAL; + if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) + return -EINVAL; link = media_add_link(&source->links); if (link == NULL) diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index a359da7773a9..9144f795fb93 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2964,7 +2964,7 @@ static int bttv_open(struct file *file) dprintk("open dev=%s\n", video_device_node_name(vdev)); - if (vdev->vfl_type == VFL_TYPE_GRABBER) { + if (vdev->vfl_type == VFL_TYPE_VIDEO) { type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } else if (vdev->vfl_type == VFL_TYPE_VBI) { type = V4L2_BUF_TYPE_VBI_CAPTURE; @@ -3905,7 +3905,7 @@ static int bttv_register_video(struct bttv *btv) if (no_overlay <= 0) btv->video_dev.device_caps |= V4L2_CAP_VIDEO_OVERLAY; - if (video_register_device(&btv->video_dev, VFL_TYPE_GRABBER, + if (video_register_device(&btv->video_dev, VFL_TYPE_VIDEO, video_nr[btv->c.nr]) < 0) goto err; pr_info("%d: registered device %s\n", diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c index c5207501d5e0..0ff37496c9ab 100644 --- a/drivers/media/pci/cobalt/cobalt-v4l2.c +++ b/drivers/media/pci/cobalt/cobalt-v4l2.c @@ -1272,7 +1272,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node) video_set_drvdata(vdev, s); ret = vb2_queue_init(q); if (!s->is_audio && ret == 0) - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); else if (!s->is_dummy) ret = cobalt_alsa_init(s); diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c index b79718519b9b..3178df3c4922 100644 --- a/drivers/media/pci/cx18/cx18-streams.c +++ b/drivers/media/pci/cx18/cx18-streams.c @@ -48,19 +48,19 @@ static struct { } cx18_stream_info[] = { { /* CX18_ENC_STREAM_TYPE_MPG */ "encoder MPEG", - VFL_TYPE_GRABBER, 0, + VFL_TYPE_VIDEO, 0, PCI_DMA_FROMDEVICE, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_AUDIO | V4L2_CAP_TUNER }, { /* CX18_ENC_STREAM_TYPE_TS */ "TS", - VFL_TYPE_GRABBER, -1, + VFL_TYPE_VIDEO, -1, PCI_DMA_FROMDEVICE, }, { /* CX18_ENC_STREAM_TYPE_YUV */ "encoder YUV", - VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, + VFL_TYPE_VIDEO, CX18_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER @@ -74,13 +74,13 @@ static struct { }, { /* CX18_ENC_STREAM_TYPE_PCM */ "encoder PCM audio", - VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, + VFL_TYPE_VIDEO, CX18_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, }, { /* CX18_ENC_STREAM_TYPE_IDX */ "encoder IDX", - VFL_TYPE_GRABBER, -1, + VFL_TYPE_VIDEO, -1, PCI_DMA_FROMDEVICE, }, { /* CX18_ENC_STREAM_TYPE_RAD */ @@ -434,7 +434,7 @@ static int cx18_reg_dev(struct cx18 *cx, int type) name = video_device_node_name(&s->video_dev); switch (vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n", name, s->name, cx->stream_buffers[type], cx->stream_buf_size[type] / 1024, diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c index 2327fe612610..434677bd4ad1 100644 --- a/drivers/media/pci/cx23885/cx23885-417.c +++ b/drivers/media/pci/cx23885/cx23885-417.c @@ -1545,7 +1545,7 @@ int cx23885_417_register(struct cx23885_dev *dev) if (dev->tuner_type != TUNER_ABSENT) dev->v4l_device->device_caps |= V4L2_CAP_TUNER; err = video_register_device(dev->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (err < 0) { pr_info("%s: can't register mpeg device\n", dev->name); return err; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 7fc408ee4934..000c108b94fd 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1304,7 +1304,7 @@ int cx23885_video_register(struct cx23885_dev *dev) V4L2_CAP_AUDIO | V4L2_CAP_VIDEO_CAPTURE; if (dev->tuner_type != TUNER_ABSENT) dev->video_dev->device_caps |= V4L2_CAP_TUNER; - err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, + err = video_register_device(dev->video_dev, VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index a10261da0db6..1b80c990cb94 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -757,7 +757,7 @@ int cx25821_video_register(struct cx25821_dev *dev) snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); video_set_drvdata(vdev, chan); - err = video_register_device(vdev, VFL_TYPE_GRABBER, + err = video_register_device(vdev, VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index d3da7f4297af..fa4ca002ed19 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1138,7 +1138,7 @@ static int blackbird_register_video(struct cx8802_dev *dev) V4L2_CAP_VIDEO_CAPTURE; if (dev->core->board.tuner_type != UNSET) dev->mpeg_dev.device_caps |= V4L2_CAP_TUNER; - err = video_register_device(&dev->mpeg_dev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&dev->mpeg_dev, VFL_TYPE_VIDEO, -1); if (err < 0) { pr_info("can't register mpeg device\n"); return err; diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index b8abcd550604..6aabc45aa93c 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1451,7 +1451,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, V4L2_CAP_VIDEO_CAPTURE; if (core->board.tuner_type != UNSET) dev->video_dev.device_caps |= V4L2_CAP_TUNER; - err = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, + err = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO, video_nr[core->nr]); if (err < 0) { pr_err("can't register video device\n"); diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 7480f0d3ad0f..82581aa5a2a3 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -550,7 +550,7 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) IRQF_SHARED, DT3155_NAME, pd); if (err) goto err_iounmap; - err = video_register_device(&pd->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&pd->vdev, VFL_TYPE_VIDEO, -1); if (err) goto err_free_irq; dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 1adfdc7ab0db..92f5eadf2c99 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1647,7 +1647,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) vdev->queue = &q->vbq; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; video_set_drvdata(vdev, cio2); - r = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (r) { dev_err(&cio2->pci_dev->dev, "failed to register video device (%d)\n", r); diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c index f7de9118f609..f04ee84bab5f 100644 --- a/drivers/media/pci/ivtv/ivtv-streams.c +++ b/drivers/media/pci/ivtv/ivtv-streams.c @@ -99,7 +99,7 @@ static struct { } ivtv_stream_info[] = { { /* IVTV_ENC_STREAM_TYPE_MPG */ "encoder MPG", - VFL_TYPE_GRABBER, 0, + VFL_TYPE_VIDEO, 0, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, @@ -107,7 +107,7 @@ static struct { }, { /* IVTV_ENC_STREAM_TYPE_YUV */ "encoder YUV", - VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_ENC_YUV_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, @@ -123,7 +123,7 @@ static struct { }, { /* IVTV_ENC_STREAM_TYPE_PCM */ "encoder PCM", - VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_ENC_PCM_OFFSET, PCI_DMA_FROMDEVICE, 0, V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_enc_fops @@ -137,7 +137,7 @@ static struct { }, { /* IVTV_DEC_STREAM_TYPE_MPG */ "decoder MPG", - VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_DEC_MPG_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops @@ -158,7 +158,7 @@ static struct { }, { /* IVTV_DEC_STREAM_TYPE_YUV */ "decoder YUV", - VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, + VFL_TYPE_VIDEO, IVTV_V4L2_DEC_YUV_OFFSET, PCI_DMA_TODEVICE, 0, V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, &ivtv_v4l2_dec_fops @@ -318,7 +318,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) name = video_device_node_name(&s->vdev); switch (vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: IVTV_INFO("Registered device %s for %s (%d kB)\n", name, s->name, itv->options.kilobytes[type]); break; diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 3a4c29bc0ba5..73e064e6f56d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1711,7 +1711,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) v4l2_ctrl_handler_setup(&meye.hdl); meye.vdev.ctrl_handler = &meye.hdl; - if (video_register_device(&meye.vdev, VFL_TYPE_GRABBER, + if (video_register_device(&meye.vdev, VFL_TYPE_VIDEO, video_nr) < 0) { v4l2_err(v4l2_dev, "video_register_device failed\n"); goto outvideoreg; diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 2d582c02adbf..e4623ed2f831 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -1214,7 +1214,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev, if (saa7134_no_overlay <= 0) dev->video_dev->device_caps |= V4L2_CAP_VIDEO_OVERLAY; - err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, + err = video_register_device(dev->video_dev,VFL_TYPE_VIDEO, video_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index cb65d345fd3e..8ad7879bd840 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -291,7 +291,7 @@ static int empress_init(struct saa7134_dev *dev) dev->empress_dev->device_caps |= V4L2_CAP_TUNER; video_set_drvdata(dev->empress_dev, dev); - err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, + err = video_register_device(dev->empress_dev,VFL_TYPE_VIDEO, empress_nr[dev->nr]); if (err < 0) { pr_info("%s: can't register video device\n", diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c index f96226930670..2214c74bbbf1 100644 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ b/drivers/media/pci/saa7146/hexium_gemini.c @@ -289,7 +289,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); + ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO); if (ret < 0) { pr_err("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c index bf5e55348f15..39d14c179d22 100644 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ b/drivers/media/pci/saa7146/hexium_orion.c @@ -362,7 +362,7 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { + if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) { pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; } diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index e6a71c17566d..129a1f8ebe1a 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -707,7 +707,7 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data vv_data.vid_ops.vidioc_g_register = vidioc_g_register; vv_data.vid_ops.vidioc_s_register = vidioc_s_register; #endif - if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); return -1; diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c index 3fca7257a720..11e1eb6a6809 100644 --- a/drivers/media/pci/saa7164/saa7164-encoder.c +++ b/drivers/media/pci/saa7164/saa7164-encoder.c @@ -1087,7 +1087,7 @@ int saa7164_encoder_register(struct saa7164_port *port) v4l2_ctrl_handler_setup(hdl); video_set_drvdata(port->v4l_device, port); result = video_register_device(port->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (result < 0) { printk(KERN_INFO "%s: can't register mpeg device\n", dev->name); diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 476d7f3b32d6..cbf85231b708 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -1304,7 +1304,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, solo_enc->vfd->queue = &solo_enc->vidq; solo_enc->vfd->lock = &solo_enc->lock; video_set_drvdata(solo_enc->vfd, solo_enc); - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, nr); + ret = video_register_device(solo_enc->vfd, VFL_TYPE_VIDEO, nr); if (ret < 0) goto vdev_release; diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c index 78792067e920..54434f3c428d 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c @@ -692,7 +692,7 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr) while (erase_off(solo_dev)) /* Do nothing */; - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, nr); + ret = video_register_device(solo_dev->vfd, VFL_TYPE_VIDEO, nr); if (ret < 0) goto fail; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index fd3de3bb0c89..798574cfad35 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1069,7 +1069,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->video_dev.lock = &vip->v4l_lock; video_set_drvdata(&vip->video_dev, vip); - ret = video_register_device(&vip->video_dev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&vip->video_dev, VFL_TYPE_VIDEO, -1); if (ret) goto vrelease; diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index f3d6c3cdb872..cabe006658dd 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -831,7 +831,7 @@ int av7110_init_v4l(struct av7110 *av7110) if (FW_VERSION(av7110->arm_app) < 0x2623) vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; - if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { + if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_VIDEO)) { ERR("cannot register capture device. skipping\n"); saa7146_vv_release(dev); return -ENODEV; diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index e2d482af2367..38cac508bd72 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -1470,7 +1470,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio vv_data.vid_ops.vidioc_g_input = vidioc_g_input; vv_data.vid_ops.vidioc_s_input = vidioc_s_input; - if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) { /* fixme: proper cleanup here */ ERR("cannot register capture v4l2 device\n"); saa7146_vv_release(dev); diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 09732eed7eb4..ec1e06da7e4f 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -1156,7 +1156,7 @@ static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) input->gop = GOP_SIZE; input->frame_interval = 1; - ret = video_register_device(&input->vdev, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(&input->vdev, VFL_TYPE_VIDEO, video_nr); if (ret) goto free_v4l2_hdl; diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c index 2fb82d50c53e..10986fcd66a5 100644 --- a/drivers/media/pci/tw68/tw68-video.c +++ b/drivers/media/pci/tw68/tw68-video.c @@ -962,7 +962,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr) dev->vdev.lock = &dev->lock; dev->vdev.queue = &dev->vidq; video_set_drvdata(&dev->vdev, dev); - return video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); + return video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr); } /* diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 9be8c6e4fb69..1ced2b0ddb24 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1282,7 +1282,7 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->device = vdev; video_set_drvdata(vdev, vc); - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err < 0) goto error; vc->num = vdev->num; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f65e98d3adf2..e01bbb9dd1c1 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -507,6 +507,18 @@ config VIDEO_SUN8I_DEINTERLACE capability found on some SoCs, like H3. To compile this driver as a module choose m here. +config VIDEO_SUN8I_ROTATE + tristate "Allwinner DE2 rotation driver" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_SUNXI || COMPILE_TEST + depends on COMMON_CLK && OF + depends on PM + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Allwinner DE2 rotation unit. + To compile this driver as a module choose m here. + endif # V4L_MEM2MEM_DRIVERS # TI VIDEO PORT Helper Modules @@ -610,49 +622,49 @@ config CEC_GPIO between compatible devices. config VIDEO_SAMSUNG_S5P_CEC - tristate "Samsung S5P CEC driver" - depends on ARCH_EXYNOS || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for Samsung S5P HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "Samsung S5P CEC driver" + depends on ARCH_EXYNOS || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for Samsung S5P HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_STI_HDMI_CEC - tristate "STMicroelectronics STiH4xx HDMI CEC driver" - depends on ARCH_STI || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for STIH4xx HDMI CEC interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "STMicroelectronics STiH4xx HDMI CEC driver" + depends on ARCH_STI || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for STIH4xx HDMI CEC interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_STM32_HDMI_CEC - tristate "STMicroelectronics STM32 HDMI CEC driver" - depends on ARCH_STM32 || COMPILE_TEST - select REGMAP - select REGMAP_MMIO - select CEC_CORE - help - This is a driver for STM32 interface. It uses the - generic CEC framework interface. - CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "STMicroelectronics STM32 HDMI CEC driver" + depends on ARCH_STM32 || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + select CEC_CORE + help + This is a driver for STM32 interface. It uses the + generic CEC framework interface. + CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_TEGRA_HDMI_CEC - tristate "Tegra HDMI CEC driver" - depends on ARCH_TEGRA || COMPILE_TEST - select CEC_CORE - select CEC_NOTIFIER - help - This is a driver for the Tegra HDMI CEC interface. It uses the - generic CEC framework interface. - The CEC bus is present in the HDMI connector and enables communication - between compatible devices. + tristate "Tegra HDMI CEC driver" + depends on ARCH_TEGRA || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + help + This is a driver for the Tegra HDMI CEC interface. It uses the + generic CEC framework interface. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. config VIDEO_SECO_CEC tristate "SECO Boards HDMI CEC driver" diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 09104304bd06..66079cc41f38 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -285,6 +285,7 @@ vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc, max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 || + ccdcparam->data_sz > VPFE_CCDC_DATA_8BITS || max_gamma > max_data) { vpfe_dbg(1, vpfe, "Invalid data line select\n"); return -EINVAL; @@ -324,7 +325,7 @@ static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc) static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_device *vpfe = to_vpfe(ccdc); u32 dma_cntl, pcr; pcr = vpfe_reg_read(ccdc, VPFE_PCR); @@ -348,7 +349,7 @@ static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_device *vpfe = to_vpfe(ccdc); struct vpfe_ccdc_config_params_raw raw_params; int x; @@ -504,7 +505,7 @@ vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc, */ static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_device *vpfe = to_vpfe(ccdc); struct vpfe_ccdc_config_params_raw *config_params = &ccdc->ccdc_cfg.bayer.config_params; struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer; @@ -609,7 +610,7 @@ static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc) static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_device *vpfe = to_vpfe(ccdc); vpfe_dbg(1, vpfe, "%s: if_type: %d, pixfmt:%s\n", __func__, ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); @@ -741,7 +742,7 @@ static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr) static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc, struct vpfe_hw_if_param *params) { - struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_device *vpfe = to_vpfe(ccdc); ccdc->ccdc_cfg.if_type = params->if_type; @@ -2267,7 +2268,7 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(vdev, vpfe); - err = video_register_device(&vpfe->video_dev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&vpfe->video_dev, VFL_TYPE_VIDEO, -1); if (err) { vpfe_err(vpfe, "Unable to register video device.\n"); diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index d8593cb2ae84..7d98db1d9b52 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1,4 +1,6 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2020 IBM Corp. +// Copyright (c) 2019-2020 Intel Corporation #include <linux/atomic.h> #include <linux/bitfield.h> @@ -72,11 +74,8 @@ #define VE_SEQ_CTRL_CAP_BUSY BIT(16) #define VE_SEQ_CTRL_COMP_BUSY BIT(18) -#ifdef CONFIG_MACH_ASPEED_G5 -#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ -#else -#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ -#endif /* CONFIG_MACH_ASPEED_G5 */ +#define AST2500_VE_SEQ_CTRL_JPEG_MODE BIT(13) +#define AST2400_VE_SEQ_CTRL_JPEG_MODE BIT(8) #define VE_CTRL 0x008 #define VE_CTRL_HSYNC_POL BIT(0) @@ -133,7 +132,8 @@ #define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22) #define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27) -#define VE_OFFSET_COMP_STREAM 0x078 +#define AST2400_VE_COMP_SIZE_READ_BACK 0x078 +#define AST2600_VE_COMP_SIZE_READ_BACK 0x084 #define VE_SRC_LR_EDGE_DET 0x090 #define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0) @@ -220,6 +220,9 @@ struct aspeed_video { struct video_device vdev; struct mutex video_lock; /* v4l2 and videobuf2 lock */ + u32 jpeg_mode; + u32 comp_size_read; + wait_queue_head_t wait; spinlock_t lock; /* buffer list lock */ struct delayed_work res_work; @@ -243,6 +246,26 @@ struct aspeed_video { #define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) +struct aspeed_video_config { + u32 jpeg_mode; + u32 comp_size_read; +}; + +static const struct aspeed_video_config ast2400_config = { + .jpeg_mode = AST2400_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK, +}; + +static const struct aspeed_video_config ast2500_config = { + .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = AST2400_VE_COMP_SIZE_READ_BACK, +}; + +static const struct aspeed_video_config ast2600_config = { + .jpeg_mode = AST2500_VE_SEQ_CTRL_JPEG_MODE, + .comp_size_read = AST2600_VE_COMP_SIZE_READ_BACK, +}; + static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = { 0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff, 0x00002d05, 0x00000000, 0x00000000, 0x00dbff00 @@ -572,7 +595,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) if (sts & VE_INTERRUPT_COMP_COMPLETE) { struct aspeed_video_buffer *buf; u32 frame_size = aspeed_video_read(video, - VE_OFFSET_COMP_STREAM); + video->comp_size_read); spin_lock(&video->lock); clear_bit(VIDEO_FRAME_INPRG, &video->flags); @@ -907,7 +930,7 @@ static void aspeed_video_init_regs(struct aspeed_video *video) FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; - u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE; + u32 seq_ctrl = video->jpeg_mode; if (video->frame_rate) ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); @@ -1565,14 +1588,14 @@ static int aspeed_video_setup_video(struct aspeed_video *video) V4L2_CAP_STREAMING; vdev->v4l2_dev = v4l2_dev; strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name)); - vdev->vfl_type = VFL_TYPE_GRABBER; + vdev->vfl_type = VFL_TYPE_VIDEO; vdev->vfl_dir = VFL_DIR_RX; vdev->release = video_device_release_empty; vdev->ioctl_ops = &aspeed_video_ioctl_ops; vdev->lock = &video->video_lock; video_set_drvdata(vdev, video); - rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0); + rc = video_register_device(vdev, VFL_TYPE_VIDEO, 0); if (rc) { vb2_queue_release(vbq); v4l2_ctrl_handler_free(&video->ctrl_handler); @@ -1653,16 +1676,37 @@ err_unprepare_eclk: return rc; } +static const struct of_device_id aspeed_video_of_match[] = { + { .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config }, + { .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config }, + { .compatible = "aspeed,ast2600-video-engine", .data = &ast2600_config }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_video_of_match); + static int aspeed_video_probe(struct platform_device *pdev) { + const struct aspeed_video_config *config; + const struct of_device_id *match; + struct aspeed_video *video; int rc; - struct resource *res; - struct aspeed_video *video = - devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); + video = devm_kzalloc(&pdev->dev, sizeof(*video), GFP_KERNEL); if (!video) return -ENOMEM; + video->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(video->base)) + return PTR_ERR(video->base); + + match = of_match_node(aspeed_video_of_match, pdev->dev.of_node); + if (!match) + return -EINVAL; + + config = match->data; + video->jpeg_mode = config->jpeg_mode; + video->comp_size_read = config->comp_size_read; + video->frame_rate = 30; video->dev = &pdev->dev; spin_lock_init(&video->lock); @@ -1671,13 +1715,6 @@ static int aspeed_video_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); INIT_LIST_HEAD(&video->buffers); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - video->base = devm_ioremap_resource(video->dev, res); - - if (IS_ERR(video->base)) - return PTR_ERR(video->base); - rc = aspeed_video_init(video); if (rc) return rc; @@ -1716,13 +1753,6 @@ static int aspeed_video_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id aspeed_video_of_match[] = { - { .compatible = "aspeed,ast2400-video-engine" }, - { .compatible = "aspeed,ast2500-video-engine" }, - {} -}; -MODULE_DEVICE_TABLE(of, aspeed_video_of_match); - static struct platform_driver aspeed_video_driver = { .driver = { .name = DEVICE_NAME, diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index d7669a03e98e..a6e9797a0ec9 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -22,6 +22,7 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/videodev2.h> +#include <linux/atmel-isc-media.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -224,10 +225,35 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) +#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) +#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) + +static inline void isc_update_v4l2_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + /* In here we set the v4l2 controls w.r.t. our pipeline config */ + v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); + v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); + v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); + + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); +} + static inline void isc_update_awb_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; + /* In here we set our actual hw pipeline config */ + regmap_write(isc->regmap, ISC_WB_O_RGR, (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); @@ -662,11 +688,9 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) bay_cfg = isc->config.sd_format->cfa_baycfg; - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - regmap_write(regmap, ISC_WB_CFG, bay_cfg); isc_update_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); @@ -1396,6 +1420,7 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) isc->try_config.sd_format != isc->config.sd_format) { isc->ctrls.hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); } /* make the try configuration active */ isc->config = isc->try_config; @@ -1814,10 +1839,6 @@ static void isc_awb_work(struct work_struct *w) ctrls->hist_id = hist_id; baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - /* if no more auto white balance, reset controls. */ - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - pm_runtime_get_sync(isc->dev); /* @@ -1842,6 +1863,8 @@ static void isc_awb_work(struct work_struct *w) if (ctrls->awb == ISC_WB_ONETIME) { v4l2_info(&isc->v4l2_dev, "Completed one time white-balance adjustment.\n"); + /* update the v4l2 controls values */ + isc_update_v4l2_ctrls(isc); ctrls->awb = ISC_WB_NONE; } } @@ -1873,6 +1896,27 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAMMA: ctrls->gamma_index = ctrl->val; break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops isc_ctrl_ops = { + .s_ctrl = isc_s_ctrl, +}; + +static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (ctrl->val == 1) ctrls->awb = ISC_WB_AUTO; @@ -1883,36 +1927,142 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) if (!isc->config.sd_format) break; - if (ctrls->hist_stat != HIST_ENABLED) - isc_reset_awb_ctrls(isc); + /* configure the controls with new values from v4l2 */ + if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; + + if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_R] = + ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_B] = + ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GR] = + ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GB] = + ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); - if (isc->ctrls.awb == ISC_WB_AUTO && + isc_update_awb_ctrls(isc); + + if (vb2_is_streaming(&isc->vb2_vidq)) { + /* + * If we are streaming, we can update profile to + * have the new settings in place. + */ + isc_update_profile(isc); + } else { + /* + * The auto cluster will activate automatically this + * control. This has to be deactivated when not + * streaming. + */ + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + } + + /* if we have autowhitebalance on, start histogram procedure */ + if (ctrls->awb == ISC_WB_AUTO && vb2_is_streaming(&isc->vb2_vidq) && ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) isc_set_histogram(isc, true); - break; - case V4L2_CID_DO_WHITE_BALANCE: - /* if AWB is enabled, do nothing */ - if (ctrls->awb == ISC_WB_AUTO) - return 0; + /* + * for one time whitebalance adjustment, check the button, + * if it's pressed, perform the one time operation. + */ + if (ctrls->awb == ISC_WB_NONE && + ctrl->cluster[ISC_CTRL_DO_WB]->is_new && + !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & + V4L2_CTRL_FLAG_INACTIVE)) { + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); + } + return 0; + } + return 0; +} - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); +static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + switch (ctrl->id) { + /* being a cluster, this id will be called for every control */ + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->cluster[ISC_CTRL_R_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_R]; + ctrl->cluster[ISC_CTRL_B_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_B]; + ctrl->cluster[ISC_CTRL_GR_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GR]; + ctrl->cluster[ISC_CTRL_GB_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GB]; + + ctrl->cluster[ISC_CTRL_R_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrl->cluster[ISC_CTRL_B_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrl->cluster[ISC_CTRL_GR_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrl->cluster[ISC_CTRL_GB_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); break; - default: - return -EINVAL; } - return 0; } -static const struct v4l2_ctrl_ops isc_ctrl_ops = { - .s_ctrl = isc_s_ctrl, +static const struct v4l2_ctrl_ops isc_awb_ops = { + .s_ctrl = isc_s_awb_ctrl, + .g_volatile_ctrl = isc_g_volatile_awb_ctrl, }; +#define ISC_CTRL_OFF(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = -4095, \ + .max = 4095, \ + .step = 1, \ + .def = 0, \ + } + +ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); +ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); +ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); +ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); + +#define ISC_CTRL_GAIN(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = 0, \ + .max = 8191, \ + .step = 1, \ + .def = 512, \ + } + +ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); +ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); +ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); +ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); + static int isc_ctrl_init(struct isc_device *isc) { const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; @@ -1923,7 +2073,7 @@ static int isc_ctrl_init(struct isc_device *isc) ctrls->hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); - ret = v4l2_ctrl_handler_init(hdl, 5); + ret = v4l2_ctrl_handler_init(hdl, 13); if (ret < 0) return ret; @@ -1933,10 +2083,13 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE, + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_DO_WHITE_BALANCE, 0, 0, 0, 0); if (!isc->do_wb_ctrl) { @@ -1947,6 +2100,21 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_activate(isc->do_wb_ctrl, false); + isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); + isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); + isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); + isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); + isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); + isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); + isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); + isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); + + /* + * The cluster is in auto mode with autowhitebalance enabled + * and manual mode otherwise. + */ + v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); + v4l2_ctrl_handler_setup(hdl); return 0; @@ -2143,7 +2311,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; video_set_drvdata(vdev, isc); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(&isc->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index bfaed2fad2b5..fc56a745c7d1 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -213,7 +213,6 @@ struct isc_device { struct fmt_config try_config; struct isc_ctrls ctrls; - struct v4l2_ctrl *do_wb_ctrl; struct work_struct awb_work; struct mutex lock; /* serialize access to file operations */ @@ -223,6 +222,28 @@ struct isc_device { struct isc_subdev_entity *current_subdev; struct list_head subdev_entities; + + struct { +#define ISC_CTRL_DO_WB 1 +#define ISC_CTRL_R_GAIN 2 +#define ISC_CTRL_B_GAIN 3 +#define ISC_CTRL_GR_GAIN 4 +#define ISC_CTRL_GB_GAIN 5 +#define ISC_CTRL_R_OFF 6 +#define ISC_CTRL_B_OFF 7 +#define ISC_CTRL_GR_OFF 8 +#define ISC_CTRL_GB_OFF 9 + struct v4l2_ctrl *awb_ctrl; + struct v4l2_ctrl *do_wb_ctrl; + struct v4l2_ctrl *r_gain_ctrl; + struct v4l2_ctrl *b_gain_ctrl; + struct v4l2_ctrl *gr_gain_ctrl; + struct v4l2_ctrl *gb_gain_ctrl; + struct v4l2_ctrl *r_off_ctrl; + struct v4l2_ctrl *b_off_ctrl; + struct v4l2_ctrl *gr_off_ctrl; + struct v4l2_ctrl *gb_off_ctrl; + }; }; #define GAMMA_MAX 2 diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 963dfd6e750e..d74aa73f26be 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1094,7 +1094,7 @@ static int isi_graph_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - ret = video_register_device(isi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(isi->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(isi->dev, "Failed to register video device\n"); return ret; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index acff10ad257a..d0d093dd8f7c 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -2726,7 +2726,7 @@ static int coda_register_device(struct coda_dev *dev, int i) v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (!ret) v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", type == CODA_INST_ENCODER ? "encoder" : "decoder", diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index b49378b18e5d..c98edb67cfb2 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -29,7 +29,7 @@ #include "ccdc_hw_device.h" /* Defaults for module configuration parameters */ -static struct isif_config_params_raw isif_config_defaults = { +static const struct isif_config_params_raw isif_config_defaults = { .linearize = { .en = 0, .corr_shft = ISIF_NO_SHIFT, diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index ae419958e420..38d3088d4d38 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1339,7 +1339,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue; err = video_register_device(&vpbe_display_layer->video_dev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, -1); if (err) return -ENODEV; diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 9b1d9643589b..f9f7dd17c57c 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -880,7 +880,7 @@ static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, /* Fill in the information about format */ pix_fmt = vpfe_lookup_pix_format(pix); if (pix_fmt) { - fmt->pixelformat = fmt->pixelformat; + fmt->pixelformat = pix_fmt->pixelformat; return 0; } return -EINVAL; @@ -1780,7 +1780,7 @@ static int vpfe_probe(struct platform_device *pdev) "video_dev=%p\n", &vpfe_dev->video_dev); vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = video_register_device(&vpfe_dev->video_dev, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(pdev->dev.driver, diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 71f4fe882d13..d9ec439faefa 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1466,7 +1466,7 @@ static int vpif_probe_complete(void) vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; video_set_drvdata(&ch->video_dev, ch); err = video_register_device(vdev, - VFL_TYPE_GRABBER, (j ? 1 : 0)); + VFL_TYPE_VIDEO, (j ? 1 : 0)); if (err) goto probe_out; } diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index abbdbac08e6f..ead14c49d4f5 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1214,7 +1214,7 @@ static int vpif_probe_complete(void) vdev->lock = &common->lock; vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; video_set_drvdata(&ch->video_dev, ch); - err = video_register_device(vdev, VFL_TYPE_GRABBER, + err = video_register_device(vdev, VFL_TYPE_VIDEO, (j ? 3 : 2)); if (err < 0) goto probe_out; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 35a1d0d6dd66..e2c162635f72 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -771,7 +771,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) return PTR_ERR(gsc->m2m.m2m_dev); } - ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&gsc->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&pdev->dev, "%s(): failed to register video device\n", __func__); diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 989cb34f19b1..be4effcbfe7b 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -13,7 +13,7 @@ config VIDEO_SAMSUNG_EXYNOS4_IS if VIDEO_SAMSUNG_EXYNOS4_IS config VIDEO_EXYNOS4_IS_COMMON - tristate + tristate config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 121d609ff856..705f182330ca 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -1808,7 +1808,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, if (ret) goto err_me_cleanup; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_ctrl_free; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index d2cbcdca0463..15f443fa7208 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -619,7 +619,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, video_set_drvdata(vdev, isp); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { media_entity_cleanup(&vdev->entity); return ret; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e87c6a09205b..394e0818f2d5 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1297,7 +1297,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) video_set_drvdata(vfd, fimc); fimc->ve.pipe = v4l2_get_subdev_hostdata(sd); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret < 0) { media_entity_cleanup(&vfd->entity); fimc->ve.pipe = NULL; @@ -1614,6 +1614,9 @@ static int fimc_lite_remove(struct platform_device *pdev) struct fimc_lite *fimc = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + if (!pm_runtime_enabled(dev)) + clk_disable_unprepare(fimc->clock); + pm_runtime_disable(dev); pm_runtime_set_suspended(dev); fimc_lite_unregister_capture_subdev(fimc); diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index c70c2cbe3eb1..4acb179556c4 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -746,7 +746,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, if (ret) goto err_me; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_vd; diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 81a8faedbba6..84633a3b8475 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1486,7 +1486,7 @@ static int viu_of_probe(struct platform_device *op) mutex_lock(&viu_dev->lock); - ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { video_device_release(viu_dev->vdev); goto err_unlock; diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/imx-pxp.c index 38d942322302..08d76eb05ed1 100644 --- a/drivers/media/platform/imx-pxp.c +++ b/drivers/media/platform/imx-pxp.c @@ -1709,7 +1709,7 @@ static int pxp_probe(struct platform_device *pdev) goto err_v4l2; } - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_m2m; diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 9ad24c86c5ab..1f89e71cdccf 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -953,7 +953,7 @@ static int deinterlace_probe(struct platform_device *pdev) vfd->lock = &pcdev->dev_mutex; vfd->v4l2_dev = &pcdev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); goto unreg_dev; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 803baf97f06e..09775b6624c6 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1802,7 +1802,7 @@ static int mccic_notify_bound(struct v4l2_async_notifier *notifier, cam->vdev.lock = &cam->s_mutex; cam->vdev.queue = &cam->vb_queue; video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (ret) { cam->sensor = NULL; goto out; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index ee802fc3bcdf..f82a81a3bdee 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -1150,7 +1150,7 @@ static int mtk_jpeg_probe(struct platform_device *pdev) jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_GRABBER, 3); + ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_VIDEO, 3); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); goto err_dec_vdev_register; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c index 9afe8161a8c0..14991685adb7 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c @@ -110,6 +110,12 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node, for (i = 0; i < ARRAY_SIZE(comp->clk); i++) { comp->clk[i] = of_clk_get(node, i); + if (IS_ERR(comp->clk[i])) { + if (PTR_ERR(comp->clk[i]) != -EPROBE_DEFER) + dev_err(dev, "Failed to get clock\n"); + + return PTR_ERR(comp->clk[i]); + } /* Only RDMA needs two clocks */ if (comp->type != MTK_MDP_RDMA) diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 7c9e2d69e21a..821f2cf325f0 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -1229,7 +1229,7 @@ int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp) goto err_m2m_init; } - ret = video_register_device(mdp->vdev, VFL_TYPE_GRABBER, 2); + ret = video_register_device(mdp->vdev, VFL_TYPE_VIDEO, 2); if (ret) { dev_err(dev, "failed to register video device\n"); goto err_vdev_register; diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c b/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c index 6720d11f50cf..b065ccd06914 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c @@ -15,7 +15,7 @@ static inline struct mtk_mdp_ctx *vpu_to_ctx(struct mtk_mdp_vpu *vpu) return container_of(vpu, struct mtk_mdp_ctx, vpu); } -static void mtk_mdp_vpu_handle_init_ack(struct mdp_ipi_comm_ack *msg) +static void mtk_mdp_vpu_handle_init_ack(const struct mdp_ipi_comm_ack *msg) { struct mtk_mdp_vpu *vpu = (struct mtk_mdp_vpu *) (unsigned long)msg->ap_inst; @@ -26,10 +26,11 @@ static void mtk_mdp_vpu_handle_init_ack(struct mdp_ipi_comm_ack *msg) vpu->inst_addr = msg->vpu_inst_addr; } -static void mtk_mdp_vpu_ipi_handler(void *data, unsigned int len, void *priv) +static void mtk_mdp_vpu_ipi_handler(const void *data, unsigned int len, + void *priv) { - unsigned int msg_id = *(unsigned int *)data; - struct mdp_ipi_comm_ack *msg = (struct mdp_ipi_comm_ack *)data; + const struct mdp_ipi_comm_ack *msg = data; + unsigned int msg_id = msg->msg_id; struct mtk_mdp_vpu *vpu = (struct mtk_mdp_vpu *) (unsigned long)msg->ap_inst; struct mtk_mdp_ctx *ctx; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 100ae8c5e702..97a1b6664c20 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -331,7 +331,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } - ret = video_register_device(vfd_dec, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0); if (ret) { mtk_v4l2_err("Failed to register video device"); goto err_dec_reg; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index 1d82aa2b6017..4d31f1ed113f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -356,7 +356,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_event_workq; } - ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1); + ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1); if (ret) { mtk_v4l2_err("Failed to register video device"); goto err_enc_reg; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index 24c1f0bf2147..257a5b5ad212 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -110,7 +110,11 @@ struct vp9_sf_ref_fb { * @buf_len_sz_c : size used to store cbcr plane ufo info (AP-R, VPU-W) * @profile : profile sparsed from vpu (AP-R, VPU-W) - * @show_frame : display this frame or not (AP-R, VPU-W) + * @show_frame : [BIT(0)] display this frame or not (AP-R, VPU-W) + * [BIT(1)] reset segment data or not (AP-R, VPU-W) + * [BIT(2)] trig decoder hardware or not (AP-R, VPU-W) + * [BIT(3)] ask VPU to set bits(0~4) accordingly (AP-W, VPU-R) + * [BIT(4)] do not reset segment data before every frame (AP-R, VPU-W) * @show_existing_frame : inform this frame is show existing frame * (AP-R, VPU-W) * @frm_to_show_idx : index to show frame (AP-R, VPU-W) @@ -494,12 +498,12 @@ static void vp9_swap_frm_bufs(struct vdec_vp9_inst *inst) frm_to_show->fb->base_y.size); } if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { - if (vsi->show_frame) + if (vsi->show_frame & BIT(0)) vp9_add_to_fb_disp_list(inst, inst->cur_fb); } } else { if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { - if (vsi->show_frame) + if (vsi->show_frame & BIT(0)) vp9_add_to_fb_disp_list(inst, frm_to_show->fb); } } @@ -800,6 +804,9 @@ static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) } inst->vsi = (struct vdec_vp9_vsi *)inst->vpu.vsi; + + inst->vsi->show_frame |= BIT(3); + init_all_fb_lists(inst); ctx->drv_handle = inst; @@ -870,13 +877,27 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, vsi->sf_frm_sz[idx]); } } - memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); + + if (!(vsi->show_frame & BIT(4))) + memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); + ret = vpu_dec_start(&inst->vpu, data, 3); if (ret) { mtk_vcodec_err(inst, "vpu_dec_start failed"); goto DECODE_ERROR; } + if (vsi->show_frame & BIT(1)) { + memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); + + if (vsi->show_frame & BIT(2)) { + if (vpu_dec_start(&inst->vpu, NULL, 0)) { + mtk_vcodec_err(inst, "vpu trig decoder failed"); + goto DECODE_ERROR; + } + } + } + ret = validate_vsi_array_indexes(inst, vsi); if (ret) { mtk_vcodec_err(inst, "Invalid values from VPU."); diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c index 70abfd4cd4b9..948a12fd9d46 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c @@ -9,7 +9,7 @@ #include "vdec_ipi_msg.h" #include "vdec_vpu_if.h" -static void handle_init_ack_msg(struct vdec_vpu_ipi_init_ack *msg) +static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) { struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) (unsigned long)msg->ap_inst_addr; @@ -34,9 +34,9 @@ static void handle_init_ack_msg(struct vdec_vpu_ipi_init_ack *msg) * This function runs in interrupt context and it means there's an IPI MSG * from VPU. */ -static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) +static void vpu_dec_ipi_handler(const void *data, unsigned int len, void *priv) { - struct vdec_vpu_ipi_ack *msg = data; + const struct vdec_vpu_ipi_ack *msg = data; struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) (unsigned long)msg->ap_inst_addr; diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c index 3e931b0ed096..9540709c1905 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -8,26 +8,26 @@ #include "venc_ipi_msg.h" #include "venc_vpu_if.h" -static void handle_enc_init_msg(struct venc_vpu_inst *vpu, void *data) +static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data) { - struct venc_vpu_ipi_msg_init *msg = data; + const struct venc_vpu_ipi_msg_init *msg = data; vpu->inst_addr = msg->vpu_inst_addr; vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr); } -static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, void *data) +static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) { - struct venc_vpu_ipi_msg_enc *msg = data; + const struct venc_vpu_ipi_msg_enc *msg = data; vpu->state = msg->state; vpu->bs_size = msg->bs_size; vpu->is_key_frm = msg->is_key_frm; } -static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) +static void vpu_enc_ipi_handler(const void *data, unsigned int len, void *priv) { - struct venc_vpu_ipi_msg_common *msg = data; + const struct venc_vpu_ipi_msg_common *msg = data; struct venc_vpu_inst *vpu = (struct venc_vpu_inst *)(unsigned long)msg->venc_inst; diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index a768707abb94..d30c08983f56 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -46,6 +46,8 @@ /* binary firmware name */ #define VPU_P_FW "vpu_p.bin" #define VPU_D_FW "vpu_d.bin" +#define VPU_P_FW_NEW "mediatek/mt8173/vpu_p.bin" +#define VPU_D_FW_NEW "mediatek/mt8173/vpu_d.bin" #define VPU_RESET 0x0 #define VPU_TCM_CFG 0x0008 @@ -203,8 +205,8 @@ struct mtk_vpu { struct vpu_run run; struct vpu_wdt wdt; struct vpu_ipi_desc ipi_desc[IPI_MAX]; - struct share_obj *recv_buf; - struct share_obj *send_buf; + struct share_obj __iomem *recv_buf; + struct share_obj __iomem *send_buf; struct device *dev; struct clk *clk; bool fw_loaded; @@ -292,7 +294,7 @@ int vpu_ipi_send(struct platform_device *pdev, unsigned int len) { struct mtk_vpu *vpu = platform_get_drvdata(pdev); - struct share_obj *send_obj = vpu->send_buf; + struct share_obj __iomem *send_obj = vpu->send_buf; unsigned long timeout; int ret = 0; @@ -325,9 +327,9 @@ int vpu_ipi_send(struct platform_device *pdev, } } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); - memcpy((void *)send_obj->share_buf, buf, len); - send_obj->len = len; - send_obj->id = id; + memcpy_toio(send_obj->share_buf, buf, len); + writel(len, &send_obj->len); + writel(id, &send_obj->id); vpu->ipi_id_ack[id] = false; /* send the command to VPU */ @@ -477,16 +479,24 @@ static int load_requested_vpu(struct mtk_vpu *vpu, size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE; size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE; char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW; + char *fw_new_name = fw_type ? VPU_D_FW_NEW : VPU_P_FW_NEW; const struct firmware *vpu_fw; size_t dl_size = 0; size_t extra_fw_size = 0; void *dest; int ret; - ret = request_firmware(&vpu_fw, fw_name, vpu->dev); + ret = request_firmware(&vpu_fw, fw_new_name, vpu->dev); if (ret < 0) { - dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret); - return ret; + dev_info(vpu->dev, "Failed to load %s, %d, retry\n", + fw_new_name, ret); + + ret = request_firmware(&vpu_fw, fw_name, vpu->dev); + if (ret < 0) { + dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, + ret); + return ret; + } } dl_size = vpu_fw->size; if (dl_size > fw_size) { @@ -600,10 +610,10 @@ OUT_LOAD_FW: } EXPORT_SYMBOL_GPL(vpu_load_firmware); -static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv) +static void vpu_init_ipi_handler(const void *data, unsigned int len, void *priv) { - struct mtk_vpu *vpu = (struct mtk_vpu *)priv; - struct vpu_run *run = (struct vpu_run *)data; + struct mtk_vpu *vpu = priv; + const struct vpu_run *run = data; vpu->run.signaled = run->signaled; strscpy(vpu->run.fw_ver, run->fw_ver, sizeof(vpu->run.fw_ver)); @@ -700,19 +710,21 @@ static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type) static void vpu_ipi_handler(struct mtk_vpu *vpu) { - struct share_obj *rcv_obj = vpu->recv_buf; + struct share_obj __iomem *rcv_obj = vpu->recv_buf; struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc; - - if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) { - ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf, - rcv_obj->len, - ipi_desc[rcv_obj->id].priv); - if (rcv_obj->id > IPI_VPU_INIT) { - vpu->ipi_id_ack[rcv_obj->id] = true; + unsigned char data[SHARE_BUF_SIZE]; + s32 id = readl(&rcv_obj->id); + + memcpy_fromio(data, rcv_obj->share_buf, sizeof(data)); + if (id < IPI_MAX && ipi_desc[id].handler) { + ipi_desc[id].handler(data, readl(&rcv_obj->len), + ipi_desc[id].priv); + if (id > IPI_VPU_INIT) { + vpu->ipi_id_ack[id] = true; wake_up(&vpu->ack_wq); } } else { - dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id); + dev_err(vpu->dev, "No such ipi id = %d\n", id); } } @@ -722,11 +734,10 @@ static int vpu_ipi_init(struct mtk_vpu *vpu) vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); /* shared buffer initialization */ - vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm + - VPU_DTCM_OFFSET); + vpu->recv_buf = vpu->reg.tcm + VPU_DTCM_OFFSET; vpu->send_buf = vpu->recv_buf + 1; - memset(vpu->recv_buf, 0, sizeof(struct share_obj)); - memset(vpu->send_buf, 0, sizeof(struct share_obj)); + memset_io(vpu->recv_buf, 0, sizeof(struct share_obj)); + memset_io(vpu->send_buf, 0, sizeof(struct share_obj)); return 0; } diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h index d4453b4bcee9..ee7c552ce928 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.h +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.h @@ -15,7 +15,7 @@ * VPU interfaces with other blocks by share memory and interrupt. **/ -typedef void (*ipi_handler_t) (void *data, +typedef void (*ipi_handler_t) (const void *data, unsigned int len, void *priv); diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 27779b75df54..df78df59da45 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -866,7 +866,7 @@ static int emmaprp_probe(struct platform_device *pdev) goto rel_vdev; } - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); goto rel_m2m; diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 513b99bf963b..21193f0b7f61 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1500,7 +1500,7 @@ static int __init omap_vout_create_video_devices(struct platform_device *pdev) /* Register the Video device with V4L2 */ vfd = vout->vfd; - if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) { + if (video_register_device(vfd, VFL_TYPE_VIDEO, -1) < 0) { dev_err(&pdev->dev, ": Could not register Video for Linux device\n"); vfd->minor = -1; diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 471ae7cdb813..0fbb2aa6dd2c 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1312,6 +1312,10 @@ static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) { struct isp_device *isp = to_isp_device(ccdc); + /* Avoid restarting the CCDC when streaming is stopping. */ + if (enable && ccdc->stopping & CCDC_STOP_REQUEST) + return; + isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index ee183c35ff3b..6f769c527fae 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1311,7 +1311,7 @@ static int isp_video_open(struct file *file) goto done; } - ret = v4l2_pipeline_pm_use(&video->video.entity, 1); + ret = v4l2_pipeline_pm_get(&video->video.entity); if (ret < 0) { omap3isp_put(video->isp); goto done; @@ -1363,7 +1363,7 @@ static int isp_video_release(struct file *file) vb2_queue_release(&handle->queue); mutex_unlock(&video->queue_lock); - v4l2_pipeline_pm_use(&video->video.entity, 0); + v4l2_pipeline_pm_put(&video->video.entity); /* Release the file handle. */ v4l2_fh_del(vfh); @@ -1453,7 +1453,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name) video->video.fops = &isp_video_fops; snprintf(video->video.name, sizeof(video->video.name), "OMAP3 ISP %s %s", name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + 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) @@ -1484,7 +1484,7 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) video->video.v4l2_dev = vdev; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) dev_err(video->isp->dev, "%s: could not register video device (%d)\n", diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 43ae645d866b..70c85a2a10f5 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2191,7 +2191,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, if (err) goto out_sensor_poweroff; - err = video_register_device(&pcdev->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&pcdev->vdev, VFL_TYPE_VIDEO, -1); if (err) { v4l2_err(v4l2_dev, "register video device failed: %d\n", err); pcdev->sensor = NULL; @@ -2440,23 +2440,23 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->base = base; /* request dma */ - pcdev->dma_chans[0] = dma_request_slave_channel(&pdev->dev, "CI_Y"); - if (!pcdev->dma_chans[0]) { + pcdev->dma_chans[0] = dma_request_chan(&pdev->dev, "CI_Y"); + if (IS_ERR(pcdev->dma_chans[0])) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - return -ENODEV; + return PTR_ERR(pcdev->dma_chans[0]); } - pcdev->dma_chans[1] = dma_request_slave_channel(&pdev->dev, "CI_U"); - if (!pcdev->dma_chans[1]) { - dev_err(&pdev->dev, "Can't request DMA for Y\n"); - err = -ENODEV; + pcdev->dma_chans[1] = dma_request_chan(&pdev->dev, "CI_U"); + if (IS_ERR(pcdev->dma_chans[1])) { + dev_err(&pdev->dev, "Can't request DMA for U\n"); + err = PTR_ERR(pcdev->dma_chans[1]); goto exit_free_dma_y; } - pcdev->dma_chans[2] = dma_request_slave_channel(&pdev->dev, "CI_V"); - if (!pcdev->dma_chans[2]) { + pcdev->dma_chans[2] = dma_request_chan(&pdev->dev, "CI_V"); + if (IS_ERR(pcdev->dma_chans[2])) { dev_err(&pdev->dev, "Can't request DMA for V\n"); - err = -ENODEV; + err = PTR_ERR(pcdev->dma_chans[2]); goto exit_free_dma_u; } diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 1d50dfbbb762..cdbd6dba1122 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -745,7 +745,7 @@ static int video_open(struct file *file) file->private_data = vfh; - ret = v4l2_pipeline_pm_use(&vdev->entity, 1); + ret = v4l2_pipeline_pm_get(&vdev->entity); if (ret < 0) { dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", ret); @@ -771,7 +771,7 @@ static int video_release(struct file *file) vb2_fop_release(file); - v4l2_pipeline_pm_use(&vdev->entity, 0); + v4l2_pipeline_pm_put(&vdev->entity); file->private_data = NULL; @@ -921,7 +921,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, vdev->lock = &video->lock; strscpy(vdev->name, name, sizeof(vdev->name)); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to register video device: %d\n", ret); diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index b44b11b03e12..64af0bc1edae 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o + hfi_parser.o pm_helpers.o venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 07312a2fab24..194b10b98767 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -3,7 +3,6 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#include <linux/clk.h> #include <linux/init.h> #include <linux/interconnect.h> #include <linux/ioctl.h> @@ -19,9 +18,8 @@ #include <media/v4l2-ioctl.h> #include "core.h" -#include "vdec.h" -#include "venc.h" #include "firmware.h" +#include "pm_helpers.h" static void venus_event_notify(struct venus_core *core, u32 event) { @@ -100,50 +98,6 @@ static void venus_sys_error_handler(struct work_struct *work) mutex_unlock(&core->lock); } -static int venus_clks_get(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - struct device *dev = core->dev; - unsigned int i; - - for (i = 0; i < res->clks_num; i++) { - core->clks[i] = devm_clk_get(dev, res->clks[i]); - if (IS_ERR(core->clks[i])) - return PTR_ERR(core->clks[i]); - } - - return 0; -} - -static int venus_clks_enable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i; - int ret; - - for (i = 0; i < res->clks_num; i++) { - ret = clk_prepare_enable(core->clks[i]); - if (ret) - goto err; - } - - return 0; -err: - while (i--) - clk_disable_unprepare(core->clks[i]); - - return ret; -} - -static void venus_clks_disable(struct venus_core *core) -{ - const struct venus_resources *res = core->res; - unsigned int i = res->clks_num; - - while (i--) - clk_disable_unprepare(core->clks[i]); -} - static u32 to_v4l2_codec_type(u32 codec) { switch (codec) { @@ -256,9 +210,15 @@ static int venus_probe(struct platform_device *pdev) if (!core->res) return -ENODEV; - ret = venus_clks_get(core); - if (ret) - return ret; + core->pm_ops = venus_pm_get(core->res->hfi_version); + if (!core->pm_ops) + return -ENODEV; + + if (core->pm_ops->core_get) { + ret = core->pm_ops->core_get(dev); + if (ret) + return ret; + } ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); if (ret) @@ -350,6 +310,7 @@ err_runtime_disable: static int venus_remove(struct platform_device *pdev) { struct venus_core *core = platform_get_drvdata(pdev); + const struct venus_pm_ops *pm_ops = core->pm_ops; struct device *dev = core->dev; int ret; @@ -368,6 +329,9 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + if (pm_ops->core_put) + pm_ops->core_put(dev); + icc_put(core->video_path); icc_put(core->cpucfg_path); @@ -379,11 +343,15 @@ static int venus_remove(struct platform_device *pdev) static __maybe_unused int venus_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; ret = hfi_core_suspend(core); + if (ret) + return ret; - venus_clks_disable(core); + if (pm_ops->core_power) + ret = pm_ops->core_power(dev, POWER_OFF); return ret; } @@ -391,21 +359,16 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) static __maybe_unused int venus_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + const struct venus_pm_ops *pm_ops = core->pm_ops; int ret; - ret = venus_clks_enable(core); - if (ret) - return ret; - - ret = hfi_core_resume(core, false); - if (ret) - goto err_clks_disable; - - return 0; + if (pm_ops->core_power) { + ret = pm_ops->core_power(dev, POWER_ON); + if (ret) + return ret; + } -err_clks_disable: - venus_clks_disable(core); - return ret; + return hfi_core_resume(core, false); } static const struct dev_pm_ops venus_pm_ops = { @@ -463,6 +426,9 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, .max_load = 2563200, .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, @@ -517,6 +483,35 @@ static const struct venus_resources sdm845_res = { .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), .clks = {"core", "iface", "bus" }, .clks_num = 3, + .vcodec0_clks = { "core", "bus" }, + .vcodec1_clks = { "core", "bus" }, + .vcodec_clks_num = 2, + .max_load = 3110400, /* 4096x2160@90 */ + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.2/venus.mdt", +}; + +static const struct venus_resources sdm845_res_v2 = { + .freq_tbl = sdm845_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .bw_tbl_enc = sdm845_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sdm845_bw_table_enc), + .bw_tbl_dec = sdm845_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sdm845_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec1_clks = { "vcodec1_core", "vcodec1_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" }, + .vcodec_pmdomains_num = 3, + .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, @@ -526,10 +521,56 @@ static const struct venus_resources sdm845_res = { .fwname = "qcom/venus-5.2/venus.mdt", }; +static const struct freq_tbl sc7180_freq_table[] = { + { 0, 500000000 }, + { 0, 434000000 }, + { 0, 340000000 }, + { 0, 270000000 }, + { 0, 150000000 }, +}; + +static const struct bw_tbl sc7180_bw_table_enc[] = { + { 972000, 750000, 0, 0, 0 }, /* 3840x2160@30 */ + { 489600, 451000, 0, 0, 0 }, /* 1920x1080@60 */ + { 244800, 234000, 0, 0, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sc7180_bw_table_dec[] = { + { 1036800, 1386000, 0, 1875000, 0 }, /* 4096x2160@30 */ + { 489600, 865000, 0, 1146000, 0 }, /* 1920x1080@60 */ + { 244800, 530000, 0, 583000, 0 }, /* 1920x1080@30 */ +}; + +static const struct venus_resources sc7180_res = { + .freq_tbl = sc7180_freq_table, + .freq_tbl_size = ARRAY_SIZE(sc7180_freq_table), + .bw_tbl_enc = sc7180_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sc7180_bw_table_enc), + .bw_tbl_dec = sc7180_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sc7180_bw_table_dec), + .codec_freq_data = sdm845_codec_freq_data, + .codec_freq_data_size = ARRAY_SIZE(sdm845_codec_freq_data), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .vcodec0_clks = { "vcodec0_core", "vcodec0_bus" }, + .vcodec_clks_num = 2, + .vcodec_pmdomains = { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .vcodec_num = 1, + .hfi_version = HFI_VERSION_4XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/venus-5.4/venus.mdt", +}; + static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, + { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, + { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, { } }; MODULE_DEVICE_TABLE(of, venus_dt_match); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 11585fb3cae3..bd3ac6a4501f 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -14,7 +14,9 @@ #include "hfi.h" -#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_CLKS_NUM_MAX 4 +#define VIDC_VCODEC_CLKS_NUM_MAX 2 +#define VIDC_PMDOMAINS_NUM_MAX 3 struct freq_tbl { unsigned int load; @@ -55,6 +57,12 @@ struct venus_resources { unsigned int codec_freq_data_size; const char * const clks[VIDC_CLKS_NUM_MAX]; unsigned int clks_num; + const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + unsigned int vcodec_clks_num; + const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX]; + unsigned int vcodec_pmdomains_num; + unsigned int vcodec_num; enum hfi_version hfi_version; u32 max_load; unsigned int vmem_id; @@ -100,10 +108,10 @@ struct venus_caps { * @base: IO memory base address * @irq: Venus irq * @clks: an array of struct clk pointers - * @core0_clk: a struct clk pointer for core0 - * @core1_clk: a struct clk pointer for core1 - * @core0_bus_clk: a struct clk pointer for core0 bus clock - * @core1_bus_clk: a struct clk pointer for core1 bus clock + * @vcodec0_clks: an array of vcodec0 struct clk pointers + * @vcodec1_clks: an array of vcodec1 struct clk pointers + * @pd_dl_venus: pmdomain device-link for venus domain + * @pmdomains: an array of pmdomains struct device pointers * @vdev_dec: a reference to video device structure for decoder instances * @vdev_enc: a reference to video device structure for encoder instances * @v4l2_dev: a holder for v4l2 device structure @@ -132,12 +140,12 @@ struct venus_core { void __iomem *base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; - struct clk *core0_clk; - struct clk *core1_clk; - struct clk *core0_bus_clk; - struct clk *core1_bus_clk; + struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; + struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; + struct device_link *pd_dl_venus; + struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -159,6 +167,7 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; + const struct venus_pm_ops *pm_ops; unsigned long enc_codecs; unsigned long dec_codecs; unsigned int max_sessions_supported; @@ -172,6 +181,8 @@ struct venus_core { struct delayed_work work; struct venus_caps caps[MAX_CODEC_NUM]; unsigned int codecs_count; + unsigned int core0_usage_count; + unsigned int core1_usage_count; }; struct vdec_controls { @@ -187,6 +198,7 @@ struct venc_controls { u32 bitrate_mode; u32 bitrate; u32 bitrate_peak; + u32 rc_enable; u32 h264_i_period; u32 h264_entropy_mode; @@ -344,6 +356,7 @@ struct venus_inst { unsigned int subscriptions; int buf_count; struct venus_ts_metadata tss[VIDEO_MAX_FRAME]; + unsigned long payloads[VIDEO_MAX_FRAME]; u64 fps; struct v4l2_fract timeperframe; const struct venus_format *fmt_out; @@ -370,6 +383,8 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; + unsigned int core_acquired: 1; + unsigned int bit_depth; }; #define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index d3d1748a7ef6..8801a6a7543d 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -44,8 +44,14 @@ static void venus_reset_cpu(struct venus_core *core) int venus_set_hw_state(struct venus_core *core, bool resume) { - if (core->use_tz) - return qcom_scm_set_remote_state(resume, 0); + int ret; + + if (core->use_tz) { + ret = qcom_scm_set_remote_state(resume, 0); + if (resume && ret == -EINVAL) + ret = 0; + return ret; + } if (resume) venus_reset_cpu(core); @@ -100,8 +106,7 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); if (!mem_va) { - dev_err(dev, "unable to map memory region: %pa+%zx\n", - &r.start, *mem_size); + dev_err(dev, "unable to map memory region: %pR\n", &r); ret = -ENOMEM; goto err_release_fw; } diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index a172f1ac0b35..bcc603804041 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -3,12 +3,8 @@ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. * Copyright (C) 2017 Linaro Ltd. */ -#include <linux/clk.h> -#include <linux/iopoll.h> -#include <linux/interconnect.h> #include <linux/list.h> #include <linux/mutex.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <media/videobuf2-dma-sg.h> #include <media/v4l2-mem2mem.h> @@ -17,7 +13,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" -#include "hfi_venus_io.h" +#include "pm_helpers.h" struct intbuf { struct list_head list; @@ -360,266 +356,6 @@ err: } EXPORT_SYMBOL_GPL(venus_helper_intbufs_realloc); -static u32 load_per_instance(struct venus_inst *inst) -{ - u32 mbs; - - if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) - return 0; - - mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); - - return mbs * inst->fps; -} - -static u32 load_per_type(struct venus_core *core, u32 session_type) -{ - struct venus_inst *inst = NULL; - u32 mbs_per_sec = 0; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - if (inst->session_type != session_type) - continue; - - mbs_per_sec += load_per_instance(inst); - } - mutex_unlock(&core->lock); - - return mbs_per_sec; -} - -static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) -{ - const struct venus_resources *res = inst->core->res; - const struct bw_tbl *bw_tbl; - unsigned int num_rows, i; - - *avg = 0; - *peak = 0; - - if (mbs == 0) - return; - - if (inst->session_type == VIDC_SESSION_TYPE_ENC) { - num_rows = res->bw_tbl_enc_size; - bw_tbl = res->bw_tbl_enc; - } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { - num_rows = res->bw_tbl_dec_size; - bw_tbl = res->bw_tbl_dec; - } else { - return; - } - - if (!bw_tbl || num_rows == 0) - return; - - for (i = 0; i < num_rows; i++) { - if (mbs > bw_tbl[i].mbs_per_sec) - break; - - if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { - *avg = bw_tbl[i].avg_10bit; - *peak = bw_tbl[i].peak_10bit; - } else { - *avg = bw_tbl[i].avg; - *peak = bw_tbl[i].peak; - } - } -} - -static int load_scale_bw(struct venus_core *core) -{ - struct venus_inst *inst = NULL; - u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - mbs_per_sec = load_per_instance(inst); - mbs_to_bw(inst, mbs_per_sec, &avg, &peak); - total_avg += avg; - total_peak += peak; - } - mutex_unlock(&core->lock); - - dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", - total_avg, total_peak); - - return icc_set_bw(core->video_path, total_avg, total_peak); -} - -static int set_clk_freq(struct venus_core *core, unsigned long freq) -{ - struct clk *clk = core->clks[0]; - int ret; - - ret = clk_set_rate(clk, freq); - if (ret) - return ret; - - ret = clk_set_rate(core->core0_clk, freq); - if (ret) - return ret; - - ret = clk_set_rate(core->core1_clk, freq); - if (ret) - return ret; - - return 0; -} - -static int scale_clocks(struct venus_inst *inst) -{ - struct venus_core *core = inst->core; - const struct freq_tbl *table = core->res->freq_tbl; - unsigned int num_rows = core->res->freq_tbl_size; - unsigned long freq = table[0].freq; - struct device *dev = core->dev; - u32 mbs_per_sec; - unsigned int i; - int ret; - - mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + - load_per_type(core, VIDC_SESSION_TYPE_DEC); - - if (mbs_per_sec > core->res->max_load) - dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", - mbs_per_sec, core->res->max_load); - - if (!mbs_per_sec && num_rows > 1) { - freq = table[num_rows - 1].freq; - goto set_freq; - } - - for (i = 0; i < num_rows; i++) { - if (mbs_per_sec > table[i].load) - break; - freq = table[i].freq; - } - -set_freq: - - ret = set_clk_freq(core, freq); - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", - freq, ret); - return ret; - } - - ret = load_scale_bw(core); - if (ret) { - dev_err(dev, "failed to set bandwidth (%d)\n", - ret); - return ret; - } - - return 0; -} - -static unsigned long calculate_inst_freq(struct venus_inst *inst, - unsigned long filled_len) -{ - unsigned long vpp_freq = 0, vsp_freq = 0; - u32 fps = (u32)inst->fps; - u32 mbs_per_sec; - - mbs_per_sec = load_per_instance(inst) / fps; - - vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; - /* 21 / 20 is overhead factor */ - vpp_freq += vpp_freq / 20; - vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; - - /* 10 / 7 is overhead factor */ - if (inst->session_type == VIDC_SESSION_TYPE_ENC) - vsp_freq += (inst->controls.enc.bitrate * 10) / 7; - else - vsp_freq += ((fps * filled_len * 8) * 10) / 7; - - return max(vpp_freq, vsp_freq); -} - -static int scale_clocks_v4(struct venus_inst *inst) -{ - struct venus_core *core = inst->core; - const struct freq_tbl *table = core->res->freq_tbl; - unsigned int num_rows = core->res->freq_tbl_size; - struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx; - struct device *dev = core->dev; - unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; - unsigned long filled_len = 0; - struct venus_buffer *buf, *n; - struct vb2_buffer *vb; - int i, ret; - - v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buf, n) { - vb = &buf->vb.vb2_buf; - filled_len = max(filled_len, vb2_get_plane_payload(vb, 0)); - } - - if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) - return 0; - - freq = calculate_inst_freq(inst, filled_len); - inst->clk_data.freq = freq; - - mutex_lock(&core->lock); - list_for_each_entry(inst, &core->instances, list) { - if (inst->clk_data.core_id == VIDC_CORE_ID_1) { - freq_core1 += inst->clk_data.freq; - } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { - freq_core2 += inst->clk_data.freq; - } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { - freq_core1 += inst->clk_data.freq; - freq_core2 += inst->clk_data.freq; - } - } - mutex_unlock(&core->lock); - - freq = max(freq_core1, freq_core2); - - if (freq >= table[0].freq) { - freq = table[0].freq; - dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", - freq, table[0].freq); - goto set_freq; - } - - for (i = num_rows - 1 ; i >= 0; i--) { - if (freq <= table[i].freq) { - freq = table[i].freq; - break; - } - } - -set_freq: - - ret = set_clk_freq(core, freq); - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", - freq, ret); - return ret; - } - - ret = load_scale_bw(core); - if (ret) { - dev_err(dev, "failed to set bandwidth (%d)\n", - ret); - return ret; - } - - return 0; -} - -int venus_helper_load_scale_clocks(struct venus_inst *inst) -{ - if (IS_V4(inst->core)) - return scale_clocks_v4(inst); - - return scale_clocks(inst); -} -EXPORT_SYMBOL_GPL(venus_helper_load_scale_clocks); - static void fill_buffer_desc(const struct venus_buffer *buf, struct hfi_buffer_desc *bd, bool response) { @@ -723,7 +459,7 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (inst->session_type == VIDC_SESSION_TYPE_DEC) put_ts_metadata(inst, vbuf); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { if (inst->session_type == VIDC_SESSION_TYPE_ENC) fdata.buffer_type = HFI_BUFFER_OUTPUT; @@ -890,6 +626,78 @@ static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) max(extradata, y_stride * 48), SZ_4K); } +static u32 get_framesize_raw_p010(u32 width, u32 height) +{ + u32 y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN((height + 1) >> 1, 16); + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines; + + return ALIGN((y_plane + uv_plane), SZ_4K); +} + +static u32 get_framesize_raw_p010_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 size; + + y_stride = ALIGN(width * 2, 256); + uv_stride = ALIGN(width * 2, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_yuv420_tp10_ubwc(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_sclines, uv_sclines; + u32 y_ubwc_plane, uv_ubwc_plane; + u32 y_meta_stride, y_meta_scanlines; + u32 uv_meta_stride, uv_meta_scanlines; + u32 y_meta_plane, uv_meta_plane; + u32 extradata = SZ_16K; + u32 size; + + y_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + uv_stride = ALIGN(ALIGN(width, 192) * 4 / 3, 256); + y_sclines = ALIGN(height, 16); + uv_sclines = ALIGN((height + 1) >> 1, 16); + + y_ubwc_plane = ALIGN(y_stride * y_sclines, SZ_4K); + uv_ubwc_plane = ALIGN(uv_stride * uv_sclines, SZ_4K); + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 48), 64); + y_meta_scanlines = ALIGN(DIV_ROUND_UP(height, 4), 16); + y_meta_plane = ALIGN(y_meta_stride * y_meta_scanlines, SZ_4K); + uv_meta_stride = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64); + uv_meta_scanlines = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16); + uv_meta_plane = ALIGN(uv_meta_stride * uv_meta_scanlines, SZ_4K); + + size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane + uv_meta_plane; + size += max(extradata + SZ_8K, y_stride * 48); + + return ALIGN(size, SZ_4K); +} + u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) { switch (hfi_fmt) { @@ -898,6 +706,12 @@ u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) return get_framesize_raw_nv12(width, height); case HFI_COLOR_FORMAT_NV12_UBWC: return get_framesize_raw_nv12_ubwc(width, height); + case HFI_COLOR_FORMAT_P010: + return get_framesize_raw_p010(width, height); + case HFI_COLOR_FORMAT_P010_UBWC: + return get_framesize_raw_p010_ubwc(width, height); + case HFI_COLOR_FORMAT_YUV420_TP10_UBWC: + return get_framesize_raw_yuv420_tp10_ubwc(width, height); default: return 0; } @@ -987,21 +801,6 @@ int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) } EXPORT_SYMBOL_GPL(venus_helper_set_work_mode); -int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage) -{ - const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; - struct hfi_videocores_usage_type cu; - - inst->clk_data.core_id = usage; - if (!IS_V4(inst->core)) - return 0; - - cu.video_core_enable_mask = usage; - - return hfi_session_set_property(inst, ptype, &cu); -} -EXPORT_SYMBOL_GPL(venus_helper_set_core_usage); - int venus_helper_init_codec_freq_data(struct venus_inst *inst) { const struct codec_freq_data *data; @@ -1289,6 +1088,15 @@ int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) } EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_prepare); +static void cache_payload(struct venus_inst *inst, struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + unsigned int idx = vbuf->vb2_buf.index; + + if (vbuf->vb2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + inst->payloads[idx] = vb2_get_plane_payload(vb, 0); +} + void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1300,6 +1108,8 @@ void venus_helper_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); + cache_payload(inst, vb); + if (inst->session_type == VIDC_SESSION_TYPE_ENC && !(inst->streamon_out && inst->streamon_cap)) goto unlock; @@ -1354,7 +1164,7 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -1365,6 +1175,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) else inst->streamon_cap = 0; + venus_pm_release_core(inst); + mutex_unlock(&inst->lock); } EXPORT_SYMBOL_GPL(venus_helper_vb2_stop_streaming); @@ -1417,7 +1229,7 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_bufs_free; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_load_res(inst); if (ret) @@ -1512,6 +1324,27 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, if (!caps) return -EINVAL; + if (inst->bit_depth == VIDC_BITDEPTH_10 && + inst->session_type == VIDC_SESSION_TYPE_DEC) { + found_ubwc = + find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + HFI_COLOR_FORMAT_YUV420_TP10_UBWC); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, + HFI_COLOR_FORMAT_NV12); + if (found_ubwc && found) { + /* + * Hard-code DPB buffers to be 10bit UBWC and decoder + * output buffers in 8bit NV12 until V4L2 is able to + * expose compressed/tiled formats to applications. + */ + *out_fmt = HFI_COLOR_FORMAT_YUV420_TP10_UBWC; + *out2_fmt = HFI_COLOR_FORMAT_NV12; + return 0; + } + + return -EINVAL; + } + if (ubwc) { ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, @@ -1542,52 +1375,3 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, return -EINVAL; } EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts); - -int venus_helper_power_enable(struct venus_core *core, u32 session_type, - bool enable) -{ - void __iomem *ctrl, *stat; - u32 val; - int ret; - - if (!IS_V3(core) && !IS_V4(core)) - return 0; - - if (IS_V3(core)) { - if (session_type == VIDC_SESSION_TYPE_DEC) - ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; - else - ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; - if (enable) - writel(0, ctrl); - else - writel(1, ctrl); - - return 0; - } - - if (session_type == VIDC_SESSION_TYPE_DEC) { - ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; - } else { - ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; - } - - if (enable) { - writel(0, ctrl); - - ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); - if (ret) - return ret; - } else { - writel(1, ctrl); - - ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(venus_helper_power_enable); diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 34dcd0c13f06..b64875564064 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -34,7 +34,6 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, u32 buftype); int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); int venus_helper_init_codec_freq_data(struct venus_inst *inst); -int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs); @@ -53,14 +52,11 @@ int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt, u32 *out2_fmt, bool ubwc); int venus_helper_alloc_dpb_bufs(struct venus_inst *inst); int venus_helper_free_dpb_bufs(struct venus_inst *inst); -int venus_helper_power_enable(struct venus_core *core, u32 session_type, - bool enable); int venus_helper_intbufs_alloc(struct venus_inst *inst); int venus_helper_intbufs_free(struct venus_inst *inst); int venus_helper_intbufs_realloc(struct venus_inst *inst); int venus_helper_queue_dpb_bufs(struct venus_inst *inst); int venus_helper_unregister_bufs(struct venus_inst *inst); -int venus_helper_load_scale_clocks(struct venus_inst *inst); int venus_helper_process_initial_cap_bufs(struct venus_inst *inst); int venus_helper_process_initial_out_bufs(struct venus_inst *inst); void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 4f645076abfb..c67e412f8201 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1207,6 +1207,8 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP: + case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE: /* not implemented on Venus 4xx */ return -ENOTSUPP; default: diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index b70551e296b7..f6613df1d16b 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -550,6 +550,7 @@ struct hfi_bitrate { #define HFI_CAPABILITY_LCU_SIZE 0x14 #define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS 0x15 #define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE 0x16 +#define HFI_CAPABILITY_MAX_VIDEOCORES 0x2b struct hfi_capability { u32 capability_type; @@ -792,6 +793,9 @@ struct hfi_h264_vui_timing_info { u32 time_scale; }; +#define VIDC_BITDEPTH_8 0x00000 +#define VIDC_BITDEPTH_10 0x20002 + struct hfi_bit_depth { u32 buffer_type; u32 bit_depth; @@ -840,8 +844,10 @@ struct hfi_extradata_input_crop { #define HFI_COLOR_FORMAT_10_BIT_BASE 0x4000 #define HFI_COLOR_FORMAT_YUV420_TP10 0x4002 +#define HFI_COLOR_FORMAT_P010 0x4003 #define HFI_COLOR_FORMAT_NV12_UBWC 0x8002 #define HFI_COLOR_FORMAT_YUV420_TP10_UBWC 0xc002 +#define HFI_COLOR_FORMAT_P010_UBWC 0xc003 #define HFI_COLOR_FORMAT_RGBA8888_UBWC 0x8010 struct hfi_uncompressed_format_select { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 2293d936e49c..7f515a4b9bd1 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -181,6 +181,7 @@ static void parse_codecs(struct venus_core *core, void *data) if (IS_V1(core)) { core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC; } } diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h index 3e931c747e19..264e6dd2415f 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.h +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -107,4 +107,9 @@ static inline u32 frate_step(struct venus_inst *inst) return cap_step(inst, HFI_CAPABILITY_FRAMERATE); } +static inline u32 core_num_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_MAX_VIDEOCORES); +} + #endif diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c new file mode 100644 index 000000000000..abf93158857b --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + * + * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org> + */ +#include <linux/clk.h> +#include <linux/interconnect.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <media/v4l2-mem2mem.h> + +#include "core.h" +#include "hfi_parser.h" +#include "hfi_venus_io.h" +#include "pm_helpers.h" + +static bool legacy_binding; + +static int core_clks_get(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + struct device *dev = core->dev; + unsigned int i; + + for (i = 0; i < res->clks_num; i++) { + core->clks[i] = devm_clk_get(dev, res->clks[i]); + if (IS_ERR(core->clks[i])) + return PTR_ERR(core->clks[i]); + } + + return 0; +} + +static int core_clks_enable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->clks_num; i++) { + ret = clk_prepare_enable(core->clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(core->clks[i]); + + return ret; +} + +static void core_clks_disable(struct venus_core *core) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->clks_num; + + while (i--) + clk_disable_unprepare(core->clks[i]); +} + +static int core_clks_set_rate(struct venus_core *core, unsigned long freq) +{ + struct clk *clk = core->clks[0]; + int ret; + + ret = clk_set_rate(clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(core->vcodec0_clks[0], freq); + if (ret) + return ret; + + ret = clk_set_rate(core->vcodec1_clks[0], freq); + if (ret) + return ret; + + return 0; +} + +static int vcodec_clks_get(struct venus_core *core, struct device *dev, + struct clk **clks, const char * const *id) +{ + const struct venus_resources *res = core->res; + unsigned int i; + + for (i = 0; i < res->vcodec_clks_num; i++) { + if (!id[i]) + continue; + clks[i] = devm_clk_get(dev, id[i]); + if (IS_ERR(clks[i])) + return PTR_ERR(clks[i]); + } + + return 0; +} + +static int vcodec_clks_enable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + for (i = 0; i < res->vcodec_clks_num; i++) { + ret = clk_prepare_enable(clks[i]); + if (ret) + goto err; + } + + return 0; +err: + while (i--) + clk_disable_unprepare(clks[i]); + + return ret; +} + +static void vcodec_clks_disable(struct venus_core *core, struct clk **clks) +{ + const struct venus_resources *res = core->res; + unsigned int i = res->vcodec_clks_num; + + while (i--) + clk_disable_unprepare(clks[i]); +} + +static u32 load_per_instance(struct venus_inst *inst) +{ + u32 mbs; + + if (!inst || !(inst->state >= INST_INIT && inst->state < INST_STOP)) + return 0; + + mbs = (ALIGN(inst->width, 16) / 16) * (ALIGN(inst->height, 16) / 16); + + return mbs * inst->fps; +} + +static u32 load_per_type(struct venus_core *core, u32 session_type) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->session_type != session_type) + continue; + + mbs_per_sec += load_per_instance(inst); + } + mutex_unlock(&core->lock); + + return mbs_per_sec; +} + +static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) +{ + const struct venus_resources *res = inst->core->res; + const struct bw_tbl *bw_tbl; + unsigned int num_rows, i; + + *avg = 0; + *peak = 0; + + if (mbs == 0) + return; + + if (inst->session_type == VIDC_SESSION_TYPE_ENC) { + num_rows = res->bw_tbl_enc_size; + bw_tbl = res->bw_tbl_enc; + } else if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + num_rows = res->bw_tbl_dec_size; + bw_tbl = res->bw_tbl_dec; + } else { + return; + } + + if (!bw_tbl || num_rows == 0) + return; + + for (i = 0; i < num_rows; i++) { + if (mbs > bw_tbl[i].mbs_per_sec) + break; + + if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { + *avg = bw_tbl[i].avg_10bit; + *peak = bw_tbl[i].peak_10bit; + } else { + *avg = bw_tbl[i].avg; + *peak = bw_tbl[i].peak; + } + } +} + +static int load_scale_bw(struct venus_core *core) +{ + struct venus_inst *inst = NULL; + u32 mbs_per_sec, avg, peak, total_avg = 0, total_peak = 0; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + mbs_per_sec = load_per_instance(inst); + mbs_to_bw(inst, mbs_per_sec, &avg, &peak); + total_avg += avg; + total_peak += peak; + } + mutex_unlock(&core->lock); + + dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", + total_avg, total_peak); + + return icc_set_bw(core->video_path, total_avg, total_peak); +} + +static int load_scale_v1(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + unsigned long freq = table[0].freq; + struct device *dev = core->dev; + u32 mbs_per_sec; + unsigned int i; + int ret; + + mbs_per_sec = load_per_type(core, VIDC_SESSION_TYPE_ENC) + + load_per_type(core, VIDC_SESSION_TYPE_DEC); + + if (mbs_per_sec > core->res->max_load) + dev_warn(dev, "HW is overloaded, needed: %d max: %d\n", + mbs_per_sec, core->res->max_load); + + if (!mbs_per_sec && num_rows > 1) { + freq = table[num_rows - 1].freq; + goto set_freq; + } + + for (i = 0; i < num_rows; i++) { + if (mbs_per_sec > table[i].load) + break; + freq = table[i].freq; + } + +set_freq: + + ret = core_clks_set_rate(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } + + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int core_get_v1(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return core_clks_get(core); +} + +static int core_power_v1(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + if (on == POWER_ON) + ret = core_clks_enable(core); + else + core_clks_disable(core); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v1 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .load_scale = load_scale_v1, +}; + +static void +vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable) +{ + void __iomem *ctrl; + + if (session_type == VIDC_SESSION_TYPE_DEC) + ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; + else + ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; + + if (enable) + writel(0, ctrl); + else + writel(1, ctrl); +} + +static int vdec_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static int vdec_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_DEC, false); + + return ret; +} + +static int venc_get_v3(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static int venc_power_v3(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, true); + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v3(core, VIDC_SESSION_TYPE_ENC, false); + + return ret; +} + +static const struct venus_pm_ops pm_ops_v3 = { + .core_get = core_get_v1, + .core_power = core_power_v1, + .vdec_get = vdec_get_v3, + .vdec_power = vdec_power_v3, + .venc_get = venc_get_v3, + .venc_power = venc_power_v3, + .load_scale = load_scale_v1, +}; + +static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) +{ + void __iomem *ctrl, *stat; + u32 val; + int ret; + + if (coreid == VIDC_CORE_ID_1) { + ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; + } else { + ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; + stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; + } + + if (enable) { + writel(0, ctrl); + + ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100); + if (ret) + return ret; + } else { + writel(1, ctrl); + + ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100); + if (ret) + return ret; + } + + return 0; +} + +static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec0_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[1]); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + vcodec_clks_disable(core, core->vcodec1_clks); + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret) + return ret; + + ret = pm_runtime_put_sync(core->pmdomains[2]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask) +{ + int ret; + + if (coreid_mask & VIDC_CORE_ID_1) { + ret = pm_runtime_get_sync(core->pmdomains[1]); + if (ret < 0) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + ret = vcodec_clks_enable(core, core->vcodec0_clks); + if (ret) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false); + if (ret < 0) + return ret; + } + + if (coreid_mask & VIDC_CORE_ID_2) { + ret = pm_runtime_get_sync(core->pmdomains[2]); + if (ret < 0) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + ret = vcodec_clks_enable(core, core->vcodec1_clks); + if (ret) + return ret; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false); + if (ret < 0) + return ret; + } + + return 0; +} + +static void +min_loaded_core(struct venus_inst *inst, u32 *min_coreid, u32 *min_load) +{ + u32 mbs_per_sec, load, core1_load = 0, core2_load = 0; + u32 cores_max = core_num_max(inst); + struct venus_core *core = inst->core; + struct venus_inst *inst_pos; + unsigned long vpp_freq; + u32 coreid; + + mutex_lock(&core->lock); + + list_for_each_entry(inst_pos, &core->instances, list) { + if (inst_pos == inst) + continue; + vpp_freq = inst_pos->clk_data.codec_freq_data->vpp_freq; + coreid = inst_pos->clk_data.core_id; + + mbs_per_sec = load_per_instance(inst_pos); + load = mbs_per_sec * vpp_freq; + + if ((coreid & VIDC_CORE_ID_3) == VIDC_CORE_ID_3) { + core1_load += load / 2; + core2_load += load / 2; + } else if (coreid & VIDC_CORE_ID_1) { + core1_load += load; + } else if (coreid & VIDC_CORE_ID_2) { + core2_load += load; + } + } + + *min_coreid = core1_load <= core2_load ? + VIDC_CORE_ID_1 : VIDC_CORE_ID_2; + *min_load = min(core1_load, core2_load); + + if (cores_max < VIDC_CORE_ID_2 || core->res->vcodec_num < 2) { + *min_coreid = VIDC_CORE_ID_1; + *min_load = core1_load; + } + + mutex_unlock(&core->lock); +} + +static int decide_core(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE; + struct venus_core *core = inst->core; + u32 min_coreid, min_load, inst_load; + struct hfi_videocores_usage_type cu; + unsigned long max_freq; + + if (legacy_binding) { + if (inst->session_type == VIDC_SESSION_TYPE_DEC) + cu.video_core_enable_mask = VIDC_CORE_ID_1; + else + cu.video_core_enable_mask = VIDC_CORE_ID_2; + + goto done; + } + + if (inst->clk_data.core_id != VIDC_CORE_ID_DEFAULT) + return 0; + + inst_load = load_per_instance(inst); + inst_load *= inst->clk_data.codec_freq_data->vpp_freq; + max_freq = core->res->freq_tbl[0].freq; + + min_loaded_core(inst, &min_coreid, &min_load); + + if ((inst_load + min_load) > max_freq) { + dev_warn(core->dev, "HW is overloaded, needed: %u max: %lu\n", + inst_load, max_freq); + return -EINVAL; + } + + inst->clk_data.core_id = min_coreid; + cu.video_core_enable_mask = min_coreid; + +done: + return hfi_session_set_property(inst, ptype, &cu); +} + +static int acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + + if (inst->core_acquired) + return 0; + + inst->core_acquired = true; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (core->core0_usage_count++) + return 0; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (core->core1_usage_count++) + return 0; + + coreid_mask |= VIDC_CORE_ID_2; + } + + return poweron_coreid(core, coreid_mask); +} + +static int release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + unsigned int coreid_mask = 0; + int ret; + + if (!inst->core_acquired) + return 0; + + if (inst->clk_data.core_id & VIDC_CORE_ID_1) { + if (--core->core0_usage_count) + goto done; + + coreid_mask = VIDC_CORE_ID_1; + } + + if (inst->clk_data.core_id & VIDC_CORE_ID_2) { + if (--core->core1_usage_count) + goto done; + + coreid_mask |= VIDC_CORE_ID_2; + } + + ret = poweroff_coreid(core, coreid_mask); + if (ret) + return ret; + +done: + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + return 0; +} + +static int coreid_power_v4(struct venus_inst *inst, int on) +{ + struct venus_core *core = inst->core; + int ret; + + if (legacy_binding) + return 0; + + if (on == POWER_ON) { + ret = decide_core(inst); + if (ret) + return ret; + + mutex_lock(&core->lock); + ret = acquire_core(inst); + mutex_unlock(&core->lock); + } else { + mutex_lock(&core->lock); + ret = release_core(inst); + mutex_unlock(&core->lock); + } + + return ret; +} + +static int vdec_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec0_clks, + core->res->vcodec0_clks); +} + +static void vdec_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec0_clks[i] = NULL; +} + +static int vdec_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec0_clks); + else + vcodec_clks_disable(core, core->vcodec0_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_1, false); + + return ret; +} + +static int venc_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + + if (!legacy_binding) + return 0; + + return vcodec_clks_get(core, dev, core->vcodec1_clks, + core->res->vcodec1_clks); +} + +static void venc_put_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + unsigned int i; + + if (!legacy_binding) + return; + + for (i = 0; i < core->res->vcodec_clks_num; i++) + core->vcodec1_clks[i] = NULL; +} + +static int venc_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret; + + if (!legacy_binding) + return 0; + + ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true); + if (ret) + return ret; + + if (on == POWER_ON) + ret = vcodec_clks_enable(core, core->vcodec1_clks); + else + vcodec_clks_disable(core, core->vcodec1_clks); + + vcodec_control_v4(core, VIDC_CORE_ID_2, false); + + return ret; +} + +static int vcodec_domains_get(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + struct device *pd; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return -ENODEV; + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + pd = dev_pm_domain_attach_by_name(dev, + res->vcodec_pmdomains[i]); + if (IS_ERR(pd)) + return PTR_ERR(pd); + core->pmdomains[i] = pd; + } + + core->pd_dl_venus = device_link_add(dev, core->pmdomains[0], + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS | + DL_FLAG_RPM_ACTIVE); + if (!core->pd_dl_venus) + return -ENODEV; + + return 0; +} + +static void vcodec_domains_put(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + unsigned int i; + + if (!res->vcodec_pmdomains_num) + return; + + if (core->pd_dl_venus) + device_link_del(core->pd_dl_venus); + + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); + } +} + +static int core_get_v4(struct device *dev) +{ + struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + int ret; + + ret = core_clks_get(core); + if (ret) + return ret; + + if (!res->vcodec_pmdomains_num) + legacy_binding = true; + + dev_info(dev, "%s legacy binding\n", legacy_binding ? "" : "non"); + + ret = vcodec_clks_get(core, dev, core->vcodec0_clks, res->vcodec0_clks); + if (ret) + return ret; + + ret = vcodec_clks_get(core, dev, core->vcodec1_clks, res->vcodec1_clks); + if (ret) + return ret; + + if (legacy_binding) + return 0; + + ret = vcodec_domains_get(dev); + if (ret) + return ret; + + return 0; +} + +static void core_put_v4(struct device *dev) +{ + if (legacy_binding) + return; + + vcodec_domains_put(dev); +} + +static int core_power_v4(struct device *dev, int on) +{ + struct venus_core *core = dev_get_drvdata(dev); + int ret = 0; + + if (on == POWER_ON) + ret = core_clks_enable(core); + else + core_clks_disable(core); + + return ret; +} + +static unsigned long calculate_inst_freq(struct venus_inst *inst, + unsigned long filled_len) +{ + unsigned long vpp_freq = 0, vsp_freq = 0; + u32 fps = (u32)inst->fps; + u32 mbs_per_sec; + + mbs_per_sec = load_per_instance(inst) / fps; + + vpp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vpp_freq; + /* 21 / 20 is overhead factor */ + vpp_freq += vpp_freq / 20; + vsp_freq = mbs_per_sec * inst->clk_data.codec_freq_data->vsp_freq; + + /* 10 / 7 is overhead factor */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + vsp_freq += (inst->controls.enc.bitrate * 10) / 7; + else + vsp_freq += ((fps * filled_len * 8) * 10) / 7; + + return max(vpp_freq, vsp_freq); +} + +static int load_scale_v4(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct freq_tbl *table = core->res->freq_tbl; + unsigned int num_rows = core->res->freq_tbl_size; + struct device *dev = core->dev; + unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0; + unsigned long filled_len = 0; + int i, ret; + + for (i = 0; i < inst->num_input_bufs; i++) + filled_len = max(filled_len, inst->payloads[i]); + + if (inst->session_type == VIDC_SESSION_TYPE_DEC && !filled_len) + return 0; + + freq = calculate_inst_freq(inst, filled_len); + inst->clk_data.freq = freq; + + mutex_lock(&core->lock); + list_for_each_entry(inst, &core->instances, list) { + if (inst->clk_data.core_id == VIDC_CORE_ID_1) { + freq_core1 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_2) { + freq_core2 += inst->clk_data.freq; + } else if (inst->clk_data.core_id == VIDC_CORE_ID_3) { + freq_core1 += inst->clk_data.freq; + freq_core2 += inst->clk_data.freq; + } + } + mutex_unlock(&core->lock); + + freq = max(freq_core1, freq_core2); + + if (freq >= table[0].freq) { + freq = table[0].freq; + dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", + freq, table[0].freq); + goto set_freq; + } + + for (i = num_rows - 1 ; i >= 0; i--) { + if (freq <= table[i].freq) { + freq = table[i].freq; + break; + } + } + +set_freq: + + ret = core_clks_set_rate(core, freq); + if (ret) { + dev_err(dev, "failed to set clock rate %lu (%d)\n", + freq, ret); + return ret; + } + + ret = load_scale_bw(core); + if (ret) { + dev_err(dev, "failed to set bandwidth (%d)\n", + ret); + return ret; + } + + return 0; +} + +static const struct venus_pm_ops pm_ops_v4 = { + .core_get = core_get_v4, + .core_put = core_put_v4, + .core_power = core_power_v4, + .vdec_get = vdec_get_v4, + .vdec_put = vdec_put_v4, + .vdec_power = vdec_power_v4, + .venc_get = venc_get_v4, + .venc_put = venc_put_v4, + .venc_power = venc_power_v4, + .coreid_power = coreid_power_v4, + .load_scale = load_scale_v4, +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version) +{ + switch (version) { + case HFI_VERSION_1XX: + default: + return &pm_ops_v1; + case HFI_VERSION_3XX: + return &pm_ops_v3; + case HFI_VERSION_4XX: + return &pm_ops_v4; + } + + return NULL; +} diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h new file mode 100644 index 000000000000..aa2f6afa2354 --- /dev/null +++ b/drivers/media/platform/qcom/venus/pm_helpers.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2019 Linaro Ltd. */ +#ifndef __VENUS_PM_HELPERS_H__ +#define __VENUS_PM_HELPERS_H__ + +struct device; + +#define POWER_ON 1 +#define POWER_OFF 0 + +struct venus_pm_ops { + int (*core_get)(struct device *dev); + void (*core_put)(struct device *dev); + int (*core_power)(struct device *dev, int on); + + int (*vdec_get)(struct device *dev); + void (*vdec_put)(struct device *dev); + int (*vdec_power)(struct device *dev, int on); + + int (*venc_get)(struct device *dev); + void (*venc_put)(struct device *dev); + int (*venc_power)(struct device *dev, int on); + + int (*coreid_power)(struct venus_inst *inst, int on); + + int (*load_scale)(struct venus_inst *inst); +}; + +const struct venus_pm_ops *venus_pm_get(enum hfi_version version); + +static inline int venus_pm_load_scale(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + + if (!core->pm_ops || !core->pm_ops->load_scale) + return 0; + + return core->pm_ops->load_scale(inst); +} + +static inline int venus_pm_acquire_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_ON); + + return ret; +} + +static inline int venus_pm_release_core(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; + + if (pm_ops && pm_ops->coreid_power) + ret = pm_ops->coreid_power(inst, POWER_OFF); + + return ret; +} + +#endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 8feaf5daece9..4ed2628585a1 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "vdec.h" +#include "pm_helpers.h" /* * Three resons to keep MPLANE formats (despite that the number of planes @@ -578,10 +579,6 @@ static int vdec_output_conf(struct venus_inst *inst) if (ret) return ret; - ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1); - if (ret) - return ret; - if (core->res->hfi_version == HFI_VERSION_1XX) { ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER; ret = hfi_session_set_property(inst, ptype, &en); @@ -868,7 +865,7 @@ reconfigure: if (ret) goto free_dpb_bufs; - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); ret = hfi_session_continue(inst); if (ret) @@ -950,6 +947,10 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&inst->lock); + ret = venus_pm_acquire_core(inst); + if (ret) + goto error; + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ret = vdec_start_capture(inst); else @@ -1076,7 +1077,8 @@ static void vdec_session_release(struct venus_inst *inst) hfi_session_abort(inst); venus_helper_free_dpb_bufs(inst); - venus_helper_load_scale_clocks(inst); + venus_pm_load_scale(inst); + venus_pm_release_core(inst); INIT_LIST_HEAD(&inst->registeredbufs); mutex_unlock(&inst->lock); @@ -1191,6 +1193,9 @@ static void vdec_event_change(struct venus_inst *inst, inst->out_width = ev_data->width; inst->out_height = ev_data->height; + if (inst->bit_depth != ev_data->bit_depth) + inst->bit_depth = ev_data->bit_depth; + dev_dbg(dev, "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); @@ -1336,6 +1341,9 @@ static int vdec_open(struct file *file) inst->num_output_bufs = 1; inst->codec_state = VENUS_DEC_STATE_DEINIT; inst->buf_count = 0; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; + inst->bit_depth = VIDC_BITDEPTH_8; init_waitqueue_head(&inst->reconf_wait); venus_helper_init_instance(inst); @@ -1432,20 +1440,14 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (IS_V3(core) || IS_V4(core)) { - core->core0_clk = devm_clk_get(dev, "core"); - if (IS_ERR(core->core0_clk)) - return PTR_ERR(core->core0_clk); - } + platform_set_drvdata(pdev, core); - if (IS_V4(core)) { - core->core0_bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(core->core0_bus_clk)) - return PTR_ERR(core->core0_bus_clk); + if (core->pm_ops->vdec_get) { + ret = core->pm_ops->vdec_get(dev); + if (ret) + return ret; } - platform_set_drvdata(pdev, core); - vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1458,7 +1460,7 @@ static int vdec_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; @@ -1482,57 +1484,33 @@ static int vdec_remove(struct platform_device *pdev) video_unregister_device(core->vdev_dec); pm_runtime_disable(core->dev_dec); + if (core->pm_ops->vdec_put) + core->pm_ops->vdec_put(core->dev_dec); + return 0; } static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - if (IS_V4(core)) - clk_disable_unprepare(core->core0_bus_clk); + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - clk_disable_unprepare(core->core0_clk); + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_OFF); - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + return ret; } static __maybe_unused int vdec_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core0_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core0_bus_clk); - - if (ret) - goto err_unprepare_core0; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + if (pm_ops->vdec_power) + ret = pm_ops->vdec_power(dev, POWER_ON); -err_unprepare_core0: - clk_disable_unprepare(core->core0_clk); -err_power_disable: - venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 453edf966d4f..9981a2a27c90 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -20,6 +20,7 @@ #include "core.h" #include "helpers.h" #include "venc.h" +#include "pm_helpers.h" #define NUM_B_FRAMES_MAX 4 @@ -655,10 +656,6 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2); - if (ret) - return ret; - ptype = HFI_PROPERTY_CONFIG_FRAME_RATE; frate.buffer_type = HFI_BUFFER_OUTPUT; frate.framerate = inst->fps * (1 << 16); @@ -731,7 +728,9 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + if (!ctr->rc_enable) + rate_control = HFI_RATE_CONTROL_OFF; + else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) rate_control = HFI_RATE_CONTROL_VBR_CFR; else rate_control = HFI_RATE_CONTROL_CBR_CFR; @@ -991,6 +990,10 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto bufs_done; + ret = venus_pm_acquire_core(inst); + if (ret) + goto deinit_sess; + ret = venc_set_properties(inst); if (ret) goto deinit_sess; @@ -1159,6 +1162,8 @@ static int venc_open(struct file *file) inst->core = core; inst->session_type = VIDC_SESSION_TYPE_ENC; + inst->clk_data.core_id = VIDC_CORE_ID_DEFAULT; + inst->core_acquired = false; venus_helper_init_instance(inst); @@ -1255,20 +1260,14 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (IS_V3(core) || IS_V4(core)) { - core->core1_clk = devm_clk_get(dev, "core"); - if (IS_ERR(core->core1_clk)) - return PTR_ERR(core->core1_clk); - } + platform_set_drvdata(pdev, core); - if (IS_V4(core)) { - core->core1_bus_clk = devm_clk_get(dev, "bus"); - if (IS_ERR(core->core1_bus_clk)) - return PTR_ERR(core->core1_bus_clk); + if (core->pm_ops->venc_get) { + ret = core->pm_ops->venc_get(dev); + if (ret) + return ret; } - platform_set_drvdata(pdev, core); - vdev = video_device_alloc(); if (!vdev) return -ENOMEM; @@ -1281,7 +1280,7 @@ static int venc_probe(struct platform_device *pdev) vdev->v4l2_dev = &core->v4l2_dev; vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto err_vdev_release; @@ -1305,57 +1304,33 @@ static int venc_remove(struct platform_device *pdev) video_unregister_device(core->vdev_enc); pm_runtime_disable(core->dev_enc); + if (core->pm_ops->venc_put) + core->pm_ops->venc_put(core->dev_enc); + return 0; } static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); - if (ret) - return ret; - - if (IS_V4(core)) - clk_disable_unprepare(core->core1_bus_clk); + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - clk_disable_unprepare(core->core1_clk); + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_OFF); - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + return ret; } static __maybe_unused int venc_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); - int ret; - - if (IS_V1(core)) - return 0; - - ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); - if (ret) - return ret; - - ret = clk_prepare_enable(core->core1_clk); - if (ret) - goto err_power_disable; - - if (IS_V4(core)) - ret = clk_prepare_enable(core->core1_bus_clk); - - if (ret) - goto err_unprepare_core1; + const struct venus_pm_ops *pm_ops = core->pm_ops; + int ret = 0; - return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); + if (pm_ops->venc_power) + ret = pm_ops->venc_power(dev, POWER_ON); -err_unprepare_core1: - clk_disable_unprepare(core->core1_clk); -err_power_disable: - venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); return ret; } diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 877c0b3299e9..8362dde7949e 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -199,6 +199,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) } mutex_unlock(&inst->lock); break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + ctr->rc_enable = ctrl->val; + break; default: return -EINVAL; } @@ -214,7 +217,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 30); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 31); if (ret) return ret; @@ -351,6 +354,9 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + ret = inst->ctrl_handler.error; if (ret) goto err; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index cf9029efeb04..1a30cd036371 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -535,7 +535,7 @@ static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) /* Set scaling coefficient */ crop_height = vin->crop.height; - if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) + if (V4L2_FIELD_HAS_BOTH(vin->format.field)) crop_height *= 2; ys = 0; @@ -564,7 +564,7 @@ static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) rvin_write(vin, 0, VNSLPOC_REG); rvin_write(vin, vin->format.width - 1, VNEPPOC_REG); - if (V4L2_FIELD_IS_INTERLACED(vin->format.field)) + if (V4L2_FIELD_HAS_BOTH(vin->format.field)) rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG); else rvin_write(vin, vin->format.height - 1, VNELPOC_REG); @@ -626,6 +626,8 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: case V4L2_FIELD_NONE: vnmc = VNMC_IM_ODD_EVEN; progressive = true; @@ -842,27 +844,52 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) struct rvin_buffer *buf; struct vb2_v4l2_buffer *vbuf; dma_addr_t phys_addr; + int prev; /* A already populated slot shall never be overwritten. */ - if (WARN_ON(vin->queue_buf[slot] != NULL)) + if (WARN_ON(vin->buf_hw[slot].buffer)) return; - vin_dbg(vin, "Filling HW slot: %d\n", slot); - - if (list_empty(&vin->buf_list)) { - vin->queue_buf[slot] = NULL; + prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1; + + if (vin->buf_hw[prev].type == HALF_TOP) { + vbuf = vin->buf_hw[prev].buffer; + vin->buf_hw[slot].buffer = vbuf; + vin->buf_hw[slot].type = HALF_BOTTOM; + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + phys_addr = vin->buf_hw[prev].phys + + vin->format.sizeimage / 4; + break; + default: + phys_addr = vin->buf_hw[prev].phys + + vin->format.sizeimage / 2; + break; + } + } else if (list_empty(&vin->buf_list)) { + vin->buf_hw[slot].buffer = NULL; + vin->buf_hw[slot].type = FULL; phys_addr = vin->scratch_phys; } else { /* Keep track of buffer we give to HW */ buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); vbuf = &buf->vb; list_del_init(to_buf_list(vbuf)); - vin->queue_buf[slot] = vbuf; + vin->buf_hw[slot].buffer = vbuf; + + vin->buf_hw[slot].type = + V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ? + HALF_TOP : FULL; /* Setup DMA */ phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); } + vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n", + slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer); + + vin->buf_hw[slot].phys = phys_addr; rvin_set_slot_addr(vin, slot, phys_addr); } @@ -870,6 +897,11 @@ static int rvin_capture_start(struct rvin_dev *vin) { int slot, ret; + for (slot = 0; slot < HW_BUFFER_NUM; slot++) { + vin->buf_hw[slot].buffer = NULL; + vin->buf_hw[slot].type = FULL; + } + for (slot = 0; slot < HW_BUFFER_NUM; slot++) rvin_fill_hw_slot(vin, slot); @@ -953,13 +985,24 @@ static irqreturn_t rvin_irq(int irq, void *data) } /* Capture frame */ - if (vin->queue_buf[slot]) { - vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); - vin->queue_buf[slot]->sequence = vin->sequence; - vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, + if (vin->buf_hw[slot].buffer) { + /* + * Nothing to do but refill the hardware slot if + * capture only filled first half of vb2 buffer. + */ + if (vin->buf_hw[slot].type == HALF_TOP) { + vin->buf_hw[slot].buffer = NULL; + rvin_fill_hw_slot(vin, slot); + goto done; + } + + vin->buf_hw[slot].buffer->field = + rvin_get_active_field(vin, vnms); + vin->buf_hw[slot].buffer->sequence = vin->sequence; + vin->buf_hw[slot].buffer->vb2_buf.timestamp = ktime_get_ns(); + vb2_buffer_done(&vin->buf_hw[slot].buffer->vb2_buf, VB2_BUF_STATE_DONE); - vin->queue_buf[slot] = NULL; + vin->buf_hw[slot].buffer = NULL; } else { /* Scratch buffer was used, dropping frame. */ vin_dbg(vin, "Dropping frame %u\n", vin->sequence); @@ -980,14 +1023,22 @@ static void return_all_buffers(struct rvin_dev *vin, enum vb2_buffer_state state) { struct rvin_buffer *buf, *node; - int i; + struct vb2_v4l2_buffer *freed[HW_BUFFER_NUM]; + unsigned int i, n; for (i = 0; i < HW_BUFFER_NUM; i++) { - if (vin->queue_buf[i]) { - vb2_buffer_done(&vin->queue_buf[i]->vb2_buf, - state); - vin->queue_buf[i] = NULL; + freed[i] = vin->buf_hw[i].buffer; + vin->buf_hw[i].buffer = NULL; + + for (n = 0; n < i; n++) { + if (freed[i] == freed[n]) { + freed[i] = NULL; + break; + } } + + if (freed[i]) + vb2_buffer_done(&freed[i]->vb2_buf, state); } list_for_each_entry_safe(buf, node, &vin->buf_list, list) { @@ -1291,7 +1342,7 @@ int rvin_dma_register(struct rvin_dev *vin, int irq) vin->state = STOPPED; for (i = 0; i < HW_BUFFER_NUM; i++) - vin->queue_buf[i] = NULL; + vin->buf_hw[i].buffer = NULL; /* buffer queue */ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 5ff565e76bca..5151a3cd8a6e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -73,11 +73,22 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin, { int i; - if (vin->info->model == RCAR_M1 && pixelformat == V4L2_PIX_FMT_XBGR32) - return NULL; - - if (pixelformat == V4L2_PIX_FMT_NV12 && !vin->info->nv12) - return NULL; + switch (pixelformat) { + case V4L2_PIX_FMT_XBGR32: + if (vin->info->model == RCAR_M1) + return NULL; + break; + case V4L2_PIX_FMT_NV12: + /* + * If NV12 is supported it's only supported on channels 0, 1, 4, + * 5, 8, 9, 12 and 13. + */ + if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333)) + return NULL; + break; + default: + break; + } for (i = 0; i < ARRAY_SIZE(rvin_formats); i++) if (rvin_formats[i].fourcc == pixelformat) @@ -107,6 +118,9 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin, break; } + if (V4L2_FIELD_IS_SEQUENTIAL(pix->field)) + align = 0x80; + return ALIGN(pix->width, align) * fmt->bpp; } @@ -137,6 +151,8 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: case V4L2_FIELD_ALTERNATE: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: break; default: pix->field = RVIN_DEFAULT_FIELD; @@ -826,7 +842,7 @@ static int rvin_open(struct file *file) goto err_unlock; if (vin->info->use_mc) - ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&vin->vdev.entity); else if (v4l2_fh_is_singular_file(file)) ret = rvin_power_parallel(vin, true); @@ -842,7 +858,7 @@ static int rvin_open(struct file *file) return 0; err_power: if (vin->info->use_mc) - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + v4l2_pipeline_pm_put(&vin->vdev.entity); else if (v4l2_fh_is_singular_file(file)) rvin_power_parallel(vin, false); err_open: @@ -870,7 +886,7 @@ static int rvin_release(struct file *file) ret = _vb2_fop_release(file, NULL); if (vin->info->use_mc) { - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + v4l2_pipeline_pm_put(&vin->vdev.entity); } else { if (fh_singular) rvin_power_parallel(vin, false); @@ -953,7 +969,7 @@ int rvin_v4l2_register(struct rvin_dev *vin) rvin_format_align(vin, &vin->format); - ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1); if (ret) { vin_err(vin, "Failed to register video device\n"); return ret; diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index a36b0824f81d..c19d077ce1cb 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -61,6 +61,23 @@ enum rvin_dma_state { }; /** + * enum rvin_buffer_type + * + * Describes how a buffer is given to the hardware. To be able + * to capture SEQ_TB/BT it's needed to capture to the same vb2 + * buffer twice so the type of buffer needs to be kept. + * + * FULL - One capture fills the whole vb2 buffer + * HALF_TOP - One capture fills the top half of the vb2 buffer + * HALF_BOTTOM - One capture fills the bottom half of the vb2 buffer + */ +enum rvin_buffer_type { + FULL, + HALF_TOP, + HALF_BOTTOM, +}; + +/** * struct rvin_video_format - Data format stored in memory * @fourcc: Pixelformat * @bpp: Bytes per pixel @@ -164,9 +181,8 @@ struct rvin_info { * @scratch: cpu address for scratch buffer * @scratch_phys: physical address of the scratch buffer * - * @qlock: protects @queue_buf, @buf_list, @sequence - * @state - * @queue_buf: Keeps track of buffers given to HW slot + * @qlock: protects @buf_hw, @buf_list, @sequence and @state + * @buf_hw: Keeps track of buffers given to HW slot * @buf_list: list of queued buffers * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state @@ -205,7 +221,11 @@ struct rvin_dev { dma_addr_t scratch_phys; spinlock_t qlock; - struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM]; + struct { + struct vb2_v4l2_buffer *buffer; + enum rvin_buffer_type type; + dma_addr_t phys; + } buf_hw[HW_BUFFER_NUM]; struct list_head buf_list; unsigned int sequence; enum rvin_dma_state state; diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index 0f267a237b42..3d2451ac347d 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -275,10 +275,14 @@ static int rcar_drif_alloc_dmachannels(struct rcar_drif_sdr *sdr) for_each_rcar_drif_channel(i, &sdr->cur_ch_mask) { struct rcar_drif *ch = sdr->ch[i]; - ch->dmach = dma_request_slave_channel(&ch->pdev->dev, "rx"); - if (!ch->dmach) { - rdrif_err(sdr, "ch%u: dma channel req failed\n", i); - ret = -ENODEV; + ch->dmach = dma_request_chan(&ch->pdev->dev, "rx"); + if (IS_ERR(ch->dmach)) { + ret = PTR_ERR(ch->dmach); + if (ret != -EPROBE_DEFER) + rdrif_err(sdr, + "ch%u: dma channel req failed: %pe\n", + i, ch->dmach); + ch->dmach = NULL; goto dmach_error; } diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index 97bed45360f0..c9448de885b6 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -2344,7 +2344,7 @@ static int fdp1_probe(struct platform_device *pdev) video_set_drvdata(vfd, fdp1); strscpy(vfd->name, fdp1_videodev.name, sizeof(vfd->name)); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&fdp1->v4l2_dev, "Failed to register video device\n"); goto release_m2m; diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 1c3f507acfc9..5250a14324e9 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1663,7 +1663,7 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); goto m2m_init_rollback; @@ -1682,7 +1682,7 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n"); goto enc_vdev_register_rollback; diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 197b3991330d..f7d71a6a7970 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -1450,7 +1450,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) V4L2_CAP_STREAMING; video_set_drvdata(vdev, ceudev); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(vdev->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index e9ff12b6b5bb..9d122429706e 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -889,7 +889,7 @@ static int rga_probe(struct platform_device *pdev) def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; def_frame.size = def_frame.stride * def_frame.height; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&rga->v4l2_dev, "Failed to register video device\n"); goto rel_vdev; diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 2fb45db8e4ba..9ca49af29542 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -1158,7 +1158,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) vfd->ctrl_handler = &vp->ctrl_handler; vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) goto err_ctrlh_free; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index f5f05ea9f521..6932fd47071b 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -695,7 +695,7 @@ static int g2d_probe(struct platform_device *pdev) vfd->lock = &dev->mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto rel_vdev; diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index ac2162235cef..86bda3947110 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2946,7 +2946,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M; jpeg->vfd_encoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); video_device_release(jpeg->vfd_encoder); @@ -2976,7 +2976,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M; jpeg->vfd_decoder->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; - ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); + ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); video_device_release(jpeg->vfd_decoder); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index b776f83e395e..5c2a23b953a4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1376,7 +1376,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_regs(dev); /* Register decoder and encoder */ - ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_dec_reg; @@ -1384,7 +1384,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) v4l2_info(&dev->v4l2_dev, "decoder registered as /dev/video%d\n", dev->vfd_dec->num); - ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_enc_reg; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 2b4c0d9d6928..f08b8fc192d8 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1160,7 +1160,7 @@ static int sh_veu_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); pm_runtime_suspend(&pdev->dev); if (ret < 0) goto evidreg; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 2236702c21b4..36e5f2ff4ef1 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1323,7 +1323,7 @@ static int sh_vou_probe(struct platform_device *pdev) goto ei2cnd; } - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) goto evregdev; diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index d1025a53709f..af2d5eb782ce 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -1066,7 +1066,7 @@ static int bdisp_register_device(struct bdisp_dev *bdisp) return PTR_ERR(bdisp->m2m.m2m_dev); } - ret = video_register_device(&bdisp->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&bdisp->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(bdisp->dev, "%s(): failed to register video device\n", __func__); diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 91369fb3ffaa..2503224eeee5 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -1781,7 +1781,7 @@ static int delta_register_device(struct delta_dev *delta) snprintf(vdev->name, sizeof(vdev->name), "%s-%s", DELTA_NAME, DELTA_FW_VERSION); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(delta->dev, "%s failed to register video device\n", DELTA_PREFIX); diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 64004d15a9c9..197b99d8fd9c 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1316,7 +1316,7 @@ static int hva_register_device(struct hva_dev *hva) snprintf(vdev->name, sizeof(vdev->name), "%s%lx", HVA_NAME, hva->ip_version); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dev, "%s failed to register video device\n", HVA_PREFIX); diff --git a/drivers/media/platform/stm32/stm32-cec.c b/drivers/media/platform/stm32/stm32-cec.c index 8a86b2cc22fa..ea4b1ebfca99 100644 --- a/drivers/media/platform/stm32/stm32-cec.c +++ b/drivers/media/platform/stm32/stm32-cec.c @@ -291,7 +291,9 @@ static int stm32_cec_probe(struct platform_device *pdev) cec->clk_cec = devm_clk_get(&pdev->dev, "cec"); if (IS_ERR(cec->clk_cec)) { - dev_err(&pdev->dev, "Cannot get cec clock\n"); + if (PTR_ERR(cec->clk_cec) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Cannot get cec clock\n"); + return PTR_ERR(cec->clk_cec); } @@ -302,10 +304,14 @@ static int stm32_cec_probe(struct platform_device *pdev) } cec->clk_hdmi_cec = devm_clk_get(&pdev->dev, "hdmi-cec"); + if (IS_ERR(cec->clk_hdmi_cec) && + PTR_ERR(cec->clk_hdmi_cec) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(cec->clk_hdmi_cec)) { ret = clk_prepare(cec->clk_hdmi_cec); if (ret) { - dev_err(&pdev->dev, "Unable to prepare hdmi-cec clock\n"); + dev_err(&pdev->dev, "Can't prepare hdmi-cec clock\n"); return ret; } } diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 9392e3409fba..b8931490b83b 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1910,10 +1910,13 @@ static int dcmi_probe(struct platform_device *pdev) return PTR_ERR(mclk); } - chan = dma_request_slave_channel(&pdev->dev, "tx"); - if (!chan) { - dev_info(&pdev->dev, "Unable to request DMA channel, defer probing\n"); - return -EPROBE_DEFER; + chan = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to request DMA channel: %d\n", ret); + return ret; } spin_lock_init(&dcmi->irqlock); @@ -1971,7 +1974,7 @@ static int dcmi_probe(struct platform_device *pdev) } dcmi->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; - ret = video_register_device(dcmi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(dcmi->vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dcmi->dev, "Failed to register video device\n"); goto err_media_entity_cleanup; diff --git a/drivers/media/platform/sunxi/Makefile b/drivers/media/platform/sunxi/Makefile index 3878cb4efdc2..ff0993f70dc3 100644 --- a/drivers/media/platform/sunxi/Makefile +++ b/drivers/media/platform/sunxi/Makefile @@ -1,3 +1,4 @@ obj-y += sun4i-csi/ obj-y += sun6i-csi/ obj-y += sun8i-di/ +obj-y += sun8i-rotate/ diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 83a3a0257c7b..1721e5aee9c6 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -214,7 +214,7 @@ static int sun4i_csi_open(struct file *file) if (ret < 0) goto err_pm_put; - ret = v4l2_pipeline_pm_use(&csi->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&csi->vdev.entity); if (ret) goto err_pm_put; @@ -227,7 +227,7 @@ static int sun4i_csi_open(struct file *file) return 0; err_pipeline_pm_put: - v4l2_pipeline_pm_use(&csi->vdev.entity, 0); + v4l2_pipeline_pm_put(&csi->vdev.entity); err_pm_put: pm_runtime_put(csi->dev); @@ -243,7 +243,7 @@ static int sun4i_csi_release(struct file *file) mutex_lock(&csi->lock); v4l2_fh_release(file); - v4l2_pipeline_pm_use(&csi->vdev.entity, 0); + v4l2_pipeline_pm_put(&csi->vdev.entity); pm_runtime_put(csi->dev); mutex_unlock(&csi->lock); @@ -374,7 +374,7 @@ int sun4i_csi_v4l2_register(struct sun4i_csi *csi) vdev->ioctl_ops = &sun4i_csi_ioctl_ops; video_set_drvdata(vdev, csi); - ret = video_register_device(&csi->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&csi->vdev, VFL_TYPE_VIDEO, -1); if (ret) return ret; diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index f0dfe68486d1..d9648b2810b9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -474,7 +474,7 @@ static int sun6i_video_open(struct file *file) if (ret < 0) goto unlock; - ret = v4l2_pipeline_pm_use(&video->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&video->vdev.entity); if (ret < 0) goto fh_release; @@ -507,7 +507,7 @@ static int sun6i_video_close(struct file *file) _vb2_fop_release(file, NULL); - v4l2_pipeline_pm_use(&video->vdev.entity, 0); + v4l2_pipeline_pm_put(&video->vdev.entity); if (last_fh) sun6i_csi_set_power(video->csi, false); @@ -648,7 +648,7 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, vdev->release = video_device_release_empty; vdev->fops = &sun6i_video_fops; vdev->ioctl_ops = &sun6i_video_ioctl_ops; - vdev->vfl_type = VFL_TYPE_GRABBER; + vdev->vfl_type = VFL_TYPE_VIDEO; vdev->vfl_dir = VFL_DIR_RX; vdev->v4l2_dev = &csi->v4l2_dev; vdev->queue = vidq; @@ -656,7 +656,7 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; video_set_drvdata(vdev, video); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { v4l2_err(&csi->v4l2_dev, "video_register_device failed: %d\n", ret); diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c index b61f3dea7c93..d78f6593ddd1 100644 --- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c +++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c @@ -814,11 +814,8 @@ static int deinterlace_probe(struct platform_device *pdev) dev->dev = &pdev->dev; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev->dev, "Failed to get IRQ\n"); - + if (irq <= 0) return irq; - } ret = devm_request_irq(dev->dev, irq, deinterlace_irq, 0, dev_name(dev->dev), dev); @@ -882,7 +879,7 @@ static int deinterlace_probe(struct platform_device *pdev) deinterlace_video_device.name); video_set_drvdata(vfd, dev); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); diff --git a/drivers/media/platform/sunxi/sun8i-rotate/Makefile b/drivers/media/platform/sunxi/sun8i-rotate/Makefile new file mode 100644 index 000000000000..40f9cf398e98 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +sun8i-rotate-y += sun8i_rotate.o +sun8i-rotate-y += sun8i_formats.o + +obj-$(CONFIG_VIDEO_SUN8I_ROTATE) += sun8i-rotate.o diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h new file mode 100644 index 000000000000..697cd5fadd5f --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-formats.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */ + +#ifndef _SUN8I_FORMATS_H_ +#define _SUN8I_FORMATS_H_ + +#include <linux/videodev2.h> + +#define ROTATE_FLAG_YUV BIT(0) +#define ROTATE_FLAG_OUTPUT BIT(1) + +struct rotate_format { + u32 fourcc; + u32 hw_format; + int planes; + int bpp[3]; + int hsub; + int vsub; + unsigned int flags; +}; + +const struct rotate_format *rotate_find_format(u32 pixelformat); +int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst); + +#endif diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h new file mode 100644 index 000000000000..32ade97ba572 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i-rotate.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allwinner DE2 rotation driver + * + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> + */ + +#ifndef _SUN8I_ROTATE_H_ +#define _SUN8I_ROTATE_H_ + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> + +#include <linux/platform_device.h> + +#define ROTATE_NAME "sun8i-rotate" + +#define ROTATE_GLB_CTL 0x00 +#define ROTATE_GLB_CTL_START BIT(31) +#define ROTATE_GLB_CTL_RESET BIT(30) +#define ROTATE_GLB_CTL_BURST_LEN(x) ((x) << 16) +#define ROTATE_GLB_CTL_HFLIP BIT(7) +#define ROTATE_GLB_CTL_VFLIP BIT(6) +#define ROTATE_GLB_CTL_ROTATION(x) ((x) << 4) +#define ROTATE_GLB_CTL_MODE(x) ((x) << 0) + +#define ROTATE_INT 0x04 +#define ROTATE_INT_FINISH_IRQ_EN BIT(16) +#define ROTATE_INT_FINISH_IRQ BIT(0) + +#define ROTATE_IN_FMT 0x20 +#define ROTATE_IN_FMT_FORMAT(x) ((x) << 0) + +#define ROTATE_IN_SIZE 0x24 +#define ROTATE_IN_PITCH0 0x30 +#define ROTATE_IN_PITCH1 0x34 +#define ROTATE_IN_PITCH2 0x38 +#define ROTATE_IN_ADDRL0 0x40 +#define ROTATE_IN_ADDRH0 0x44 +#define ROTATE_IN_ADDRL1 0x48 +#define ROTATE_IN_ADDRH1 0x4c +#define ROTATE_IN_ADDRL2 0x50 +#define ROTATE_IN_ADDRH2 0x54 +#define ROTATE_OUT_SIZE 0x84 +#define ROTATE_OUT_PITCH0 0x90 +#define ROTATE_OUT_PITCH1 0x94 +#define ROTATE_OUT_PITCH2 0x98 +#define ROTATE_OUT_ADDRL0 0xA0 +#define ROTATE_OUT_ADDRH0 0xA4 +#define ROTATE_OUT_ADDRL1 0xA8 +#define ROTATE_OUT_ADDRH1 0xAC +#define ROTATE_OUT_ADDRL2 0xB0 +#define ROTATE_OUT_ADDRH2 0xB4 + +#define ROTATE_BURST_8 0x07 +#define ROTATE_BURST_16 0x0f +#define ROTATE_BURST_32 0x1f +#define ROTATE_BURST_64 0x3f + +#define ROTATE_MODE_COPY_ROTATE 0x01 + +#define ROTATE_FORMAT_ARGB32 0x00 +#define ROTATE_FORMAT_ABGR32 0x01 +#define ROTATE_FORMAT_RGBA32 0x02 +#define ROTATE_FORMAT_BGRA32 0x03 +#define ROTATE_FORMAT_XRGB32 0x04 +#define ROTATE_FORMAT_XBGR32 0x05 +#define ROTATE_FORMAT_RGBX32 0x06 +#define ROTATE_FORMAT_BGRX32 0x07 +#define ROTATE_FORMAT_RGB24 0x08 +#define ROTATE_FORMAT_BGR24 0x09 +#define ROTATE_FORMAT_RGB565 0x0a +#define ROTATE_FORMAT_BGR565 0x0b +#define ROTATE_FORMAT_ARGB4444 0x0c +#define ROTATE_FORMAT_ABGR4444 0x0d +#define ROTATE_FORMAT_RGBA4444 0x0e +#define ROTATE_FORMAT_BGRA4444 0x0f +#define ROTATE_FORMAT_ARGB1555 0x10 +#define ROTATE_FORMAT_ABGR1555 0x11 +#define ROTATE_FORMAT_RGBA5551 0x12 +#define ROTATE_FORMAT_BGRA5551 0x13 + +#define ROTATE_FORMAT_YUYV 0x20 +#define ROTATE_FORMAT_UYVY 0x21 +#define ROTATE_FORMAT_YVYU 0x22 +#define ROTATE_FORMAT_VYUV 0x23 +#define ROTATE_FORMAT_NV61 0x24 +#define ROTATE_FORMAT_NV16 0x25 +#define ROTATE_FORMAT_YUV422P 0x26 +#define ROTATE_FORMAT_NV21 0x28 +#define ROTATE_FORMAT_NV12 0x29 +#define ROTATE_FORMAT_YUV420P 0x2A + +#define ROTATE_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1)) + +#define ROTATE_MIN_WIDTH 8U +#define ROTATE_MIN_HEIGHT 8U +#define ROTATE_MAX_WIDTH 4096U +#define ROTATE_MAX_HEIGHT 4096U + +struct rotate_ctx { + struct v4l2_fh fh; + struct rotate_dev *dev; + + struct v4l2_pix_format src_fmt; + struct v4l2_pix_format dst_fmt; + + struct v4l2_ctrl_handler ctrl_handler; + + u32 hflip; + u32 vflip; + u32 rotate; +}; + +struct rotate_dev { + struct v4l2_device v4l2_dev; + struct video_device vfd; + struct device *dev; + struct v4l2_m2m_dev *m2m_dev; + + /* Device file mutex */ + struct mutex dev_mutex; + + void __iomem *base; + + struct clk *bus_clk; + struct clk *mod_clk; + + struct reset_control *rstc; +}; + +#endif diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c new file mode 100644 index 000000000000..cebfbc5def38 --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_formats.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> */ + +#include "sun8i-formats.h" +#include "sun8i-rotate.h" + +/* + * Formats not included in array: + * ROTATE_FORMAT_BGR565 + * ROTATE_FORMAT_VYUV + */ + +static const struct rotate_format rotate_formats[] = { + { + .fourcc = V4L2_PIX_FMT_ARGB32, + .hw_format = ROTATE_FORMAT_ARGB32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_ABGR32, + .hw_format = ROTATE_FORMAT_ABGR32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGBA32, + .hw_format = ROTATE_FORMAT_RGBA32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_BGRA32, + .hw_format = ROTATE_FORMAT_BGRA32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_XRGB32, + .hw_format = ROTATE_FORMAT_XRGB32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_XBGR32, + .hw_format = ROTATE_FORMAT_XBGR32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGB32, + .hw_format = ROTATE_FORMAT_RGBX32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_BGR32, + .hw_format = ROTATE_FORMAT_BGRX32, + .planes = 1, + .bpp = { 4, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGB24, + .hw_format = ROTATE_FORMAT_RGB24, + .planes = 1, + .bpp = { 3, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .hw_format = ROTATE_FORMAT_BGR24, + .planes = 1, + .bpp = { 3, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .hw_format = ROTATE_FORMAT_RGB565, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_ARGB444, + .hw_format = ROTATE_FORMAT_ARGB4444, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_ABGR444, + .hw_format = ROTATE_FORMAT_ABGR4444, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGBA444, + .hw_format = ROTATE_FORMAT_RGBA4444, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_BGRA444, + .hw_format = ROTATE_FORMAT_BGRA4444, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_ARGB555, + .hw_format = ROTATE_FORMAT_ARGB1555, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_ABGR555, + .hw_format = ROTATE_FORMAT_ABGR1555, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_RGBA555, + .hw_format = ROTATE_FORMAT_RGBA5551, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_BGRA555, + .hw_format = ROTATE_FORMAT_BGRA5551, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 1, + .vsub = 1, + .flags = ROTATE_FLAG_OUTPUT + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .hw_format = ROTATE_FORMAT_YVYU, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .hw_format = ROTATE_FORMAT_UYVY, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .hw_format = ROTATE_FORMAT_YUYV, + .planes = 1, + .bpp = { 2, 0, 0 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_NV61, + .hw_format = ROTATE_FORMAT_NV61, + .planes = 2, + .bpp = { 1, 2, 0 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_NV16, + .hw_format = ROTATE_FORMAT_NV16, + .planes = 2, + .bpp = { 1, 2, 0 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_YUV422P, + .hw_format = ROTATE_FORMAT_YUV422P, + .planes = 3, + .bpp = { 1, 1, 1 }, + .hsub = 2, + .vsub = 1, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .hw_format = ROTATE_FORMAT_NV21, + .planes = 2, + .bpp = { 1, 2, 0 }, + .hsub = 2, + .vsub = 2, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .hw_format = ROTATE_FORMAT_NV12, + .planes = 2, + .bpp = { 1, 2, 0 }, + .hsub = 2, + .vsub = 2, + .flags = ROTATE_FLAG_YUV + }, { + .fourcc = V4L2_PIX_FMT_YUV420, + .hw_format = ROTATE_FORMAT_YUV420P, + .planes = 3, + .bpp = { 1, 1, 1 }, + .hsub = 2, + .vsub = 2, + .flags = ROTATE_FLAG_YUV | ROTATE_FLAG_OUTPUT + }, +}; + +const struct rotate_format *rotate_find_format(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rotate_formats); i++) + if (rotate_formats[i].fourcc == pixelformat) + return &rotate_formats[i]; + + return NULL; +} + +int rotate_enum_fmt(struct v4l2_fmtdesc *f, bool dst) +{ + int i, index; + + index = 0; + + for (i = 0; i < ARRAY_SIZE(rotate_formats); i++) { + /* not all formats can be used for capture buffers */ + if (dst && !(rotate_formats[i].flags & ROTATE_FLAG_OUTPUT)) + continue; + + if (index == f->index) { + f->pixelformat = rotate_formats[i].fourcc; + + return 0; + } + + index++; + } + + return -EINVAL; +} diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c new file mode 100644 index 000000000000..94f505d3cbad --- /dev/null +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -0,0 +1,924 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner sun8i DE2 rotation driver + * + * Copyright (C) 2020 Jernej Skrabec <jernej.skrabec@siol.net> + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> + +#include "sun8i-formats.h" +#include "sun8i-rotate.h" + +static inline u32 rotate_read(struct rotate_dev *dev, u32 reg) +{ + return readl(dev->base + reg); +} + +static inline void rotate_write(struct rotate_dev *dev, u32 reg, u32 value) +{ + writel(value, dev->base + reg); +} + +static inline void rotate_set_bits(struct rotate_dev *dev, u32 reg, u32 bits) +{ + writel(readl(dev->base + reg) | bits, dev->base + reg); +} + +static void rotate_calc_addr_pitch(dma_addr_t buffer, + u32 bytesperline, u32 height, + const struct rotate_format *fmt, + dma_addr_t *addr, u32 *pitch) +{ + u32 size; + int i; + + for (i = 0; i < fmt->planes; i++) { + pitch[i] = bytesperline; + addr[i] = buffer; + if (i > 0) + pitch[i] /= fmt->hsub / fmt->bpp[i]; + size = pitch[i] * height; + if (i > 0) + size /= fmt->vsub; + buffer += size; + } +} + +static void rotate_device_run(void *priv) +{ + struct rotate_ctx *ctx = priv; + struct rotate_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src, *dst; + const struct rotate_format *fmt; + dma_addr_t addr[3]; + u32 val, pitch[3]; + + src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + v4l2_m2m_buf_copy_metadata(src, dst, true); + + val = ROTATE_GLB_CTL_MODE(ROTATE_MODE_COPY_ROTATE); + if (ctx->hflip) + val |= ROTATE_GLB_CTL_HFLIP; + if (ctx->vflip) + val |= ROTATE_GLB_CTL_VFLIP; + val |= ROTATE_GLB_CTL_ROTATION(ctx->rotate / 90); + if (ctx->rotate != 90 && ctx->rotate != 270) + val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_64); + else + val |= ROTATE_GLB_CTL_BURST_LEN(ROTATE_BURST_8); + rotate_write(dev, ROTATE_GLB_CTL, val); + + fmt = rotate_find_format(ctx->src_fmt.pixelformat); + if (!fmt) + return; + + rotate_write(dev, ROTATE_IN_FMT, ROTATE_IN_FMT_FORMAT(fmt->hw_format)); + + rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0), + ctx->src_fmt.bytesperline, ctx->src_fmt.height, + fmt, addr, pitch); + + rotate_write(dev, ROTATE_IN_SIZE, + ROTATE_SIZE(ctx->src_fmt.width, ctx->src_fmt.height)); + + rotate_write(dev, ROTATE_IN_PITCH0, pitch[0]); + rotate_write(dev, ROTATE_IN_PITCH1, pitch[1]); + rotate_write(dev, ROTATE_IN_PITCH2, pitch[2]); + + rotate_write(dev, ROTATE_IN_ADDRL0, addr[0]); + rotate_write(dev, ROTATE_IN_ADDRL1, addr[1]); + rotate_write(dev, ROTATE_IN_ADDRL2, addr[2]); + + rotate_write(dev, ROTATE_IN_ADDRH0, 0); + rotate_write(dev, ROTATE_IN_ADDRH1, 0); + rotate_write(dev, ROTATE_IN_ADDRH2, 0); + + fmt = rotate_find_format(ctx->dst_fmt.pixelformat); + if (!fmt) + return; + + rotate_calc_addr_pitch(vb2_dma_contig_plane_dma_addr(&dst->vb2_buf, 0), + ctx->dst_fmt.bytesperline, ctx->dst_fmt.height, + fmt, addr, pitch); + + rotate_write(dev, ROTATE_OUT_SIZE, + ROTATE_SIZE(ctx->dst_fmt.width, ctx->dst_fmt.height)); + + rotate_write(dev, ROTATE_OUT_PITCH0, pitch[0]); + rotate_write(dev, ROTATE_OUT_PITCH1, pitch[1]); + rotate_write(dev, ROTATE_OUT_PITCH2, pitch[2]); + + rotate_write(dev, ROTATE_OUT_ADDRL0, addr[0]); + rotate_write(dev, ROTATE_OUT_ADDRL1, addr[1]); + rotate_write(dev, ROTATE_OUT_ADDRL2, addr[2]); + + rotate_write(dev, ROTATE_OUT_ADDRH0, 0); + rotate_write(dev, ROTATE_OUT_ADDRH1, 0); + rotate_write(dev, ROTATE_OUT_ADDRH2, 0); + + rotate_set_bits(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ_EN); + rotate_set_bits(dev, ROTATE_GLB_CTL, ROTATE_GLB_CTL_START); +} + +static irqreturn_t rotate_irq(int irq, void *data) +{ + struct vb2_v4l2_buffer *buffer; + struct rotate_dev *dev = data; + struct rotate_ctx *ctx; + unsigned int val; + + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + if (!ctx) { + v4l2_err(&dev->v4l2_dev, + "Instance released before the end of transaction\n"); + return IRQ_NONE; + } + + val = rotate_read(dev, ROTATE_INT); + if (!(val & ROTATE_INT_FINISH_IRQ)) + return IRQ_NONE; + + /* clear flag and disable irq */ + rotate_write(dev, ROTATE_INT, ROTATE_INT_FINISH_IRQ); + + buffer = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE); + + buffer = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_DONE); + + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx); + + return IRQ_HANDLED; +} + +static inline struct rotate_ctx *rotate_file2ctx(struct file *file) +{ + return container_of(file->private_data, struct rotate_ctx, fh); +} + +static void rotate_prepare_format(struct v4l2_pix_format *pix_fmt) +{ + unsigned int height, width, alignment, sizeimage, size, bpl; + const struct rotate_format *fmt; + int i; + + fmt = rotate_find_format(pix_fmt->pixelformat); + if (!fmt) + return; + + width = ALIGN(pix_fmt->width, fmt->hsub); + height = ALIGN(pix_fmt->height, fmt->vsub); + + /* all pitches have to be 16 byte aligned */ + alignment = 16; + if (fmt->planes > 1) + alignment *= fmt->hsub / fmt->bpp[1]; + bpl = ALIGN(width * fmt->bpp[0], alignment); + + sizeimage = 0; + for (i = 0; i < fmt->planes; i++) { + size = bpl * height; + if (i > 0) { + size *= fmt->bpp[i]; + size /= fmt->hsub; + size /= fmt->vsub; + } + sizeimage += size; + } + + pix_fmt->width = width; + pix_fmt->height = height; + pix_fmt->bytesperline = bpl; + pix_fmt->sizeimage = sizeimage; +} + +static int rotate_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, ROTATE_NAME, sizeof(cap->driver)); + strscpy(cap->card, ROTATE_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", ROTATE_NAME); + + return 0; +} + +static int rotate_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return rotate_enum_fmt(f, true); +} + +static int rotate_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return rotate_enum_fmt(f, false); +} + +static int rotate_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + const struct rotate_format *fmt; + + if (fsize->index != 0) + return -EINVAL; + + fmt = rotate_find_format(fsize->pixel_format); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = ROTATE_MIN_WIDTH; + fsize->stepwise.min_height = ROTATE_MIN_HEIGHT; + fsize->stepwise.max_width = ROTATE_MAX_WIDTH; + fsize->stepwise.max_height = ROTATE_MAX_HEIGHT; + fsize->stepwise.step_width = fmt->hsub; + fsize->stepwise.step_height = fmt->vsub; + + return 0; +} + +static int rotate_set_cap_format(struct rotate_ctx *ctx, + struct v4l2_pix_format *f, + u32 rotate) +{ + const struct rotate_format *fmt; + + fmt = rotate_find_format(ctx->src_fmt.pixelformat); + if (!fmt) + return -EINVAL; + + if (fmt->flags & ROTATE_FLAG_YUV) + f->pixelformat = V4L2_PIX_FMT_YUV420; + else + f->pixelformat = ctx->src_fmt.pixelformat; + + f->field = V4L2_FIELD_NONE; + + if (rotate == 90 || rotate == 270) { + f->width = ctx->src_fmt.height; + f->height = ctx->src_fmt.width; + } else { + f->width = ctx->src_fmt.width; + f->height = ctx->src_fmt.height; + } + + rotate_prepare_format(f); + + return 0; +} + +static int rotate_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rotate_ctx *ctx = rotate_file2ctx(file); + + f->fmt.pix = ctx->dst_fmt; + + return 0; +} + +static int rotate_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rotate_ctx *ctx = rotate_file2ctx(file); + + f->fmt.pix = ctx->src_fmt; + + return 0; +} + +static int rotate_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rotate_ctx *ctx = rotate_file2ctx(file); + + return rotate_set_cap_format(ctx, &f->fmt.pix, ctx->rotate); +} + +static int rotate_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + if (!rotate_find_format(f->fmt.pix.pixelformat)) + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ARGB32; + + if (f->fmt.pix.width < ROTATE_MIN_WIDTH) + f->fmt.pix.width = ROTATE_MIN_WIDTH; + if (f->fmt.pix.height < ROTATE_MIN_HEIGHT) + f->fmt.pix.height = ROTATE_MIN_HEIGHT; + + if (f->fmt.pix.width > ROTATE_MAX_WIDTH) + f->fmt.pix.width = ROTATE_MAX_WIDTH; + if (f->fmt.pix.height > ROTATE_MAX_HEIGHT) + f->fmt.pix.height = ROTATE_MAX_HEIGHT; + + f->fmt.pix.field = V4L2_FIELD_NONE; + + rotate_prepare_format(&f->fmt.pix); + + return 0; +} + +static int rotate_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rotate_ctx *ctx = rotate_file2ctx(file); + struct vb2_queue *vq; + int ret; + + ret = rotate_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->dst_fmt = f->fmt.pix; + + return 0; +} + +static int rotate_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rotate_ctx *ctx = rotate_file2ctx(file); + struct vb2_queue *vq; + int ret; + + ret = rotate_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + /* + * Capture queue has to be also checked, because format and size + * depends on output format and size. + */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (vb2_is_busy(vq)) + return -EBUSY; + + ctx->src_fmt = f->fmt.pix; + + /* Propagate colorspace information to capture. */ + ctx->dst_fmt.colorspace = f->fmt.pix.colorspace; + ctx->dst_fmt.xfer_func = f->fmt.pix.xfer_func; + ctx->dst_fmt.ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->dst_fmt.quantization = f->fmt.pix.quantization; + + return rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate); +} + +static const struct v4l2_ioctl_ops rotate_ioctl_ops = { + .vidioc_querycap = rotate_querycap, + + .vidioc_enum_framesizes = rotate_enum_framesizes, + + .vidioc_enum_fmt_vid_cap = rotate_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = rotate_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = rotate_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = rotate_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = rotate_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = rotate_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = rotate_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = rotate_s_fmt_vid_out, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int rotate_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (*nplanes) { + if (sizes[0] < pix_fmt->sizeimage) + return -EINVAL; + } else { + sizes[0] = pix_fmt->sizeimage; + *nplanes = 1; + } + + return 0; +} + +static int rotate_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); + struct v4l2_pix_format *pix_fmt; + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + pix_fmt = &ctx->src_fmt; + else + pix_fmt = &ctx->dst_fmt; + + if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage) + return -EINVAL; + + vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage); + + return 0; +} + +static void rotate_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rotate_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void rotate_queue_cleanup(struct vb2_queue *vq, u32 state) +{ + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); + struct vb2_v4l2_buffer *vbuf; + + do { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + if (vbuf) + v4l2_m2m_buf_done(vbuf, state); + } while (vbuf); +} + +static int rotate_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx->dev->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable module\n"); + + return ret; + } + } + + return 0; +} + +static void rotate_stop_streaming(struct vb2_queue *vq) +{ + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + struct rotate_ctx *ctx = vb2_get_drv_priv(vq); + + pm_runtime_put(ctx->dev->dev); + } + + rotate_queue_cleanup(vq, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops rotate_qops = { + .queue_setup = rotate_queue_setup, + .buf_prepare = rotate_buf_prepare, + .buf_queue = rotate_buf_queue, + .start_streaming = rotate_start_streaming, + .stop_streaming = rotate_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int rotate_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct rotate_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->min_buffers_needed = 1; + src_vq->ops = &rotate_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->min_buffers_needed = 2; + dst_vq->ops = &rotate_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = ctx->dev->dev; + + ret = vb2_queue_init(dst_vq); + if (ret) + return ret; + + return 0; +} + +static int rotate_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rotate_ctx *ctx = container_of(ctrl->handler, + struct rotate_ctx, + ctrl_handler); + struct v4l2_pix_format fmt; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + ctx->hflip = ctrl->val; + break; + case V4L2_CID_VFLIP: + ctx->vflip = ctrl->val; + break; + case V4L2_CID_ROTATE: + rotate_set_cap_format(ctx, &fmt, ctrl->val); + + /* Check if capture format needs to be changed */ + if (fmt.width != ctx->dst_fmt.width || + fmt.height != ctx->dst_fmt.height || + fmt.bytesperline != ctx->dst_fmt.bytesperline || + fmt.sizeimage != ctx->dst_fmt.sizeimage) { + struct vb2_queue *vq; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (vb2_is_busy(vq)) + return -EBUSY; + + rotate_set_cap_format(ctx, &ctx->dst_fmt, ctrl->val); + } + + ctx->rotate = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops rotate_ctrl_ops = { + .s_ctrl = rotate_s_ctrl, +}; + +static int rotate_setup_ctrls(struct rotate_ctx *ctx) +{ + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rotate_ctrl_ops, + V4L2_CID_ROTATE, 0, 270, 90, 0); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + v4l2_err(&ctx->dev->v4l2_dev, "control setup failed!\n"); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + return err; + } + + return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); +} + +static int rotate_open(struct file *file) +{ + struct rotate_dev *dev = video_drvdata(file); + struct rotate_ctx *ctx = NULL; + int ret; + + if (mutex_lock_interruptible(&dev->dev_mutex)) + return -ERESTARTSYS; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + mutex_unlock(&dev->dev_mutex); + return -ENOMEM; + } + + /* default output format */ + ctx->src_fmt.pixelformat = V4L2_PIX_FMT_ARGB32; + ctx->src_fmt.field = V4L2_FIELD_NONE; + ctx->src_fmt.width = 640; + ctx->src_fmt.height = 480; + rotate_prepare_format(&ctx->src_fmt); + + /* default capture format */ + rotate_set_cap_format(ctx, &ctx->dst_fmt, ctx->rotate); + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, + &rotate_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto err_free; + } + + v4l2_fh_add(&ctx->fh); + + ret = rotate_setup_ctrls(ctx); + if (ret) + goto err_free; + + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + mutex_unlock(&dev->dev_mutex); + + return 0; + +err_free: + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int rotate_release(struct file *file) +{ + struct rotate_dev *dev = video_drvdata(file); + struct rotate_ctx *ctx = container_of(file->private_data, + struct rotate_ctx, fh); + + mutex_lock(&dev->dev_mutex); + + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + + kfree(ctx); + + mutex_unlock(&dev->dev_mutex); + + return 0; +} + +static const struct v4l2_file_operations rotate_fops = { + .owner = THIS_MODULE, + .open = rotate_open, + .release = rotate_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device rotate_video_device = { + .name = ROTATE_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &rotate_fops, + .ioctl_ops = &rotate_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_m2m_ops rotate_m2m_ops = { + .device_run = rotate_device_run, +}; + +static int rotate_probe(struct platform_device *pdev) +{ + struct rotate_dev *dev; + struct video_device *vfd; + int irq, ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->vfd = rotate_video_device; + dev->dev = &pdev->dev; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev->dev, "Failed to get IRQ\n"); + + return irq; + } + + ret = devm_request_irq(dev->dev, irq, rotate_irq, + 0, dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "Failed to request IRQ\n"); + + return ret; + } + + dev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->bus_clk = devm_clk_get(dev->dev, "bus"); + if (IS_ERR(dev->bus_clk)) { + dev_err(dev->dev, "Failed to get bus clock\n"); + + return PTR_ERR(dev->bus_clk); + } + + dev->mod_clk = devm_clk_get(dev->dev, "mod"); + if (IS_ERR(dev->mod_clk)) { + dev_err(dev->dev, "Failed to get mod clock\n"); + + return PTR_ERR(dev->mod_clk); + } + + dev->rstc = devm_reset_control_get(dev->dev, NULL); + if (IS_ERR(dev->rstc)) { + dev_err(dev->dev, "Failed to get reset control\n"); + + return PTR_ERR(dev->rstc); + } + + mutex_init(&dev->dev_mutex); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "Failed to register V4L2 device\n"); + + return ret; + } + + vfd = &dev->vfd; + vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + + snprintf(vfd->name, sizeof(vfd->name), "%s", + rotate_video_device.name); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + + goto err_v4l2; + } + + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + dev->m2m_dev = v4l2_m2m_init(&rotate_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, + "Failed to initialize V4L2 M2M device\n"); + ret = PTR_ERR(dev->m2m_dev); + + goto err_video; + } + + platform_set_drvdata(pdev, dev); + + pm_runtime_enable(dev->dev); + + return 0; + +err_video: + video_unregister_device(&dev->vfd); +err_v4l2: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int rotate_remove(struct platform_device *pdev) +{ + struct rotate_dev *dev = platform_get_drvdata(pdev); + + v4l2_m2m_release(dev->m2m_dev); + video_unregister_device(&dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + pm_runtime_force_suspend(&pdev->dev); + + return 0; +} + +static int rotate_runtime_resume(struct device *device) +{ + struct rotate_dev *dev = dev_get_drvdata(device); + int ret; + + ret = clk_prepare_enable(dev->bus_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable bus clock\n"); + + return ret; + } + + ret = clk_prepare_enable(dev->mod_clk); + if (ret) { + dev_err(dev->dev, "Failed to enable mod clock\n"); + + goto err_bus_clk; + } + + ret = reset_control_deassert(dev->rstc); + if (ret) { + dev_err(dev->dev, "Failed to apply reset\n"); + + goto err_mod_clk; + } + + return 0; + +err_mod_clk: + clk_disable_unprepare(dev->mod_clk); +err_bus_clk: + clk_disable_unprepare(dev->bus_clk); + + return ret; +} + +static int rotate_runtime_suspend(struct device *device) +{ + struct rotate_dev *dev = dev_get_drvdata(device); + + reset_control_assert(dev->rstc); + + clk_disable_unprepare(dev->mod_clk); + clk_disable_unprepare(dev->bus_clk); + + return 0; +} + +static const struct of_device_id rotate_dt_match[] = { + { .compatible = "allwinner,sun8i-a83t-de2-rotate" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rotate_dt_match); + +static const struct dev_pm_ops rotate_pm_ops = { + .runtime_resume = rotate_runtime_resume, + .runtime_suspend = rotate_runtime_suspend, +}; + +static struct platform_driver rotate_driver = { + .probe = rotate_probe, + .remove = rotate_remove, + .driver = { + .name = ROTATE_NAME, + .of_match_table = rotate_dt_match, + .pm = &rotate_pm_ops, + }, +}; +module_platform_driver(rotate_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>"); +MODULE_DESCRIPTION("Allwinner DE2 rotate driver"); diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index be54806180a5..6c8f3702eac0 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -372,8 +372,6 @@ struct cal_ctx { struct v4l2_subdev *sensor; struct v4l2_fwnode_endpoint endpoint; - struct v4l2_async_subdev asd; - struct v4l2_fh fh; struct cal_dev *dev; struct cc_data *cc; @@ -722,16 +720,16 @@ static void enable_irqs(struct cal_ctx *ctx) static void disable_irqs(struct cal_ctx *ctx) { + u32 val; + /* Disable IRQ_WDMA_END 0/1 */ - reg_write_field(ctx->dev, - CAL_HL_IRQENABLE_CLR(2), - CAL_HL_IRQ_CLEAR, - CAL_HL_IRQ_MASK(ctx->csi2_port)); + val = 0; + set_field(&val, CAL_HL_IRQ_CLEAR, CAL_HL_IRQ_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); /* Disable IRQ_WDMA_START 0/1 */ - reg_write_field(ctx->dev, - CAL_HL_IRQENABLE_CLR(3), - CAL_HL_IRQ_CLEAR, - CAL_HL_IRQ_MASK(ctx->csi2_port)); + val = 0; + set_field(&val, CAL_HL_IRQ_CLEAR, CAL_HL_IRQ_MASK(ctx->csi2_port)); + reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); } @@ -1948,7 +1946,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx) vfd->lock = &ctx->mutex; video_set_drvdata(vfd, ctx); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) return ret; @@ -2032,7 +2030,6 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) parent = pdev->dev.of_node; - asd = &ctx->asd; endpoint = &ctx->endpoint; ep_node = NULL; @@ -2079,8 +2076,6 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) ctx_dbg(3, ctx, "can't get remote parent\n"); goto cleanup_exit; } - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = of_fwnode_handle(sensor_node); v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); @@ -2110,9 +2105,17 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) v4l2_async_notifier_init(&ctx->notifier); + asd = kzalloc(sizeof(*asd), GFP_KERNEL); + if (!asd) + goto cleanup_exit; + + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + asd->match.fwnode = of_fwnode_handle(sensor_node); + ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); if (ret) { ctx_err(ctx, "Error adding asd\n"); + kfree(asd); goto cleanup_exit; } diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 65c2c048b018..cff2fcd6d812 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2500,7 +2500,7 @@ static void vpe_fw_cb(struct platform_device *pdev) vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { vpe_err(dev, "Failed to register video device\n"); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 78841b9015ce..c88fd3403eda 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -1262,7 +1262,7 @@ static int viacam_probe(struct platform_device *pdev) cam->vdev.lock = &cam->lock; cam->vdev.queue = vq; video_set_drvdata(&cam->vdev, cam); - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (ret) goto out_irq; diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 82350097503e..30ced1c21387 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -117,15 +117,10 @@ struct vicodec_ctx { struct vicodec_dev *dev; bool is_enc; bool is_stateless; - bool is_draining; - bool next_is_last; - bool has_stopped; spinlock_t *lock; struct v4l2_ctrl_handler hdl; - struct vb2_v4l2_buffer *last_src_buf; - /* Source and destination queue data */ struct vicodec_q_data q_data[2]; struct v4l2_fwht_state state; @@ -431,11 +426,11 @@ static void device_run(void *priv) v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); spin_lock(ctx->lock); - if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) { + if (!ctx->comp_has_next_frame && + v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - ctx->is_draining = false; - ctx->has_stopped = true; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); } if (ctx->is_enc || ctx->is_stateless) { src_buf->sequence = q_src->sequence++; @@ -586,8 +581,6 @@ static int job_ready(void *priv) unsigned int max_to_copy; unsigned int comp_frame_size; - if (ctx->has_stopped) - return 0; if (ctx->source_changed) return 0; if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) @@ -607,7 +600,8 @@ restart: if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { state = get_next_header(ctx, &p, p_src + sz - p); if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { - if (ctx->is_draining && src_buf == ctx->last_src_buf) + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, + src_buf)) return 1; job_remove_src_buf(ctx, state); goto restart; @@ -636,7 +630,8 @@ restart: p += copy; ctx->comp_size += copy; if (ctx->comp_size < max_to_copy) { - if (ctx->is_draining && src_buf == ctx->last_src_buf) + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, + src_buf)) return 1; job_remove_src_buf(ctx, state); goto restart; @@ -1219,41 +1214,6 @@ static int vidioc_s_selection(struct file *file, void *priv, return 0; } -static int vicodec_mark_last_buf(struct vicodec_ctx *ctx) -{ - struct vb2_v4l2_buffer *next_dst_buf; - int ret = 0; - - spin_lock(ctx->lock); - if (ctx->is_draining) { - ret = -EBUSY; - goto unlock; - } - if (ctx->has_stopped) - goto unlock; - - ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - ctx->is_draining = true; - if (ctx->last_src_buf) - goto unlock; - - next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!next_dst_buf) { - ctx->next_is_last = true; - goto unlock; - } - - next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; - vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); - ctx->is_draining = false; - ctx->has_stopped = true; - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - -unlock: - spin_unlock(ctx->lock); - return ret; -} - static int vicodec_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) { @@ -1268,18 +1228,19 @@ static int vicodec_encoder_cmd(struct file *file, void *fh, !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; - if (ec->cmd == V4L2_ENC_CMD_STOP) - return vicodec_mark_last_buf(ctx); - ret = 0; - spin_lock(ctx->lock); - if (ctx->is_draining) { - ret = -EBUSY; - } else if (ctx->has_stopped) { - ctx->has_stopped = false; + ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + if (ec->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + + if (ec->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - } - spin_unlock(ctx->lock); - return ret; + + return 0; } static int vicodec_decoder_cmd(struct file *file, void *fh, @@ -1296,18 +1257,19 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; - if (dc->cmd == V4L2_DEC_CMD_STOP) - return vicodec_mark_last_buf(ctx); - ret = 0; - spin_lock(ctx->lock); - if (ctx->is_draining) { - ret = -EBUSY; - } else if (ctx->has_stopped) { - ctx->has_stopped = false; + ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + if (dc->cmd == V4L2_DEC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + + if (dc->cmd == V4L2_DEC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - } - spin_unlock(ctx->lock); - return ret; + + return 0; } static int vicodec_enum_framesizes(struct file *file, void *fh, @@ -1480,23 +1442,21 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - if (vb2_is_streaming(vq_cap)) { - if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && - ctx->next_is_last) { - unsigned int i; + if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + unsigned int i; - for (i = 0; i < vb->num_planes; i++) - vb->planes[i].bytesused = 0; - vbuf->flags = V4L2_BUF_FLAG_LAST; - vbuf->field = V4L2_FIELD_NONE; - vbuf->sequence = get_q_data(ctx, vb->vb2_queue->type)->sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - ctx->is_draining = false; - ctx->has_stopped = true; - ctx->next_is_last = false; - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - return; - } + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; + + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = + get_q_data(ctx, vb->vb2_queue->type)->sequence++; + + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + return; } /* buf_queue handles only the first source change event */ @@ -1609,8 +1569,7 @@ static int vicodec_start_streaming(struct vb2_queue *q, chroma_div = info->width_div * info->height_div; q_data->sequence = 0; - if (V4L2_TYPE_IS_OUTPUT(q->type)) - ctx->last_src_buf = NULL; + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); state->gop_cnt = 0; @@ -1689,29 +1648,12 @@ static void vicodec_stop_streaming(struct vb2_queue *q) vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - if (ctx->is_draining) { - struct vb2_v4l2_buffer *next_dst_buf; - - spin_lock(ctx->lock); - ctx->last_src_buf = NULL; - next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!next_dst_buf) { - ctx->next_is_last = true; - } else { - next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; - vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); - ctx->is_draining = false; - ctx->has_stopped = true; - v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - } - spin_unlock(ctx->lock); - } - } else { - ctx->is_draining = false; - ctx->has_stopped = false; - ctx->next_is_last = false; - } + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type)) ctx->first_source_change_sent = false; @@ -2120,7 +2062,7 @@ static int register_instance(struct vicodec_dev *dev, } video_set_drvdata(vfd, dev); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device '%s'\n", name); v4l2_m2m_release(dev_instance->m2m_dev); diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 8d6b09623d88..ac6717fbb764 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1333,7 +1333,7 @@ static int vim2m_probe(struct platform_device *pdev) vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto error_v4l2; diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 76c015898cfd..23e740c1c5c0 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -325,20 +325,20 @@ static const struct media_entity_operations vimc_cap_mops = { .link_validate = vimc_vdev_link_validate, }; -static void vimc_cap_release(struct video_device *vdev) +void vimc_cap_release(struct vimc_ent_device *ved) { struct vimc_cap_device *vcap = - container_of(vdev, struct vimc_cap_device, vdev); + container_of(ved, struct vimc_cap_device, ved); media_entity_cleanup(vcap->ved.ent); kfree(vcap); } -void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) +void vimc_cap_unregister(struct vimc_ent_device *ved) { - struct vimc_cap_device *vcap; + struct vimc_cap_device *vcap = + container_of(ved, struct vimc_cap_device, ved); - vcap = container_of(ved, struct vimc_cap_device, ved); vb2_queue_release(&vcap->queue); video_unregister_device(&vcap->vdev); } @@ -423,7 +423,7 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, ret = vb2_queue_init(q); if (ret) { - dev_err(&vimc->pdev.dev, "%s: vb2 queue init failed (err=%d)\n", + dev_err(vimc->mdev.dev, "%s: vb2 queue init failed (err=%d)\n", vcfg_name, ret); goto err_clean_m_ent; } @@ -443,13 +443,13 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, vcap->ved.ent = &vcap->vdev.entity; vcap->ved.process_frame = vimc_cap_process_frame; vcap->ved.vdev_get_format = vimc_cap_get_format; - vcap->ved.dev = &vimc->pdev.dev; + vcap->ved.dev = vimc->mdev.dev; /* Initialize the video_device struct */ vdev = &vcap->vdev; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; vdev->entity.ops = &vimc_cap_mops; - vdev->release = vimc_cap_release; + vdev->release = video_device_release_empty; vdev->fops = &vimc_cap_fops; vdev->ioctl_ops = &vimc_cap_ioctl_ops; vdev->lock = &vcap->lock; @@ -460,9 +460,9 @@ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, video_set_drvdata(vdev, &vcap->ved); /* Register the video_device with the v4l2 and the media framework */ - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { - dev_err(&vimc->pdev.dev, "%s: video register failed (err=%d)\n", + dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", vcap->vdev.name, ret); goto err_release_queue; } diff --git a/drivers/media/platform/vimc/vimc-common.c b/drivers/media/platform/vimc/vimc-common.c index 16ce9f3b7c75..c95c17c048f2 100644 --- a/drivers/media/platform/vimc/vimc-common.c +++ b/drivers/media/platform/vimc/vimc-common.c @@ -327,7 +327,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, - const struct v4l2_subdev_internal_ops *sd_int_ops, const struct v4l2_subdev_ops *sd_ops) { int ret; @@ -337,7 +336,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, /* Initialize the subdev */ v4l2_subdev_init(sd, sd_ops); - sd->internal_ops = sd_int_ops; sd->entity.function = function; sd->entity.ops = &vimc_ent_sd_mops; sd->owner = THIS_MODULE; diff --git a/drivers/media/platform/vimc/vimc-common.h b/drivers/media/platform/vimc/vimc-common.h index 87eb8259c2a8..616d5a6b0754 100644 --- a/drivers/media/platform/vimc/vimc-common.h +++ b/drivers/media/platform/vimc/vimc-common.h @@ -106,14 +106,12 @@ struct vimc_ent_device { /** * struct vimc_device - main device for vimc driver * - * @pdev pointer to the platform device * @pipe_cfg pointer to the vimc pipeline configuration structure * @ent_devs array of vimc_ent_device pointers * @mdev the associated media_device parent * @v4l2_dev Internal v4l2 parent device */ struct vimc_device { - struct platform_device pdev; const struct vimc_pipeline_config *pipe_cfg; struct vimc_ent_device **ent_devs; struct media_device mdev; @@ -127,16 +125,18 @@ struct vimc_device { * @name entity name * @ved pointer to vimc_ent_device (a node in the * topology) - * @add subdev add hook - initializes and registers - * subdev called from vimc-core - * @rm subdev rm hook - unregisters and frees - * subdev called from vimc-core + * @add initializes and registers + * vim entity - called from vimc-core + * @unregister unregisters vimc entity - called from vimc-core + * @release releases vimc entity - called from the v4l2_dev + * release callback */ struct vimc_ent_config { const char *name; struct vimc_ent_device *(*add)(struct vimc_device *vimc, const char *vcfg_name); - void (*rm)(struct vimc_device *vimc, struct vimc_ent_device *ved); + void (*unregister)(struct vimc_ent_device *ved); + void (*release)(struct vimc_ent_device *ved); }; /** @@ -147,22 +147,23 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -/* prototypes for vimc_ent_config add and rm hooks */ +/* prototypes for vimc_ent_config hooks */ struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, const char *vcfg_name); -void vimc_cap_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); +void vimc_cap_unregister(struct vimc_ent_device *ved); +void vimc_cap_release(struct vimc_ent_device *ved); struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, const char *vcfg_name); -void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); +void vimc_deb_release(struct vimc_ent_device *ved); struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, const char *vcfg_name); -void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); +void vimc_sca_release(struct vimc_ent_device *ved); struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, const char *vcfg_name); -void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved); +void vimc_sen_release(struct vimc_ent_device *ved); /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index @@ -197,7 +198,6 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @num_pads: number of pads to initialize * @pads: the array of pads of the entity, the caller should set the flags of the pads - * @sd_int_ops: pointer to &struct v4l2_subdev_internal_ops * @sd_ops: pointer to &struct v4l2_subdev_ops. * * Helper function initialize and register the struct vimc_ent_device and struct @@ -210,7 +210,6 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, - const struct v4l2_subdev_internal_ops *sd_int_ops, const struct v4l2_subdev_ops *sd_ops); /** diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 97a272f3350a..339126e565dc 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -48,48 +48,51 @@ static struct vimc_ent_config ent_config[] = { { .name = "Sensor A", .add = vimc_sen_add, - .rm = vimc_sen_rm, + .release = vimc_sen_release, }, { .name = "Sensor B", .add = vimc_sen_add, - .rm = vimc_sen_rm, + .release = vimc_sen_release, }, { .name = "Debayer A", .add = vimc_deb_add, - .rm = vimc_deb_rm, + .release = vimc_deb_release, }, { .name = "Debayer B", .add = vimc_deb_add, - .rm = vimc_deb_rm, + .release = vimc_deb_release, }, { .name = "Raw Capture 0", .add = vimc_cap_add, - .rm = vimc_cap_rm, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, }, { .name = "Raw Capture 1", .add = vimc_cap_add, - .rm = vimc_cap_rm, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, }, { /* TODO: change this to vimc-input when it is implemented */ .name = "RGB/YUV Input", .add = vimc_sen_add, - .rm = vimc_sen_rm, + .release = vimc_sen_release, }, { .name = "Scaler", .add = vimc_sca_add, - .rm = vimc_sca_rm, + .release = vimc_sca_release, }, { .name = "RGB/YUV Capture", .add = vimc_cap_add, - .rm = vimc_cap_rm, + .unregister = vimc_cap_unregister, + .release = vimc_cap_release, }, }; @@ -162,12 +165,12 @@ static int vimc_add_subdevs(struct vimc_device *vimc) unsigned int i; for (i = 0; i < vimc->pipe_cfg->num_ents; i++) { - dev_dbg(&vimc->pdev.dev, "new entity for %s\n", + dev_dbg(vimc->mdev.dev, "new entity for %s\n", vimc->pipe_cfg->ents[i].name); vimc->ent_devs[i] = vimc->pipe_cfg->ents[i].add(vimc, vimc->pipe_cfg->ents[i].name); if (!vimc->ent_devs[i]) { - dev_err(&vimc->pdev.dev, "add new entity for %s\n", + dev_err(vimc->mdev.dev, "add new entity for %s\n", vimc->pipe_cfg->ents[i].name); return -EINVAL; } @@ -175,13 +178,33 @@ static int vimc_add_subdevs(struct vimc_device *vimc) return 0; } -static void vimc_rm_subdevs(struct vimc_device *vimc) +static void vimc_release_subdevs(struct vimc_device *vimc) { unsigned int i; for (i = 0; i < vimc->pipe_cfg->num_ents; i++) if (vimc->ent_devs[i]) - vimc->pipe_cfg->ents[i].rm(vimc, vimc->ent_devs[i]); + vimc->pipe_cfg->ents[i].release(vimc->ent_devs[i]); +} + +static void vimc_unregister_subdevs(struct vimc_device *vimc) +{ + unsigned int i; + + for (i = 0; i < vimc->pipe_cfg->num_ents; i++) + if (vimc->ent_devs[i] && vimc->pipe_cfg->ents[i].unregister) + vimc->pipe_cfg->ents[i].unregister(vimc->ent_devs[i]); +} + +static void vimc_v4l2_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vimc_device *vimc = + container_of(v4l2_dev, struct vimc_device, v4l2_dev); + + vimc_release_subdevs(vimc); + media_device_cleanup(&vimc->mdev); + kfree(vimc->ent_devs); + kfree(vimc); } static int vimc_register_devices(struct vimc_device *vimc) @@ -195,7 +218,6 @@ static int vimc_register_devices(struct vimc_device *vimc) "v4l2 device register failed (err=%d)\n", ret); return ret; } - /* allocate ent_devs */ vimc->ent_devs = kcalloc(vimc->pipe_cfg->num_ents, sizeof(*vimc->ent_devs), GFP_KERNEL); @@ -236,9 +258,9 @@ static int vimc_register_devices(struct vimc_device *vimc) err_mdev_unregister: media_device_unregister(&vimc->mdev); - media_device_cleanup(&vimc->mdev); err_rm_subdevs: - vimc_rm_subdevs(vimc); + vimc_unregister_subdevs(vimc); + vimc_release_subdevs(vimc); kfree(vimc->ent_devs); err_v4l2_unregister: v4l2_device_unregister(&vimc->v4l2_dev); @@ -248,20 +270,23 @@ err_v4l2_unregister: static void vimc_unregister(struct vimc_device *vimc) { + vimc_unregister_subdevs(vimc); media_device_unregister(&vimc->mdev); - media_device_cleanup(&vimc->mdev); v4l2_device_unregister(&vimc->v4l2_dev); - kfree(vimc->ent_devs); } static int vimc_probe(struct platform_device *pdev) { - struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev); + struct vimc_device *vimc; int ret; dev_dbg(&pdev->dev, "probe"); - memset(&vimc->mdev, 0, sizeof(vimc->mdev)); + vimc = kzalloc(sizeof(*vimc), GFP_KERNEL); + if (!vimc) + return -ENOMEM; + + vimc->pipe_cfg = &pipe_cfg; /* Link the media device within the v4l2_device */ vimc->v4l2_dev.mdev = &vimc->mdev; @@ -277,20 +302,27 @@ static int vimc_probe(struct platform_device *pdev) ret = vimc_register_devices(vimc); if (ret) { media_device_cleanup(&vimc->mdev); + kfree(vimc); return ret; } + /* + * the release cb is set only after successful registration. + * if the registration fails, we release directly from probe + */ + vimc->v4l2_dev.release = vimc_v4l2_dev_release; + platform_set_drvdata(pdev, vimc); return 0; } static int vimc_remove(struct platform_device *pdev) { - struct vimc_device *vimc = container_of(pdev, struct vimc_device, pdev); + struct vimc_device *vimc = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "remove"); - vimc_rm_subdevs(vimc); vimc_unregister(vimc); + v4l2_device_put(&vimc->v4l2_dev); return 0; } @@ -299,12 +331,9 @@ static void vimc_dev_release(struct device *dev) { } -static struct vimc_device vimc_dev = { - .pipe_cfg = &pipe_cfg, - .pdev = { - .name = VIMC_PDEV_NAME, - .dev.release = vimc_dev_release, - } +static struct platform_device vimc_pdev = { + .name = VIMC_PDEV_NAME, + .dev.release = vimc_dev_release, }; static struct platform_driver vimc_pdrv = { @@ -319,16 +348,16 @@ static int __init vimc_init(void) { int ret; - ret = platform_device_register(&vimc_dev.pdev); + ret = platform_device_register(&vimc_pdev); if (ret) { - dev_err(&vimc_dev.pdev.dev, + dev_err(&vimc_pdev.dev, "platform device registration failed (err=%d)\n", ret); return ret; } ret = platform_driver_register(&vimc_pdrv); if (ret) { - dev_err(&vimc_dev.pdev.dev, + dev_err(&vimc_pdev.dev, "platform driver registration failed (err=%d)\n", ret); platform_driver_unregister(&vimc_pdrv); return ret; @@ -341,7 +370,7 @@ static void __exit vimc_exit(void) { platform_driver_unregister(&vimc_pdrv); - platform_device_unregister(&vimc_dev.pdev); + platform_device_unregister(&vimc_pdev); } module_init(vimc_init); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 5d1b67d684bb..baf6bf9f65b5 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -494,28 +494,16 @@ static const struct v4l2_ctrl_ops vimc_deb_ctrl_ops = { .s_ctrl = vimc_deb_s_ctrl, }; -static void vimc_deb_release(struct v4l2_subdev *sd) +void vimc_deb_release(struct vimc_ent_device *ved) { struct vimc_deb_device *vdeb = - container_of(sd, struct vimc_deb_device, sd); + container_of(ved, struct vimc_deb_device, ved); v4l2_ctrl_handler_free(&vdeb->hdl); media_entity_cleanup(vdeb->ved.ent); kfree(vdeb); } -static const struct v4l2_subdev_internal_ops vimc_deb_int_ops = { - .release = vimc_deb_release, -}; - -void vimc_deb_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) -{ - struct vimc_deb_device *vdeb; - - vdeb = container_of(ved, struct vimc_deb_device, ved); - v4l2_device_unregister_subdev(&vdeb->sd); -} - static const struct v4l2_ctrl_config vimc_deb_ctrl_class = { .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, .id = VIMC_CID_VIMC_CLASS, @@ -563,13 +551,12 @@ struct vimc_ent_device *vimc_deb_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vdeb->ved, &vdeb->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdeb->pads, - &vimc_deb_int_ops, &vimc_deb_ops); + vdeb->pads, &vimc_deb_ops); if (ret) goto err_free_hdl; vdeb->ved.process_frame = vimc_deb_process_frame; - vdeb->ved.dev = &vimc->pdev.dev; + vdeb->ved.dev = vimc->mdev.dev; vdeb->mean_win_size = vimc_deb_ctrl_mean_win_size.def; /* Initialize the frame format */ diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index e2e551bc3ded..7521439747c5 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -464,27 +464,15 @@ static void *vimc_sca_process_frame(struct vimc_ent_device *ved, return vsca->src_frame; }; -static void vimc_sca_release(struct v4l2_subdev *sd) +void vimc_sca_release(struct vimc_ent_device *ved) { struct vimc_sca_device *vsca = - container_of(sd, struct vimc_sca_device, sd); + container_of(ved, struct vimc_sca_device, ved); media_entity_cleanup(vsca->ved.ent); kfree(vsca); } -static const struct v4l2_subdev_internal_ops vimc_sca_int_ops = { - .release = vimc_sca_release, -}; - -void vimc_sca_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) -{ - struct vimc_sca_device *vsca; - - vsca = container_of(ved, struct vimc_sca_device, ved); - v4l2_device_unregister_subdev(&vsca->sd); -} - struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, const char *vcfg_name) { @@ -504,15 +492,14 @@ struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vsca->pads, - &vimc_sca_int_ops, &vimc_sca_ops); + vsca->pads, &vimc_sca_ops); if (ret) { kfree(vsca); return NULL; } vsca->ved.process_frame = vimc_sca_process_frame; - vsca->ved.dev = &vimc->pdev.dev; + vsca->ved.dev = vimc->mdev.dev; /* Initialize the frame format */ vsca->sink_fmt = sink_fmt_default; diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 32380f504591..92daee58209e 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -279,10 +279,10 @@ static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = { .s_ctrl = vimc_sen_s_ctrl, }; -static void vimc_sen_release(struct v4l2_subdev *sd) +void vimc_sen_release(struct vimc_ent_device *ved) { struct vimc_sen_device *vsen = - container_of(sd, struct vimc_sen_device, sd); + container_of(ved, struct vimc_sen_device, ved); v4l2_ctrl_handler_free(&vsen->hdl); tpg_free(&vsen->tpg); @@ -290,18 +290,6 @@ static void vimc_sen_release(struct v4l2_subdev *sd) kfree(vsen); } -static const struct v4l2_subdev_internal_ops vimc_sen_int_ops = { - .release = vimc_sen_release, -}; - -void vimc_sen_rm(struct vimc_device *vimc, struct vimc_ent_device *ved) -{ - struct vimc_sen_device *vsen; - - vsen = container_of(ved, struct vimc_sen_device, ved); - v4l2_device_unregister_subdev(&vsen->sd); -} - /* Image Processing Controls */ static const struct v4l2_ctrl_config vimc_sen_ctrl_class = { .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, @@ -365,12 +353,12 @@ struct vimc_ent_device *vimc_sen_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_CAM_SENSOR, 1, &vsen->pad, - &vimc_sen_int_ops, &vimc_sen_ops); + &vimc_sen_ops); if (ret) goto err_free_tpg; vsen->ved.process_frame = vimc_sen_process_frame; - vsen->ved.dev = &vimc->pdev.dev; + vsen->ved.dev = vimc->mdev.dev; /* Initialize the frame format */ vsen->mbus_format = fmt_default; diff --git a/drivers/media/platform/vimc/vimc-streamer.c b/drivers/media/platform/vimc/vimc-streamer.c index cd6b55433c9e..65feb3c596db 100644 --- a/drivers/media/platform/vimc/vimc-streamer.c +++ b/drivers/media/platform/vimc/vimc-streamer.c @@ -207,16 +207,27 @@ int vimc_streamer_s_stream(struct vimc_stream *stream, stream->kthread = kthread_run(vimc_streamer_thread, stream, "vimc-streamer thread"); - if (IS_ERR(stream->kthread)) - return PTR_ERR(stream->kthread); + if (IS_ERR(stream->kthread)) { + ret = PTR_ERR(stream->kthread); + dev_err(ved->dev, "kthread_run failed with %d\n", ret); + vimc_streamer_pipeline_terminate(stream); + stream->kthread = NULL; + return ret; + } } else { if (!stream->kthread) return 0; ret = kthread_stop(stream->kthread); + /* + * kthread_stop returns -EINTR in cases when streamon was + * immediately followed by streamoff, and the thread didn't had + * a chance to run. Ignore errors to stop the stream in the + * pipeline. + */ if (ret) - return ret; + dev_dbg(ved->dev, "kthread_stop returned '%d'\n", ret); stream->kthread = NULL; diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 15091cbf6de7..6c740e3e6999 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -407,7 +407,7 @@ static int vidioc_log_status(struct file *file, void *fh) struct video_device *vdev = video_devdata(file); v4l2_ctrl_log_status(file, fh); - if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_dir == VFL_DIR_RX && vdev->vfl_type == VFL_TYPE_VIDEO) tpg_log_status(&dev->tpg); return 0; } @@ -1525,7 +1525,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_cap_nr[inst]); if (ret < 0) goto unreg_dev; v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", @@ -1571,14 +1571,14 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); - if (i <= out_type_counter[HDMI]) - cec_s_phys_addr(dev->cec_tx_adap[i], i << 12, false); + if (i < out_type_counter[HDMI]) + cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false); else cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); } #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_out_nr[inst]); if (ret < 0) goto unreg_dev; v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n", @@ -1734,7 +1734,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) goto unreg_dev; #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, + ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_cap_nr[inst]); if (ret < 0) goto unreg_dev; @@ -1764,7 +1764,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) goto unreg_dev; #endif - ret = video_register_device(vfd, VFL_TYPE_GRABBER, + ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_out_nr[inst]); if (ret < 0) goto unreg_dev; diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c index 30d751f2cccf..a91e142bcb94 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.c +++ b/drivers/media/platform/vsp1/vsp1_histo.c @@ -551,7 +551,7 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, histo->video.fops = &histo_v4l2_fops; snprintf(histo->video.name, sizeof(histo->video.name), "%s histo", histo->entity.subdev.name); - histo->video.vfl_type = VFL_TYPE_GRABBER; + histo->video.vfl_type = VFL_TYPE_VIDEO; histo->video.release = video_device_release_empty; histo->video.ioctl_ops = &histo_v4l2_ioctl_ops; histo->video.device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; @@ -576,7 +576,7 @@ int vsp1_histogram_init(struct vsp1_device *vsp1, struct vsp1_histogram *histo, /* ... and register the video device. */ histo->video.queue = &histo->queue; - ret = video_register_device(&histo->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&histo->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(vsp1->dev, "failed to register video device\n"); goto error; diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 5c67ff92d97a..fe3130db1fa2 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -706,7 +706,7 @@ #define VI6_HGT_HUE_AREA_LOWER_SHIFT 16 #define VI6_HGT_HUE_AREA_UPPER_SHIFT 0 #define VI6_HGT_LB_TH 0x3424 -#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8) +#define VI6_HGT_LBn_H(n) (0x3428 + (n) * 8) #define VI6_HGT_LBn_V(n) (0x342c + (n) * 8) #define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4) #define VI6_HGT_MAXMIN 0x3750 diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 5e59ed2c3614..044eb5778820 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1293,7 +1293,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &vsp1_video_ioctl_ops; @@ -1316,7 +1316,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and register the video device. */ video->video.queue = &video->queue; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(video->vsp1->dev, "failed to register video device\n"); goto error; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index b211380a11f2..2a56201cb853 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -685,7 +685,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, xdev->dev->of_node, type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "output" : "input", port); - dma->video.vfl_type = VFL_TYPE_GRABBER; + dma->video.vfl_type = VFL_TYPE_VIDEO; dma->video.vfl_dir = type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? VFL_DIR_RX : VFL_DIR_TX; dma->video.release = video_device_release_empty; @@ -725,16 +725,17 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, /* ... and the DMA channel. */ snprintf(name, sizeof(name), "port%u", port); - dma->dma = dma_request_slave_channel(dma->xdev->dev, name); - if (dma->dma == NULL) { - dev_err(dma->xdev->dev, "no VDMA channel found\n"); - ret = -ENODEV; + dma->dma = dma_request_chan(dma->xdev->dev, name); + if (IS_ERR(dma->dma)) { + ret = PTR_ERR(dma->dma); + if (ret != -EPROBE_DEFER) + dev_err(dma->xdev->dev, "no VDMA channel found\n"); goto error; } dma->align = 1 << dma->dma->device->copy_align; - ret = video_register_device(&dma->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&dma->video, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(dma->xdev->dev, "failed to register video device\n"); goto error; @@ -752,7 +753,7 @@ void xvip_dma_cleanup(struct xvip_dma *dma) if (video_is_registered(&dma->video)) video_unregister_device(&dma->video); - if (dma->dma) + if (!IS_ERR_OR_NULL(dma->dma)) dma_release_channel(dma->dma); media_entity_cleanup(&dma->video.entity); diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig index 537f8e1601f3..a1ba8bc54b62 100644 --- a/drivers/media/radio/si470x/Kconfig +++ b/drivers/media/radio/si470x/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config RADIO_SI470X - tristate "Silicon Labs Si470x FM Radio Receiver support" - depends on VIDEO_V4L2 + tristate "Silicon Labs Si470x FM Radio Receiver support" + depends on VIDEO_V4L2 help This is a driver for devices with the Silicon Labs SI470x chip (either via USB or I2C buses). diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c index 0a0ce620e4a2..0f3417d161b8 100644 --- a/drivers/media/rc/bpf-lirc.c +++ b/drivers/media/rc/bpf-lirc.c @@ -35,11 +35,6 @@ static const struct bpf_func_proto rc_repeat_proto = { .arg1_type = ARG_PTR_TO_CTX, }; -/* - * Currently rc-core does not support 64-bit scancodes, but there are many - * known protocols with more than 32 bits. So, define the interface as u64 - * as a future-proof. - */ BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode, u32, toggle) { diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index a7deca1fefb7..3c8bd13d029a 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -76,7 +76,7 @@ struct send_packet { uint8_t channels; uint8_t busy7; uint8_t busy4; - uint8_t payload[0]; + uint8_t payload[]; }; static void process_ir_data(struct iguanair *ir, unsigned len) diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c index 74a1d30fae6e..4c3d03876200 100644 --- a/drivers/media/rc/ir-xmp-decoder.c +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -166,7 +166,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) { /* store nibble raw data, decode after trailer */ if (data->count == 16) { - dev_dbg(&dev->dev, "to many pulses (%d) ignoring: %u\n", + dev_dbg(&dev->dev, "too many pulses (%d) ignoring: %u\n", data->count, ev.duration); data->state = STATE_INACTIVE; return -EINVAL; diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 63261ef6380a..aaa1bf81d00d 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ + rc-videostrong-kii-pro.o \ rc-wetek-hub.o \ rc-wetek-play2.o \ rc-winfast.o \ diff --git a/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c new file mode 100644 index 000000000000..414d4d231e7e --- /dev/null +++ b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Mohammad Rasim <mohammad.rasim96@gmail.com> + +#include <media/rc-map.h> +#include <linux/module.h> + +// +// Keytable for the Videostrong KII Pro STB remote control +// + +static struct rc_map_table kii_pro[] = { + { 0x59, KEY_POWER }, + { 0x19, KEY_MUTE }, + { 0x42, KEY_RED }, + { 0x40, KEY_GREEN }, + { 0x00, KEY_YELLOW }, + { 0x03, KEY_BLUE }, + { 0x4a, KEY_BACK }, + { 0x48, KEY_FORWARD }, + { 0x08, KEY_PREVIOUSSONG}, + { 0x0b, KEY_NEXTSONG}, + { 0x46, KEY_PLAYPAUSE }, + { 0x44, KEY_STOP }, + { 0x1f, KEY_FAVORITES}, //KEY_F5? + { 0x04, KEY_PVR }, + { 0x4d, KEY_EPG }, + { 0x02, KEY_INFO }, + { 0x09, KEY_SUBTITLE }, + { 0x01, KEY_AUDIO }, + { 0x0d, KEY_HOMEPAGE }, + { 0x11, KEY_TV }, // DTV ? + { 0x06, KEY_UP }, + { 0x5a, KEY_LEFT }, + { 0x1a, KEY_ENTER }, // KEY_OK ? + { 0x1b, KEY_RIGHT }, + { 0x16, KEY_DOWN }, + { 0x45, KEY_MENU }, + { 0x05, KEY_ESC }, + { 0x13, KEY_VOLUMEUP }, + { 0x17, KEY_VOLUMEDOWN }, + { 0x58, KEY_APPSELECT }, + { 0x12, KEY_VENDOR }, // mouse + { 0x55, KEY_PAGEUP }, // KEY_CHANNELUP ? + { 0x15, KEY_PAGEDOWN }, // KEY_CHANNELDOWN ? + { 0x52, KEY_1 }, + { 0x50, KEY_2 }, + { 0x10, KEY_3 }, + { 0x56, KEY_4 }, + { 0x54, KEY_5 }, + { 0x14, KEY_6 }, + { 0x4e, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x0c, KEY_9 }, + { 0x18, KEY_WWW }, // KEY_F7 + { 0x0f, KEY_0 }, + { 0x51, KEY_BACKSPACE }, +}; + +static struct rc_map_list kii_pro_map = { + .map = { + .scan = kii_pro, + .size = ARRAY_SIZE(kii_pro), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_KII_PRO, + } +}; + +static int __init init_rc_map_kii_pro(void) +{ + return rc_map_register(&kii_pro_map); +} + +static void __exit exit_rc_map_kii_pro(void) +{ + rc_map_unregister(&kii_pro_map); +} + +module_init(init_rc_map_kii_pro) +module_exit(exit_rc_map_kii_pro) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mohammad Rasim <mohammad.rasim96@gmail.com>"); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 9a8c1cf54ac4..583e4f32a0da 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -269,12 +269,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, goto out_unlock; } - /* - * The scancode field in lirc_scancode is 64-bit simply - * to future-proof it, since there are IR protocols encode - * use more than 32 bits. For now only 32-bit protocols - * are supported. - */ + /* We only have encoders for 32-bit protocols. */ if (scan.scancode > U32_MAX || !rc_validate_scancode(scan.rc_proto, scan.scancode)) { ret = -EINVAL; diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 5c2cd8d2d155..48a69bf23236 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -230,10 +230,10 @@ static ssize_t wakeup_data_show(struct device *dev, for (i = 0; i < fifo_len; i++) { duration = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); duration = (duration & BUF_LEN_MASK) * SAMPLE_PERIOD; - buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, + buf_len += scnprintf(buf + buf_len, PAGE_SIZE - buf_len, "%d ", duration); } - buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n"); + buf_len += scnprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n"); spin_unlock_irqrestore(&nvt->lock, flags); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 6f80c251f641..d7064d664d52 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -164,6 +164,41 @@ static struct rc_map_list empty_map = { }; /** + * scancode_to_u64() - converts scancode in &struct input_keymap_entry + * @ke: keymap entry containing scancode to be converted. + * @scancode: pointer to the location where converted scancode should + * be stored. + * + * This function is a version of input_scancode_to_scalar specialized for + * rc-core. + */ +static int scancode_to_u64(const struct input_keymap_entry *ke, u64 *scancode) +{ + switch (ke->len) { + case 1: + *scancode = *((u8 *)ke->scancode); + break; + + case 2: + *scancode = *((u16 *)ke->scancode); + break; + + case 4: + *scancode = *((u32 *)ke->scancode); + break; + + case 8: + *scancode = *((u64 *)ke->scancode); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/** * ir_create_table() - initializes a scancode table * @dev: the rc_dev device * @rc_map: the rc_map to initialize @@ -285,13 +320,13 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, /* Did the user wish to remove the mapping? */ if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { - dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04x\n", + dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04llx\n", index, rc_map->scan[index].scancode); rc_map->len--; memmove(&rc_map->scan[index], &rc_map->scan[index+ 1], (rc_map->len - index) * sizeof(struct rc_map_table)); } else { - dev_dbg(&dev->dev, "#%d: %s scan 0x%04x with key 0x%04x\n", + dev_dbg(&dev->dev, "#%d: %s scan 0x%04llx with key 0x%04x\n", index, old_keycode == KEY_RESERVED ? "New" : "Replacing", rc_map->scan[index].scancode, new_keycode); @@ -334,8 +369,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, */ static unsigned int ir_establish_scancode(struct rc_dev *dev, struct rc_map *rc_map, - unsigned int scancode, - bool resize) + u64 scancode, bool resize) { unsigned int i; @@ -394,7 +428,7 @@ static int ir_setkeycode(struct input_dev *idev, struct rc_dev *rdev = input_get_drvdata(idev); struct rc_map *rc_map = &rdev->rc_map; unsigned int index; - unsigned int scancode; + u64 scancode; int retval = 0; unsigned long flags; @@ -407,7 +441,7 @@ static int ir_setkeycode(struct input_dev *idev, goto out; } } else { - retval = input_scancode_to_scalar(ke, &scancode); + retval = scancode_to_u64(ke, &scancode); if (retval) goto out; @@ -434,8 +468,7 @@ out: * * return: -ENOMEM if all keycodes could not be inserted, otherwise zero. */ -static int ir_setkeytable(struct rc_dev *dev, - const struct rc_map *from) +static int ir_setkeytable(struct rc_dev *dev, const struct rc_map *from) { struct rc_map *rc_map = &dev->rc_map; unsigned int i, index; @@ -466,7 +499,7 @@ static int ir_setkeytable(struct rc_dev *dev, static int rc_map_cmp(const void *key, const void *elt) { - const unsigned int *scancode = key; + const u64 *scancode = key; const struct rc_map_table *e = elt; if (*scancode < e->scancode) @@ -487,7 +520,7 @@ static int rc_map_cmp(const void *key, const void *elt) * return: index in the table, -1U if not found */ static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map, - unsigned int scancode) + u64 scancode) { struct rc_map_table *res; @@ -516,7 +549,7 @@ static int ir_getkeycode(struct input_dev *idev, struct rc_map_table *entry; unsigned long flags; unsigned int index; - unsigned int scancode; + u64 scancode; int retval; spin_lock_irqsave(&rc_map->lock, flags); @@ -524,7 +557,7 @@ static int ir_getkeycode(struct input_dev *idev, if (ke->flags & INPUT_KEYMAP_BY_INDEX) { index = ke->index; } else { - retval = input_scancode_to_scalar(ke, &scancode); + retval = scancode_to_u64(ke, &scancode); if (retval) goto out; @@ -538,7 +571,6 @@ static int ir_getkeycode(struct input_dev *idev, ke->keycode = entry->keycode; ke->len = sizeof(entry->scancode); memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode)); - } else if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) { /* * We do not really know the valid range of scancodes @@ -570,7 +602,7 @@ out: * * return: the corresponding keycode, or KEY_RESERVED */ -u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode) +u32 rc_g_keycode_from_table(struct rc_dev *dev, u64 scancode) { struct rc_map *rc_map = &dev->rc_map; unsigned int keycode; @@ -586,7 +618,7 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode) spin_unlock_irqrestore(&rc_map->lock, flags); if (keycode != KEY_RESERVED) - dev_dbg(&dev->dev, "%s: scancode 0x%04x keycode 0x%02x\n", + dev_dbg(&dev->dev, "%s: scancode 0x%04llx keycode 0x%02x\n", dev->device_name, scancode, keycode); return keycode; @@ -719,8 +751,11 @@ void rc_repeat(struct rc_dev *dev) spin_lock_irqsave(&dev->keylock, flags); - input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode); - input_sync(dev->input_dev); + if (dev->last_scancode <= U32_MAX) { + input_event(dev->input_dev, EV_MSC, MSC_SCAN, + dev->last_scancode); + input_sync(dev->input_dev); + } if (dev->keypressed) { dev->keyup_jiffies = jiffies + timeout; @@ -743,7 +778,7 @@ EXPORT_SYMBOL_GPL(rc_repeat); * called with keylock held. */ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u32 keycode, u8 toggle) + u64 scancode, u32 keycode, u8 toggle) { bool new_event = (!dev->keypressed || dev->last_protocol != protocol || @@ -761,7 +796,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, if (new_event && dev->keypressed) ir_do_keyup(dev, false); - input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); + if (scancode <= U32_MAX) + input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); dev->last_protocol = protocol; dev->last_scancode = scancode; @@ -772,7 +808,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, /* Register a keypress */ dev->keypressed = true; - dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", + dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08llx\n", dev->device_name, keycode, protocol, scancode); input_report_key(dev->input_dev, keycode, 1); @@ -809,7 +845,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, * This routine is used to signal that a key has been pressed on the * remote control. */ -void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode, +void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode, u8 toggle) { unsigned long flags; @@ -840,7 +876,7 @@ EXPORT_SYMBOL_GPL(rc_keydown); * remote control. The driver must manually call rc_keyup() at a later stage. */ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u8 toggle) + u64 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c index d789d82df7c4..f86ef1ca1288 100644 --- a/drivers/media/spi/gs1662.c +++ b/drivers/media/spi/gs1662.c @@ -147,11 +147,17 @@ static int gs_read_register(struct spi_device *spi, u16 addr, u16 *value) { .tx_buf = &buf_addr, .len = 2, - .delay_usecs = 1, + .delay = { + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, }, { .rx_buf = &buf_value, .len = 2, - .delay_usecs = 1, + .delay = { + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, }, }; @@ -175,11 +181,17 @@ static int gs_write_register(struct spi_device *spi, u16 addr, u16 value) { .tx_buf = &buf_addr, .len = 2, - .delay_usecs = 1, + .delay = { + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, }, { .tx_buf = &buf_value, .len = 2, - .delay_usecs = 1, + .delay = { + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, }, }; diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 03c2944f6273..e678d3d11467 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -25,7 +25,6 @@ if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" -source "drivers/media/usb/usbvision/Kconfig" source "drivers/media/usb/stk1160/Kconfig" source "drivers/media/usb/go7007/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 21e46b10caa5..169aa07c97bd 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ -obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_STK1160) += stk1160/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index d1895334cbbf..51b8d14fb4dc 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1042,7 +1042,7 @@ static int au0828_v4l2_close(struct file *filp) dev->streaming_users, dev->users); mutex_lock(&dev->lock); - if (vdev->vfl_type == VFL_TYPE_GRABBER && dev->vid_timeout_running) { + if (vdev->vfl_type == VFL_TYPE_VIDEO && dev->vid_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */ dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); @@ -2007,7 +2007,7 @@ int au0828_analog_register(struct au0828_dev *dev, /* Register the v4l2 device */ video_set_drvdata(&dev->vdev, dev); - retval = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + retval = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 039963a7765b..198ddfb8d2b1 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -511,6 +511,9 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb) return ret; } + if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1) + return -ENODEV; + switch (fc_usb->udev->speed) { case USB_SPEED_LOW: err("cannot handle USB speed because it is too slow."); @@ -544,9 +547,6 @@ static int flexcop_usb_probe(struct usb_interface *intf, struct flexcop_device *fc = NULL; int ret; - if (intf->cur_altsetting->desc.bNumEndpoints < 1) - return -ENODEV; - if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { err("out of memory\n"); return -ENOMEM; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index 9d3d05125d7b..e488e7870f42 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -1134,7 +1134,7 @@ int cpia2_register_camera(struct camera_data *cam) reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&cam->vdev, VFL_TYPE_VIDEO, video_nr) < 0) { ERR("video_register_device failed\n"); return -ENODEV; } diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 1aec4459f50a..b0cd51134654 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1790,7 +1790,7 @@ int cx231xx_417_register(struct cx231xx *dev) dev->v4l_device.queue = q; err = video_register_device(&dev->v4l_device, - VFL_TYPE_GRABBER, -1); + VFL_TYPE_VIDEO, -1); if (err < 0) { dprintk(3, "%s: can't register mpeg device\n", dev->name); v4l2_ctrl_handler_free(&dev->mpeg_ctrl_handler.hdl); diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index e205f7f0a56a..0037b4b1381e 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -147,7 +147,7 @@ static struct tda18271_config pv_tda18271_config = { .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, }; -static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { +static const struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { .qam_if_khz = 4000, .vsb_if_khz = 3250, .spectral_inversion = 1, diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 69abafaebbf3..8bff7d8a0310 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1785,7 +1785,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->vdev.device_caps |= V4L2_CAP_TUNER; /* register v4l2 video video_device */ - ret = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); if (ret) { dev_err(dev->dev, diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 0514e87405b6..89a1b204b90c 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -318,14 +318,14 @@ static struct tda10023_config anysee_tda10023_tda18212_config = { .deltaf = 0xba02, }; -static struct tda18212_config anysee_tda18212_config = { +static const struct tda18212_config anysee_tda18212_config = { .if_dvbt_6 = 4150, .if_dvbt_7 = 4150, .if_dvbt_8 = 4150, .if_dvbc = 5000, }; -static struct tda18212_config anysee_tda18212_config2 = { +static const struct tda18212_config anysee_tda18212_config2 = { .if_dvbt_6 = 3550, .if_dvbt_7 = 3700, .if_dvbt_8 = 4150, diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 62d3566bf7ee..fd8b42bb9a84 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -486,13 +486,10 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], static u8 obuf[64], ibuf[64]; int i, read, read_o; u16 len; - u8 gate = st->i2c_gate; + u8 gate; mutex_lock(&d->i2c_mutex); - if (gate == 0) - gate = 5; - for (i = 0; i < num; i++) { read_o = msg[i].flags & I2C_M_RD; read = i + 1 < num && msg[i + 1].flags & I2C_M_RD; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c6881a1b3232..2080f6ef4be1 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -552,6 +552,9 @@ tuner_found: if (ret) goto err; + /* slave demod needs some time to wake up */ + msleep(20); + /* check slave answers */ ret = rtl28xxu_ctrl_msg(d, &req_mn88472); if (ret == 0 && buf[0] == 0x02) { diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c index 0699f718d052..001cae648797 100644 --- a/drivers/media/usb/dvb-usb/cxusb-analog.c +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -1223,7 +1223,7 @@ static int cxusb_medion_g_tuner(struct file *file, void *fh, if (tuner->index != 0) return -EINVAL; - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) tuner->type = V4L2_TUNER_ANALOG_TV; else tuner->type = V4L2_TUNER_RADIO; @@ -1259,7 +1259,7 @@ static int cxusb_medion_g_tuner(struct file *file, void *fh, if (ret != 0) return ret; - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); else strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); @@ -1292,7 +1292,7 @@ static int cxusb_medion_s_tuner(struct file *file, void *fh, * make sure that cx25840 is in a correct TV / radio mode, * since calls above may have changed it for tuner / IF demod */ - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); else v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); @@ -1335,7 +1335,7 @@ static int cxusb_medion_s_frequency(struct file *file, void *fh, * make sure that cx25840 is in a correct TV / radio mode, * since calls above may have changed it for tuner / IF demod */ - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); else v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); @@ -1564,7 +1564,7 @@ static int cxusb_videoradio_release(struct file *f) cxusb_vprintk(dvbdev, OPS, "got release\n"); - if (vdev->vfl_type == VFL_TYPE_GRABBER) + if (vdev->vfl_type == VFL_TYPE_VIDEO) ret = vb2_fop_release(f); else ret = v4l2_fh_release(f); @@ -1663,7 +1663,7 @@ static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) cxdev->videodev->lock = &cxdev->dev_lock; video_set_drvdata(cxdev->videodev, dvbdev); - ret = video_register_device(cxdev->videodev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(cxdev->videodev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&dvbdev->udev->dev, "video device register failed, ret = %d\n", ret); diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index e53c58ab6488..ef62dd6c5ae4 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -818,7 +818,7 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ - if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1) + if (intf->cur_altsetting->desc.bNumEndpoints < rc_ep + 1) return -ENODEV; purb = usb_alloc_urb(0, GFP_KERNEL); @@ -838,7 +838,7 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) * Some devices like the Hauppauge NovaTD model 52009 use an interrupt * endpoint, while others use a bulk one. */ - e = &intf->altsetting[0].endpoint[rc_ep].desc; + e = &intf->cur_altsetting->endpoint[rc_ep].desc; if (usb_endpoint_dir_in(e)) { if (usb_endpoint_xfer_bulk(e)) { pipe = usb_rcvbulkpipe(d->udev, rc_ep); diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 8b584507dd59..1007366a295b 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1524,6 +1524,29 @@ static int m88rs2000_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; } +static int tt_s2_4600_frontend_attach_probe_demod(struct dvb_usb_device *d, + const int probe_addr) +{ + struct dw2102_state *state = d->priv; + + state->data[0] = 0x9; + state->data[1] = 0x1; + state->data[2] = 0x1; + state->data[3] = probe_addr; + state->data[4] = 0x0; + + if (dvb_usb_generic_rw(d, state->data, 5, state->data, 2, 0) < 0) { + err("i2c probe for address 0x%x failed.", probe_addr); + return 0; + } + + if (state->data[0] != 8) /* fail(7) or error, no device at address */ + return 0; + + /* probing successful */ + return 1; +} + static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap->dev; @@ -1533,6 +1556,7 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) struct i2c_board_info board_info; struct m88ds3103_platform_data m88ds3103_pdata = {}; struct ts2020_config ts2020_config = {}; + int demod_addr; mutex_lock(&d->data_mutex); @@ -1570,8 +1594,22 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) if (dvb_usb_generic_rw(d, state->data, 1, state->data, 1, 0) < 0) err("command 0x51 transfer failed."); + /* probe for demodulator i2c address */ + demod_addr = -1; + if (tt_s2_4600_frontend_attach_probe_demod(d, 0x68)) + demod_addr = 0x68; + else if (tt_s2_4600_frontend_attach_probe_demod(d, 0x69)) + demod_addr = 0x69; + else if (tt_s2_4600_frontend_attach_probe_demod(d, 0x6a)) + demod_addr = 0x6a; + mutex_unlock(&d->data_mutex); + if (demod_addr < 0) { + err("probing for demodulator failed. Is the external power switched on?"); + return -ENODEV; + } + /* attach demod */ m88ds3103_pdata.clk = 27000000; m88ds3103_pdata.i2c_wr_max = 33; @@ -1586,8 +1624,11 @@ static int tt_s2_4600_frontend_attach(struct dvb_usb_adapter *adap) m88ds3103_pdata.lnb_hv_pol = 1; m88ds3103_pdata.lnb_en_pol = 0; memset(&board_info, 0, sizeof(board_info)); - strscpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); - board_info.addr = 0x68; + if (demod_addr == 0x6a) + strscpy(board_info.type, "m88ds3103b", I2C_NAME_SIZE); + else + strscpy(board_info.type, "m88ds3103", I2C_NAME_SIZE); + board_info.addr = demod_addr; board_info.platform_data = &m88ds3103_pdata; request_module("m88ds3103"); client = i2c_new_client_device(&d->i2c_adap, &board_info); diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index def9cdd931a9..a8c321d11827 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2398,6 +2398,20 @@ const struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_PINNACLE_PCTV_HD, }, /* + * 2013:0259 PCTV DVB-S2 Stick (461e_v2) + * Empia EM28178, Montage M88DS3103b, Montage M88TS2022, Allegro A8293 + */ + [EM28178_BOARD_PCTV_461E_V2] = { + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + .name = "PCTV DVB-S2 Stick (461e v2)", + .tuner_type = TUNER_ABSENT, + .tuner_gpio = pctv_461e, + .has_dvb = 1, + .ir_codes = RC_MAP_PINNACLE_PCTV_HD, + }, + /* * 2013:025f PCTV tripleStick (292e). * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2157 */ @@ -2696,6 +2710,10 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2765_BOARD_SPEEDLINK_VAD_LAPLACE }, { USB_DEVICE(0x2013, 0x0258), .driver_info = EM28178_BOARD_PCTV_461E }, + { USB_DEVICE(0x2013, 0x0461), + .driver_info = EM28178_BOARD_PCTV_461E_V2 }, + { USB_DEVICE(0x2013, 0x0259), + .driver_info = EM28178_BOARD_PCTV_461E_V2 }, { USB_DEVICE(0x2013, 0x025f), .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x2013, 0x0264), /* Hauppauge WinTV-soloHD 292e SE */ diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 0ab6c493bc74..fb9cbfa81a84 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1219,6 +1219,61 @@ static int em28178_dvb_init_pctv_461e(struct em28xx *dev) return 0; } +static int em28178_dvb_init_pctv_461e_v2(struct em28xx *dev) +{ + struct em28xx_dvb *dvb = dev->dvb; + struct i2c_adapter *i2c_adapter; + struct m88ds3103_platform_data m88ds3103_pdata = {}; + struct ts2020_config ts2020_config = {}; + struct a8293_platform_data a8293_pdata = {}; + + /* attach demod */ + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.ts_mode = M88DS3103_TS_PARALLEL; + m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk_pol = 0; + m88ds3103_pdata.agc = 0x99; + m88ds3103_pdata.agc_inv = 0; + m88ds3103_pdata.spec_inv = 0; + dvb->i2c_client_demod = dvb_module_probe("m88ds3103", "m88ds3103b", + &dev->i2c_adap[dev->def_i2c_bus], + 0x6a, &m88ds3103_pdata); + + if (!dvb->i2c_client_demod) + return -ENODEV; + + dvb->fe[0] = m88ds3103_pdata.get_dvb_frontend(dvb->i2c_client_demod); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(dvb->i2c_client_demod); + + /* attach tuner */ + ts2020_config.fe = dvb->fe[0]; + dvb->i2c_client_tuner = dvb_module_probe("ts2020", "ts2022", + i2c_adapter, + 0x60, &ts2020_config); + if (!dvb->i2c_client_tuner) { + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + /* delegate signal strength measurement to tuner */ + dvb->fe[0]->ops.read_signal_strength = + dvb->fe[0]->ops.tuner_ops.get_rf_strength; + + /* attach SEC */ + a8293_pdata.dvb_frontend = dvb->fe[0]; + dvb->i2c_client_sec = dvb_module_probe("a8293", NULL, + &dev->i2c_adap[dev->def_i2c_bus], + 0x08, &a8293_pdata); + if (!dvb->i2c_client_sec) { + dvb_module_release(dvb->i2c_client_tuner); + dvb_module_release(dvb->i2c_client_demod); + return -ENODEV; + } + + return 0; +} + static int em28178_dvb_init_pctv_292e(struct em28xx *dev) { struct em28xx_dvb *dvb = dev->dvb; @@ -1860,6 +1915,11 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result) goto out_free; break; + case EM28178_BOARD_PCTV_461E_V2: + result = em28178_dvb_init_pctv_461e_v2(dev); + if (result) + goto out_free; + break; case EM28178_BOARD_PCTV_292E: result = em28178_dvb_init_pctv_292e(dev); if (result) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b0f7390e4b4f..6b84c3413e83 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2141,7 +2141,7 @@ static int em28xx_v4l2_open(struct file *filp) int ret; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; break; case VFL_TYPE_VBI: @@ -2789,7 +2789,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) } /* register v4l2 video video_device */ - ret = video_register_device(&v4l2->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, video_nr[dev->devno]); if (ret) { dev_err(&dev->intf->dev, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 4ecadd57dac7..acbb62397314 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -150,6 +150,7 @@ #define EM2884_BOARD_TERRATEC_H6 101 #define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102 #define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 +#define EM28178_BOARD_PCTV_461E_V2 104 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index ff2aa057c1fb..f889c9d740cd 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1044,6 +1044,7 @@ static int go7007_usb_probe(struct usb_interface *intf, struct go7007_usb *usb; const struct go7007_usb_board *board; struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_host_endpoint *ep; unsigned num_i2c_devs; char *name; int video_pipe, i, v_urb_len; @@ -1140,7 +1141,8 @@ static int go7007_usb_probe(struct usb_interface *intf, if (usb->intr_urb->transfer_buffer == NULL) goto allocfail; - if (go->board_id == GO7007_BOARDID_SENSORAY_2250) + ep = usb->usbdev->ep_in[4]; + if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) usb_fill_bulk_urb(usb->intr_urb, usb->usbdev, usb_rcvbulkpipe(usb->usbdev, 4), usb->intr_urb->transfer_buffer, 2*sizeof(u16), diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index 0b3d185f3cb0..b2edc4deaca3 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -1138,7 +1138,7 @@ int go7007_v4l2_init(struct go7007 *go) go7007_s_input(go); if (go->board_info->sensor_flags & GO7007_SENSOR_TV) go7007_s_std(go); - rv = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + rv = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (rv < 0) return rv; dev_info(go->dev, "registered device %s [v4l2]\n", diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index c1b307bbe540..0566e00d6fea 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1555,7 +1555,7 @@ int gspca_dev_probe2(struct usb_interface *intf, /* init video stuff */ ret = video_register_device(&gspca_dev->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, -1); if (ret < 0) { pr_err("video_register_device err %d\n", ret); diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index f417dfc0b872..0afe70a3f9a2 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -3477,6 +3477,11 @@ static void ov511_mode_init_regs(struct sd *sd) return; } + if (alt->desc.bNumEndpoints < 1) { + sd->gspca_dev.usb_err = -ENODEV; + return; + } + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); @@ -3603,6 +3608,11 @@ static void ov518_mode_init_regs(struct sd *sd) return; } + if (alt->desc.bNumEndpoints < 1) { + sd->gspca_dev.usb_err = -ENODEV; + return; + } + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c index 79653d409951..95673fc0a99c 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c @@ -282,6 +282,9 @@ static int stv06xx_start(struct gspca_dev *gspca_dev) return -EIO; } + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size); if (err < 0) @@ -306,11 +309,21 @@ out: static int stv06xx_isoc_init(struct gspca_dev *gspca_dev) { + struct usb_interface_cache *intfc; struct usb_host_interface *alt; struct sd *sd = (struct sd *) gspca_dev; + intfc = gspca_dev->dev->actconfig->intf_cache[0]; + + if (intfc->num_altsetting < 2) + return -ENODEV; + + alt = &intfc->altsetting[1]; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(sd->sensor->max_packet_size[gspca_dev->curr_mode]); @@ -323,6 +336,10 @@ static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev) struct usb_host_interface *alt; struct sd *sd = (struct sd *) gspca_dev; + /* + * Existence of altsetting and endpoint was verified in + * stv06xx_isoc_init() + */ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode]; diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c index 6d1007715ff7..ae382b3b5f7f 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c @@ -185,6 +185,10 @@ static int pb0100_start(struct sd *sd) alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); if (!alt) return -ENODEV; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); /* If we don't have enough bandwidth use a lower framerate */ diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c index 934a90bd78c2..c579b100f066 100644 --- a/drivers/media/usb/gspca/xirlink_cit.c +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -1442,6 +1442,9 @@ static int cit_get_packet_size(struct gspca_dev *gspca_dev) return -EIO; } + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); } @@ -2626,6 +2629,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static int sd_isoc_init(struct gspca_dev *gspca_dev) { + struct usb_interface_cache *intfc; struct usb_host_interface *alt; int max_packet_size; @@ -2641,8 +2645,17 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) break; } + intfc = gspca_dev->dev->actconfig->intf_cache[0]; + + if (intfc->num_altsetting < 2) + return -ENODEV; + + alt = &intfc->altsetting[1]; + + if (alt->desc.bNumEndpoints < 1) + return -ENODEV; + /* Start isoc bandwidth "negotiation" at max isoc bandwidth */ - alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size); return 0; @@ -2665,6 +2678,9 @@ static int sd_isoc_nego(struct gspca_dev *gspca_dev) break; } + /* + * Existence of altsetting and endpoint was verified in sd_isoc_init() + */ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1]; packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); if (packet_size <= min_packet_size) diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index bad71d863d39..563128d11731 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -1238,7 +1238,7 @@ int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, dev->video_dev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&dev->video_dev, dev); - res = video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, devnum); + res = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO, devnum); if (res < 0) { v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); goto error; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index eaa08c7999d4..9657c1883311 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1196,7 +1196,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, hdw = vp->channel.mc_head->hdw; dip->v4l_type = v4l_type; switch (v4l_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: dip->stream = &vp->channel.mc_head->video_stream; dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_video; @@ -1276,7 +1276,7 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) /* register streams */ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); if (!vp->dev_video) goto fail; - pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); + pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_VIDEO); if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & (1 << PVR2_CVAL_INPUT_RADIO)) { vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 9b76cf133d52..d57b8b786506 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1116,7 +1116,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); + rc = video_register_device(&pdev->vdev, VFL_TYPE_VIDEO, -1); if (rc < 0) { PWC_ERROR("Failed to register as video device (%d).\n", rc); goto err_unregister_v4l2_dev; diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 329ec8089592..4af55e2478be 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1649,11 +1649,11 @@ static int s2255_probe_v4l(struct s2255_dev *dev) video_set_drvdata(&vc->vdev, vc); if (video_nr == -1) ret = video_register_device(&vc->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, video_nr); else ret = video_register_device(&vc->vdev, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, cur_nr + i); if (ret) { diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index bcd14c66e8df..6a4eb616d516 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -830,7 +830,7 @@ int stk1160_video_register(struct stk1160 *dev) dev->norm); video_set_drvdata(&dev->vdev, dev); - rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + rc = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (rc < 0) { stk1160_err("video_register_device failed (%d)\n", rc); return rc; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index b22501f76b78..a45d464427c4 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1254,7 +1254,7 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; video_set_drvdata(&dev->vdev, dev); - err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); if (err) pr_err("v4l registration failed\n"); else diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index c07a81a6cbe2..bfba06ea60e9 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1300,7 +1300,7 @@ static int __tm6000_open(struct file *file) video_device_node_name(vdev)); switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; break; case VFL_TYPE_VBI: @@ -1639,7 +1639,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev) INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); - ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, video_nr); + ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr); if (ret < 0) { printk(KERN_INFO "%s: can't register video device\n", diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 5095c380b2c1..ee9c656d121f 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -56,7 +56,7 @@ int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size) ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 0); + value, index, NULL, 0, USB_CTRL_GET_TIMEOUT); if (ret < 0) return ret; } diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 3d9284a09ee5..c89efcd46163 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -800,7 +800,8 @@ static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) ret = usb_control_msg(usbtv->udev, usb_rcvctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, USBTV_BASE + 0x0244, (void *)data, 3, 0); + 0, USBTV_BASE + 0x0244, (void *)data, 3, + USB_CTRL_GET_TIMEOUT); if (ret < 0) goto error; } @@ -851,7 +852,7 @@ static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) ret = usb_control_msg(usbtv->udev, usb_sndctrlpipe(usbtv->udev, 0), USBTV_CONTROL_REG, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, (void *)data, size, 0); + 0, index, (void *)data, size, USB_CTRL_SET_TIMEOUT); error: if (ret < 0) @@ -940,7 +941,7 @@ int usbtv_video_init(struct usbtv *usbtv) usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; video_set_drvdata(&usbtv->vdev, usbtv); - ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&usbtv->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_warn(usbtv->dev, "Could not register video device\n"); goto vdev_fail; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 99883550375e..431d86e1c94b 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2014,7 +2014,7 @@ int uvc_register_video_device(struct uvc_device *dev, */ video_set_drvdata(vdev, stream); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n", v4l2_type_names[type], ret); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 57dbcc8083bf..8c670934d920 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1516,7 +1516,7 @@ static int zr364xx_probe(struct usb_interface *intf, V4L2_FIELD_NONE, sizeof(struct zr364xx_buffer), cam, &cam->lock); - err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); goto fail; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 2928c5e0a73d..93d33d1db4e8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -4296,10 +4296,17 @@ void v4l2_ctrl_request_complete(struct media_request *req, continue; v4l2_ctrl_lock(ctrl); - if (ref->req) + if (ref->req) { ptr_to_ptr(ctrl, ref->req->p_req, ref->p_req); - else + } else { ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req); + /* + * Set ref->req to ensure that when userspace wants to + * obtain the controls of this request it will take + * this value and not the current value of the control. + */ + ref->req = ref; + } v4l2_ctrl_unlock(ctrl); } diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index da42d172714a..97b6a3af1361 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -542,13 +542,13 @@ static void determine_valid_ioctls(struct video_device *vdev) V4L2_CAP_META_OUTPUT; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; - bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & vid_caps); bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI; bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO; bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR; bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & meta_caps); bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; @@ -783,7 +783,7 @@ static int video_register_media_controller(struct video_device *vdev) vdev->entity.function = MEDIA_ENT_F_UNKNOWN; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: intf_type = MEDIA_INTF_T_V4L_VIDEO; vdev->entity.function = MEDIA_ENT_F_IO_V4L; break; @@ -891,7 +891,7 @@ int __video_register_device(struct video_device *vdev, /* Part 1: check device type */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: name_base = "video"; break; case VFL_TYPE_VBI: @@ -935,7 +935,7 @@ int __video_register_device(struct video_device *vdev, * of 128-191 and just pick the first free minor there * (new style). */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: minor_offset = 0; minor_cnt = 64; break; diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 63d6b147b21e..c69941214bb2 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -111,9 +111,6 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister); int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd) { -#if defined(CONFIG_MEDIA_CONTROLLER) - struct media_entity *entity = &sd->entity; -#endif int err; /* Check for valid input */ @@ -143,7 +140,7 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { - err = media_device_register_entity(v4l2_dev->mdev, entity); + err = media_device_register_entity(v4l2_dev->mdev, &sd->entity); if (err < 0) goto error_module; } @@ -163,7 +160,7 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, error_unregister: #if defined(CONFIG_MEDIA_CONTROLLER) - media_device_unregister_entity(entity); + media_device_unregister_entity(&sd->entity); #endif error_module: if (!sd->owner_v4l2_dev) @@ -179,6 +176,7 @@ static void v4l2_subdev_release(struct v4l2_subdev *sd) if (sd->internal_ops && sd->internal_ops->release) sd->internal_ops->release(sd); + sd->devnode = NULL; module_put(owner); } diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 6ece4320e1d2..97f0f8b23b5d 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -557,33 +557,28 @@ int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); -int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, +int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, struct v4l2_fwnode_link *link) { - const char *port_prop = is_of_node(__fwnode) ? "reg" : "port"; - struct fwnode_handle *fwnode; + struct fwnode_endpoint fwep; memset(link, 0, sizeof(*link)); - fwnode = fwnode_get_parent(__fwnode); - fwnode_property_read_u32(fwnode, port_prop, &link->local_port); - fwnode = fwnode_get_next_parent(fwnode); - if (is_of_node(fwnode) && of_node_name_eq(to_of_node(fwnode), "ports")) - fwnode = fwnode_get_next_parent(fwnode); - link->local_node = fwnode; + fwnode_graph_parse_endpoint(fwnode, &fwep); + link->local_id = fwep.id; + link->local_port = fwep.port; + link->local_node = fwnode_graph_get_port_parent(fwnode); - fwnode = fwnode_graph_get_remote_endpoint(__fwnode); + fwnode = fwnode_graph_get_remote_endpoint(fwnode); if (!fwnode) { fwnode_handle_put(fwnode); return -ENOLINK; } - fwnode = fwnode_get_parent(fwnode); - fwnode_property_read_u32(fwnode, port_prop, &link->remote_port); - fwnode = fwnode_get_next_parent(fwnode); - if (is_of_node(fwnode) && of_node_name_eq(to_of_node(fwnode), "ports")) - fwnode = fwnode_get_next_parent(fwnode); - link->remote_node = fwnode; + fwnode_graph_parse_endpoint(fwnode, &fwep); + link->remote_id = fwep.id; + link->remote_port = fwep.port; + link->remote_node = fwnode_graph_get_port_parent(fwnode); return 0; } @@ -596,6 +591,171 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) } EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); +static const struct v4l2_fwnode_connector_conv { + enum v4l2_connector_type type; + const char *compatible; +} connectors[] = { + { + .type = V4L2_CONN_COMPOSITE, + .compatible = "composite-video-connector", + }, { + .type = V4L2_CONN_SVIDEO, + .compatible = "svideo-connector", + }, +}; + +static enum v4l2_connector_type +v4l2_fwnode_string_to_connector_type(const char *con_str) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(connectors); i++) + if (!strcmp(con_str, connectors[i].compatible)) + return connectors[i].type; + + return V4L2_CONN_UNKNOWN; +} + +static void +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *vc) +{ + u32 stds; + int ret; + + ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds); + + /* The property is optional. */ + vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds; +} + +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector) +{ + struct v4l2_connector_link *link, *tmp; + + if (IS_ERR_OR_NULL(connector) || connector->type == V4L2_CONN_UNKNOWN) + return; + + list_for_each_entry_safe(link, tmp, &connector->links, head) { + v4l2_fwnode_put_link(&link->fwnode_link); + list_del(&link->head); + kfree(link); + } + + kfree(connector->label); + connector->label = NULL; + connector->type = V4L2_CONN_UNKNOWN; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free); + +static enum v4l2_connector_type +v4l2_fwnode_get_connector_type(struct fwnode_handle *fwnode) +{ + const char *type_name; + int err; + + if (!fwnode) + return V4L2_CONN_UNKNOWN; + + /* The connector-type is stored within the compatible string. */ + err = fwnode_property_read_string(fwnode, "compatible", &type_name); + if (err) + return V4L2_CONN_UNKNOWN; + + return v4l2_fwnode_string_to_connector_type(type_name); +} + +int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector) +{ + struct fwnode_handle *connector_node; + enum v4l2_connector_type connector_type; + const char *label; + int err; + + if (!fwnode) + return -EINVAL; + + memset(connector, 0, sizeof(*connector)); + + INIT_LIST_HEAD(&connector->links); + + connector_node = fwnode_graph_get_port_parent(fwnode); + connector_type = v4l2_fwnode_get_connector_type(connector_node); + if (connector_type == V4L2_CONN_UNKNOWN) { + fwnode_handle_put(connector_node); + connector_node = fwnode_graph_get_remote_port_parent(fwnode); + connector_type = v4l2_fwnode_get_connector_type(connector_node); + } + + if (connector_type == V4L2_CONN_UNKNOWN) { + pr_err("Unknown connector type\n"); + err = -ENOTCONN; + goto out; + } + + connector->type = connector_type; + connector->name = fwnode_get_name(connector_node); + err = fwnode_property_read_string(connector_node, "label", &label); + connector->label = err ? NULL : kstrdup_const(label, GFP_KERNEL); + + /* Parse the connector specific properties. */ + switch (connector->type) { + case V4L2_CONN_COMPOSITE: + case V4L2_CONN_SVIDEO: + v4l2_fwnode_connector_parse_analog(connector_node, connector); + break; + /* Avoid compiler warnings */ + case V4L2_CONN_UNKNOWN: + break; + } + +out: + fwnode_handle_put(connector_node); + + return err; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse); + +int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector) +{ + struct fwnode_handle *connector_ep; + struct v4l2_connector_link *link; + int err; + + if (!fwnode || !connector || connector->type == V4L2_CONN_UNKNOWN) + return -EINVAL; + + connector_ep = fwnode_graph_get_remote_endpoint(fwnode); + if (!connector_ep) + return -ENOTCONN; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) { + err = -ENOMEM; + goto err; + } + + err = v4l2_fwnode_parse_link(connector_ep, &link->fwnode_link); + if (err) + goto err; + + fwnode_handle_put(connector_ep); + + list_add(&link->head, &connector->links); + connector->nr_of_links++; + + return 0; + +err: + kfree(link); + fwnode_handle_put(connector_ep); + + return err; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link); + static int v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev, struct v4l2_async_notifier *notifier, diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c index 5bf99e7c0c09..b4acca75644b 100644 --- a/drivers/media/v4l2-core/v4l2-i2c.c +++ b/drivers/media/v4l2-core/v4l2-i2c.c @@ -74,10 +74,10 @@ struct v4l2_subdev /* Create the i2c client */ if (info->addr == 0 && probe_addrs) - client = i2c_new_probed_device(adapter, info, probe_addrs, - NULL); + client = i2c_new_scanned_device(adapter, info, probe_addrs, + NULL); else - client = i2c_new_device(adapter, info); + client = i2c_new_client_device(adapter, info); /* * Note: by loading the module first we are certain that c->driver @@ -88,7 +88,7 @@ struct v4l2_subdev * want to use the i2c device, so explicitly loading the module * is the best alternative. */ - if (!client || !client->dev.driver) + if (!i2c_client_has_driver(client)) goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ @@ -110,7 +110,7 @@ error: * If we have a client but no subdev, then something went wrong and * we must unregister the client. */ - if (client && !sd) + if (!IS_ERR(client) && !sd) i2c_unregister_device(client); return sd; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aaf83e254272..b2ef8e60ea7d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -941,12 +941,12 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) V4L2_CAP_META_OUTPUT; struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & vid_caps); bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & meta_caps); bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; @@ -1222,6 +1222,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_Y6: descr = "6-bit Greyscale"; break; case V4L2_PIX_FMT_Y10: descr = "10-bit Greyscale"; break; case V4L2_PIX_FMT_Y12: descr = "12-bit Greyscale"; break; + case V4L2_PIX_FMT_Y14: descr = "14-bit Greyscale"; break; case V4L2_PIX_FMT_Y16: descr = "16-bit Greyscale"; break; case V4L2_PIX_FMT_Y16_BE: descr = "16-bit Greyscale BE"; break; case V4L2_PIX_FMT_Y10BPACK: descr = "10-bit Greyscale (Packed)"; break; @@ -1306,6 +1307,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_SGBRG12P: descr = "12-bit Bayer GBGB/RGRG Packed"; break; case V4L2_PIX_FMT_SGRBG12P: descr = "12-bit Bayer GRGR/BGBG Packed"; break; case V4L2_PIX_FMT_SRGGB12P: descr = "12-bit Bayer RGRG/GBGB Packed"; break; + case V4L2_PIX_FMT_SBGGR14: descr = "14-bit Bayer BGBG/GRGR"; break; + case V4L2_PIX_FMT_SGBRG14: descr = "14-bit Bayer GBGB/RGRG"; break; + case V4L2_PIX_FMT_SGRBG14: descr = "14-bit Bayer GRGR/BGBG"; break; + case V4L2_PIX_FMT_SRGGB14: descr = "14-bit Bayer RGRG/GBGB"; break; case V4L2_PIX_FMT_SBGGR14P: descr = "14-bit Bayer BGBG/GRGR Packed"; break; case V4L2_PIX_FMT_SGBRG14P: descr = "14-bit Bayer GBGB/RGRG Packed"; break; case V4L2_PIX_FMT_SGRBG14P: descr = "14-bit Bayer GRGR/BGBG Packed"; break; diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 014a2a97cadd..0fffdd3ce6a4 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -321,7 +321,7 @@ EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); * use_count field stores the total number of users of all video device nodes * in the pipeline. * - * The v4l2_pipeline_pm_use() function must be called in the open() and + * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and * close() handlers of video device nodes. It increments or decrements the use * count of all subdev entities in the pipeline. * @@ -423,7 +423,7 @@ static int pipeline_pm_power(struct media_entity *entity, int change, return ret; } -int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use) { struct media_device *mdev = entity->graph_obj.mdev; int change = use ? 1 : -1; @@ -444,7 +444,19 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use) return ret; } -EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use); + +int v4l2_pipeline_pm_get(struct media_entity *entity) +{ + return v4l2_pipeline_pm_use(entity, 1); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get); + +void v4l2_pipeline_pm_put(struct media_entity *entity) +{ + /* Powering off entities shouldn't fail. */ + WARN_ON(v4l2_pipeline_pm_use(entity, 0)); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put); int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index cc34c5ab7009..8986c31176e9 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -340,6 +340,11 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || dst->vb2_buf.timestamp != src->vb2_buf.timestamp; + if (m2m_ctx->has_stopped) { + dprintk("Device has stopped\n"); + goto job_unlock; + } + if (m2m_dev->m2m_ops->job_ready && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { dprintk("Driver not ready\n"); @@ -556,6 +561,140 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); +/* + * This will add the LAST flag and mark the buffer management + * state as stopped. + * This is called when the last capture buffer must be flagged as LAST + * in draining mode from the encoder/decoder driver buf_queue() callback + * or from v4l2_update_last_buf_state() when a capture buffer is available. + */ +void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); + + v4l2_m2m_mark_stopped(m2m_ctx); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_last_buffer_done); + +/* When stop command is issued, update buffer management state */ +static int v4l2_update_last_buf_state(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct vb2_v4l2_buffer *next_dst_buf; + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + return 0; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + + /* + * The processing of the last output buffer queued before + * the STOP command is expected to mark the buffer management + * state as stopped with v4l2_m2m_mark_stopped(). + */ + if (m2m_ctx->last_src_buf) + return 0; + + /* + * In case the output queue is empty, try to mark the last capture + * buffer as LAST. + */ + next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!next_dst_buf) { + /* + * Wait for the next queued one in encoder/decoder driver + * buf_queue() callback using the v4l2_m2m_dst_buf_is_last() + * helper or in v4l2_m2m_qbuf() if encoder/decoder is not yet + * streaming. + */ + m2m_ctx->next_buf_last = true; + return 0; + } + + v4l2_m2m_last_buffer_done(m2m_ctx, next_dst_buf); + + return 0; +} + +/* + * Updates the encoding/decoding buffer management state, should + * be called from encoder/decoder drivers start_streaming() + */ +void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + /* If start streaming again, untag the last output buffer */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) + m2m_ctx->last_src_buf = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_update_start_streaming_state); + +/* + * Updates the encoding/decoding buffer management state, should + * be called from encoder/decoder driver stop_streaming() + */ +void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + /* + * If in draining state, either mark next dst buffer as + * done or flag next one to be marked as done either + * in encoder/decoder driver buf_queue() callback using + * the v4l2_m2m_dst_buf_is_last() helper or in v4l2_m2m_qbuf() + * if encoder/decoder is not yet streaming + */ + if (m2m_ctx->is_draining) { + struct vb2_v4l2_buffer *next_dst_buf; + + m2m_ctx->last_src_buf = NULL; + next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!next_dst_buf) + m2m_ctx->next_buf_last = true; + else + v4l2_m2m_last_buffer_done(m2m_ctx, + next_dst_buf); + } + } else { + v4l2_m2m_clear_state(m2m_ctx); + } +} +EXPORT_SYMBOL_GPL(v4l2_m2m_update_stop_streaming_state); + +static void v4l2_m2m_force_last_buf_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + unsigned int i; + + if (WARN_ON(q->is_output)) + return; + if (list_empty(&q->queued_list)) + return; + + vb = list_first_entry(&q->queued_list, struct vb2_buffer, queued_entry); + for (i = 0; i < vb->num_planes; i++) + vb2_set_plane_payload(vb, i, 0); + + /* + * Since the buffer hasn't been queued to the ready queue, + * mark is active and owned before marking it LAST and DONE + */ + vb->state = VB2_BUF_STATE_ACTIVE; + atomic_inc(&q->owned_by_drv_count); + + vbuf = to_vb2_v4l2_buffer(vb); + vbuf->field = V4L2_FIELD_NONE; + + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf); +} + int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -570,11 +709,25 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, __func__); return -EPERM; } + ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); - if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) + if (ret) + return ret; + + /* + * If the capture queue is streaming, but streaming hasn't started + * on the device, but was asked to stop, mark the previously queued + * buffer as DONE with LAST flag since it won't be queued on the + * device. + */ + if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && + (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) + v4l2_m2m_force_last_buf_done(m2m_ctx, vq); + else if (!(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) v4l2_m2m_try_schedule(m2m_ctx); - return ret; + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); @@ -1225,6 +1378,70 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, } EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); +/* + * Updates the encoding state on ENC_CMD_STOP/ENC_CMD_START + * Should be called from the encoder driver encoder_cmd() callback + */ +int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_encoder_cmd *ec) +{ + if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START) + return -EINVAL; + + if (ec->cmd == V4L2_ENC_CMD_STOP) + return v4l2_update_last_buf_state(m2m_ctx); + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_encoder_cmd); + +/* + * Updates the decoding state on DEC_CMD_STOP/DEC_CMD_START + * Should be called from the decoder driver decoder_cmd() callback + */ +int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START) + return -EINVAL; + + if (dc->cmd == V4L2_DEC_CMD_STOP) + return v4l2_update_last_buf_state(m2m_ctx); + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd); + +int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *ec) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_encoder_cmd(file, fh->m2m_ctx, ec); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_encoder_cmd); + +int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *dc) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd); + int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index c394abffea86..e59a846bc909 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -42,4 +42,8 @@ source "drivers/staging/media/phy-rockchip-dphy-rx0/Kconfig" source "drivers/staging/media/rkisp1/Kconfig" +if MEDIA_ANALOG_TV_SUPPORT +source "drivers/staging/media/usbvision/Kconfig" +endif + endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index ea9fce8014bb..23c682461b62 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ obj-$(CONFIG_SOC_CAMERA) += soc_camera/ obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rkisp1/ +obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ diff --git a/drivers/staging/media/allegro-dvt/Makefile b/drivers/staging/media/allegro-dvt/Makefile index 80817160815c..8e306dcdc55c 100644 --- a/drivers/staging/media/allegro-dvt/Makefile +++ b/drivers/staging/media/allegro-dvt/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -allegro-objs := allegro-core.o nal-h264.o +allegro-objs := allegro-core.o nal-h264.o allegro-mail.o obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c index 3be41698df4c..34c3e55be902 100644 --- a/drivers/staging/media/allegro-dvt/allegro-core.c +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -5,7 +5,9 @@ * Allegro DVT video encoder driver */ +#include <linux/bits.h> #include <linux/firmware.h> +#include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -26,6 +28,7 @@ #include <media/videobuf2-dma-contig.h> #include <media/videobuf2-v4l2.h> +#include "allegro-mail.h" #include "nal-h264.h" /* @@ -40,6 +43,8 @@ #define ALLEGRO_HEIGHT_DEFAULT 1080 #define ALLEGRO_HEIGHT_MAX 2160 +#define ALLEGRO_FRAMERATE_DEFAULT ((struct v4l2_fract) { 30, 1 }) + #define ALLEGRO_GOP_SIZE_DEFAULT 25 #define ALLEGRO_GOP_SIZE_MAX 1000 @@ -176,6 +181,7 @@ struct allegro_channel { unsigned int width; unsigned int height; unsigned int stride; + struct v4l2_fract framerate; enum v4l2_colorspace colorspace; enum v4l2_ycbcr_encoding ycbcr_enc; @@ -192,7 +198,7 @@ struct allegro_channel { unsigned int sizeimage_encoded; unsigned int csequence; - enum v4l2_mpeg_video_bitrate_mode bitrate_mode; + bool frame_rc_enable; unsigned int bitrate; unsigned int bitrate_peak; unsigned int cpb_size; @@ -200,9 +206,17 @@ struct allegro_channel { struct v4l2_ctrl *mpeg_video_h264_profile; struct v4l2_ctrl *mpeg_video_h264_level; - struct v4l2_ctrl *mpeg_video_bitrate_mode; - struct v4l2_ctrl *mpeg_video_bitrate; - struct v4l2_ctrl *mpeg_video_bitrate_peak; + struct v4l2_ctrl *mpeg_video_h264_i_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_max_qp; + struct v4l2_ctrl *mpeg_video_h264_min_qp; + struct v4l2_ctrl *mpeg_video_h264_p_frame_qp; + struct v4l2_ctrl *mpeg_video_h264_b_frame_qp; + struct v4l2_ctrl *mpeg_video_frame_rc_enable; + struct { /* video bitrate mode control cluster */ + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + }; struct v4l2_ctrl *mpeg_video_cpb_size; struct v4l2_ctrl *mpeg_video_gop_size; @@ -215,6 +229,11 @@ struct allegro_channel { struct list_head buffers_reference; struct list_head buffers_intermediate; + struct list_head source_shadow_list; + struct list_head stream_shadow_list; + /* protect shadow lists of buffers passed to firmware */ + struct mutex shadow_list_lock; + struct list_head list; struct completion completion; @@ -236,6 +255,14 @@ allegro_get_state(struct allegro_channel *channel) return channel->state; } +struct allegro_m2m_buffer { + struct v4l2_m2m_buffer buf; + struct list_head head; +}; + +#define to_allegro_m2m_buffer(__buf) \ + container_of(__buf, struct allegro_m2m_buffer, buf) + struct fw_info { unsigned int id; unsigned int id_codec; @@ -258,276 +285,33 @@ static const struct fw_info supported_firmware[] = { }, }; -enum mcu_msg_type { - MCU_MSG_TYPE_INIT = 0x0000, - MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, - MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, - MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, - MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, - MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, - MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, -}; +static inline u32 to_mcu_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys) || (lower_32_bits(phys) & MCU_CACHE_OFFSET)) + v4l2_warn(&dev->v4l2_dev, + "address %pad is outside mcu window\n", &phys); + + return lower_32_bits(phys) | MCU_CACHE_OFFSET; +} -static const char *msg_type_name(enum mcu_msg_type type) +static inline u32 to_mcu_size(struct allegro_dev *dev, size_t size) { - static char buf[9]; + return lower_32_bits(size); +} - switch (type) { - case MCU_MSG_TYPE_INIT: - return "INIT"; - case MCU_MSG_TYPE_CREATE_CHANNEL: - return "CREATE_CHANNEL"; - case MCU_MSG_TYPE_DESTROY_CHANNEL: - return "DESTROY_CHANNEL"; - case MCU_MSG_TYPE_ENCODE_FRAME: - return "ENCODE_FRAME"; - case MCU_MSG_TYPE_PUT_STREAM_BUFFER: - return "PUT_STREAM_BUFFER"; - case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: - return "PUSH_BUFFER_INTERMEDIATE"; - case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: - return "PUSH_BUFFER_REFERENCE"; - default: - snprintf(buf, sizeof(buf), "(0x%04x)", type); - return buf; - } -} - -struct mcu_msg_header { - u16 length; /* length of the body in bytes */ - u16 type; -} __attribute__ ((__packed__)); - -struct mcu_msg_init_request { - struct mcu_msg_header header; - u32 reserved0; /* maybe a unused channel id */ - u32 suballoc_dma; - u32 suballoc_size; - s32 l2_cache[3]; -} __attribute__ ((__packed__)); - -struct mcu_msg_init_response { - struct mcu_msg_header header; - u32 reserved0; -} __attribute__ ((__packed__)); - -struct mcu_msg_create_channel { - struct mcu_msg_header header; - u32 user_id; - u16 width; - u16 height; - u32 format; - u32 colorspace; - u32 src_mode; - u8 profile; - u16 constraint_set_flags; - s8 codec; - u16 level; - u16 tier; - u32 sps_param; - u32 pps_param; - - u32 enc_option; -#define AL_OPT_WPP BIT(0) -#define AL_OPT_TILE BIT(1) -#define AL_OPT_LF BIT(2) -#define AL_OPT_LF_X_SLICE BIT(3) -#define AL_OPT_LF_X_TILE BIT(4) -#define AL_OPT_SCL_LST BIT(5) -#define AL_OPT_CONST_INTRA_PRED BIT(6) -#define AL_OPT_QP_TAB_RELATIVE BIT(7) -#define AL_OPT_FIX_PREDICTOR BIT(8) -#define AL_OPT_CUSTOM_LDA BIT(9) -#define AL_OPT_ENABLE_AUTO_QP BIT(10) -#define AL_OPT_ADAPT_AUTO_QP BIT(11) -#define AL_OPT_TRANSFO_SKIP BIT(13) -#define AL_OPT_FORCE_REC BIT(15) -#define AL_OPT_FORCE_MV_OUT BIT(16) -#define AL_OPT_FORCE_MV_CLIP BIT(17) -#define AL_OPT_LOWLAT_SYNC BIT(18) -#define AL_OPT_LOWLAT_INT BIT(19) -#define AL_OPT_RDO_COST_MODE BIT(20) - - s8 beta_offset; - s8 tc_offset; - u16 reserved10; - u32 unknown11; - u32 unknown12; - u16 num_slices; - u16 prefetch_auto; - u32 prefetch_mem_offset; - u32 prefetch_mem_size; - u16 clip_hrz_range; - u16 clip_vrt_range; - u16 me_range[4]; - u8 max_cu_size; - u8 min_cu_size; - u8 max_tu_size; - u8 min_tu_size; - u8 max_transfo_depth_inter; - u8 max_transfo_depth_intra; - u16 reserved20; - u32 entropy_mode; - u32 wp_mode; - - /* rate control param */ - u32 rate_control_mode; - u32 initial_rem_delay; - u32 cpb_size; - u16 framerate; - u16 clk_ratio; - u32 target_bitrate; - u32 max_bitrate; - u16 initial_qp; - u16 min_qp; - u16 max_qp; - s16 ip_delta; - s16 pb_delta; - u16 golden_ref; - u16 golden_delta; - u16 golden_ref_frequency; - u32 rate_control_option; - - /* gop param */ - u32 gop_ctrl_mode; - u32 freq_ird; - u32 freq_lt; - u32 gdr_mode; - u32 gop_length; - u32 unknown39; - - u32 subframe_latency; - u32 lda_control_mode; -} __attribute__ ((__packed__)); - -struct mcu_msg_create_channel_response { - struct mcu_msg_header header; - u32 channel_id; - u32 user_id; - u32 options; - u32 num_core; - u32 pps_param; - u32 int_buffers_count; - u32 int_buffers_size; - u32 rec_buffers_count; - u32 rec_buffers_size; - u32 reserved; - u32 error_code; -} __attribute__ ((__packed__)); - -struct mcu_msg_destroy_channel { - struct mcu_msg_header header; - u32 channel_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_destroy_channel_response { - struct mcu_msg_header header; - u32 channel_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_push_buffers_internal_buffer { - u32 dma_addr; - u32 mcu_addr; - u32 size; -} __attribute__ ((__packed__)); - -struct mcu_msg_push_buffers_internal { - struct mcu_msg_header header; - u32 channel_id; - struct mcu_msg_push_buffers_internal_buffer buffer[0]; -} __attribute__ ((__packed__)); - -struct mcu_msg_put_stream_buffer { - struct mcu_msg_header header; - u32 channel_id; - u32 dma_addr; - u32 mcu_addr; - u32 size; - u32 offset; - u64 stream_id; -} __attribute__ ((__packed__)); - -struct mcu_msg_encode_frame { - struct mcu_msg_header header; - u32 channel_id; - u32 reserved; - - u32 encoding_options; -#define AL_OPT_USE_QP_TABLE BIT(0) -#define AL_OPT_FORCE_LOAD BIT(1) -#define AL_OPT_USE_L2 BIT(2) -#define AL_OPT_DISABLE_INTRA BIT(3) -#define AL_OPT_DEPENDENT_SLICES BIT(4) - - s16 pps_qp; - u16 padding; - u64 user_param; - u64 src_handle; +static inline u32 to_codec_addr(struct allegro_dev *dev, dma_addr_t phys) +{ + if (upper_32_bits(phys)) + v4l2_warn(&dev->v4l2_dev, + "address %pad cannot be used by codec\n", &phys); - u32 request_options; -#define AL_OPT_SCENE_CHANGE BIT(0) -#define AL_OPT_RESTART_GOP BIT(1) -#define AL_OPT_USE_LONG_TERM BIT(2) -#define AL_OPT_UPDATE_PARAMS BIT(3) - - /* u32 scene_change_delay (optional) */ - /* rate control param (optional) */ - /* gop param (optional) */ - u32 src_y; - u32 src_uv; - u32 stride; - u32 ep2; - u64 ep2_v; -} __attribute__ ((__packed__)); - -struct mcu_msg_encode_frame_response { - struct mcu_msg_header header; - u32 channel_id; - u64 stream_id; /* see mcu_msg_put_stream_buffer */ - u64 user_param; /* see mcu_msg_encode_frame */ - u64 src_handle; /* see mcu_msg_encode_frame */ - u16 skip; - u16 is_ref; - u32 initial_removal_delay; - u32 dpb_output_delay; - u32 size; - u32 frame_tag_size; - s32 stuffing; - s32 filler; - u16 num_column; - u16 num_row; - u16 qp; - u8 num_ref_idx_l0; - u8 num_ref_idx_l1; - u32 partition_table_offset; - s32 partition_table_size; - u32 sum_complex; - s32 tile_width[4]; - s32 tile_height[22]; - u32 error_code; - - u32 slice_type; -#define AL_ENC_SLICE_TYPE_B 0 -#define AL_ENC_SLICE_TYPE_P 1 -#define AL_ENC_SLICE_TYPE_I 2 - - u32 pic_struct; - u8 is_idr; - u8 is_first_slice; - u8 is_last_slice; - u8 reserved; - u16 pps_qp; - u16 reserved1; - u32 reserved2; -} __attribute__ ((__packed__)); - -union mcu_msg_response { - struct mcu_msg_header header; - struct mcu_msg_init_response init; - struct mcu_msg_create_channel_response create_channel; - struct mcu_msg_destroy_channel_response destroy_channel; - struct mcu_msg_encode_frame_response encode_frame; -}; + return lower_32_bits(phys); +} + +static inline u64 ptr_to_u64(const void *ptr) +{ + return (uintptr_t)ptr; +} /* Helper functions for channel and user operations */ @@ -572,6 +356,56 @@ static inline bool channel_exists(struct allegro_channel *channel) return channel->mcu_channel_id != -1; } +#define AL_ERROR 0x80 +#define AL_ERR_INIT_FAILED 0x81 +#define AL_ERR_NO_FRAME_DECODED 0x82 +#define AL_ERR_RESOLUTION_CHANGE 0x85 +#define AL_ERR_NO_MEMORY 0x87 +#define AL_ERR_STREAM_OVERFLOW 0x88 +#define AL_ERR_TOO_MANY_SLICES 0x89 +#define AL_ERR_BUF_NOT_READY 0x8c +#define AL_ERR_NO_CHANNEL_AVAILABLE 0x8d +#define AL_ERR_RESOURCE_UNAVAILABLE 0x8e +#define AL_ERR_NOT_ENOUGH_CORES 0x8f +#define AL_ERR_REQUEST_MALFORMED 0x90 +#define AL_ERR_CMD_NOT_ALLOWED 0x91 +#define AL_ERR_INVALID_CMD_VALUE 0x92 + +static inline const char *allegro_err_to_string(unsigned int err) +{ + switch (err) { + case AL_ERR_INIT_FAILED: + return "initialization failed"; + case AL_ERR_NO_FRAME_DECODED: + return "no frame decoded"; + case AL_ERR_RESOLUTION_CHANGE: + return "resolution change"; + case AL_ERR_NO_MEMORY: + return "out of memory"; + case AL_ERR_STREAM_OVERFLOW: + return "stream buffer overflow"; + case AL_ERR_TOO_MANY_SLICES: + return "too many slices"; + case AL_ERR_BUF_NOT_READY: + return "buffer not ready"; + case AL_ERR_NO_CHANNEL_AVAILABLE: + return "no channel available"; + case AL_ERR_RESOURCE_UNAVAILABLE: + return "resource unavailable"; + case AL_ERR_NOT_ENOUGH_CORES: + return "not enough cores"; + case AL_ERR_REQUEST_MALFORMED: + return "request malformed"; + case AL_ERR_CMD_NOT_ALLOWED: + return "command not allowed"; + case AL_ERR_INVALID_CMD_VALUE: + return "invalid command value"; + case AL_ERROR: + default: + return "unknown error"; + } +} + static unsigned int estimate_stream_size(unsigned int width, unsigned int height) { @@ -781,7 +615,7 @@ static int allegro_mbox_write(struct allegro_dev *dev, if (size > mbox->size) { v4l2_err(&dev->v4l2_dev, - "message (%zu bytes) to large for mailbox (%zu bytes)\n", + "message (%zu bytes) too large for mailbox (%zu bytes)\n", size, mbox->size); return -EINVAL; } @@ -894,8 +728,8 @@ static void allegro_mcu_send_init(struct allegro_dev *dev, msg.header.type = MCU_MSG_TYPE_INIT; msg.header.length = sizeof(msg) - sizeof(msg.header); - msg.suballoc_dma = lower_32_bits(suballoc_dma) | MCU_CACHE_OFFSET; - msg.suballoc_size = suballoc_size; + msg.suballoc_dma = to_mcu_addr(dev, suballoc_dma); + msg.suballoc_size = to_mcu_size(dev, suballoc_size); /* disable L2 cache */ msg.l2_cache[0] = -1; @@ -1001,6 +835,103 @@ v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) } } +static u32 v4l2_cpb_size_to_mcu(unsigned int cpb_size, unsigned int bitrate) +{ + unsigned int cpb_size_kbit; + unsigned int bitrate_kbps; + + /* + * The mcu expects the CPB size in units of a 90 kHz clock, but the + * channel follows the V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE and stores + * the CPB size in kilobytes. + */ + cpb_size_kbit = cpb_size * BITS_PER_BYTE; + bitrate_kbps = bitrate / 1000; + + return (cpb_size_kbit * 90000) / bitrate_kbps; +} + +static s16 get_qp_delta(int minuend, int subtrahend) +{ + if (minuend == subtrahend) + return -1; + else + return minuend - subtrahend; +} + +static int fill_create_channel_param(struct allegro_channel *channel, + struct create_channel_param *param) +{ + int i_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_i_frame_qp); + int p_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_p_frame_qp); + int b_frame_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_b_frame_qp); + int bitrate_mode = v4l2_ctrl_g_ctrl(channel->mpeg_video_bitrate_mode); + + param->width = channel->width; + param->height = channel->height; + param->format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); + param->colorspace = + v4l2_colorspace_to_mcu_colorspace(channel->colorspace); + param->src_mode = 0x0; + param->profile = v4l2_profile_to_mcu_profile(channel->profile); + param->constraint_set_flags = BIT(1); + param->codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + param->level = v4l2_level_to_mcu_level(channel->level); + param->tier = 0; + param->sps_param = BIT(20) | 0x4a; + param->pps_param = BIT(2); + param->enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | + AL_OPT_LF_X_SLICE | AL_OPT_LF; + param->beta_offset = -1; + param->tc_offset = -1; + param->num_slices = 1; + param->me_range[0] = 8; + param->me_range[1] = 8; + param->me_range[2] = 16; + param->me_range[3] = 16; + param->max_cu_size = ilog2(SIZE_MACROBLOCK); + param->min_cu_size = ilog2(8); + param->max_tu_size = 2; + param->min_tu_size = 2; + param->max_transfo_depth_intra = 1; + param->max_transfo_depth_inter = 1; + + param->prefetch_auto = 0; + param->prefetch_mem_offset = 0; + param->prefetch_mem_size = 0; + + param->rate_control_mode = channel->frame_rc_enable ? + v4l2_bitrate_mode_to_mcu_mode(bitrate_mode) : 0; + + param->cpb_size = v4l2_cpb_size_to_mcu(channel->cpb_size, + channel->bitrate_peak); + /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ + param->initial_rem_delay = param->cpb_size; + param->framerate = DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator); + param->clk_ratio = channel->framerate.denominator == 1001 ? 1001 : 1000; + param->target_bitrate = channel->bitrate; + param->max_bitrate = channel->bitrate_peak; + param->initial_qp = i_frame_qp; + param->min_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_min_qp); + param->max_qp = v4l2_ctrl_g_ctrl(channel->mpeg_video_h264_max_qp); + param->ip_delta = get_qp_delta(i_frame_qp, p_frame_qp); + param->pb_delta = get_qp_delta(p_frame_qp, b_frame_qp); + param->golden_ref = 0; + param->golden_delta = 2; + param->golden_ref_frequency = 10; + param->rate_control_option = 0x00000000; + + param->gop_ctrl_mode = 0x00000000; + param->freq_idr = channel->gop_size; + param->freq_lt = 0; + param->gdr_mode = 0x00000000; + param->gop_length = channel->gop_size; + param->subframe_latency = 0x00000000; + + return 0; +} + static int allegro_mcu_send_create_channel(struct allegro_dev *dev, struct allegro_channel *channel) { @@ -1012,63 +943,8 @@ static int allegro_mcu_send_create_channel(struct allegro_dev *dev, msg.header.length = sizeof(msg) - sizeof(msg.header); msg.user_id = channel->user_id; - msg.width = channel->width; - msg.height = channel->height; - msg.format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); - msg.colorspace = v4l2_colorspace_to_mcu_colorspace(channel->colorspace); - msg.src_mode = 0x0; - msg.profile = v4l2_profile_to_mcu_profile(channel->profile); - msg.constraint_set_flags = BIT(1); - msg.codec = v4l2_pixelformat_to_mcu_codec(channel->codec); - msg.level = v4l2_level_to_mcu_level(channel->level); - msg.tier = 0; - msg.sps_param = BIT(20) | 0x4a; - msg.pps_param = BIT(2); - msg.enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | - AL_OPT_LF_X_SLICE | AL_OPT_LF; - msg.beta_offset = -1; - msg.tc_offset = -1; - msg.num_slices = 1; - msg.me_range[0] = 8; - msg.me_range[1] = 8; - msg.me_range[2] = 16; - msg.me_range[3] = 16; - msg.max_cu_size = ilog2(SIZE_MACROBLOCK); - msg.min_cu_size = ilog2(8); - msg.max_tu_size = 2; - msg.min_tu_size = 2; - msg.max_transfo_depth_intra = 1; - msg.max_transfo_depth_inter = 1; - - msg.rate_control_mode = - v4l2_bitrate_mode_to_mcu_mode(channel->bitrate_mode); - /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ - msg.initial_rem_delay = - ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; - /* Encoder expects cpb_size in units of a 90 kHz clock. */ - msg.cpb_size = - ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; - msg.framerate = 25; - msg.clk_ratio = 1000; - msg.target_bitrate = channel->bitrate; - msg.max_bitrate = channel->bitrate_peak; - msg.initial_qp = 25; - msg.min_qp = 10; - msg.max_qp = 51; - msg.ip_delta = -1; - msg.pb_delta = -1; - msg.golden_ref = 0; - msg.golden_delta = 2; - msg.golden_ref_frequency = 10; - msg.rate_control_option = 0x00000000; - - msg.gop_ctrl_mode = 0x00000000; - msg.freq_ird = 0x7fffffff; - msg.freq_lt = 0; - msg.gdr_mode = 0x00000000; - msg.gop_length = channel->gop_size; - msg.subframe_latency = 0x00000000; - msg.lda_control_mode = 0x700d0000; + + fill_create_channel_param(channel, &msg.param); allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1097,7 +973,8 @@ static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, struct allegro_channel *channel, dma_addr_t paddr, - unsigned long size) + unsigned long size, + u64 stream_id) { struct mcu_msg_put_stream_buffer msg; @@ -1107,11 +984,12 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, msg.header.length = sizeof(msg) - sizeof(msg.header); msg.channel_id = channel->mcu_channel_id; - msg.dma_addr = paddr; - msg.mcu_addr = paddr | MCU_CACHE_OFFSET; + msg.dma_addr = to_codec_addr(dev, paddr); + msg.mcu_addr = to_mcu_addr(dev, paddr); msg.size = size; msg.offset = ENCODER_STREAM_OFFSET; - msg.stream_id = 0; /* copied to mcu_msg_encode_frame_response */ + /* copied to mcu_msg_encode_frame_response */ + msg.stream_id = stream_id; allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1121,7 +999,8 @@ static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, struct allegro_channel *channel, - dma_addr_t src_y, dma_addr_t src_uv) + dma_addr_t src_y, dma_addr_t src_uv, + u64 src_handle) { struct mcu_msg_encode_frame msg; @@ -1134,12 +1013,13 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, msg.encoding_options = AL_OPT_FORCE_LOAD; msg.pps_qp = 26; /* qp are relative to 26 */ msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ - msg.src_handle = 0; /* copied to mcu_msg_encode_frame_response */ - msg.src_y = src_y; - msg.src_uv = src_uv; + /* src_handle is copied to mcu_msg_encode_frame_response */ + msg.src_handle = src_handle; + msg.src_y = to_codec_addr(dev, src_y); + msg.src_uv = to_codec_addr(dev, src_uv); msg.stride = channel->stride; msg.ep2 = 0x0; - msg.ep2_v = msg.ep2 | MCU_CACHE_OFFSET; + msg.ep2_v = to_mcu_addr(dev, msg.ep2); allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); allegro_mcu_interrupt(dev); @@ -1198,10 +1078,9 @@ static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, buffer = msg->buffer; list_for_each_entry(al_buffer, list, head) { - buffer->dma_addr = lower_32_bits(al_buffer->paddr); - buffer->mcu_addr = - lower_32_bits(al_buffer->paddr) | MCU_CACHE_OFFSET; - buffer->size = al_buffer->size; + buffer->dma_addr = to_codec_addr(dev, al_buffer->paddr); + buffer->mcu_addr = to_mcu_addr(dev, al_buffer->paddr); + buffer->size = to_mcu_size(dev, al_buffer->size); buffer++; } @@ -1360,9 +1239,11 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, sps->vui.chroma_loc_info_present_flag = 1; sps->vui.chroma_sample_loc_type_top_field = 0; sps->vui.chroma_sample_loc_type_bottom_field = 0; + sps->vui.timing_info_present_flag = 1; - sps->vui.num_units_in_tick = 1; - sps->vui.time_scale = 50; + sps->vui.num_units_in_tick = channel->framerate.denominator; + sps->vui.time_scale = 2 * channel->framerate.numerator; + sps->vui.fixed_frame_rate_flag = 1; sps->vui.nal_hrd_parameters_present_flag = 0; sps->vui.vcl_hrd_parameters_present_flag = 1; @@ -1375,7 +1256,8 @@ static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; - sps->vui.vcl_hrd_parameters.cbr_flag[0] = 1; + sps->vui.vcl_hrd_parameters.cbr_flag[0] = + !v4l2_ctrl_g_ctrl(channel->mpeg_video_frame_rc_enable); sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; @@ -1438,8 +1320,11 @@ static bool allegro_channel_is_at_eos(struct allegro_channel *channel) break; case ALLEGRO_STATE_DRAIN: case ALLEGRO_STATE_WAIT_FOR_BUFFER: - if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0) + mutex_lock(&channel->shadow_list_lock); + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0 && + list_empty(&channel->source_shadow_list)) is_at_eos = true; + mutex_unlock(&channel->shadow_list_lock); break; default: break; @@ -1466,6 +1351,41 @@ static void allegro_channel_buf_done(struct allegro_channel *channel, v4l2_m2m_buf_done(buf, state); } +static u64 allegro_put_buffer(struct allegro_channel *channel, + struct list_head *list, + struct vb2_v4l2_buffer *buffer) +{ + struct v4l2_m2m_buffer *b = container_of(buffer, + struct v4l2_m2m_buffer, vb); + struct allegro_m2m_buffer *shadow = to_allegro_m2m_buffer(b); + + mutex_lock(&channel->shadow_list_lock); + list_add_tail(&shadow->head, list); + mutex_unlock(&channel->shadow_list_lock); + + return ptr_to_u64(buffer); +} + +static struct vb2_v4l2_buffer * +allegro_get_buffer(struct allegro_channel *channel, + struct list_head *list, u64 handle) +{ + struct allegro_m2m_buffer *shadow, *tmp; + struct vb2_v4l2_buffer *buffer = NULL; + + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, list, head) { + if (handle == ptr_to_u64(&shadow->buf.vb)) { + buffer = &shadow->buf.vb; + list_del_init(&shadow->head); + break; + } + } + mutex_unlock(&channel->shadow_list_lock); + + return buffer; +} + static void allegro_channel_finish_frame(struct allegro_channel *channel, struct mcu_msg_encode_frame_response *msg) { @@ -1481,15 +1401,31 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, ssize_t len; ssize_t free; - src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + src_buf = allegro_get_buffer(channel, &channel->source_shadow_list, + msg->src_handle); + if (!src_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid source buffer\n", + channel->mcu_channel_id); + + dst_buf = allegro_get_buffer(channel, &channel->stream_shadow_list, + msg->stream_id); + if (!dst_buf) + v4l2_warn(&dev->v4l2_dev, + "channel %d: invalid stream buffer\n", + channel->mcu_channel_id); + + if (!src_buf || !dst_buf) + goto err; - dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); dst_buf->sequence = channel->csequence++; - if (msg->error_code) { + if (msg->error_code & AL_ERROR) { v4l2_err(&dev->v4l2_dev, - "channel %d: error while encoding frame: %x\n", - channel->mcu_channel_id, msg->error_code); + "channel %d: failed to encode frame: %s (%x)\n", + channel->mcu_channel_id, + allegro_err_to_string(msg->error_code), + msg->error_code); goto err; } @@ -1562,17 +1498,22 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, channel->mcu_channel_id, len); } - len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); - if (len < 0) { - v4l2_err(&dev->v4l2_dev, - "failed to write %zd filler data\n", free); - goto err; + if (msg->slice_type != AL_ENC_SLICE_TYPE_I && !msg->is_idr) { + dst_buf->vb2_buf.planes[0].data_offset = free; + free = 0; + } else { + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to write %zd filler data\n", free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: wrote %zd bytes filler nal unit\n", + channel->mcu_channel_id, len); } - curr += len; - free -= len; - v4l2_dbg(2, debug, &dev->v4l2_dev, - "channel %d: wrote %zd bytes filler nal unit\n", - channel->mcu_channel_id, len); if (free != 0) { v4l2_err(&dev->v4l2_dev, @@ -1590,20 +1531,20 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel, dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; v4l2_dbg(1, debug, &dev->v4l2_dev, - "channel %d: encoded frame #%03d (%s%s, %d bytes)\n", + "channel %d: encoded frame #%03d (%s%s, QP %d, %d bytes)\n", channel->mcu_channel_id, dst_buf->sequence, msg->is_idr ? "IDR, " : "", msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", - partition->size); + msg->qp, partition->size); err: - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - - allegro_channel_buf_done(channel, dst_buf, state); + if (src_buf) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); + if (dst_buf) + allegro_channel_buf_done(channel, dst_buf, state); } static int allegro_handle_init(struct allegro_dev *dev, @@ -1621,6 +1562,12 @@ allegro_handle_create_channel(struct allegro_dev *dev, struct allegro_channel *channel; int err = 0; + if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) + v4l2_warn(&dev->v4l2_dev, + "received message has %d bytes, but expected %zu\n", + msg->header.length, + sizeof(*msg) - sizeof(msg->header)); + channel = allegro_find_channel_by_user_id(dev, msg->user_id); if (IS_ERR(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -1632,8 +1579,10 @@ allegro_handle_create_channel(struct allegro_dev *dev, if (msg->error_code) { v4l2_err(&dev->v4l2_dev, - "user %d: mcu failed to create channel: error %x\n", - channel->user_id, msg->error_code); + "user %d: mcu failed to create channel: %s (%x)\n", + channel->user_id, + allegro_err_to_string(msg->error_code), + msg->error_code); err = -EIO; goto out; } @@ -1712,6 +1661,12 @@ allegro_handle_encode_frame(struct allegro_dev *dev, { struct allegro_channel *channel; + if (msg->header.length != sizeof(*msg) - sizeof(msg->header)) + v4l2_warn(&dev->v4l2_dev, + "received message has %d bytes, but expected %zu\n", + msg->header.length, + sizeof(*msg) - sizeof(msg->header)); + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); if (IS_ERR(channel)) { v4l2_err(&dev->v4l2_dev, @@ -1920,6 +1875,14 @@ static int allegro_mcu_reset(struct allegro_dev *dev) { int err; + /* + * Ensure that the AL5_MCU_WAKEUP bit is set to 0 otherwise the mcu + * does not go to sleep after the reset. + */ + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + err = regmap_write(dev->regmap, AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); if (err < 0) @@ -1955,6 +1918,12 @@ static void allegro_destroy_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, false); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); @@ -2000,7 +1969,9 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_dbg(1, debug, &dev->v4l2_dev, "user %d: creating channel (%4.4s, %dx%d@%d)\n", channel->user_id, - (char *)&channel->codec, channel->width, channel->height, 25); + (char *)&channel->codec, channel->width, channel->height, + DIV_ROUND_UP(channel->framerate.numerator, + channel->framerate.denominator)); min_level = select_minimum_h264_level(channel->width, channel->height); if (channel->level < min_level) { @@ -2014,6 +1985,12 @@ static int allegro_create_channel(struct allegro_channel *channel) v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_i_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_max_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_min_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_p_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_b_frame_qp, true); + v4l2_ctrl_grab(channel->mpeg_video_frame_rc_enable, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); @@ -2046,6 +2023,7 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->width = ALLEGRO_WIDTH_DEFAULT; channel->height = ALLEGRO_HEIGHT_DEFAULT; channel->stride = round_up(channel->width, 32); + channel->framerate = ALLEGRO_FRAMERATE_DEFAULT; channel->colorspace = V4L2_COLORSPACE_REC709; channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; @@ -2062,7 +2040,6 @@ static void allegro_set_default_params(struct allegro_channel *channel) channel->sizeimage_encoded = estimate_stream_size(channel->width, channel->height); - channel->bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; channel->bitrate = maximum_bitrate(channel->level); channel->bitrate_peak = maximum_bitrate(channel->level); channel->cpb_size = maximum_cpb_size(channel->level); @@ -2163,16 +2140,33 @@ static void allegro_stop_streaming(struct vb2_queue *q) struct allegro_channel *channel = vb2_get_drv_priv(q); struct allegro_dev *dev = channel->dev; struct vb2_v4l2_buffer *buffer; + struct allegro_m2m_buffer *shadow, *tmp; v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: stop streaming\n", V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); if (V4L2_TYPE_IS_OUTPUT(q->type)) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->source_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&channel->shadow_list_lock); + list_for_each_entry_safe(shadow, tmp, + &channel->stream_shadow_list, head) { + list_del(&shadow->head); + v4l2_m2m_buf_done(&shadow->buf.vb, VB2_BUF_STATE_ERROR); + } + mutex_unlock(&channel->shadow_list_lock); + allegro_destroy_channel(channel); while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); @@ -2203,7 +2197,7 @@ static int allegro_queue_init(void *priv, src_vq->drv_priv = channel; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->ops = &allegro_queue_ops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); src_vq->lock = &channel->dev->lock; err = vb2_queue_init(src_vq); if (err) @@ -2216,7 +2210,7 @@ static int allegro_queue_init(void *priv, dst_vq->drv_priv = channel; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->ops = &allegro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->buf_struct_size = sizeof(struct allegro_m2m_buffer); dst_vq->lock = &channel->dev->lock; err = vb2_queue_init(dst_vq); if (err) @@ -2225,6 +2219,52 @@ static int allegro_queue_init(void *priv, return 0; } +static int allegro_clamp_qp(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *next_ctrl; + + if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_p_frame_qp; + else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP) + next_ctrl = channel->mpeg_video_h264_b_frame_qp; + else + return 0; + + /* Modify range automatically updates the value */ + __v4l2_ctrl_modify_range(next_ctrl, ctrl->val, 51, 1, ctrl->val); + + return allegro_clamp_qp(channel, next_ctrl); +} + +static int allegro_clamp_bitrate(struct allegro_channel *channel, + struct v4l2_ctrl *ctrl) +{ + struct v4l2_ctrl *ctrl_bitrate = channel->mpeg_video_bitrate; + struct v4l2_ctrl *ctrl_bitrate_peak = channel->mpeg_video_bitrate_peak; + + if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + ctrl_bitrate_peak->val < ctrl_bitrate->val) + ctrl_bitrate_peak->val = ctrl_bitrate->val; + + return 0; +} + +static int allegro_try_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + allegro_clamp_bitrate(channel, ctrl); + break; + } + + return 0; +} + static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) { struct allegro_channel *channel = container_of(ctrl->handler, @@ -2239,14 +2279,14 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LEVEL: channel->level = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - channel->bitrate_mode = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - channel->bitrate = ctrl->val; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + channel->frame_rc_enable = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - channel->bitrate_peak = ctrl->val; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + channel->bitrate = channel->mpeg_video_bitrate->val; + channel->bitrate_peak = channel->mpeg_video_bitrate_peak->val; + v4l2_ctrl_activate(channel->mpeg_video_bitrate_peak, + ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); break; case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: channel->cpb_size = ctrl->val; @@ -2254,12 +2294,18 @@ static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_GOP_SIZE: channel->gop_size = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: + case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: + allegro_clamp_qp(channel, ctrl); + break; } return 0; } static const struct v4l2_ctrl_ops allegro_ctrl_ops = { + .try_ctrl = allegro_try_ctrl, .s_ctrl = allegro_s_ctrl, }; @@ -2270,16 +2316,18 @@ static int allegro_open(struct file *file) struct allegro_channel *channel = NULL; struct v4l2_ctrl_handler *handler; u64 mask; + int ret; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) return -ENOMEM; v4l2_fh_init(&channel->fh, vdev); - file->private_data = &channel->fh; - v4l2_fh_add(&channel->fh); init_completion(&channel->completion); + INIT_LIST_HEAD(&channel->source_shadow_list); + INIT_LIST_HEAD(&channel->stream_shadow_list); + mutex_init(&channel->shadow_list_lock); channel->dev = dev; @@ -2298,11 +2346,42 @@ static int allegro_open(struct file *file) V4L2_CID_MPEG_VIDEO_H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + channel->mpeg_video_h264_i_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_max_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + channel->mpeg_video_h264_min_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 0); + channel->mpeg_video_h264_p_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_h264_b_frame_qp = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, + 0, 51, 1, 30); + channel->mpeg_video_frame_rc_enable = + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + false, 0x1, + true, false); channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, - channel->bitrate_mode); + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, &allegro_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, @@ -2328,8 +2407,15 @@ static int allegro_open(struct file *file) V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1); + if (handler->error != 0) { + ret = handler->error; + goto error; + } + channel->fh.ctrl_handler = handler; + v4l2_ctrl_cluster(3, &channel->mpeg_video_bitrate_mode); + channel->mcu_channel_id = -1; channel->user_id = -1; @@ -2341,7 +2427,20 @@ static int allegro_open(struct file *file) channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, allegro_queue_init); + if (IS_ERR(channel->fh.m2m_ctx)) { + ret = PTR_ERR(channel->fh.m2m_ctx); + goto error; + } + + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + return 0; + +error: + v4l2_ctrl_handler_free(handler); + kfree(channel); + return ret; } static int allegro_release(struct file *file) @@ -2636,6 +2735,46 @@ static int allegro_ioctl_streamon(struct file *file, void *priv, return v4l2_m2m_streamon(file, fh->m2m_ctx, type); } +static int allegro_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + timeperframe->numerator = channel->framerate.denominator; + timeperframe->denominator = channel->framerate.numerator; + + return 0; +} + +static int allegro_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct allegro_channel *channel = fh_to_channel(fh); + struct v4l2_fract *timeperframe; + int div; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + timeperframe = &a->parm.output.timeperframe; + + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) + return allegro_g_parm(file, fh, a); + + div = gcd(timeperframe->denominator, timeperframe->numerator); + channel->framerate.numerator = timeperframe->denominator / div; + channel->framerate.denominator = timeperframe->numerator / div; + + return 0; +} + static int allegro_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) { @@ -2674,6 +2813,9 @@ static const struct v4l2_ioctl_ops allegro_ioctl_ops = { .vidioc_encoder_cmd = allegro_encoder_cmd, .vidioc_enum_framesizes = allegro_enum_framesizes, + .vidioc_g_parm = allegro_g_parm, + .vidioc_s_parm = allegro_s_parm, + .vidioc_subscribe_event = allegro_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -2701,7 +2843,7 @@ static int allegro_register_device(struct allegro_dev *dev) video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; video_set_drvdata(video_dev, dev); - return video_register_device(video_dev, VFL_TYPE_GRABBER, 0); + return video_register_device(video_dev, VFL_TYPE_VIDEO, 0); } static void allegro_device_run(void *priv) @@ -2714,18 +2856,26 @@ static void allegro_device_run(void *priv) dma_addr_t src_uv; dma_addr_t dst_addr; unsigned long dst_size; + u64 src_handle; + u64 dst_handle; - dst_buf = v4l2_m2m_next_dst_buf(channel->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); - allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size); + dst_handle = allegro_put_buffer(channel, &channel->stream_shadow_list, + dst_buf); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size, + dst_handle); - src_buf = v4l2_m2m_next_src_buf(channel->fh.m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); src_buf->sequence = channel->osequence++; - src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); src_uv = src_y + (channel->stride * channel->height); - allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv); + src_handle = allegro_put_buffer(channel, &channel->source_shadow_list, + src_buf); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv, src_handle); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); } static const struct v4l2_m2m_ops allegro_m2m_ops = { diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.c b/drivers/staging/media/allegro-dvt/allegro-mail.c new file mode 100644 index 000000000000..df0d8d26a6fb --- /dev/null +++ b/drivers/staging/media/allegro-dvt/allegro-mail.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Helper functions for handling messages that are send via mailbox to the + * Allegro VCU firmware. + */ + +#include <linux/export.h> + +#include "allegro-mail.h" + +const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} +EXPORT_SYMBOL(msg_type_name); diff --git a/drivers/staging/media/allegro-dvt/allegro-mail.h b/drivers/staging/media/allegro-dvt/allegro-mail.h new file mode 100644 index 000000000000..1fd36f65be78 --- /dev/null +++ b/drivers/staging/media/allegro-dvt/allegro-mail.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Allegro VCU firmware mailbox mail definitions + */ + +#ifndef ALLEGRO_MAIL_H +#define ALLEGRO_MAIL_H + +#include <linux/kernel.h> + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +const char *msg_type_name(enum mcu_msg_type type); + +struct mcu_msg_header { + u16 length; /* length of the body in bytes */ + u16 type; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +} __attribute__ ((__packed__)); + +struct create_channel_param { + u16 width; + u16 height; + u32 format; + u32 colorspace; + u32 src_mode; + u8 profile; + u16 constraint_set_flags; + s8 codec; + u16 level; + u16 tier; + u32 sps_param; + u32 pps_param; + + u32 enc_option; +#define AL_OPT_WPP BIT(0) +#define AL_OPT_TILE BIT(1) +#define AL_OPT_LF BIT(2) +#define AL_OPT_LF_X_SLICE BIT(3) +#define AL_OPT_LF_X_TILE BIT(4) +#define AL_OPT_SCL_LST BIT(5) +#define AL_OPT_CONST_INTRA_PRED BIT(6) +#define AL_OPT_QP_TAB_RELATIVE BIT(7) +#define AL_OPT_FIX_PREDICTOR BIT(8) +#define AL_OPT_CUSTOM_LDA BIT(9) +#define AL_OPT_ENABLE_AUTO_QP BIT(10) +#define AL_OPT_ADAPT_AUTO_QP BIT(11) +#define AL_OPT_TRANSFO_SKIP BIT(13) +#define AL_OPT_FORCE_REC BIT(15) +#define AL_OPT_FORCE_MV_OUT BIT(16) +#define AL_OPT_FORCE_MV_CLIP BIT(17) +#define AL_OPT_LOWLAT_SYNC BIT(18) +#define AL_OPT_LOWLAT_INT BIT(19) +#define AL_OPT_RDO_COST_MODE BIT(20) + + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_idr; + u32 freq_lt; + u32 gdr_mode; + u16 gop_length; + u8 num_b; + u8 freq_golden_ref; + + u32 subframe_latency; + u32 lda_control_mode; + u32 unknown41; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + struct create_channel_param param; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 pps_param; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + struct mcu_msg_push_buffers_internal_buffer buffer[0]; +} __attribute__ ((__packed__)); + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 stream_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame { + struct mcu_msg_header header; + u32 channel_id; + u32 reserved; + + u32 encoding_options; +#define AL_OPT_USE_QP_TABLE BIT(0) +#define AL_OPT_FORCE_LOAD BIT(1) +#define AL_OPT_USE_L2 BIT(2) +#define AL_OPT_DISABLE_INTRA BIT(3) +#define AL_OPT_DEPENDENT_SLICES BIT(4) + + s16 pps_qp; + u16 padding; + u64 user_param; + u64 src_handle; + + u32 request_options; +#define AL_OPT_SCENE_CHANGE BIT(0) +#define AL_OPT_RESTART_GOP BIT(1) +#define AL_OPT_USE_LONG_TERM BIT(2) +#define AL_OPT_UPDATE_PARAMS BIT(3) + + /* u32 scene_change_delay (optional) */ + /* rate control param (optional) */ + /* gop param (optional) */ + u32 src_y; + u32 src_uv; + u32 stride; + u32 ep2; + u64 ep2_v; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame_response { + struct mcu_msg_header header; + u32 channel_id; + u64 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; +} __attribute__ ((__packed__)); + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +#endif diff --git a/drivers/staging/media/hantro/Kconfig b/drivers/staging/media/hantro/Kconfig index de77fe6554e7..99aed9a5b0b9 100644 --- a/drivers/staging/media/hantro/Kconfig +++ b/drivers/staging/media/hantro/Kconfig @@ -1,19 +1,27 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_HANTRO tristate "Hantro VPU driver" - depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on ARCH_MXC || ARCH_ROCKCHIP || COMPILE_TEST depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER depends on MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV help - Support for the Hantro IP based Video Processing Unit present on - Rockchip SoC, which accelerates video and image encoding and - decoding. + Support for the Hantro IP based Video Processing Units present on + Rockchip and NXP i.MX8M SoCs, which accelerate video and image + encoding and decoding. To compile this driver as a module, choose M here: the module will be called hantro-vpu. +config VIDEO_HANTRO_IMX8M + bool "Hantro VPU i.MX8M support" + depends on VIDEO_HANTRO + depends on ARCH_MXC || COMPILE_TEST + default y + help + Enable support for i.MX8M SoCs. + config VIDEO_HANTRO_ROCKCHIP bool "Hantro VPU Rockchip support" depends on VIDEO_HANTRO diff --git a/drivers/staging/media/hantro/Makefile b/drivers/staging/media/hantro/Makefile index 496b30c3c396..68c29a9c4946 100644 --- a/drivers/staging/media/hantro/Makefile +++ b/drivers/staging/media/hantro/Makefile @@ -16,6 +16,9 @@ hantro-vpu-y += \ hantro_mpeg2.o \ hantro_vp8.o +hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \ + imx8m_vpu_hw.o + hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ rk3288_vpu_hw.o \ rk3399_vpu_hw.o diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h index b0faa43b3f79..327ddef45345 100644 --- a/drivers/staging/media/hantro/hantro.h +++ b/drivers/staging/media/hantro/hantro.h @@ -423,7 +423,7 @@ hantro_get_dst_buf(struct hantro_ctx *ctx) static inline bool hantro_needs_postproc(struct hantro_ctx *ctx, const struct hantro_fmt *fmt) { - return fmt->fourcc != V4L2_PIX_FMT_NV12; + return !hantro_is_encoder_ctx(ctx) && fmt->fourcc != V4L2_PIX_FMT_NV12; } static inline dma_addr_t diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c index c98835326135..ace13973e2d0 100644 --- a/drivers/staging/media/hantro/hantro_drv.c +++ b/drivers/staging/media/hantro/hantro_drv.c @@ -362,6 +362,16 @@ static const struct hantro_ctrl controls[] = { .max = V4L2_MPEG_VIDEO_H264_START_CODE_ANNEX_B, }, }, { + .codec = HANTRO_H264_DECODER, + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + } + }, { }, }; @@ -489,6 +499,9 @@ static const struct of_device_id of_hantro_match[] = { { .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, }, { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, }, #endif +#ifdef CONFIG_VIDEO_HANTRO_IMX8M + { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, +#endif { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_hantro_match); @@ -664,7 +677,7 @@ static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) video_set_drvdata(vfd, vpu); - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c index 0d8afc3e5d71..b22418436823 100644 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c @@ -67,12 +67,23 @@ hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(i)); + } - reg = get_unaligned_be32(&chroma_qtable[i]); + for (i = 0; i < H1_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, H1_REG_JPEG_CHROMA_QUAT(i)); } } @@ -103,8 +114,8 @@ void hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) hantro_h1_set_src_img_ctrl(vpu, ctx); hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); hantro_h1_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 | H1_REG_AXI_CTRL_INPUT_SWAP16 diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h index 2398d4c1f207..7dfc9bad7297 100644 --- a/drivers/staging/media/hantro/hantro_hw.h +++ b/drivers/staging/media/hantro/hantro_hw.h @@ -151,6 +151,7 @@ enum hantro_enc_fmt { extern const struct hantro_variant rk3399_vpu_variant; extern const struct hantro_variant rk3328_vpu_variant; extern const struct hantro_variant rk3288_vpu_variant; +extern const struct hantro_variant imx8mq_vpu_variant; extern const struct hantro_postproc_regs hantro_g1_postproc_regs; diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c index 125eb41f2ede..36c140fc6a36 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ b/drivers/staging/media/hantro/hantro_jpeg.c @@ -23,19 +23,21 @@ #define HUFF_CHROMA_AC_OFF 409 /* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K.3, I + * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 */ static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x7c, 0x8c, 0x97, 0xa1, - 0x0c, 0x0c, 0x0e, 0x13, 0x7e, 0x9e, 0xa0, 0x9b, - 0x0e, 0x0d, 0x10, 0x18, 0x8c, 0x9d, 0xa9, 0x9c, - 0x0e, 0x11, 0x16, 0x1d, 0x97, 0xbb, 0xb4, 0xa2, - 0x12, 0x16, 0x25, 0x38, 0xa8, 0x6d, 0x67, 0xb1, - 0x18, 0x23, 0x37, 0x40, 0xb5, 0x68, 0x71, 0xc0, + 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, + 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, + 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, + 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, + 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, + 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0xc7, + 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 }; +static unsigned char luma_q_table_reordered[ARRAY_SIZE(luma_q_table)]; + static const unsigned char chroma_q_table[] = { 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, @@ -47,6 +49,30 @@ static const unsigned char chroma_q_table[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 }; +static unsigned char chroma_q_table_reordered[ARRAY_SIZE(chroma_q_table)]; + +static const unsigned char zigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const u32 hw_reorder[64] = { + 0, 8, 16, 24, 1, 9, 17, 25, + 32, 40, 48, 56, 33, 41, 49, 57, + 2, 10, 18, 26, 3, 11, 19, 27, + 34, 42, 50, 58, 35, 43, 51, 59, + 4, 12, 20, 28, 5, 13, 21, 29, + 36, 44, 52, 60, 37, 45, 53, 61, + 6, 14, 22, 30, 7, 15, 23, 31, + 38, 46, 54, 62, 39, 47, 55, 63 +}; + /* Huffman tables are shared with CODA */ static const unsigned char luma_dc_table[] = { 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -225,20 +251,29 @@ static const unsigned char hantro_jpeg_header[JPEG_HEADER_SIZE] = { 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, }; +static unsigned char jpeg_scale_qp(const unsigned char qp, int scale) +{ + unsigned int temp; + + temp = DIV_ROUND_CLOSEST((unsigned int)qp * scale, 100); + if (temp <= 0) + temp = 1; + if (temp > 255) + temp = 255; + + return (unsigned char)temp; +} + static void -jpeg_scale_quant_table(unsigned char *q_tab, +jpeg_scale_quant_table(unsigned char *file_q_tab, + unsigned char *reordered_q_tab, const unsigned char *tab, int scale) { - unsigned int temp; int i; for (i = 0; i < 64; i++) { - temp = DIV_ROUND_CLOSEST((unsigned int)tab[i] * scale, 100); - if (temp <= 0) - temp = 1; - if (temp > 255) - temp = 255; - q_tab[i] = (unsigned char)temp; + file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -256,17 +291,18 @@ static void jpeg_set_quality(unsigned char *buffer, int quality) scale = 200 - 2 * quality; jpeg_scale_quant_table(buffer + LUMA_QUANT_OFF, + luma_q_table_reordered, luma_q_table, scale); jpeg_scale_quant_table(buffer + CHROMA_QUANT_OFF, + chroma_q_table_reordered, chroma_q_table, scale); } -unsigned char * -hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index) +unsigned char *hantro_jpeg_get_qtable(int index) { if (index == 0) - return ctx->buffer + LUMA_QUANT_OFF; - return ctx->buffer + CHROMA_QUANT_OFF; + return luma_q_table_reordered; + return chroma_q_table_reordered; } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h index 9e8397c71388..9474a00277f8 100644 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ b/drivers/staging/media/hantro/hantro_jpeg.h @@ -9,5 +9,5 @@ struct hantro_jpeg_ctx { unsigned char *buffer; }; -unsigned char *hantro_jpeg_get_qtable(struct hantro_jpeg_ctx *ctx, int index); +unsigned char *hantro_jpeg_get_qtable(int index); void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c index 0198bcda26b7..f4ae2cee0f18 100644 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ b/drivers/staging/media/hantro/hantro_v4l2.c @@ -295,7 +295,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f, * +---------------------------+ */ if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && - !hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) + !hantro_needs_postproc(ctx, fmt)) pix_mp->plane_fmt[0].sizeimage += 64 * MB_WIDTH(pix_mp->width) * MB_WIDTH(pix_mp->height) + 32; diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c new file mode 100644 index 000000000000..cb2420c5526e --- /dev/null +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hantro VPU codec driver + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> + +#include "hantro.h" +#include "hantro_jpeg.h" +#include "hantro_g1_regs.h" + +#define CTRL_SOFT_RESET 0x00 +#define RESET_G1 BIT(1) +#define RESET_G2 BIT(0) + +#define CTRL_CLOCK_ENABLE 0x04 +#define CLOCK_G1 BIT(1) +#define CLOCK_G2 BIT(0) + +#define CTRL_G1_DEC_FUSE 0x08 +#define CTRL_G1_PP_FUSE 0x0c +#define CTRL_G2_DEC_FUSE 0x10 + +static void imx8m_soft_reset(struct hantro_dev *vpu, u32 reset_bits) +{ + u32 val; + + /* Assert */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val &= ~reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); + + udelay(2); + + /* Release */ + val = readl(vpu->ctrl_base + CTRL_SOFT_RESET); + val |= reset_bits; + writel(val, vpu->ctrl_base + CTRL_SOFT_RESET); +} + +static void imx8m_clk_enable(struct hantro_dev *vpu, u32 clock_bits) +{ + u32 val; + + val = readl(vpu->ctrl_base + CTRL_CLOCK_ENABLE); + val |= clock_bits; + writel(val, vpu->ctrl_base + CTRL_CLOCK_ENABLE); +} + +static int imx8mq_runtime_resume(struct hantro_dev *vpu) +{ + int ret; + + ret = clk_bulk_prepare_enable(vpu->variant->num_clocks, vpu->clocks); + if (ret) { + dev_err(vpu->dev, "Failed to enable clocks\n"); + return ret; + } + + imx8m_soft_reset(vpu, RESET_G1 | RESET_G2); + imx8m_clk_enable(vpu, CLOCK_G1 | CLOCK_G2); + + /* Set values of the fuse registers */ + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_DEC_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G1_PP_FUSE); + writel(0xffffffff, vpu->ctrl_base + CTRL_G2_DEC_FUSE); + + clk_bulk_disable_unprepare(vpu->variant->num_clocks, vpu->clocks); + + return 0; +} + +/* + * Supported formats. + */ + +static const struct hantro_fmt imx8m_vpu_postproc_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .codec_mode = HANTRO_MODE_NONE, + }, +}; + +static const struct hantro_fmt imx8m_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = HANTRO_MODE_NONE, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = HANTRO_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 1920, + .step_width = MB_DIM, + .min_height = 48, + .max_height = 1088, + .step_height = MB_DIM, + }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8_FRAME, + .codec_mode = HANTRO_MODE_VP8_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 3840, + .step_width = 16, + .min_height = 48, + .max_height = 2160, + .step_height = 16, + }, + }, + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .codec_mode = HANTRO_MODE_H264_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 3840, + .step_width = MB_DIM, + .min_height = 48, + .max_height = 2160, + .step_height = MB_DIM, + }, + }, +}; + +static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id) +{ + struct hantro_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, G1_REG_INTERRUPT); + state = (status & G1_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, G1_REG_INTERRUPT); + vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); + + hantro_irq_done(vpu, 0, state); + + return IRQ_HANDLED; +} + +static int imx8mq_vpu_hw_init(struct hantro_dev *vpu) +{ + vpu->dec_base = vpu->reg_bases[0]; + vpu->ctrl_base = vpu->reg_bases[vpu->variant->num_regs - 1]; + + return 0; +} + +static void imx8m_vpu_g1_reset(struct hantro_ctx *ctx) +{ + struct hantro_dev *vpu = ctx->dev; + + imx8m_soft_reset(vpu, RESET_G1); +} + +/* + * Supported codec ops. + */ + +static const struct hantro_codec_ops imx8mq_vpu_codec_ops[] = { + [HANTRO_MODE_MPEG2_DEC] = { + .run = hantro_g1_mpeg2_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_mpeg2_dec_init, + .exit = hantro_mpeg2_dec_exit, + }, + [HANTRO_MODE_VP8_DEC] = { + .run = hantro_g1_vp8_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_vp8_dec_init, + .exit = hantro_vp8_dec_exit, + }, + [HANTRO_MODE_H264_DEC] = { + .run = hantro_g1_h264_dec_run, + .reset = imx8m_vpu_g1_reset, + .init = hantro_h264_dec_init, + .exit = hantro_h264_dec_exit, + }, +}; + +/* + * VPU variants. + */ + +static const struct hantro_irq imx8mq_irqs[] = { + { "g1", imx8m_vpu_g1_irq }, + { "g2", NULL /* TODO: imx8m_vpu_g2_irq */ }, +}; + +static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; +static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; + +const struct hantro_variant imx8mq_vpu_variant = { + .dec_fmts = imx8m_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), + .postproc_fmts = imx8m_vpu_postproc_fmts, + .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_postproc_fmts), + .postproc_regs = &hantro_g1_postproc_regs, + .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | + HANTRO_H264_DECODER, + .codec_ops = imx8mq_vpu_codec_ops, + .init = imx8mq_vpu_hw_init, + .runtime_resume = imx8mq_runtime_resume, + .irqs = imx8mq_irqs, + .num_irqs = ARRAY_SIZE(imx8mq_irqs), + .clk_names = imx8mq_clk_names, + .num_clocks = ARRAY_SIZE(imx8mq_clk_names), + .reg_names = imx8mq_reg_names, + .num_regs = ARRAY_SIZE(imx8mq_reg_names) +}; diff --git a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c index 4c2d43fb6fd1..3498e6124acd 100644 --- a/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/hantro/rk3399_vpu_hw_jpeg_enc.c @@ -18,9 +18,8 @@ * * Quantization luma table values are written to registers * VEPU_swreg_0-VEPU_swreg_15, and chroma table values to - * VEPU_swreg_16-VEPU_swreg_31. - * - * JPEG zigzag order is expected on the quantization tables. + * VEPU_swreg_16-VEPU_swreg_31. A special order is needed, neither + * zigzag, nor linear. */ #include <asm/unaligned.h> @@ -98,12 +97,23 @@ rk3399_vpu_jpeg_enc_set_qtable(struct hantro_dev *vpu, unsigned char *chroma_qtable) { u32 reg, i; + __be32 *luma_qtable_p; + __be32 *chroma_qtable_p; + + luma_qtable_p = (__be32 *)luma_qtable; + chroma_qtable_p = (__be32 *)chroma_qtable; + /* + * Quantization table registers must be written in contiguous blocks. + * DO NOT collapse the below two "for" loops into one. + */ for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { - reg = get_unaligned_be32(&luma_qtable[i]); + reg = get_unaligned_be32(&luma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(i)); + } - reg = get_unaligned_be32(&chroma_qtable[i]); + for (i = 0; i < VEPU_JPEG_QUANT_TABLE_COUNT; i++) { + reg = get_unaligned_be32(&chroma_qtable_p[i]); vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_CHROMA_QUAT(i)); } } @@ -134,8 +144,8 @@ void rk3399_vpu_jpeg_enc_run(struct hantro_ctx *ctx) rk3399_vpu_set_src_img_ctrl(vpu, ctx); rk3399_vpu_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf); rk3399_vpu_jpeg_enc_set_qtable(vpu, - hantro_jpeg_get_qtable(&jpeg_ctx, 0), - hantro_jpeg_get_qtable(&jpeg_ctx, 1)); + hantro_jpeg_get_qtable(0), + hantro_jpeg_get_qtable(1)); reg = VEPU_REG_OUTPUT_SWAP32 | VEPU_REG_OUTPUT_SWAP16 diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 7712e7be8625..d37b776ff86d 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -643,7 +643,7 @@ static int capture_open(struct file *file) if (ret) v4l2_err(priv->src_sd, "v4l2_fh_open failed\n"); - ret = v4l2_pipeline_pm_use(&vfd->entity, 1); + ret = v4l2_pipeline_pm_get(&vfd->entity); if (ret) v4l2_fh_release(file); @@ -664,7 +664,7 @@ static int capture_release(struct file *file) vq->owner = NULL; } - v4l2_pipeline_pm_use(&vfd->entity, 0); + v4l2_pipeline_pm_put(&vfd->entity); v4l2_fh_release(file); mutex_unlock(&priv->mutex); @@ -742,7 +742,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(sd, "Failed to register video device\n"); return ret; @@ -778,7 +778,7 @@ int imx_media_capture_device_register(struct imx_media_video_dev *vdev) /* setup default format */ fmt_src.pad = priv->src_sd_pad; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; - v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt_src); if (ret) { v4l2_err(sd, "failed to get src_sd format\n"); goto unreg; diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c index 2b635ebf62d6..2cc77f6e84b6 100644 --- a/drivers/staging/media/imx/imx-media-csc-scaler.c +++ b/drivers/staging/media/imx/imx-media-csc-scaler.c @@ -849,7 +849,7 @@ int imx_media_csc_scaler_device_register(struct imx_media_video_dev *vdev) vfd->v4l2_dev = &priv->md->v4l2_dev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(vfd->v4l2_dev, "Failed to register video device\n"); return ret; diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index b60ed4f22f6d..e76a6a85baa3 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -457,7 +457,8 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_SGBRG16: case V4L2_PIX_FMT_SGRBG16: case V4L2_PIX_FMT_SRGGB16: - case V4L2_PIX_FMT_Y16: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y12: burst_size = 8; passthrough_bits = 16; break; @@ -1459,6 +1460,8 @@ static void csi_try_fmt(struct csi_priv *priv, /* propagate colorimetry from sink */ sdformat->format.colorspace = infmt->colorspace; sdformat->format.xfer_func = infmt->xfer_func; + sdformat->format.quantization = infmt->quantization; + sdformat->format.ycbcr_enc = infmt->ycbcr_enc; break; case CSI_SINK_PAD: diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 0788a1874557..fae981698c49 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -161,17 +161,24 @@ static const struct imx_media_pixfmt rgb_formats[] = { .bayer = true, }, { .fourcc = V4L2_PIX_FMT_GREY, - .codes = {MEDIA_BUS_FMT_Y8_1X8}, - .cs = IPUV3_COLORSPACE_RGB, - .bpp = 8, - .bayer = true, - }, { - .fourcc = V4L2_PIX_FMT_Y16, .codes = { + MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y12_1X12, }, .cs = IPUV3_COLORSPACE_RGB, + .bpp = 8, + .bayer = true, + }, { + .fourcc = V4L2_PIX_FMT_Y10, + .codes = {MEDIA_BUS_FMT_Y10_1X10}, + .cs = IPUV3_COLORSPACE_RGB, + .bpp = 16, + .bayer = true, + }, { + .fourcc = V4L2_PIX_FMT_Y12, + .codes = {MEDIA_BUS_FMT_Y12_1X12}, + .cs = IPUV3_COLORSPACE_RGB, .bpp = 16, .bayer = true, }, diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index db30e2c70f2f..acbdffb77668 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -292,7 +292,7 @@ static void imx7_csi_hw_disable(struct imx7_csi *csi) static void imx7_csi_dma_reflash(struct imx7_csi *csi) { - u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr3; cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); cr3 |= BIT_DMA_REFLASH_RFF; @@ -804,6 +804,22 @@ static int imx7_csi_configure(struct imx7_csi *csi) case V4L2_PIX_FMT_YUYV: cr18 |= BIT_MIPI_DATA_FORMAT_YUV422_8B; break; + case V4L2_PIX_FMT_GREY: + if (in_code == MEDIA_BUS_FMT_Y8_1X8) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; + else if (in_code == MEDIA_BUS_FMT_Y10_1X10) + cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; + else + cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; + break; + case V4L2_PIX_FMT_Y10: + cr18 |= BIT_MIPI_DATA_FORMAT_RAW10; + cr1 |= BIT_PIXEL_BIT; + break; + case V4L2_PIX_FMT_Y12: + cr18 |= BIT_MIPI_DATA_FORMAT_RAW12; + cr1 |= BIT_PIXEL_BIT; + break; case V4L2_PIX_FMT_SBGGR8: cr18 |= BIT_MIPI_DATA_FORMAT_RAW8; break; @@ -1009,10 +1025,13 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi, sdformat->format.width = in_fmt->width; sdformat->format.height = in_fmt->height; sdformat->format.code = in_fmt->code; + sdformat->format.field = in_fmt->field; *cc = in_cc; sdformat->format.colorspace = in_fmt->colorspace; sdformat->format.xfer_func = in_fmt->xfer_func; + sdformat->format.quantization = in_fmt->quantization; + sdformat->format.ycbcr_enc = in_fmt->ycbcr_enc; break; case IMX7_CSI_PAD_SINK: *cc = imx_media_find_mbus_format(sdformat->format.code, @@ -1023,6 +1042,9 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi, false); sdformat->format.code = (*cc)->codes[0]; } + + if (sdformat->format.field != V4L2_FIELD_INTERLACED) + sdformat->format.field = V4L2_FIELD_NONE; break; default: return -EINVAL; diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index 383abecb3bec..de17a1d22873 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -280,6 +280,18 @@ static const struct csis_pix_format mipi_csis_formats[] = { .code = MEDIA_BUS_FMT_YUYV8_2X8, .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT, .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8, + .data_alignment = 8, + }, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10, + .data_alignment = 16, + }, { + .code = MEDIA_BUS_FMT_Y12_1X12, + .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW12, + .data_alignment = 16, } }; @@ -301,6 +313,7 @@ static int mipi_csis_dump_regs(struct csi_state *state) { 0x20, "DPHYSTS" }, { 0x10, "INTMSK" }, { 0x40, "CONFIG_CH0" }, + { 0x44, "RESOL_CH0" }, { 0xC0, "DBG_CONFIG" }, { 0x38, "DPHYSLAVE_L" }, { 0x3C, "DPHYSLAVE_H" }, @@ -417,6 +430,7 @@ static void mipi_csis_set_params(struct csi_state *state) val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL); val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK; val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET; + val |= MIPI_CSIS_CMN_CTRL_INTER_MODE; mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val); __mipi_csis_set_format(state); @@ -577,7 +591,7 @@ static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable) state->flags |= ST_STREAMING; } else { v4l2_subdev_call(state->src_sd, video, s_stream, 0); - ret = v4l2_subdev_call(state->src_sd, core, s_power, 1); + ret = v4l2_subdev_call(state->src_sd, core, s_power, 0); mipi_csis_stop_stream(state); state->flags &= ~ST_STREAMING; if (state->debug) diff --git a/drivers/staging/media/ipu3/TODO b/drivers/staging/media/ipu3/TODO index dc356d6f03c4..52063b651810 100644 --- a/drivers/staging/media/ipu3/TODO +++ b/drivers/staging/media/ipu3/TODO @@ -13,8 +13,6 @@ staging directory. - Elaborate the functionality of different selection rectangles in driver documentation. This may require driver changes as well. -- More detailed documentation on calculating BDS, GCD etc. sizes needed. - - Document different operation modes, and which buffer queues are relevant in each mode. To process an image, which queues require a buffer an in which ones is it optional? diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index f36de501edc6..4f04fe838b0c 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -210,12 +210,12 @@ static int imgu_hw_wait(void __iomem *base, int reg, u32 mask, u32 cmp) /* Initialize the IPU3 CSS hardware and associated h/w blocks */ -int imgu_css_set_powerup(struct device *dev, void __iomem *base) +int imgu_css_set_powerup(struct device *dev, void __iomem *base, + unsigned int freq) { - static const unsigned int freq = 450; u32 pm_ctrl, state, val; - dev_dbg(dev, "%s\n", __func__); + dev_dbg(dev, "%s with freq %u\n", __func__, freq); /* Clear the CSS busy signal */ readl(base + IMGU_REG_GP_BUSY); writel(0, base + IMGU_REG_GP_BUSY); diff --git a/drivers/staging/media/ipu3/ipu3-css.h b/drivers/staging/media/ipu3/ipu3-css.h index 6b8bab27ab1f..6108a068b228 100644 --- a/drivers/staging/media/ipu3/ipu3-css.h +++ b/drivers/staging/media/ipu3/ipu3-css.h @@ -187,7 +187,8 @@ bool imgu_css_is_streaming(struct imgu_css *css); bool imgu_css_pipe_queue_empty(struct imgu_css *css, unsigned int pipe); /******************* css hw *******************/ -int imgu_css_set_powerup(struct device *dev, void __iomem *base); +int imgu_css_set_powerup(struct device *dev, void __iomem *base, + unsigned int freq); void imgu_css_set_powerdown(struct device *dev, void __iomem *base); int imgu_css_irq_ack(struct imgu_css *css); diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index 3d969b0522ab..5f3ff964f3e7 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -130,7 +130,7 @@ static u32 *imgu_mmu_alloc_page_table(u32 pteval) for (pte = 0; pte < IPU3_PT_PTES; pte++) pt[pte] = pteval; - set_memory_uc((unsigned long int)pt, IPU3_PT_ORDER); + set_memory_uc((unsigned long)pt, IPU3_PT_ORDER); return pt; } @@ -141,7 +141,7 @@ static u32 *imgu_mmu_alloc_page_table(u32 pteval) */ static void imgu_mmu_free_page_table(u32 *pt) { - set_memory_wb((unsigned long int)pt, IPU3_PT_ORDER); + set_memory_wb((unsigned long)pt, IPU3_PT_ORDER); free_page((unsigned long)pt); } diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 569e27b824c8..09c8ede1457c 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -1245,7 +1245,7 @@ static int imgu_v4l2_node_setup(struct imgu_device *imgu, unsigned int pipe, vdev->queue = &node->vbq; vdev->vfl_dir = node->output ? VFL_DIR_TX : VFL_DIR_RX; video_set_drvdata(vdev, imgu); - r = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + r = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (r) { dev_err(dev, "failed to register video device (%d)", r); media_entity_cleanup(&vdev->entity); diff --git a/drivers/staging/media/ipu3/ipu3.c b/drivers/staging/media/ipu3/ipu3.c index 06a61f31ca50..4d53aad31483 100644 --- a/drivers/staging/media/ipu3/ipu3.c +++ b/drivers/staging/media/ipu3/ipu3.c @@ -345,8 +345,20 @@ failed: static int imgu_powerup(struct imgu_device *imgu) { int r; + unsigned int pipe; + unsigned int freq = 200; + struct v4l2_mbus_framefmt *fmt; + + /* input larger than 2048*1152, ask imgu to work on high freq */ + for_each_set_bit(pipe, imgu->css.enabled_pipes, IMGU_MAX_PIPE_NUM) { + fmt = &imgu->imgu_pipe[pipe].nodes[IMGU_NODE_IN].pad_fmt; + dev_dbg(&imgu->pci_dev->dev, "pipe %u input format = %ux%u", + pipe, fmt->width, fmt->height); + if ((fmt->width * fmt->height) >= (2048 * 1152)) + freq = 450; + } - r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&imgu->pci_dev->dev, imgu->base, freq); if (r) return r; @@ -666,7 +678,7 @@ static int imgu_pci_probe(struct pci_dev *pci_dev, atomic_set(&imgu->qbuf_barrier, 0); init_waitqueue_head(&imgu->buf_drain_wq); - r = imgu_css_set_powerup(&pci_dev->dev, imgu->base); + r = imgu_css_set_powerup(&pci_dev->dev, imgu->base, 200); if (r) { dev_err(&pci_dev->dev, "failed to power up CSS (%d)\n", r); diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile index 6bea129084b7..6e726af84ac9 100644 --- a/drivers/staging/media/meson/vdec/Makefile +++ b/drivers/staging/media/meson/vdec/Makefile @@ -2,7 +2,7 @@ # Makefile for Amlogic meson video decoder driver meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o -meson-vdec-objs += vdec_1.o -meson-vdec-objs += codec_mpeg12.o +meson-vdec-objs += vdec_1.o vdec_hevc.o +meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o codec_vp9.o obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c new file mode 100644 index 000000000000..c61128fc4bb9 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_h264.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "vdec_helpers.h" +#include "dos_regs.h" +#include "codec_h264.h" + +#define SIZE_EXT_FW (20 * SZ_1K) +#define SIZE_WORKSPACE 0x1ee000 +#define SIZE_SEI (8 * SZ_1K) + +/* + * Offset added by the firmware which must be substracted + * from the workspace phyaddr + */ +#define WORKSPACE_BUF_OFFSET 0x1000000 + +/* ISR status */ +#define CMD_MASK GENMASK(7, 0) +#define CMD_SRC_CHANGE 1 +#define CMD_FRAMES_READY 2 +#define CMD_FATAL_ERROR 6 +#define CMD_BAD_WIDTH 7 +#define CMD_BAD_HEIGHT 8 + +#define SEI_DATA_READY BIT(15) + +/* Picture type */ +#define PIC_TOP_BOT 5 +#define PIC_BOT_TOP 6 + +/* Size of Motion Vector per macroblock */ +#define MB_MV_SIZE 96 + +/* Frame status data */ +#define PIC_STRUCT_BIT 5 +#define PIC_STRUCT_MASK GENMASK(2, 0) +#define BUF_IDX_MASK GENMASK(4, 0) +#define ERROR_FLAG BIT(9) +#define OFFSET_BIT 16 +#define OFFSET_MASK GENMASK(15, 0) + +/* Bitstream parsed data */ +#define MB_TOTAL_BIT 8 +#define MB_TOTAL_MASK GENMASK(15, 0) +#define MB_WIDTH_MASK GENMASK(7, 0) +#define MAX_REF_BIT 24 +#define MAX_REF_MASK GENMASK(6, 0) +#define AR_IDC_BIT 16 +#define AR_IDC_MASK GENMASK(7, 0) +#define AR_PRESENT_FLAG BIT(0) +#define AR_EXTEND 0xff + +/* + * Buffer to send to the ESPARSER to signal End Of Stream for H.264. + * This is a 16x16 encoded picture that will trigger drain firmware-side. + * There is no known alternative. + */ +static const u8 eos_sequence[SZ_4K] = { + 0x00, 0x00, 0x00, 0x01, 0x06, 0x05, 0xff, 0xe4, 0xdc, 0x45, 0xe9, 0xbd, + 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef, + 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x20, + 0x36, 0x37, 0x20, 0x72, 0x31, 0x31, 0x33, 0x30, 0x20, 0x38, 0x34, 0x37, + 0x35, 0x39, 0x37, 0x37, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, + 0x2f, 0x4d, 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, + 0x63, 0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, + 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, 0x33, 0x2d, 0x32, 0x30, + 0x30, 0x39, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, + 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, + 0x6d, 0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, 0x31, 0x20, 0x72, 0x65, + 0x66, 0x3d, 0x31, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3d, + 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, + 0x65, 0x3d, 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, + 0x6d, 0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, + 0x3d, 0x36, 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, + 0x30, 0x3a, 0x30, 0x2e, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x66, 0x3d, 0x30, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, + 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, + 0x73, 0x3d, 0x30, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, + 0x20, 0x63, 0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, + 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x63, 0x68, + 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, + 0x73, 0x3d, 0x31, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x63, + 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x6d, 0x62, 0x61, 0x66, + 0x66, 0x3d, 0x30, 0x20, 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, + 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30, + 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, + 0x32, 0x35, 0x20, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x63, 0x75, 0x74, 0x3d, + 0x34, 0x30, 0x20, 0x72, 0x63, 0x3d, 0x61, 0x62, 0x72, 0x20, 0x62, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x30, 0x20, 0x72, 0x61, 0x74, + 0x65, 0x74, 0x6f, 0x6c, 0x3d, 0x31, 0x2e, 0x30, 0x20, 0x71, 0x63, 0x6f, + 0x6d, 0x70, 0x3d, 0x30, 0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, + 0x6e, 0x3d, 0x31, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x35, + 0x31, 0x20, 0x71, 0x70, 0x73, 0x74, 0x65, 0x70, 0x3d, 0x34, 0x20, 0x69, + 0x70, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x3d, 0x31, 0x2e, 0x34, 0x30, + 0x20, 0x61, 0x71, 0x3d, 0x31, 0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x01, 0x67, 0x4d, 0x40, 0x0a, 0x9a, 0x74, 0xf4, 0x20, + 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00, 0x06, 0x51, 0xe2, 0x44, 0xd4, + 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x32, 0xc8, 0x00, 0x00, 0x00, 0x01, + 0x65, 0x88, 0x80, 0x20, 0x00, 0x08, 0x7f, 0xea, 0x6a, 0xe2, 0x99, 0xb6, + 0x57, 0xae, 0x49, 0x30, 0xf5, 0xfe, 0x5e, 0x46, 0x0b, 0x72, 0x44, 0xc4, + 0xe1, 0xfc, 0x62, 0xda, 0xf1, 0xfb, 0xa2, 0xdb, 0xd6, 0xbe, 0x5c, 0xd7, + 0x24, 0xa3, 0xf5, 0xb9, 0x2f, 0x57, 0x16, 0x49, 0x75, 0x47, 0x77, 0x09, + 0x5c, 0xa1, 0xb4, 0xc3, 0x4f, 0x60, 0x2b, 0xb0, 0x0c, 0xc8, 0xd6, 0x66, + 0xba, 0x9b, 0x82, 0x29, 0x33, 0x92, 0x26, 0x99, 0x31, 0x1c, 0x7f, 0x9b, + 0x00, 0x00, 0x01, 0x0ff, +}; + +static const u8 *codec_h264_eos_sequence(u32 *len) +{ + *len = ARRAY_SIZE(eos_sequence); + return eos_sequence; +} + +struct codec_h264 { + /* H.264 decoder requires an extended firmware */ + void *ext_fw_vaddr; + dma_addr_t ext_fw_paddr; + + /* Buffer for the H.264 Workspace */ + void *workspace_vaddr; + dma_addr_t workspace_paddr; + + /* Buffer for the H.264 references MV */ + void *ref_vaddr; + dma_addr_t ref_paddr; + u32 ref_size; + + /* Buffer for parsed SEI data */ + void *sei_vaddr; + dma_addr_t sei_paddr; + + u32 mb_width; + u32 mb_height; + u32 max_refs; +}; + +static int codec_h264_can_recycle(struct amvdec_core *core) +{ + return !amvdec_read_dos(core, AV_SCRATCH_7) || + !amvdec_read_dos(core, AV_SCRATCH_8); +} + +static void codec_h264_recycle(struct amvdec_core *core, u32 buf_idx) +{ + /* + * Tell the firmware it can recycle this buffer. + * AV_SCRATCH_8 serves the same purpose. + */ + if (!amvdec_read_dos(core, AV_SCRATCH_7)) + amvdec_write_dos(core, AV_SCRATCH_7, buf_idx + 1); + else + amvdec_write_dos(core, AV_SCRATCH_8, buf_idx + 1); +} + +static int codec_h264_start(struct amvdec_session *sess) +{ + u32 workspace_offset; + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + + /* Allocate some memory for the H.264 decoder's state */ + h264->workspace_vaddr = + dma_alloc_coherent(core->dev, SIZE_WORKSPACE, + &h264->workspace_paddr, GFP_KERNEL); + if (!h264->workspace_vaddr) + return -ENOMEM; + + /* Allocate some memory for the H.264 SEI dump */ + h264->sei_vaddr = dma_alloc_coherent(core->dev, SIZE_SEI, + &h264->sei_paddr, GFP_KERNEL); + if (!h264->sei_vaddr) + return -ENOMEM; + + amvdec_write_dos_bits(core, POWER_CTL_VLD, BIT(9) | BIT(6)); + + workspace_offset = h264->workspace_paddr - WORKSPACE_BUF_OFFSET; + amvdec_write_dos(core, AV_SCRATCH_1, workspace_offset); + amvdec_write_dos(core, AV_SCRATCH_G, h264->ext_fw_paddr); + amvdec_write_dos(core, AV_SCRATCH_I, h264->sei_paddr - + workspace_offset); + + /* Enable "error correction" */ + amvdec_write_dos(core, AV_SCRATCH_F, + (amvdec_read_dos(core, AV_SCRATCH_F) & 0xffffffc3) | + BIT(4) | BIT(7)); + + amvdec_write_dos(core, MDEC_PIC_DC_THRESH, 0x404038aa); + + return 0; +} + +static int codec_h264_stop(struct amvdec_session *sess) +{ + struct codec_h264 *h264 = sess->priv; + struct amvdec_core *core = sess->core; + + if (h264->ext_fw_vaddr) + dma_free_coherent(core->dev, SIZE_EXT_FW, + h264->ext_fw_vaddr, h264->ext_fw_paddr); + + if (h264->workspace_vaddr) + dma_free_coherent(core->dev, SIZE_WORKSPACE, + h264->workspace_vaddr, h264->workspace_paddr); + + if (h264->ref_vaddr) + dma_free_coherent(core->dev, h264->ref_size, + h264->ref_vaddr, h264->ref_paddr); + + if (h264->sei_vaddr) + dma_free_coherent(core->dev, SIZE_SEI, + h264->sei_vaddr, h264->sei_paddr); + + return 0; +} + +static int codec_h264_load_extended_firmware(struct amvdec_session *sess, + const u8 *data, u32 len) +{ + struct codec_h264 *h264; + struct amvdec_core *core = sess->core; + + if (len < SIZE_EXT_FW) + return -EINVAL; + + h264 = kzalloc(sizeof(*h264), GFP_KERNEL); + if (!h264) + return -ENOMEM; + + h264->ext_fw_vaddr = dma_alloc_coherent(core->dev, SIZE_EXT_FW, + &h264->ext_fw_paddr, + GFP_KERNEL); + if (!h264->ext_fw_vaddr) { + kfree(h264); + return -ENOMEM; + } + + memcpy(h264->ext_fw_vaddr, data, SIZE_EXT_FW); + sess->priv = h264; + + return 0; +} + +static const struct v4l2_fract par_table[] = { + { 1, 1 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, + { 16, 11 }, { 40, 33 }, { 24, 11 }, { 20, 11 }, + { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, + { 64, 33 }, { 160, 99 }, { 4, 3 }, { 3, 2 }, + { 2, 1 } +}; + +static void codec_h264_set_par(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 seq_info = amvdec_read_dos(core, AV_SCRATCH_2); + u32 ar_idc = (seq_info >> AR_IDC_BIT) & AR_IDC_MASK; + + if (!(seq_info & AR_PRESENT_FLAG)) + return; + + if (ar_idc == AR_EXTEND) { + u32 ar_info = amvdec_read_dos(core, AV_SCRATCH_3); + + sess->pixelaspect.numerator = ar_info & 0xffff; + sess->pixelaspect.denominator = (ar_info >> 16) & 0xffff; + return; + } + + if (ar_idc >= ARRAY_SIZE(par_table)) + return; + + sess->pixelaspect = par_table[ar_idc]; +} + +static void codec_h264_resume(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + u32 mb_width, mb_height, mb_total; + + amvdec_set_canvases(sess, + (u32[]){ ANC0_CANVAS_ADDR, 0 }, + (u32[]){ 24, 0 }); + + dev_dbg(core->dev, "max_refs = %u; actual_dpb_size = %u\n", + h264->max_refs, sess->num_dst_bufs); + + /* Align to a multiple of 4 macroblocks */ + mb_width = ALIGN(h264->mb_width, 4); + mb_height = ALIGN(h264->mb_height, 4); + mb_total = mb_width * mb_height; + + h264->ref_size = mb_total * MB_MV_SIZE * h264->max_refs; + h264->ref_vaddr = dma_alloc_coherent(core->dev, h264->ref_size, + &h264->ref_paddr, GFP_KERNEL); + if (!h264->ref_vaddr) { + amvdec_abort(sess); + return; + } + + /* Address to store the references' MVs */ + amvdec_write_dos(core, AV_SCRATCH_1, h264->ref_paddr); + /* End of ref MV */ + amvdec_write_dos(core, AV_SCRATCH_4, h264->ref_paddr + h264->ref_size); + + amvdec_write_dos(core, AV_SCRATCH_0, (h264->max_refs << 24) | + (sess->num_dst_bufs << 16) | + ((h264->max_refs - 1) << 8)); +} + +/* + * Configure the H.264 decoder when the parser detected a parameter set change + */ +static void codec_h264_src_change(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_h264 *h264 = sess->priv; + u32 parsed_info, mb_total; + u32 crop_infor, crop_bottom, crop_right; + u32 frame_width, frame_height; + + sess->keyframe_found = 1; + + parsed_info = amvdec_read_dos(core, AV_SCRATCH_1); + + /* Total number of 16x16 macroblocks */ + mb_total = (parsed_info >> MB_TOTAL_BIT) & MB_TOTAL_MASK; + /* Number of macroblocks per line */ + h264->mb_width = parsed_info & MB_WIDTH_MASK; + /* Number of macroblock lines */ + h264->mb_height = mb_total / h264->mb_width; + + h264->max_refs = ((parsed_info >> MAX_REF_BIT) & MAX_REF_MASK) + 1; + + crop_infor = amvdec_read_dos(core, AV_SCRATCH_6); + crop_bottom = (crop_infor & 0xff); + crop_right = (crop_infor >> 16) & 0xff; + + frame_width = h264->mb_width * 16 - crop_right; + frame_height = h264->mb_height * 16 - crop_bottom; + + dev_dbg(core->dev, "frame: %ux%u; crop: %u %u\n", + frame_width, frame_height, crop_right, crop_bottom); + + codec_h264_set_par(sess); + amvdec_src_change(sess, frame_width, frame_height, h264->max_refs + 5); +} + +/* + * The bitstream offset is split in half in 2 different registers. + * Fetch its MSB here, which location depends on the frame number. + */ +static u32 get_offset_msb(struct amvdec_core *core, int frame_num) +{ + int take_msb = frame_num % 2; + int reg_offset = (frame_num / 2) * 4; + u32 offset_msb = amvdec_read_dos(core, AV_SCRATCH_A + reg_offset); + + if (take_msb) + return offset_msb & 0xffff0000; + + return (offset_msb & 0x0000ffff) << 16; +} + +static void codec_h264_frames_ready(struct amvdec_session *sess, u32 status) +{ + struct amvdec_core *core = sess->core; + int error_count; + int num_frames; + int i; + + error_count = amvdec_read_dos(core, AV_SCRATCH_D); + num_frames = (status >> 8) & 0xff; + if (error_count) { + dev_warn(core->dev, + "decoder error(s) happened, count %d\n", error_count); + amvdec_write_dos(core, AV_SCRATCH_D, 0); + } + + for (i = 0; i < num_frames; i++) { + u32 frame_status = amvdec_read_dos(core, AV_SCRATCH_1 + i * 4); + u32 buffer_index = frame_status & BUF_IDX_MASK; + u32 pic_struct = (frame_status >> PIC_STRUCT_BIT) & + PIC_STRUCT_MASK; + u32 offset = (frame_status >> OFFSET_BIT) & OFFSET_MASK; + u32 field = V4L2_FIELD_NONE; + + /* + * A buffer decode error means it was decoded, + * but part of the picture will have artifacts. + * Typical reason is a temporarily corrupted bitstream + */ + if (frame_status & ERROR_FLAG) + dev_dbg(core->dev, "Buffer %d decode error\n", + buffer_index); + + if (pic_struct == PIC_TOP_BOT) + field = V4L2_FIELD_INTERLACED_TB; + else if (pic_struct == PIC_BOT_TOP) + field = V4L2_FIELD_INTERLACED_BT; + + offset |= get_offset_msb(core, i); + amvdec_dst_buf_done_idx(sess, buffer_index, offset, field); + } +} + +static irqreturn_t codec_h264_threaded_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 status; + u32 size; + u8 cmd; + + status = amvdec_read_dos(core, AV_SCRATCH_0); + cmd = status & CMD_MASK; + + switch (cmd) { + case CMD_SRC_CHANGE: + codec_h264_src_change(sess); + break; + case CMD_FRAMES_READY: + codec_h264_frames_ready(sess, status); + break; + case CMD_FATAL_ERROR: + dev_err(core->dev, "H.264 decoder fatal error\n"); + goto abort; + case CMD_BAD_WIDTH: + size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; + dev_err(core->dev, "Unsupported video width: %u\n", size); + goto abort; + case CMD_BAD_HEIGHT: + size = (amvdec_read_dos(core, AV_SCRATCH_1) + 1) * 16; + dev_err(core->dev, "Unsupported video height: %u\n", size); + goto abort; + case 0: /* Unused but not worth printing for */ + case 9: + break; + default: + dev_info(core->dev, "Unexpected H264 ISR: %08X\n", cmd); + break; + } + + if (cmd && cmd != CMD_SRC_CHANGE) + amvdec_write_dos(core, AV_SCRATCH_0, 0); + + /* Decoder has some SEI data for us ; ignore */ + if (amvdec_read_dos(core, AV_SCRATCH_J) & SEI_DATA_READY) + amvdec_write_dos(core, AV_SCRATCH_J, 0); + + return IRQ_HANDLED; +abort: + amvdec_abort(sess); + return IRQ_HANDLED; +} + +static irqreturn_t codec_h264_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_WAKE_THREAD; +} + +struct amvdec_codec_ops codec_h264_ops = { + .start = codec_h264_start, + .stop = codec_h264_stop, + .load_extended_firmware = codec_h264_load_extended_firmware, + .isr = codec_h264_isr, + .threaded_isr = codec_h264_threaded_isr, + .can_recycle = codec_h264_can_recycle, + .recycle = codec_h264_recycle, + .eos_sequence = codec_h264_eos_sequence, + .resume = codec_h264_resume, +}; diff --git a/drivers/staging/media/meson/vdec/codec_h264.h b/drivers/staging/media/meson/vdec/codec_h264.h new file mode 100644 index 000000000000..7cb4fb86ff36 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_h264.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_CODEC_H264_H_ +#define __MESON_VDEC_CODEC_H264_H_ + +#include "vdec.h" + +extern struct amvdec_codec_ops codec_h264_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c new file mode 100644 index 000000000000..0315cc0911cd --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "codec_hevc_common.h" +#include "vdec_helpers.h" +#include "hevc_regs.h" + +#define MMU_COMPRESS_HEADER_SIZE 0x48000 +#define MMU_MAP_SIZE 0x4800 + +const u16 vdec_hevc_parser_cmd[] = { + 0x0401, 0x8401, 0x0800, 0x0402, + 0x9002, 0x1423, 0x8CC3, 0x1423, + 0x8804, 0x9825, 0x0800, 0x04FE, + 0x8406, 0x8411, 0x1800, 0x8408, + 0x8409, 0x8C2A, 0x9C2B, 0x1C00, + 0x840F, 0x8407, 0x8000, 0x8408, + 0x2000, 0xA800, 0x8410, 0x04DE, + 0x840C, 0x840D, 0xAC00, 0xA000, + 0x08C0, 0x08E0, 0xA40E, 0xFC00, + 0x7C00 +}; + +/* Configure decode head read mode */ +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit) +{ + struct amvdec_core *core = sess->core; + u32 body_size = amvdec_am21c_body_size(sess->width, sess->height); + u32 head_size = amvdec_am21c_head_size(sess->width, sess->height); + + if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) { + /* Enable 2-plane reference read mode */ + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31)); + return; + } + + if (codec_hevc_use_mmu(core->platform->revision, + sess->pixfmt_cap, is_10bit)) + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4)); + else + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0); + + if (core->platform->revision < VDEC_REVISION_SM1) + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32); + amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size); + amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size); + amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size); +} +EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head); + +static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + struct v4l2_m2m_buffer *buf; + u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); + dma_addr_t buf_y_paddr = 0; + dma_addr_t buf_uv_paddr = 0; + u32 idx = 0; + u32 val; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + struct vb2_buffer *vb = &buf->vb.vb2_buf; + + idx = vb->index; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) + buf_y_paddr = comm->fbc_buffer_paddr[idx]; + else + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) { + val = buf_y_paddr | (idx << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + } else { + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1); + val = buf_y_paddr | ((idx * 2) << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1; + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + val); + } + } + + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) + val = buf_y_paddr | (idx << 8) | 1; + else + val = buf_y_paddr | ((idx * 2) << 8) | 1; + + /* Fill the remaining unused slots with the last buffer's Y addr */ + for (i = buf_num; i < MAX_REF_PIC_NUM; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val); + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + for (i = 0; i < 32; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); +} + +static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + struct v4l2_m2m_buffer *buf; + u32 revision = core->platform->revision; + u32 pixfmt_cap = sess->pixfmt_cap; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, + BIT(2) | BIT(1)); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + struct vb2_buffer *vb = &buf->vb.vb2_buf; + dma_addr_t buf_y_paddr = 0; + dma_addr_t buf_uv_paddr = 0; + u32 idx = vb->index; + + if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit)) + buf_y_paddr = comm->mmu_header_paddr[idx]; + else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit)) + buf_y_paddr = comm->fbc_buffer_paddr[idx]; + else + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA, + buf_y_paddr >> 5); + + if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) { + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA, + buf_uv_paddr >> 5); + } + } + + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + for (i = 0; i < 32; ++i) + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); +} + +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + u32 am21_size = amvdec_am21c_size(sess->width, sess->height); + int i; + + for (i = 0; i < MAX_REF_PIC_NUM; ++i) { + if (comm->fbc_buffer_vaddr[i]) { + dma_free_coherent(dev, am21_size, + comm->fbc_buffer_vaddr[i], + comm->fbc_buffer_paddr[i]); + comm->fbc_buffer_vaddr[i] = NULL; + } + } +} +EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers); + +static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + struct v4l2_m2m_buffer *buf; + u32 am21_size = amvdec_am21c_size(sess->width, sess->height); + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + u32 idx = buf->vb.vb2_buf.index; + dma_addr_t paddr; + void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr, + GFP_KERNEL); + if (!vaddr) { + codec_hevc_free_fbc_buffers(sess, comm); + return -ENOMEM; + } + + comm->fbc_buffer_vaddr[idx] = vaddr; + comm->fbc_buffer_paddr[idx] = paddr; + } + + return 0; +} + +void codec_hevc_free_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + int i; + + for (i = 0; i < MAX_REF_PIC_NUM; ++i) { + if (comm->mmu_header_vaddr[i]) { + dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE, + comm->mmu_header_vaddr[i], + comm->mmu_header_paddr[i]); + comm->mmu_header_vaddr[i] = NULL; + } + } + + if (comm->mmu_map_vaddr) { + dma_free_coherent(dev, MMU_MAP_SIZE, + comm->mmu_map_vaddr, + comm->mmu_map_paddr); + comm->mmu_map_vaddr = NULL; + } +} +EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers); + +static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm) +{ + struct device *dev = sess->core->dev; + struct v4l2_m2m_buffer *buf; + + comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE, + &comm->mmu_map_paddr, + GFP_KERNEL); + if (!comm->mmu_map_vaddr) + return -ENOMEM; + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + u32 idx = buf->vb.vb2_buf.index; + dma_addr_t paddr; + void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE, + &paddr, GFP_KERNEL); + if (!vaddr) { + codec_hevc_free_mmu_headers(sess, comm); + return -ENOMEM; + } + + comm->mmu_header_vaddr[idx] = vaddr; + comm->mmu_header_paddr[idx] = paddr; + } + + return 0; +} + +int codec_hevc_setup_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit) +{ + struct amvdec_core *core = sess->core; + int ret; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) { + ret = codec_hevc_alloc_fbc_buffers(sess, comm); + if (ret) + return ret; + } + + if (codec_hevc_use_mmu(core->platform->revision, + sess->pixfmt_cap, is_10bit)) { + ret = codec_hevc_alloc_mmu_headers(sess, comm); + if (ret) { + codec_hevc_free_fbc_buffers(sess, comm); + return ret; + } + } + + if (core->platform->revision == VDEC_REVISION_GXBB) + codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit); + else + codec_hevc_setup_buffers_gxl(sess, comm, is_10bit); + + return 0; +} +EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers); + +void codec_hevc_fill_mmu_map(struct amvdec_session *sess, + struct codec_hevc_common *comm, + struct vb2_buffer *vb) +{ + u32 size = amvdec_am21c_size(sess->width, sess->height); + u32 nb_pages = size / PAGE_SIZE; + u32 *mmu_map = comm->mmu_map_vaddr; + u32 first_page; + u32 i; + + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) + first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT; + else + first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT; + + for (i = 0; i < nb_pages; ++i) + mmu_map[i] = first_page + i; +} +EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map); diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h new file mode 100644 index 000000000000..88e4379ba1ee --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_HEVC_COMMON_H_ +#define __MESON_VDEC_HEVC_COMMON_H_ + +#include "vdec.h" + +#define PARSER_CMD_SKIP_CFG_0 0x0000090b +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f +#define PARSER_CMD_SKIP_CFG_2 0x001b1910 + +#define VDEC_HEVC_PARSER_CMD_LEN 37 +extern const u16 vdec_hevc_parser_cmd[VDEC_HEVC_PARSER_CMD_LEN]; + +#define MAX_REF_PIC_NUM 24 + +struct codec_hevc_common { + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM]; + + void *mmu_header_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM]; + + void *mmu_map_vaddr; + dma_addr_t mmu_map_paddr; +}; + +/* Returns 1 if we must use framebuffer compression */ +static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit) +{ + /* TOFIX: Handle Amlogic Compressed buffer for 8bit also */ + return is_10bit; +} + +/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */ +static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit) +{ + return is_10bit; +} + +/* Returns 1 if we are decoding using the IOMMU */ +static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit) +{ + return revision >= VDEC_REVISION_G12A && + codec_hevc_use_fbc(pixfmt, is_10bit); +} + +/** + * Configure decode head read mode + */ +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit); + +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm); + +void codec_hevc_free_mmu_headers(struct amvdec_session *sess, + struct codec_hevc_common *comm); + +int codec_hevc_setup_buffers(struct amvdec_session *sess, + struct codec_hevc_common *comm, + int is_10bit); + +void codec_hevc_fill_mmu_map(struct amvdec_session *sess, + struct codec_hevc_common *comm, + struct vb2_buffer *vb); + +#endif diff --git a/drivers/staging/media/meson/vdec/codec_vp9.c b/drivers/staging/media/meson/vdec/codec_vp9.c new file mode 100644 index 000000000000..60e4fc0052b3 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_vp9.c @@ -0,0 +1,2141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <mjourdan@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "dos_regs.h" +#include "hevc_regs.h" +#include "codec_vp9.h" +#include "vdec_helpers.h" +#include "codec_hevc_common.h" + +/* HEVC reg mapping */ +#define VP9_DEC_STATUS_REG HEVC_ASSIST_SCRATCH_0 + #define VP9_10B_DECODE_SLICE 5 + #define VP9_HEAD_PARSER_DONE 0xf0 +#define VP9_RPM_BUFFER HEVC_ASSIST_SCRATCH_1 +#define VP9_SHORT_TERM_RPS HEVC_ASSIST_SCRATCH_2 +#define VP9_ADAPT_PROB_REG HEVC_ASSIST_SCRATCH_3 +#define VP9_MMU_MAP_BUFFER HEVC_ASSIST_SCRATCH_4 +#define VP9_PPS_BUFFER HEVC_ASSIST_SCRATCH_5 +#define VP9_SAO_UP HEVC_ASSIST_SCRATCH_6 +#define VP9_STREAM_SWAP_BUFFER HEVC_ASSIST_SCRATCH_7 +#define VP9_STREAM_SWAP_BUFFER2 HEVC_ASSIST_SCRATCH_8 +#define VP9_PROB_SWAP_BUFFER HEVC_ASSIST_SCRATCH_9 +#define VP9_COUNT_SWAP_BUFFER HEVC_ASSIST_SCRATCH_A +#define VP9_SEG_MAP_BUFFER HEVC_ASSIST_SCRATCH_B +#define VP9_SCALELUT HEVC_ASSIST_SCRATCH_D +#define VP9_WAIT_FLAG HEVC_ASSIST_SCRATCH_E +#define LMEM_DUMP_ADR HEVC_ASSIST_SCRATCH_F +#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I +#define VP9_DECODE_MODE HEVC_ASSIST_SCRATCH_J + #define DECODE_MODE_SINGLE 0 +#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K +#define HEVC_DECODE_COUNT HEVC_ASSIST_SCRATCH_M +#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N + +/* VP9 Constants */ +#define LCU_SIZE 64 +#define MAX_REF_PIC_NUM 24 +#define REFS_PER_FRAME 3 +#define REF_FRAMES 8 +#define MV_MEM_UNIT 0x240 +#define ADAPT_PROB_SIZE 0xf80 + +enum FRAME_TYPE { + KEY_FRAME = 0, + INTER_FRAME = 1, + FRAME_TYPES, +}; + +/* VP9 Workspace layout */ +#define MPRED_MV_BUF_SIZE 0x120000 + +#define IPP_SIZE 0x4000 +#define SAO_ABV_SIZE 0x30000 +#define SAO_VB_SIZE 0x30000 +#define SH_TM_RPS_SIZE 0x800 +#define VPS_SIZE 0x800 +#define SPS_SIZE 0x800 +#define PPS_SIZE 0x2000 +#define SAO_UP_SIZE 0x2800 +#define SWAP_BUF_SIZE 0x800 +#define SWAP_BUF2_SIZE 0x800 +#define SCALELUT_SIZE 0x8000 +#define DBLK_PARA_SIZE 0x80000 +#define DBLK_DATA_SIZE 0x80000 +#define SEG_MAP_SIZE 0xd800 +#define PROB_SIZE 0x5000 +#define COUNT_SIZE 0x3000 +#define MMU_VBH_SIZE 0x5000 +#define MPRED_ABV_SIZE 0x10000 +#define MPRED_MV_SIZE (MPRED_MV_BUF_SIZE * MAX_REF_PIC_NUM) +#define RPM_BUF_SIZE 0x100 +#define LMEM_SIZE 0x800 + +#define IPP_OFFSET 0x00 +#define SAO_ABV_OFFSET (IPP_OFFSET + IPP_SIZE) +#define SAO_VB_OFFSET (SAO_ABV_OFFSET + SAO_ABV_SIZE) +#define SH_TM_RPS_OFFSET (SAO_VB_OFFSET + SAO_VB_SIZE) +#define VPS_OFFSET (SH_TM_RPS_OFFSET + SH_TM_RPS_SIZE) +#define SPS_OFFSET (VPS_OFFSET + VPS_SIZE) +#define PPS_OFFSET (SPS_OFFSET + SPS_SIZE) +#define SAO_UP_OFFSET (PPS_OFFSET + PPS_SIZE) +#define SWAP_BUF_OFFSET (SAO_UP_OFFSET + SAO_UP_SIZE) +#define SWAP_BUF2_OFFSET (SWAP_BUF_OFFSET + SWAP_BUF_SIZE) +#define SCALELUT_OFFSET (SWAP_BUF2_OFFSET + SWAP_BUF2_SIZE) +#define DBLK_PARA_OFFSET (SCALELUT_OFFSET + SCALELUT_SIZE) +#define DBLK_DATA_OFFSET (DBLK_PARA_OFFSET + DBLK_PARA_SIZE) +#define SEG_MAP_OFFSET (DBLK_DATA_OFFSET + DBLK_DATA_SIZE) +#define PROB_OFFSET (SEG_MAP_OFFSET + SEG_MAP_SIZE) +#define COUNT_OFFSET (PROB_OFFSET + PROB_SIZE) +#define MMU_VBH_OFFSET (COUNT_OFFSET + COUNT_SIZE) +#define MPRED_ABV_OFFSET (MMU_VBH_OFFSET + MMU_VBH_SIZE) +#define MPRED_MV_OFFSET (MPRED_ABV_OFFSET + MPRED_ABV_SIZE) +#define RPM_OFFSET (MPRED_MV_OFFSET + MPRED_MV_SIZE) +#define LMEM_OFFSET (RPM_OFFSET + RPM_BUF_SIZE) + +#define SIZE_WORKSPACE ALIGN(LMEM_OFFSET + LMEM_SIZE, 64 * SZ_1K) + +#define NONE -1 +#define INTRA_FRAME 0 +#define LAST_FRAME 1 +#define GOLDEN_FRAME 2 +#define ALTREF_FRAME 3 +#define MAX_REF_FRAMES 4 + +/* + * Defines, declarations, sub-functions for vp9 de-block loop + filter Thr/Lvl table update + * - struct segmentation is for loop filter only (removed something) + * - function "vp9_loop_filter_init" and "vp9_loop_filter_frame_init" will + be instantiated in C_Entry + * - vp9_loop_filter_init run once before decoding start + * - vp9_loop_filter_frame_init run before every frame decoding start + * - set video format to VP9 is in vp9_loop_filter_init + */ +#define MAX_LOOP_FILTER 63 +#define MAX_REF_LF_DELTAS 4 +#define MAX_MODE_LF_DELTAS 2 +#define SEGMENT_DELTADATA 0 +#define SEGMENT_ABSDATA 1 +#define MAX_SEGMENTS 8 + +/* VP9 PROB processing defines */ +#define VP9_PARTITION_START 0 +#define VP9_PARTITION_SIZE_STEP (3 * 4) +#define VP9_PARTITION_ONE_SIZE (4 * VP9_PARTITION_SIZE_STEP) +#define VP9_PARTITION_KEY_START 0 +#define VP9_PARTITION_P_START VP9_PARTITION_ONE_SIZE +#define VP9_PARTITION_SIZE (2 * VP9_PARTITION_ONE_SIZE) +#define VP9_SKIP_START (VP9_PARTITION_START + VP9_PARTITION_SIZE) +#define VP9_SKIP_SIZE 4 /* only use 3*/ +#define VP9_TX_MODE_START (VP9_SKIP_START + VP9_SKIP_SIZE) +#define VP9_TX_MODE_8_0_OFFSET 0 +#define VP9_TX_MODE_8_1_OFFSET 1 +#define VP9_TX_MODE_16_0_OFFSET 2 +#define VP9_TX_MODE_16_1_OFFSET 4 +#define VP9_TX_MODE_32_0_OFFSET 6 +#define VP9_TX_MODE_32_1_OFFSET 9 +#define VP9_TX_MODE_SIZE 12 +#define VP9_COEF_START (VP9_TX_MODE_START + VP9_TX_MODE_SIZE) +#define VP9_COEF_BAND_0_OFFSET 0 +#define VP9_COEF_BAND_1_OFFSET (VP9_COEF_BAND_0_OFFSET + 3 * 3 + 1) +#define VP9_COEF_BAND_2_OFFSET (VP9_COEF_BAND_1_OFFSET + 6 * 3) +#define VP9_COEF_BAND_3_OFFSET (VP9_COEF_BAND_2_OFFSET + 6 * 3) +#define VP9_COEF_BAND_4_OFFSET (VP9_COEF_BAND_3_OFFSET + 6 * 3) +#define VP9_COEF_BAND_5_OFFSET (VP9_COEF_BAND_4_OFFSET + 6 * 3) +#define VP9_COEF_SIZE_ONE_SET 100 /* ((3 + 5 * 6) * 3 + 1 padding)*/ +#define VP9_COEF_4X4_START (VP9_COEF_START + 0 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_8X8_START (VP9_COEF_START + 4 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_16X16_START (VP9_COEF_START + 8 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_32X32_START (VP9_COEF_START + 12 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE_PLANE (2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE (4 * 2 * 2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_INTER_MODE_START (VP9_COEF_START + VP9_COEF_SIZE) +#define VP9_INTER_MODE_SIZE 24 /* only use 21 (# * 7)*/ +#define VP9_INTERP_START (VP9_INTER_MODE_START + VP9_INTER_MODE_SIZE) +#define VP9_INTERP_SIZE 8 +#define VP9_INTRA_INTER_START (VP9_INTERP_START + VP9_INTERP_SIZE) +#define VP9_INTRA_INTER_SIZE 4 +#define VP9_INTERP_INTRA_INTER_START VP9_INTERP_START +#define VP9_INTERP_INTRA_INTER_SIZE (VP9_INTERP_SIZE + VP9_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_START \ + (VP9_INTERP_INTRA_INTER_START + VP9_INTERP_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_SIZE 5 +#define VP9_COMP_REF_START (VP9_COMP_INTER_START + VP9_COMP_INTER_SIZE) +#define VP9_COMP_REF_SIZE 5 +#define VP9_SINGLE_REF_START (VP9_COMP_REF_START + VP9_COMP_REF_SIZE) +#define VP9_SINGLE_REF_SIZE 10 +#define VP9_REF_MODE_START VP9_COMP_INTER_START +#define VP9_REF_MODE_SIZE \ + (VP9_COMP_INTER_SIZE + VP9_COMP_REF_SIZE + VP9_SINGLE_REF_SIZE) +#define VP9_IF_Y_MODE_START (VP9_REF_MODE_START + VP9_REF_MODE_SIZE) +#define VP9_IF_Y_MODE_SIZE 36 +#define VP9_IF_UV_MODE_START (VP9_IF_Y_MODE_START + VP9_IF_Y_MODE_SIZE) +#define VP9_IF_UV_MODE_SIZE 92 /* only use 90*/ +#define VP9_MV_JOINTS_START (VP9_IF_UV_MODE_START + VP9_IF_UV_MODE_SIZE) +#define VP9_MV_JOINTS_SIZE 3 +#define VP9_MV_SIGN_0_START (VP9_MV_JOINTS_START + VP9_MV_JOINTS_SIZE) +#define VP9_MV_SIGN_0_SIZE 1 +#define VP9_MV_CLASSES_0_START (VP9_MV_SIGN_0_START + VP9_MV_SIGN_0_SIZE) +#define VP9_MV_CLASSES_0_SIZE 10 +#define VP9_MV_CLASS0_0_START \ + (VP9_MV_CLASSES_0_START + VP9_MV_CLASSES_0_SIZE) +#define VP9_MV_CLASS0_0_SIZE 1 +#define VP9_MV_BITS_0_START (VP9_MV_CLASS0_0_START + VP9_MV_CLASS0_0_SIZE) +#define VP9_MV_BITS_0_SIZE 10 +#define VP9_MV_SIGN_1_START (VP9_MV_BITS_0_START + VP9_MV_BITS_0_SIZE) +#define VP9_MV_SIGN_1_SIZE 1 +#define VP9_MV_CLASSES_1_START \ + (VP9_MV_SIGN_1_START + VP9_MV_SIGN_1_SIZE) +#define VP9_MV_CLASSES_1_SIZE 10 +#define VP9_MV_CLASS0_1_START \ + (VP9_MV_CLASSES_1_START + VP9_MV_CLASSES_1_SIZE) +#define VP9_MV_CLASS0_1_SIZE 1 +#define VP9_MV_BITS_1_START \ + (VP9_MV_CLASS0_1_START + VP9_MV_CLASS0_1_SIZE) +#define VP9_MV_BITS_1_SIZE 10 +#define VP9_MV_CLASS0_FP_0_START \ + (VP9_MV_BITS_1_START + VP9_MV_BITS_1_SIZE) +#define VP9_MV_CLASS0_FP_0_SIZE 9 +#define VP9_MV_CLASS0_FP_1_START \ + (VP9_MV_CLASS0_FP_0_START + VP9_MV_CLASS0_FP_0_SIZE) +#define VP9_MV_CLASS0_FP_1_SIZE 9 +#define VP9_MV_CLASS0_HP_0_START \ + (VP9_MV_CLASS0_FP_1_START + VP9_MV_CLASS0_FP_1_SIZE) +#define VP9_MV_CLASS0_HP_0_SIZE 2 +#define VP9_MV_CLASS0_HP_1_START \ + (VP9_MV_CLASS0_HP_0_START + VP9_MV_CLASS0_HP_0_SIZE) +#define VP9_MV_CLASS0_HP_1_SIZE 2 +#define VP9_MV_START VP9_MV_JOINTS_START +#define VP9_MV_SIZE 72 /*only use 69*/ + +#define VP9_TOTAL_SIZE (VP9_MV_START + VP9_MV_SIZE) + +/* VP9 COUNT mem processing defines */ +#define VP9_COEF_COUNT_START 0 +#define VP9_COEF_COUNT_BAND_0_OFFSET 0 +#define VP9_COEF_COUNT_BAND_1_OFFSET \ + (VP9_COEF_COUNT_BAND_0_OFFSET + 3 * 5) +#define VP9_COEF_COUNT_BAND_2_OFFSET \ + (VP9_COEF_COUNT_BAND_1_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_3_OFFSET \ + (VP9_COEF_COUNT_BAND_2_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_4_OFFSET \ + (VP9_COEF_COUNT_BAND_3_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_BAND_5_OFFSET \ + (VP9_COEF_COUNT_BAND_4_OFFSET + 6 * 5) +#define VP9_COEF_COUNT_SIZE_ONE_SET 165 /* ((3 + 5 * 6) * 5 */ +#define VP9_COEF_COUNT_4X4_START \ + (VP9_COEF_COUNT_START + 0 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_8X8_START \ + (VP9_COEF_COUNT_START + 4 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_16X16_START \ + (VP9_COEF_COUNT_START + 8 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_32X32_START \ + (VP9_COEF_COUNT_START + 12 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE_PLANE (2 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE (4 * 2 * 2 * VP9_COEF_COUNT_SIZE_ONE_SET) + +#define VP9_INTRA_INTER_COUNT_START \ + (VP9_COEF_COUNT_START + VP9_COEF_COUNT_SIZE) +#define VP9_INTRA_INTER_COUNT_SIZE (4 * 2) +#define VP9_COMP_INTER_COUNT_START \ + (VP9_INTRA_INTER_COUNT_START + VP9_INTRA_INTER_COUNT_SIZE) +#define VP9_COMP_INTER_COUNT_SIZE (5 * 2) +#define VP9_COMP_REF_COUNT_START \ + (VP9_COMP_INTER_COUNT_START + VP9_COMP_INTER_COUNT_SIZE) +#define VP9_COMP_REF_COUNT_SIZE (5 * 2) +#define VP9_SINGLE_REF_COUNT_START \ + (VP9_COMP_REF_COUNT_START + VP9_COMP_REF_COUNT_SIZE) +#define VP9_SINGLE_REF_COUNT_SIZE (10 * 2) +#define VP9_TX_MODE_COUNT_START \ + (VP9_SINGLE_REF_COUNT_START + VP9_SINGLE_REF_COUNT_SIZE) +#define VP9_TX_MODE_COUNT_SIZE (12 * 2) +#define VP9_SKIP_COUNT_START \ + (VP9_TX_MODE_COUNT_START + VP9_TX_MODE_COUNT_SIZE) +#define VP9_SKIP_COUNT_SIZE (3 * 2) +#define VP9_MV_SIGN_0_COUNT_START \ + (VP9_SKIP_COUNT_START + VP9_SKIP_COUNT_SIZE) +#define VP9_MV_SIGN_0_COUNT_SIZE (1 * 2) +#define VP9_MV_SIGN_1_COUNT_START \ + (VP9_MV_SIGN_0_COUNT_START + VP9_MV_SIGN_0_COUNT_SIZE) +#define VP9_MV_SIGN_1_COUNT_SIZE (1 * 2) +#define VP9_MV_BITS_0_COUNT_START \ + (VP9_MV_SIGN_1_COUNT_START + VP9_MV_SIGN_1_COUNT_SIZE) +#define VP9_MV_BITS_0_COUNT_SIZE (10 * 2) +#define VP9_MV_BITS_1_COUNT_START \ + (VP9_MV_BITS_0_COUNT_START + VP9_MV_BITS_0_COUNT_SIZE) +#define VP9_MV_BITS_1_COUNT_SIZE (10 * 2) +#define VP9_MV_CLASS0_HP_0_COUNT_START \ + (VP9_MV_BITS_1_COUNT_START + VP9_MV_BITS_1_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_0_COUNT_SIZE (2 * 2) +#define VP9_MV_CLASS0_HP_1_COUNT_START \ + (VP9_MV_CLASS0_HP_0_COUNT_START + VP9_MV_CLASS0_HP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_1_COUNT_SIZE (2 * 2) + +/* Start merge_tree */ +#define VP9_INTER_MODE_COUNT_START \ + (VP9_MV_CLASS0_HP_1_COUNT_START + VP9_MV_CLASS0_HP_1_COUNT_SIZE) +#define VP9_INTER_MODE_COUNT_SIZE (7 * 4) +#define VP9_IF_Y_MODE_COUNT_START \ + (VP9_INTER_MODE_COUNT_START + VP9_INTER_MODE_COUNT_SIZE) +#define VP9_IF_Y_MODE_COUNT_SIZE (10 * 4) +#define VP9_IF_UV_MODE_COUNT_START \ + (VP9_IF_Y_MODE_COUNT_START + VP9_IF_Y_MODE_COUNT_SIZE) +#define VP9_IF_UV_MODE_COUNT_SIZE (10 * 10) +#define VP9_PARTITION_P_COUNT_START \ + (VP9_IF_UV_MODE_COUNT_START + VP9_IF_UV_MODE_COUNT_SIZE) +#define VP9_PARTITION_P_COUNT_SIZE (4 * 4 * 4) +#define VP9_INTERP_COUNT_START \ + (VP9_PARTITION_P_COUNT_START + VP9_PARTITION_P_COUNT_SIZE) +#define VP9_INTERP_COUNT_SIZE (4 * 3) +#define VP9_MV_JOINTS_COUNT_START \ + (VP9_INTERP_COUNT_START + VP9_INTERP_COUNT_SIZE) +#define VP9_MV_JOINTS_COUNT_SIZE (1 * 4) +#define VP9_MV_CLASSES_0_COUNT_START \ + (VP9_MV_JOINTS_COUNT_START + VP9_MV_JOINTS_COUNT_SIZE) +#define VP9_MV_CLASSES_0_COUNT_SIZE (1 * 11) +#define VP9_MV_CLASS0_0_COUNT_START \ + (VP9_MV_CLASSES_0_COUNT_START + VP9_MV_CLASSES_0_COUNT_SIZE) +#define VP9_MV_CLASS0_0_COUNT_SIZE (1 * 2) +#define VP9_MV_CLASSES_1_COUNT_START \ + (VP9_MV_CLASS0_0_COUNT_START + VP9_MV_CLASS0_0_COUNT_SIZE) +#define VP9_MV_CLASSES_1_COUNT_SIZE (1 * 11) +#define VP9_MV_CLASS0_1_COUNT_START \ + (VP9_MV_CLASSES_1_COUNT_START + VP9_MV_CLASSES_1_COUNT_SIZE) +#define VP9_MV_CLASS0_1_COUNT_SIZE (1 * 2) +#define VP9_MV_CLASS0_FP_0_COUNT_START \ + (VP9_MV_CLASS0_1_COUNT_START + VP9_MV_CLASS0_1_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_0_COUNT_SIZE (3 * 4) +#define VP9_MV_CLASS0_FP_1_COUNT_START \ + (VP9_MV_CLASS0_FP_0_COUNT_START + VP9_MV_CLASS0_FP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_1_COUNT_SIZE (3 * 4) + +#define DC_PRED 0 /* Average of above and left pixels */ +#define V_PRED 1 /* Vertical */ +#define H_PRED 2 /* Horizontal */ +#define D45_PRED 3 /* Directional 45 deg = round(arctan(1/1) * 180/pi) */ +#define D135_PRED 4 /* Directional 135 deg = 180 - 45 */ +#define D117_PRED 5 /* Directional 117 deg = 180 - 63 */ +#define D153_PRED 6 /* Directional 153 deg = 180 - 27 */ +#define D207_PRED 7 /* Directional 207 deg = 180 + 27 */ +#define D63_PRED 8 /* Directional 63 deg = round(arctan(2/1) * 180/pi) */ +#define TM_PRED 9 /* True-motion */ + +/* Use a static inline to avoid possible side effect from num being reused */ +static inline int round_power_of_two(int value, int num) +{ + return (value + (1 << (num - 1))) >> num; +} + +#define MODE_MV_COUNT_SAT 20 +static const int count_to_update_factor[MODE_MV_COUNT_SAT + 1] = { + 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, + 70, 76, 83, 89, 96, 102, 108, 115, 121, 128 +}; + +union rpm_param { + struct { + u16 data[RPM_BUF_SIZE]; + } l; + struct { + u16 profile; + u16 show_existing_frame; + u16 frame_to_show_idx; + u16 frame_type; /*1 bit*/ + u16 show_frame; /*1 bit*/ + u16 error_resilient_mode; /*1 bit*/ + u16 intra_only; /*1 bit*/ + u16 display_size_present; /*1 bit*/ + u16 reset_frame_context; + u16 refresh_frame_flags; + u16 width; + u16 height; + u16 display_width; + u16 display_height; + u16 ref_info; + u16 same_frame_size; + u16 mode_ref_delta_enabled; + u16 ref_deltas[4]; + u16 mode_deltas[2]; + u16 filter_level; + u16 sharpness_level; + u16 bit_depth; + u16 seg_quant_info[8]; + u16 seg_enabled; + u16 seg_abs_delta; + /* bit 15: feature enabled; bit 8, sign; bit[5:0], data */ + u16 seg_lf_info[8]; + } p; +}; + +enum SEG_LVL_FEATURES { + SEG_LVL_ALT_Q = 0, /* Use alternate Quantizer */ + SEG_LVL_ALT_LF = 1, /* Use alternate loop filter value */ + SEG_LVL_REF_FRAME = 2, /* Optional Segment reference frame */ + SEG_LVL_SKIP = 3, /* Optional Segment (0,0) + skip mode */ + SEG_LVL_MAX = 4 /* Number of features supported */ +}; + +struct segmentation { + u8 enabled; + u8 update_map; + u8 update_data; + u8 abs_delta; + u8 temporal_update; + s16 feature_data[MAX_SEGMENTS][SEG_LVL_MAX]; + unsigned int feature_mask[MAX_SEGMENTS]; +}; + +struct loop_filter_thresh { + u8 mblim; + u8 lim; + u8 hev_thr; +}; + +struct loop_filter_info_n { + struct loop_filter_thresh lfthr[MAX_LOOP_FILTER + 1]; + u8 lvl[MAX_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS]; +}; + +struct loopfilter { + int filter_level; + + int sharpness_level; + int last_sharpness_level; + + u8 mode_ref_delta_enabled; + u8 mode_ref_delta_update; + + /*0 = Intra, Last, GF, ARF*/ + signed char ref_deltas[MAX_REF_LF_DELTAS]; + signed char last_ref_deltas[MAX_REF_LF_DELTAS]; + + /*0 = ZERO_MV, MV*/ + signed char mode_deltas[MAX_MODE_LF_DELTAS]; + signed char last_mode_deltas[MAX_MODE_LF_DELTAS]; +}; + +struct vp9_frame { + struct list_head list; + struct vb2_v4l2_buffer *vbuf; + int index; + int intra_only; + int show; + int type; + int done; + unsigned int width; + unsigned int height; +}; + +struct codec_vp9 { + /* VP9 context lock */ + struct mutex lock; + + /* Common part with the HEVC decoder */ + struct codec_hevc_common common; + + /* Buffer for the VP9 Workspace */ + void *workspace_vaddr; + dma_addr_t workspace_paddr; + + /* Contains many information parsed from the bitstream */ + union rpm_param rpm_param; + + /* Whether we detected the bitstream as 10-bit */ + int is_10bit; + + /* Coded resolution reported by the hardware */ + u32 width, height; + + /* All ref frames used by the HW at a given time */ + struct list_head ref_frames_list; + u32 frames_num; + + /* In case of downsampling (decoding with FBC but outputting in NV12M), + * we need to allocate additional buffers for FBC. + */ + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM]; + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM]; + + int ref_frame_map[REF_FRAMES]; + int next_ref_frame_map[REF_FRAMES]; + struct vp9_frame *frame_refs[REFS_PER_FRAME]; + + u32 lcu_total; + + /* loop filter */ + int default_filt_lvl; + struct loop_filter_info_n lfi; + struct loopfilter lf; + struct segmentation seg_4lf; + + struct vp9_frame *cur_frame; + struct vp9_frame *prev_frame; +}; + +static int div_r32(s64 m, int n) +{ + s64 qu = div_s64(m, n); + + return (int)qu; +} + +static int clip_prob(int p) +{ + return clamp_val(p, 1, 255); +} + +static int segfeature_active(struct segmentation *seg, int segment_id, + enum SEG_LVL_FEATURES feature_id) +{ + return seg->enabled && + (seg->feature_mask[segment_id] & (1 << feature_id)); +} + +static int get_segdata(struct segmentation *seg, int segment_id, + enum SEG_LVL_FEATURES feature_id) +{ + return seg->feature_data[segment_id][feature_id]; +} + +static void vp9_update_sharpness(struct loop_filter_info_n *lfi, + int sharpness_lvl) +{ + int lvl; + + /* For each possible value for the loop filter fill out limits*/ + for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) { + /* Set loop filter parameters that control sharpness.*/ + int block_inside_limit = lvl >> ((sharpness_lvl > 0) + + (sharpness_lvl > 4)); + + if (sharpness_lvl > 0) { + if (block_inside_limit > (9 - sharpness_lvl)) + block_inside_limit = (9 - sharpness_lvl); + } + + if (block_inside_limit < 1) + block_inside_limit = 1; + + lfi->lfthr[lvl].lim = (u8)block_inside_limit; + lfi->lfthr[lvl].mblim = (u8)(2 * (lvl + 2) + + block_inside_limit); + } +} + +/* Instantiate this function once when decode is started */ +static void +vp9_loop_filter_init(struct amvdec_core *core, struct codec_vp9 *vp9) +{ + struct loop_filter_info_n *lfi = &vp9->lfi; + struct loopfilter *lf = &vp9->lf; + struct segmentation *seg_4lf = &vp9->seg_4lf; + int i; + + memset(lfi, 0, sizeof(struct loop_filter_info_n)); + memset(lf, 0, sizeof(struct loopfilter)); + memset(seg_4lf, 0, sizeof(struct segmentation)); + lf->sharpness_level = 0; + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + + for (i = 0; i < 32; i++) { + unsigned int thr; + + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr << 16) | ((lfi->lfthr[i * 2].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2].mblim & 0xff); + + amvdec_write_dos(core, HEVC_DBLK_CFG9, thr); + } + + if (core->platform->revision >= VDEC_REVISION_SM1) + amvdec_write_dos(core, HEVC_DBLK_CFGB, + (0x3 << 14) | /* dw fifo thres r and b */ + (0x3 << 12) | /* dw fifo thres r or b */ + (0x3 << 10) | /* dw fifo thres not r/b */ + BIT(0)); /* VP9 video format */ + else if (core->platform->revision >= VDEC_REVISION_G12A) + /* VP9 video format */ + amvdec_write_dos(core, HEVC_DBLK_CFGB, (0x54 << 8) | BIT(0)); + else + amvdec_write_dos(core, HEVC_DBLK_CFGB, 0x40400001); +} + +static void +vp9_loop_filter_frame_init(struct amvdec_core *core, struct segmentation *seg, + struct loop_filter_info_n *lfi, + struct loopfilter *lf, int default_filt_lvl) +{ + int i; + int seg_id; + + /* + * n_shift is the multiplier for lf_deltas + * the multiplier is: + * - 1 for when filter_lvl is between 0 and 31 + * - 2 when filter_lvl is between 32 and 63 + */ + const int scale = 1 << (default_filt_lvl >> 5); + + /* update limits if sharpness has changed */ + if (lf->last_sharpness_level != lf->sharpness_level) { + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + + /* Write to register */ + for (i = 0; i < 32; i++) { + unsigned int thr; + + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr << 16) | + ((lfi->lfthr[i * 2].lim & 0x3f) << 8) | + (lfi->lfthr[i * 2].mblim & 0xff); + + amvdec_write_dos(core, HEVC_DBLK_CFG9, thr); + } + } + + for (seg_id = 0; seg_id < MAX_SEGMENTS; seg_id++) { + int lvl_seg = default_filt_lvl; + + if (segfeature_active(seg, seg_id, SEG_LVL_ALT_LF)) { + const int data = get_segdata(seg, seg_id, + SEG_LVL_ALT_LF); + lvl_seg = clamp_t(int, + seg->abs_delta == SEGMENT_ABSDATA ? + data : default_filt_lvl + data, + 0, MAX_LOOP_FILTER); + } + + if (!lf->mode_ref_delta_enabled) { + /* + * We could get rid of this if we assume that deltas + * are set to zero when not in use. + * encoder always uses deltas + */ + memset(lfi->lvl[seg_id], lvl_seg, + sizeof(lfi->lvl[seg_id])); + } else { + int ref, mode; + const int intra_lvl = + lvl_seg + lf->ref_deltas[INTRA_FRAME] * scale; + lfi->lvl[seg_id][INTRA_FRAME][0] = + clamp_val(intra_lvl, 0, MAX_LOOP_FILTER); + + for (ref = LAST_FRAME; ref < MAX_REF_FRAMES; ++ref) { + for (mode = 0; mode < MAX_MODE_LF_DELTAS; + ++mode) { + const int inter_lvl = + lvl_seg + + lf->ref_deltas[ref] * scale + + lf->mode_deltas[mode] * scale; + lfi->lvl[seg_id][ref][mode] = + clamp_val(inter_lvl, 0, + MAX_LOOP_FILTER); + } + } + } + } + + for (i = 0; i < 16; i++) { + unsigned int level; + + level = ((lfi->lvl[i >> 1][3][i & 1] & 0x3f) << 24) | + ((lfi->lvl[i >> 1][2][i & 1] & 0x3f) << 16) | + ((lfi->lvl[i >> 1][1][i & 1] & 0x3f) << 8) | + (lfi->lvl[i >> 1][0][i & 1] & 0x3f); + if (!default_filt_lvl) + level = 0; + + amvdec_write_dos(core, HEVC_DBLK_CFGA, level); + } +} + +static void codec_vp9_flush_output(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp, *n; + + mutex_lock(&vp9->lock); + list_for_each_entry_safe(tmp, n, &vp9->ref_frames_list, list) { + if (!tmp->done) { + if (tmp->show) + amvdec_dst_buf_done(sess, tmp->vbuf, + V4L2_FIELD_NONE); + else + v4l2_m2m_buf_queue(sess->m2m_ctx, tmp->vbuf); + + vp9->frames_num--; + } + + list_del(&tmp->list); + kfree(tmp); + } + mutex_unlock(&vp9->lock); +} + +static u32 codec_vp9_num_pending_bufs(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + + if (!vp9) + return 0; + + return vp9->frames_num; +} + +static int codec_vp9_alloc_workspace(struct amvdec_core *core, + struct codec_vp9 *vp9) +{ + /* Allocate some memory for the VP9 decoder's state */ + vp9->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, + &vp9->workspace_paddr, + GFP_KERNEL); + if (!vp9->workspace_vaddr) { + dev_err(core->dev, "Failed to allocate VP9 Workspace\n"); + return -ENOMEM; + } + + return 0; +} + +static void codec_vp9_setup_workspace(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + u32 revision = core->platform->revision; + dma_addr_t wkaddr = vp9->workspace_paddr; + + amvdec_write_dos(core, HEVCD_IPP_LINEBUFF_BASE, wkaddr + IPP_OFFSET); + amvdec_write_dos(core, VP9_RPM_BUFFER, wkaddr + RPM_OFFSET); + amvdec_write_dos(core, VP9_SHORT_TERM_RPS, wkaddr + SH_TM_RPS_OFFSET); + amvdec_write_dos(core, VP9_PPS_BUFFER, wkaddr + PPS_OFFSET); + amvdec_write_dos(core, VP9_SAO_UP, wkaddr + SAO_UP_OFFSET); + + amvdec_write_dos(core, VP9_STREAM_SWAP_BUFFER, + wkaddr + SWAP_BUF_OFFSET); + amvdec_write_dos(core, VP9_STREAM_SWAP_BUFFER2, + wkaddr + SWAP_BUF2_OFFSET); + amvdec_write_dos(core, VP9_SCALELUT, wkaddr + SCALELUT_OFFSET); + + if (core->platform->revision >= VDEC_REVISION_G12A) + amvdec_write_dos(core, HEVC_DBLK_CFGE, + wkaddr + DBLK_PARA_OFFSET); + + amvdec_write_dos(core, HEVC_DBLK_CFG4, wkaddr + DBLK_PARA_OFFSET); + amvdec_write_dos(core, HEVC_DBLK_CFG5, wkaddr + DBLK_DATA_OFFSET); + amvdec_write_dos(core, VP9_SEG_MAP_BUFFER, wkaddr + SEG_MAP_OFFSET); + amvdec_write_dos(core, VP9_PROB_SWAP_BUFFER, wkaddr + PROB_OFFSET); + amvdec_write_dos(core, VP9_COUNT_SWAP_BUFFER, wkaddr + COUNT_OFFSET); + amvdec_write_dos(core, LMEM_DUMP_ADR, wkaddr + LMEM_OFFSET); + + if (codec_hevc_use_mmu(revision, sess->pixfmt_cap, vp9->is_10bit)) { + amvdec_write_dos(core, HEVC_SAO_MMU_VH0_ADDR, + wkaddr + MMU_VBH_OFFSET); + amvdec_write_dos(core, HEVC_SAO_MMU_VH1_ADDR, + wkaddr + MMU_VBH_OFFSET + (MMU_VBH_SIZE / 2)); + + if (revision >= VDEC_REVISION_G12A) + amvdec_write_dos(core, HEVC_ASSIST_MMU_MAP_ADDR, + vp9->common.mmu_map_paddr); + else + amvdec_write_dos(core, VP9_MMU_MAP_BUFFER, + vp9->common.mmu_map_paddr); + } +} + +static int codec_vp9_start(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9; + u32 val; + int i; + int ret; + + vp9 = kzalloc(sizeof(*vp9), GFP_KERNEL); + if (!vp9) + return -ENOMEM; + + ret = codec_vp9_alloc_workspace(core, vp9); + if (ret) + goto free_vp9; + + codec_vp9_setup_workspace(sess, vp9); + amvdec_write_dos_bits(core, HEVC_STREAM_CONTROL, BIT(0)); + /* stream_fifo_hole */ + if (core->platform->revision >= VDEC_REVISION_G12A) + amvdec_write_dos_bits(core, HEVC_STREAM_FIFO_CTL, BIT(29)); + + val = amvdec_read_dos(core, HEVC_PARSER_INT_CONTROL) & 0x7fffffff; + val |= (3 << 29) | BIT(24) | BIT(22) | BIT(7) | BIT(4) | BIT(0); + amvdec_write_dos(core, HEVC_PARSER_INT_CONTROL, val); + amvdec_write_dos_bits(core, HEVC_SHIFT_STATUS, BIT(0)); + amvdec_write_dos(core, HEVC_SHIFT_CONTROL, BIT(10) | BIT(9) | + (3 << 6) | BIT(5) | BIT(2) | BIT(1) | BIT(0)); + amvdec_write_dos(core, HEVC_CABAC_CONTROL, BIT(0)); + amvdec_write_dos(core, HEVC_PARSER_CORE_CONTROL, BIT(0)); + amvdec_write_dos(core, HEVC_SHIFT_STARTCODE, 0x00000001); + + amvdec_write_dos(core, VP9_DEC_STATUS_REG, 0); + + amvdec_write_dos(core, HEVC_PARSER_CMD_WRITE, BIT(16)); + for (i = 0; i < ARRAY_SIZE(vdec_hevc_parser_cmd); ++i) + amvdec_write_dos(core, HEVC_PARSER_CMD_WRITE, + vdec_hevc_parser_cmd[i]); + + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0); + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1); + amvdec_write_dos(core, HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2); + amvdec_write_dos(core, HEVC_PARSER_IF_CONTROL, + BIT(5) | BIT(2) | BIT(0)); + + amvdec_write_dos(core, HEVCD_IPP_TOP_CNTL, BIT(0)); + amvdec_write_dos(core, HEVCD_IPP_TOP_CNTL, BIT(1)); + + amvdec_write_dos(core, VP9_WAIT_FLAG, 1); + + /* clear mailbox interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_CLR_REG, 1); + /* enable mailbox interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 1); + /* disable PSCALE for hardware sharing */ + amvdec_write_dos(core, HEVC_PSCALE_CTRL, 0); + /* Let the uCode do all the parsing */ + amvdec_write_dos(core, NAL_SEARCH_CTL, 0x8); + + amvdec_write_dos(core, DECODE_STOP_POS, 0); + amvdec_write_dos(core, VP9_DECODE_MODE, DECODE_MODE_SINGLE); + + pr_debug("decode_count: %u; decode_size: %u\n", + amvdec_read_dos(core, HEVC_DECODE_COUNT), + amvdec_read_dos(core, HEVC_DECODE_SIZE)); + + vp9_loop_filter_init(core, vp9); + + INIT_LIST_HEAD(&vp9->ref_frames_list); + mutex_init(&vp9->lock); + memset(&vp9->ref_frame_map, -1, sizeof(vp9->ref_frame_map)); + memset(&vp9->next_ref_frame_map, -1, sizeof(vp9->next_ref_frame_map)); + for (i = 0; i < REFS_PER_FRAME; ++i) + vp9->frame_refs[i] = NULL; + sess->priv = vp9; + + return 0; + +free_vp9: + kfree(vp9); + return ret; +} + +static int codec_vp9_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + + mutex_lock(&vp9->lock); + if (vp9->workspace_vaddr) + dma_free_coherent(core->dev, SIZE_WORKSPACE, + vp9->workspace_vaddr, + vp9->workspace_paddr); + + codec_hevc_free_fbc_buffers(sess, &vp9->common); + mutex_unlock(&vp9->lock); + + return 0; +} + +static void codec_vp9_set_sao(struct amvdec_session *sess, + struct vb2_buffer *vb) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + + dma_addr_t buf_y_paddr; + dma_addr_t buf_u_v_paddr; + u32 val; + + if (codec_hevc_use_downsample(sess->pixfmt_cap, vp9->is_10bit)) + buf_y_paddr = + vp9->common.fbc_buffer_paddr[vb->index]; + else + buf_y_paddr = + vb2_dma_contig_plane_dma_addr(vb, 0); + + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) { + val = amvdec_read_dos(core, HEVC_SAO_CTRL5) & ~0xff0200; + amvdec_write_dos(core, HEVC_SAO_CTRL5, val); + amvdec_write_dos(core, HEVC_CM_BODY_START_ADDR, buf_y_paddr); + } + + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) { + buf_y_paddr = + vb2_dma_contig_plane_dma_addr(vb, 0); + buf_u_v_paddr = + vb2_dma_contig_plane_dma_addr(vb, 1); + amvdec_write_dos(core, HEVC_SAO_Y_START_ADDR, buf_y_paddr); + amvdec_write_dos(core, HEVC_SAO_C_START_ADDR, buf_u_v_paddr); + amvdec_write_dos(core, HEVC_SAO_Y_WPTR, buf_y_paddr); + amvdec_write_dos(core, HEVC_SAO_C_WPTR, buf_u_v_paddr); + } + + if (codec_hevc_use_mmu(core->platform->revision, sess->pixfmt_cap, + vp9->is_10bit)) { + amvdec_write_dos(core, HEVC_CM_HEADER_START_ADDR, + vp9->common.mmu_header_paddr[vb->index]); + /* use HEVC_CM_HEADER_START_ADDR */ + amvdec_write_dos_bits(core, HEVC_SAO_CTRL5, BIT(10)); + } + + amvdec_write_dos(core, HEVC_SAO_Y_LENGTH, + amvdec_get_output_size(sess)); + amvdec_write_dos(core, HEVC_SAO_C_LENGTH, + (amvdec_get_output_size(sess) / 2)); + + if (core->platform->revision >= VDEC_REVISION_G12A) { + amvdec_clear_dos_bits(core, HEVC_DBLK_CFGB, + BIT(4) | BIT(5) | BIT(8) | BIT(9)); + /* enable first, compressed write */ + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(8)); + + /* enable second, uncompressed write */ + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(9)); + + /* dblk pipeline mode=1 for performance */ + if (sess->width >= 1280) + amvdec_write_dos_bits(core, HEVC_DBLK_CFGB, BIT(4)); + + pr_debug("HEVC_DBLK_CFGB: %08X\n", + amvdec_read_dos(core, HEVC_DBLK_CFGB)); + } + + val = amvdec_read_dos(core, HEVC_SAO_CTRL1) & ~0x3ff0; + val |= 0xff0; /* Set endianness for 2-bytes swaps (nv12) */ + if (core->platform->revision < VDEC_REVISION_G12A) { + val &= ~0x3; + if (!codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) + val |= BIT(0); /* disable cm compression */ + /* TOFIX: Handle Amlogic Framebuffer compression */ + } + + amvdec_write_dos(core, HEVC_SAO_CTRL1, val); + pr_debug("HEVC_SAO_CTRL1: %08X\n", val); + + /* no downscale for NV12 */ + val = amvdec_read_dos(core, HEVC_SAO_CTRL5) & ~0xff0000; + amvdec_write_dos(core, HEVC_SAO_CTRL5, val); + + val = amvdec_read_dos(core, HEVCD_IPP_AXIIF_CONFIG) & ~0x30; + val |= 0xf; + val &= ~BIT(12); /* NV12 */ + amvdec_write_dos(core, HEVCD_IPP_AXIIF_CONFIG, val); +} + +static dma_addr_t codec_vp9_get_frame_mv_paddr(struct codec_vp9 *vp9, + struct vp9_frame *frame) +{ + return vp9->workspace_paddr + MPRED_MV_OFFSET + + (frame->index * MPRED_MV_BUF_SIZE); +} + +static void codec_vp9_set_mpred_mv(struct amvdec_core *core, + struct codec_vp9 *vp9) +{ + int mpred_mv_rd_end_addr; + int use_prev_frame_mvs = vp9->prev_frame->width == + vp9->cur_frame->width && + vp9->prev_frame->height == + vp9->cur_frame->height && + !vp9->prev_frame->intra_only && + vp9->prev_frame->show && + vp9->prev_frame->type != KEY_FRAME; + + amvdec_write_dos(core, HEVC_MPRED_CTRL3, 0x24122412); + amvdec_write_dos(core, HEVC_MPRED_ABV_START_ADDR, + vp9->workspace_paddr + MPRED_ABV_OFFSET); + + amvdec_clear_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + if (use_prev_frame_mvs) + amvdec_write_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + + amvdec_write_dos(core, HEVC_MPRED_MV_WR_START_ADDR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->cur_frame)); + amvdec_write_dos(core, HEVC_MPRED_MV_WPTR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->cur_frame)); + + amvdec_write_dos(core, HEVC_MPRED_MV_RD_START_ADDR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame)); + amvdec_write_dos(core, HEVC_MPRED_MV_RPTR, + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame)); + + mpred_mv_rd_end_addr = + codec_vp9_get_frame_mv_paddr(vp9, vp9->prev_frame) + + (vp9->lcu_total * MV_MEM_UNIT); + amvdec_write_dos(core, HEVC_MPRED_MV_RD_END_ADDR, mpred_mv_rd_end_addr); +} + +static void codec_vp9_update_next_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + u32 buf_idx = vp9->cur_frame->index; + int ref_index = 0; + int refresh_frame_flags; + int mask; + + refresh_frame_flags = vp9->cur_frame->type == KEY_FRAME ? + 0xff : param->p.refresh_frame_flags; + + for (mask = refresh_frame_flags; mask; mask >>= 1) { + pr_debug("mask=%08X; ref_index=%d\n", mask, ref_index); + if (mask & 1) + vp9->next_ref_frame_map[ref_index] = buf_idx; + else + vp9->next_ref_frame_map[ref_index] = + vp9->ref_frame_map[ref_index]; + + ++ref_index; + } + + for (; ref_index < REF_FRAMES; ++ref_index) + vp9->next_ref_frame_map[ref_index] = + vp9->ref_frame_map[ref_index]; +} + +static void codec_vp9_save_refs(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + const int ref = (param->p.ref_info >> + (((REFS_PER_FRAME - i - 1) * 4) + 1)) & 0x7; + + if (vp9->ref_frame_map[ref] < 0) + continue; + + pr_warn("%s: FIXME, would need to save ref %d\n", + __func__, vp9->ref_frame_map[ref]); + } +} + +static void codec_vp9_update_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int ref_index = 0; + int mask; + int refresh_frame_flags; + + if (!vp9->cur_frame) + return; + + refresh_frame_flags = vp9->cur_frame->type == KEY_FRAME ? + 0xff : param->p.refresh_frame_flags; + + for (mask = refresh_frame_flags; mask; mask >>= 1) { + vp9->ref_frame_map[ref_index] = + vp9->next_ref_frame_map[ref_index]; + ++ref_index; + } + + if (param->p.show_existing_frame) + return; + + for (; ref_index < REF_FRAMES; ++ref_index) + vp9->ref_frame_map[ref_index] = + vp9->next_ref_frame_map[ref_index]; +} + +static struct vp9_frame *codec_vp9_get_frame_by_idx(struct codec_vp9 *vp9, + int idx) +{ + struct vp9_frame *frame; + + list_for_each_entry(frame, &vp9->ref_frames_list, list) { + if (frame->index == idx) + return frame; + } + + return NULL; +} + +static void codec_vp9_sync_ref(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + const int ref = (param->p.ref_info >> + (((REFS_PER_FRAME - i - 1) * 4) + 1)) & 0x7; + const int idx = vp9->ref_frame_map[ref]; + + vp9->frame_refs[i] = codec_vp9_get_frame_by_idx(vp9, idx); + if (!vp9->frame_refs[i]) + pr_warn("%s: couldn't find VP9 ref %d\n", __func__, + idx); + } +} + +static void codec_vp9_set_refs(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + int i; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct vp9_frame *frame = vp9->frame_refs[i]; + int id_y; + int id_u_v; + + if (!frame) + continue; + + if (codec_hevc_use_fbc(sess->pixfmt_cap, vp9->is_10bit)) { + id_y = frame->index; + id_u_v = id_y; + } else { + id_y = frame->index * 2; + id_u_v = id_y + 1; + } + + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (id_u_v << 16) | (id_u_v << 8) | id_y); + } +} + +static void codec_vp9_set_mc(struct amvdec_session *sess, + struct codec_vp9 *vp9) +{ + struct amvdec_core *core = sess->core; + u32 scale = 0; + u32 sz; + int i; + + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1); + codec_vp9_set_refs(sess, vp9); + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (16 << 8) | 1); + codec_vp9_set_refs(sess, vp9); + + amvdec_write_dos(core, VP9D_MPP_REFINFO_TBL_ACCCONFIG, BIT(2)); + for (i = 0; i < REFS_PER_FRAME; ++i) { + if (!vp9->frame_refs[i]) + continue; + + if (vp9->frame_refs[i]->width != vp9->width || + vp9->frame_refs[i]->height != vp9->height) + scale = 1; + + sz = amvdec_am21c_body_size(vp9->frame_refs[i]->width, + vp9->frame_refs[i]->height); + + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + vp9->frame_refs[i]->width); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + vp9->frame_refs[i]->height); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + (vp9->frame_refs[i]->width << 14) / + vp9->width); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, + (vp9->frame_refs[i]->height << 14) / + vp9->height); + amvdec_write_dos(core, VP9D_MPP_REFINFO_DATA, sz >> 5); + } + + amvdec_write_dos(core, VP9D_MPP_REF_SCALE_ENBL, scale); +} + +static struct vp9_frame *codec_vp9_get_new_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + union rpm_param *param = &vp9->rpm_param; + struct vb2_v4l2_buffer *vbuf; + struct vp9_frame *new_frame; + + new_frame = kzalloc(sizeof(*new_frame), GFP_KERNEL); + if (!new_frame) + return NULL; + + vbuf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx); + if (!vbuf) { + dev_err(sess->core->dev, "No dst buffer available\n"); + kfree(new_frame); + return NULL; + } + + while (codec_vp9_get_frame_by_idx(vp9, vbuf->vb2_buf.index)) { + struct vb2_v4l2_buffer *old_vbuf = vbuf; + + vbuf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx); + v4l2_m2m_buf_queue(sess->m2m_ctx, old_vbuf); + if (!vbuf) { + dev_err(sess->core->dev, "No dst buffer available\n"); + kfree(new_frame); + return NULL; + } + } + + new_frame->vbuf = vbuf; + new_frame->index = vbuf->vb2_buf.index; + new_frame->intra_only = param->p.intra_only; + new_frame->show = param->p.show_frame; + new_frame->type = param->p.frame_type; + new_frame->width = vp9->width; + new_frame->height = vp9->height; + list_add_tail(&new_frame->list, &vp9->ref_frames_list); + vp9->frames_num++; + + return new_frame; +} + +static void codec_vp9_show_existing_frame(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + + if (!param->p.show_existing_frame) + return; + + pr_debug("showing frame %u\n", param->p.frame_to_show_idx); +} + +static void codec_vp9_rm_noshow_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp; + + list_for_each_entry(tmp, &vp9->ref_frames_list, list) { + if (tmp->show) + continue; + + pr_debug("rm noshow: %u\n", tmp->index); + v4l2_m2m_buf_queue(sess->m2m_ctx, tmp->vbuf); + list_del(&tmp->list); + kfree(tmp); + vp9->frames_num--; + return; + } +} + +static void codec_vp9_process_frame(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + union rpm_param *param = &vp9->rpm_param; + int intra_only; + + if (!param->p.show_frame) + codec_vp9_rm_noshow_frame(sess); + + vp9->cur_frame = codec_vp9_get_new_frame(sess); + if (!vp9->cur_frame) + return; + + pr_debug("frame %d: type: %08X; show_exist: %u; show: %u, intra_only: %u\n", + vp9->cur_frame->index, + param->p.frame_type, param->p.show_existing_frame, + param->p.show_frame, param->p.intra_only); + + if (param->p.frame_type != KEY_FRAME) + codec_vp9_sync_ref(vp9); + codec_vp9_update_next_ref(vp9); + codec_vp9_show_existing_frame(vp9); + + if (codec_hevc_use_mmu(core->platform->revision, sess->pixfmt_cap, + vp9->is_10bit)) + codec_hevc_fill_mmu_map(sess, &vp9->common, + &vp9->cur_frame->vbuf->vb2_buf); + + intra_only = param->p.show_frame ? 0 : param->p.intra_only; + + /* clear mpred (for keyframe only) */ + if (param->p.frame_type != KEY_FRAME && !intra_only) { + codec_vp9_set_mc(sess, vp9); + codec_vp9_set_mpred_mv(core, vp9); + } else { + amvdec_clear_dos_bits(core, HEVC_MPRED_CTRL4, BIT(6)); + } + + amvdec_write_dos(core, HEVC_PARSER_PICTURE_SIZE, + (vp9->height << 16) | vp9->width); + codec_vp9_set_sao(sess, &vp9->cur_frame->vbuf->vb2_buf); + + vp9_loop_filter_frame_init(core, &vp9->seg_4lf, + &vp9->lfi, &vp9->lf, + vp9->default_filt_lvl); + + /* ask uCode to start decoding */ + amvdec_write_dos(core, VP9_DEC_STATUS_REG, VP9_10B_DECODE_SLICE); +} + +static void codec_vp9_process_lf(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int i; + + vp9->lf.mode_ref_delta_enabled = param->p.mode_ref_delta_enabled; + vp9->lf.sharpness_level = param->p.sharpness_level; + vp9->default_filt_lvl = param->p.filter_level; + vp9->seg_4lf.enabled = param->p.seg_enabled; + vp9->seg_4lf.abs_delta = param->p.seg_abs_delta; + + for (i = 0; i < 4; i++) + vp9->lf.ref_deltas[i] = param->p.ref_deltas[i]; + + for (i = 0; i < 2; i++) + vp9->lf.mode_deltas[i] = param->p.mode_deltas[i]; + + for (i = 0; i < MAX_SEGMENTS; i++) + vp9->seg_4lf.feature_mask[i] = + (param->p.seg_lf_info[i] & 0x8000) ? + (1 << SEG_LVL_ALT_LF) : 0; + + for (i = 0; i < MAX_SEGMENTS; i++) + vp9->seg_4lf.feature_data[i][SEG_LVL_ALT_LF] = + (param->p.seg_lf_info[i] & 0x100) ? + -(param->p.seg_lf_info[i] & 0x3f) + : (param->p.seg_lf_info[i] & 0x3f); +} + +static void codec_vp9_resume(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + + mutex_lock(&vp9->lock); + if (codec_hevc_setup_buffers(sess, &vp9->common, vp9->is_10bit)) { + mutex_unlock(&vp9->lock); + amvdec_abort(sess); + return; + } + + codec_vp9_setup_workspace(sess, vp9); + codec_hevc_setup_decode_head(sess, vp9->is_10bit); + codec_vp9_process_lf(vp9); + codec_vp9_process_frame(sess); + + mutex_unlock(&vp9->lock); +} + +/* + * The RPM section within the workspace contains + * many information regarding the parsed bitstream + */ +static void codec_vp9_fetch_rpm(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + u16 *rpm_vaddr = vp9->workspace_vaddr + RPM_OFFSET; + int i, j; + + for (i = 0; i < RPM_BUF_SIZE; i += 4) + for (j = 0; j < 4; j++) + vp9->rpm_param.l.data[i + j] = rpm_vaddr[i + 3 - j]; +} + +static int codec_vp9_process_rpm(struct codec_vp9 *vp9) +{ + union rpm_param *param = &vp9->rpm_param; + int src_changed = 0; + int is_10bit = 0; + int pic_width_64 = ALIGN(param->p.width, 64); + int pic_height_32 = ALIGN(param->p.height, 32); + int pic_width_lcu = (pic_width_64 % LCU_SIZE) ? + pic_width_64 / LCU_SIZE + 1 + : pic_width_64 / LCU_SIZE; + int pic_height_lcu = (pic_height_32 % LCU_SIZE) ? + pic_height_32 / LCU_SIZE + 1 + : pic_height_32 / LCU_SIZE; + vp9->lcu_total = pic_width_lcu * pic_height_lcu; + + if (param->p.bit_depth == 10) + is_10bit = 1; + + if (vp9->width != param->p.width || vp9->height != param->p.height || + vp9->is_10bit != is_10bit) + src_changed = 1; + + vp9->width = param->p.width; + vp9->height = param->p.height; + vp9->is_10bit = is_10bit; + + pr_debug("width: %u; height: %u; is_10bit: %d; src_changed: %d\n", + vp9->width, vp9->height, is_10bit, src_changed); + + return src_changed; +} + +static bool codec_vp9_is_ref(struct codec_vp9 *vp9, struct vp9_frame *frame) +{ + int i; + + for (i = 0; i < REF_FRAMES; ++i) + if (vp9->ref_frame_map[i] == frame->index) + return true; + + return false; +} + +static void codec_vp9_show_frame(struct amvdec_session *sess) +{ + struct codec_vp9 *vp9 = sess->priv; + struct vp9_frame *tmp, *n; + + list_for_each_entry_safe(tmp, n, &vp9->ref_frames_list, list) { + if (!tmp->show || tmp == vp9->cur_frame) + continue; + + if (!tmp->done) { + pr_debug("Doning %u\n", tmp->index); + amvdec_dst_buf_done(sess, tmp->vbuf, V4L2_FIELD_NONE); + tmp->done = 1; + vp9->frames_num--; + } + + if (codec_vp9_is_ref(vp9, tmp) || tmp == vp9->prev_frame) + continue; + + pr_debug("deleting %d\n", tmp->index); + list_del(&tmp->list); + kfree(tmp); + } +} + +static void vp9_tree_merge_probs(unsigned int *prev_prob, + unsigned int *cur_prob, + int coef_node_start, int tree_left, + int tree_right, + int tree_i, int node) +{ + int prob_32, prob_res, prob_shift; + int pre_prob, new_prob; + int den, m_count, get_prob, factor; + + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + den = tree_left + tree_right; + + if (den == 0) { + new_prob = pre_prob; + } else { + m_count = den < MODE_MV_COUNT_SAT ? den : MODE_MV_COUNT_SAT; + get_prob = + clip_prob(div_r32(((int64_t)tree_left * 256 + + (den >> 1)), + den)); + + /* weighted_prob */ + factor = count_to_update_factor[m_count]; + new_prob = round_power_of_two(pre_prob * (256 - factor) + + get_prob * factor, 8); + } + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & (~(0xff << prob_shift))) | + (new_prob << prob_shift); +} + +static void adapt_coef_probs_cxt(unsigned int *prev_prob, + unsigned int *cur_prob, + unsigned int *count, + int update_factor, + int cxt_num, + int coef_cxt_start, + int coef_count_cxt_start) +{ + int prob_32, prob_res, prob_shift; + int pre_prob, new_prob; + int num, den, m_count, get_prob, factor; + int node, coef_node_start; + int count_sat = 24; + int cxt; + + for (cxt = 0; cxt < cxt_num; cxt++) { + const int n0 = count[coef_count_cxt_start]; + const int n1 = count[coef_count_cxt_start + 1]; + const int n2 = count[coef_count_cxt_start + 2]; + const int neob = count[coef_count_cxt_start + 3]; + const int nneob = count[coef_count_cxt_start + 4]; + const unsigned int branch_ct[3][2] = { + { neob, nneob }, + { n0, n1 + n2 }, + { n1, n2 } + }; + + coef_node_start = coef_cxt_start; + for (node = 0 ; node < 3 ; node++) { + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + /* get binary prob */ + num = branch_ct[node][0]; + den = branch_ct[node][0] + branch_ct[node][1]; + m_count = den < count_sat ? den : count_sat; + + get_prob = (den == 0) ? + 128u : + clip_prob(div_r32(((int64_t)num * 256 + + (den >> 1)), den)); + + factor = update_factor * m_count / count_sat; + new_prob = + round_power_of_two(pre_prob * (256 - factor) + + get_prob * factor, 8); + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & + (~(0xff << prob_shift))) | + (new_prob << prob_shift); + + coef_node_start += 1; + } + + coef_cxt_start = coef_cxt_start + 3; + coef_count_cxt_start = coef_count_cxt_start + 5; + } +} + +static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc, + unsigned int *prev_prob, unsigned int *cur_prob, + unsigned int *count) +{ + int tx_size, coef_tx_size_start, coef_count_tx_size_start; + int plane, coef_plane_start, coef_count_plane_start; + int type, coef_type_start, coef_count_type_start; + int band, coef_band_start, coef_count_band_start; + int cxt_num; + int coef_cxt_start, coef_count_cxt_start; + int node, coef_node_start, coef_count_node_start; + + int tree_i, tree_left, tree_right; + int mvd_i; + + int update_factor = cur_kf ? 112 : (prev_kf ? 128 : 112); + + int prob_32; + int prob_res; + int prob_shift; + int pre_prob; + + int den; + int get_prob; + int m_count; + int factor; + + int new_prob; + + for (tx_size = 0 ; tx_size < 4 ; tx_size++) { + coef_tx_size_start = VP9_COEF_START + + tx_size * 4 * VP9_COEF_SIZE_ONE_SET; + coef_count_tx_size_start = VP9_COEF_COUNT_START + + tx_size * 4 * VP9_COEF_COUNT_SIZE_ONE_SET; + coef_plane_start = coef_tx_size_start; + coef_count_plane_start = coef_count_tx_size_start; + + for (plane = 0 ; plane < 2 ; plane++) { + coef_type_start = coef_plane_start; + coef_count_type_start = coef_count_plane_start; + + for (type = 0 ; type < 2 ; type++) { + coef_band_start = coef_type_start; + coef_count_band_start = coef_count_type_start; + + for (band = 0 ; band < 6 ; band++) { + if (band == 0) + cxt_num = 3; + else + cxt_num = 6; + coef_cxt_start = coef_band_start; + coef_count_cxt_start = + coef_count_band_start; + + adapt_coef_probs_cxt(prev_prob, + cur_prob, + count, + update_factor, + cxt_num, + coef_cxt_start, + coef_count_cxt_start); + + if (band == 0) { + coef_band_start += 10; + coef_count_band_start += 15; + } else { + coef_band_start += 18; + coef_count_band_start += 30; + } + } + coef_type_start += VP9_COEF_SIZE_ONE_SET; + coef_count_type_start += + VP9_COEF_COUNT_SIZE_ONE_SET; + } + + coef_plane_start += 2 * VP9_COEF_SIZE_ONE_SET; + coef_count_plane_start += + 2 * VP9_COEF_COUNT_SIZE_ONE_SET; + } + } + + if (cur_kf == 0) { + /* mode_mv_merge_probs - merge_intra_inter_prob */ + for (coef_count_node_start = VP9_INTRA_INTER_COUNT_START; + coef_count_node_start < (VP9_MV_CLASS0_HP_1_COUNT_START + + VP9_MV_CLASS0_HP_1_COUNT_SIZE); + coef_count_node_start += 2) { + if (coef_count_node_start == + VP9_INTRA_INTER_COUNT_START) + coef_node_start = VP9_INTRA_INTER_START; + else if (coef_count_node_start == + VP9_COMP_INTER_COUNT_START) + coef_node_start = VP9_COMP_INTER_START; + else if (coef_count_node_start == + VP9_TX_MODE_COUNT_START) + coef_node_start = VP9_TX_MODE_START; + else if (coef_count_node_start == + VP9_SKIP_COUNT_START) + coef_node_start = VP9_SKIP_START; + else if (coef_count_node_start == + VP9_MV_SIGN_0_COUNT_START) + coef_node_start = VP9_MV_SIGN_0_START; + else if (coef_count_node_start == + VP9_MV_SIGN_1_COUNT_START) + coef_node_start = VP9_MV_SIGN_1_START; + else if (coef_count_node_start == + VP9_MV_BITS_0_COUNT_START) + coef_node_start = VP9_MV_BITS_0_START; + else if (coef_count_node_start == + VP9_MV_BITS_1_COUNT_START) + coef_node_start = VP9_MV_BITS_1_START; + else if (coef_count_node_start == + VP9_MV_CLASS0_HP_0_COUNT_START) + coef_node_start = VP9_MV_CLASS0_HP_0_START; + + den = count[coef_count_node_start] + + count[coef_count_node_start + 1]; + + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + if (den == 0) { + new_prob = pre_prob; + } else { + m_count = den < MODE_MV_COUNT_SAT ? + den : MODE_MV_COUNT_SAT; + get_prob = + clip_prob(div_r32(((int64_t) + count[coef_count_node_start] * 256 + + (den >> 1)), + den)); + + /* weighted prob */ + factor = count_to_update_factor[m_count]; + new_prob = + round_power_of_two(pre_prob * + (256 - factor) + + get_prob * factor, + 8); + } + + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & + (~(0xff << prob_shift))) | + (new_prob << prob_shift); + + coef_node_start = coef_node_start + 1; + } + + coef_node_start = VP9_INTER_MODE_START; + coef_count_node_start = VP9_INTER_MODE_COUNT_START; + for (tree_i = 0 ; tree_i < 7 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 1]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 3]; + break; + default: + tree_left = count[start + 2]; + tree_right = count[start + 0] + + count[start + 1] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_count_node_start = coef_count_node_start + 4; + } + + coef_node_start = VP9_IF_Y_MODE_START; + coef_count_node_start = VP9_IF_Y_MODE_COUNT_START; + for (tree_i = 0 ; tree_i < 14 ; tree_i++) { + for (node = 0 ; node < 9 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 8: + tree_left = + count[start + D153_PRED]; + tree_right = + count[start + D207_PRED]; + break; + case 7: + tree_left = + count[start + D63_PRED]; + tree_right = + count[start + D207_PRED] + + count[start + D153_PRED]; + break; + case 6: + tree_left = + count[start + D45_PRED]; + tree_right = + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 5: + tree_left = + count[start + D135_PRED]; + tree_right = + count[start + D117_PRED]; + break; + case 4: + tree_left = + count[start + H_PRED]; + tree_right = + count[start + D117_PRED] + + count[start + D135_PRED]; + break; + case 3: + tree_left = + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED]; + tree_right = + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 2: + tree_left = + count[start + V_PRED]; + tree_right = + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + case 1: + tree_left = + count[start + TM_PRED]; + tree_right = + count[start + V_PRED] + + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + default: + tree_left = + count[start + DC_PRED]; + tree_right = + count[start + TM_PRED] + + count[start + V_PRED] + + count[start + H_PRED] + + count[start + D117_PRED] + + count[start + D135_PRED] + + count[start + D45_PRED] + + count[start + D207_PRED] + + count[start + D153_PRED] + + count[start + D63_PRED]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 10; + } + + coef_node_start = VP9_PARTITION_P_START; + coef_count_node_start = VP9_PARTITION_P_COUNT_START; + for (tree_i = 0 ; tree_i < 16 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_count_node_start = coef_count_node_start + 4; + } + + coef_node_start = VP9_INTERP_START; + coef_count_node_start = VP9_INTERP_COUNT_START; + for (tree_i = 0 ; tree_i < 4 ; tree_i++) { + for (node = 0 ; node < 2 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 3; + } + + coef_node_start = VP9_MV_JOINTS_START; + coef_count_node_start = VP9_MV_JOINTS_COUNT_START; + for (tree_i = 0 ; tree_i < 1 ; tree_i++) { + for (node = 0 ; node < 3 ; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + + for (mvd_i = 0 ; mvd_i < 2 ; mvd_i++) { + coef_node_start = mvd_i ? VP9_MV_CLASSES_1_START : + VP9_MV_CLASSES_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASSES_1_COUNT_START : + VP9_MV_CLASSES_0_COUNT_START; + tree_i = 0; + for (node = 0; node < 10; node++) { + unsigned int start = coef_count_node_start; + + switch (node) { + case 9: + tree_left = count[start + 9]; + tree_right = count[start + 10]; + break; + case 8: + tree_left = count[start + 7]; + tree_right = count[start + 8]; + break; + case 7: + tree_left = count[start + 7] + + count[start + 8]; + tree_right = count[start + 9] + + count[start + 10]; + break; + case 6: + tree_left = count[start + 6]; + tree_right = count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 5: + tree_left = count[start + 4]; + tree_right = count[start + 5]; + break; + case 4: + tree_left = count[start + 4] + + count[start + 5]; + tree_right = count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 3: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 2: + tree_left = count[start + 2] + + count[start + 3]; + tree_right = count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3] + + count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3] + + count[start + 4] + + count[start + 5] + + count[start + 6] + + count[start + 7] + + count[start + 8] + + count[start + 9] + + count[start + 10]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + coef_node_start = mvd_i ? VP9_MV_CLASS0_1_START : + VP9_MV_CLASS0_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASS0_1_COUNT_START : + VP9_MV_CLASS0_0_COUNT_START; + tree_i = 0; + node = 0; + tree_left = count[coef_count_node_start + 0]; + tree_right = count[coef_count_node_start + 1]; + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, + tree_i, node); + coef_node_start = mvd_i ? VP9_MV_CLASS0_FP_1_START : + VP9_MV_CLASS0_FP_0_START; + coef_count_node_start = mvd_i ? + VP9_MV_CLASS0_FP_1_COUNT_START : + VP9_MV_CLASS0_FP_0_COUNT_START; + + for (tree_i = 0; tree_i < 3; tree_i++) { + for (node = 0; node < 3; node++) { + unsigned int start = + coef_count_node_start; + switch (node) { + case 2: + tree_left = count[start + 2]; + tree_right = count[start + 3]; + break; + case 1: + tree_left = count[start + 1]; + tree_right = count[start + 2] + + count[start + 3]; + break; + default: + tree_left = count[start + 0]; + tree_right = count[start + 1] + + count[start + 2] + + count[start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, + cur_prob, + coef_node_start, + tree_left, + tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = + coef_count_node_start + 4; + } + } + } +} + +static irqreturn_t codec_vp9_threaded_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_vp9 *vp9 = sess->priv; + u32 dec_status = amvdec_read_dos(core, VP9_DEC_STATUS_REG); + u32 prob_status = amvdec_read_dos(core, VP9_ADAPT_PROB_REG); + int i; + + if (!vp9) + return IRQ_HANDLED; + + mutex_lock(&vp9->lock); + if (dec_status != VP9_HEAD_PARSER_DONE) { + dev_err(core->dev_dec, "Unrecognized dec_status: %08X\n", + dec_status); + amvdec_abort(sess); + goto unlock; + } + + pr_debug("ISR: %08X;%08X\n", dec_status, prob_status); + sess->keyframe_found = 1; + + if ((prob_status & 0xff) == 0xfd && vp9->cur_frame) { + /* VP9_REQ_ADAPT_PROB */ + u8 *prev_prob_b = ((u8 *)vp9->workspace_vaddr + + PROB_OFFSET) + + ((prob_status >> 8) * 0x1000); + u8 *cur_prob_b = ((u8 *)vp9->workspace_vaddr + + PROB_OFFSET) + 0x4000; + u8 *count_b = (u8 *)vp9->workspace_vaddr + + COUNT_OFFSET; + int last_frame_type = vp9->prev_frame ? + vp9->prev_frame->type : + KEY_FRAME; + + adapt_coef_probs(last_frame_type == KEY_FRAME, + vp9->cur_frame->type == KEY_FRAME ? 1 : 0, + prob_status >> 8, + (unsigned int *)prev_prob_b, + (unsigned int *)cur_prob_b, + (unsigned int *)count_b); + + memcpy(prev_prob_b, cur_prob_b, ADAPT_PROB_SIZE); + amvdec_write_dos(core, VP9_ADAPT_PROB_REG, 0); + } + + /* Invalidate first 3 refs */ + for (i = 0; i < REFS_PER_FRAME ; ++i) + vp9->frame_refs[i] = NULL; + + vp9->prev_frame = vp9->cur_frame; + codec_vp9_update_ref(vp9); + + codec_vp9_fetch_rpm(sess); + if (codec_vp9_process_rpm(vp9)) { + amvdec_src_change(sess, vp9->width, vp9->height, 16); + + /* No frame is actually processed */ + vp9->cur_frame = NULL; + + /* Show the remaining frame */ + codec_vp9_show_frame(sess); + + /* FIXME: Save refs for resized frame */ + if (vp9->frames_num) + codec_vp9_save_refs(vp9); + + goto unlock; + } + + codec_vp9_process_lf(vp9); + codec_vp9_process_frame(sess); + codec_vp9_show_frame(sess); + +unlock: + mutex_unlock(&vp9->lock); + return IRQ_HANDLED; +} + +static irqreturn_t codec_vp9_isr(struct amvdec_session *sess) +{ + return IRQ_WAKE_THREAD; +} + +struct amvdec_codec_ops codec_vp9_ops = { + .start = codec_vp9_start, + .stop = codec_vp9_stop, + .isr = codec_vp9_isr, + .threaded_isr = codec_vp9_threaded_isr, + .num_pending_bufs = codec_vp9_num_pending_bufs, + .drain = codec_vp9_flush_output, + .resume = codec_vp9_resume, +}; diff --git a/drivers/staging/media/meson/vdec/codec_vp9.h b/drivers/staging/media/meson/vdec/codec_vp9.h new file mode 100644 index 000000000000..62db65a2b939 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_vp9.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + */ + +#ifndef __MESON_VDEC_CODEC_VP9_H_ +#define __MESON_VDEC_CODEC_VP9_H_ + +#include "vdec.h" + +extern struct amvdec_codec_ops codec_vp9_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c index 95102a4bdc62..db7022707ff8 100644 --- a/drivers/staging/media/meson/vdec/esparser.c +++ b/drivers/staging/media/meson/vdec/esparser.c @@ -52,6 +52,7 @@ #define PARSER_VIDEO_HOLE 0x90 #define SEARCH_PATTERN_LEN 512 +#define VP9_HEADER_SIZE 16 static DECLARE_WAIT_QUEUE_HEAD(wq); static int search_done; @@ -74,27 +75,140 @@ static irqreturn_t esparser_isr(int irq, void *dev) return IRQ_HANDLED; } +/* + * VP9 frame headers need to be appended by a 16-byte long + * Amlogic custom header + */ +static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf) +{ + u8 *dp; + u8 marker; + int dsize; + int num_frames, cur_frame; + int cur_mag, mag, mag_ptr; + int frame_size[8], tot_frame_size[8]; + int total_datasize = 0; + int new_frame_size; + unsigned char *old_header = NULL; + + dp = (uint8_t *)vb2_plane_vaddr(buf, 0); + dsize = vb2_get_plane_payload(buf, 0); + + if (dsize == vb2_plane_size(buf, 0)) { + dev_warn(core->dev, "%s: unable to update header\n", __func__); + return 0; + } + + marker = dp[dsize - 1]; + if ((marker & 0xe0) == 0xc0) { + num_frames = (marker & 0x7) + 1; + mag = ((marker >> 3) & 0x3) + 1; + mag_ptr = dsize - mag * num_frames - 2; + if (dp[mag_ptr] != marker) + return 0; + + mag_ptr++; + for (cur_frame = 0; cur_frame < num_frames; cur_frame++) { + frame_size[cur_frame] = 0; + for (cur_mag = 0; cur_mag < mag; cur_mag++) { + frame_size[cur_frame] |= + (dp[mag_ptr] << (cur_mag * 8)); + mag_ptr++; + } + if (cur_frame == 0) + tot_frame_size[cur_frame] = + frame_size[cur_frame]; + else + tot_frame_size[cur_frame] = + tot_frame_size[cur_frame - 1] + + frame_size[cur_frame]; + total_datasize += frame_size[cur_frame]; + } + } else { + num_frames = 1; + frame_size[0] = dsize; + tot_frame_size[0] = dsize; + total_datasize = dsize; + } + + new_frame_size = total_datasize + num_frames * VP9_HEADER_SIZE; + + if (new_frame_size >= vb2_plane_size(buf, 0)) { + dev_warn(core->dev, "%s: unable to update header\n", __func__); + return 0; + } + + for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) { + int framesize = frame_size[cur_frame]; + int framesize_header = framesize + 4; + int oldframeoff = tot_frame_size[cur_frame] - framesize; + int outheaderoff = oldframeoff + cur_frame * VP9_HEADER_SIZE; + u8 *fdata = dp + outheaderoff; + u8 *old_framedata = dp + oldframeoff; + + memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize); + + fdata[0] = (framesize_header >> 24) & 0xff; + fdata[1] = (framesize_header >> 16) & 0xff; + fdata[2] = (framesize_header >> 8) & 0xff; + fdata[3] = (framesize_header >> 0) & 0xff; + fdata[4] = ((framesize_header >> 24) & 0xff) ^ 0xff; + fdata[5] = ((framesize_header >> 16) & 0xff) ^ 0xff; + fdata[6] = ((framesize_header >> 8) & 0xff) ^ 0xff; + fdata[7] = ((framesize_header >> 0) & 0xff) ^ 0xff; + fdata[8] = 0; + fdata[9] = 0; + fdata[10] = 0; + fdata[11] = 1; + fdata[12] = 'A'; + fdata[13] = 'M'; + fdata[14] = 'L'; + fdata[15] = 'V'; + + if (!old_header) { + /* nothing */ + } else if (old_header > fdata + 16 + framesize) { + dev_dbg(core->dev, "%s: data has gaps, setting to 0\n", + __func__); + memset(fdata + 16 + framesize, 0, + (old_header - fdata + 16 + framesize)); + } else if (old_header < fdata + 16 + framesize) { + dev_err(core->dev, "%s: data overwritten\n", __func__); + } + old_header = fdata; + } + + return new_frame_size; +} + /* Pad the packet to at least 4KiB bytes otherwise the VDEC unit won't trigger * ISRs. * Also append a start code 000001ff at the end to trigger * the ESPARSER interrupt. */ -static u32 esparser_pad_start_code(struct vb2_buffer *vb) +static u32 esparser_pad_start_code(struct amvdec_core *core, + struct vb2_buffer *vb, + u32 payload_size) { - u32 payload_size = vb2_get_plane_payload(vb, 0); u32 pad_size = 0; - u8 *vaddr = vb2_plane_vaddr(vb, 0) + payload_size; + u8 *vaddr = vb2_plane_vaddr(vb, 0); if (payload_size < ESPARSER_MIN_PACKET_SIZE) { pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size; - memset(vaddr, 0, pad_size); + memset(vaddr + payload_size, 0, pad_size); + } + + if ((payload_size + pad_size + SEARCH_PATTERN_LEN) > + vb2_plane_size(vb, 0)) { + dev_warn(core->dev, "%s: unable to pad start code\n", __func__); + return pad_size; } - memset(vaddr + pad_size, 0, SEARCH_PATTERN_LEN); - vaddr[pad_size] = 0x00; - vaddr[pad_size + 1] = 0x00; - vaddr[pad_size + 2] = 0x01; - vaddr[pad_size + 3] = 0xff; + memset(vaddr + payload_size + pad_size, 0, SEARCH_PATTERN_LEN); + vaddr[payload_size + pad_size] = 0x00; + vaddr[payload_size + pad_size + 1] = 0x00; + vaddr[payload_size + pad_size + 2] = 0x01; + vaddr[payload_size + pad_size + 3] = 0xff; return pad_size; } @@ -181,30 +295,60 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) struct vb2_buffer *vb = &vbuf->vb2_buf; struct amvdec_core *core = sess->core; struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; - u32 num_dst_bufs = 0; u32 payload_size = vb2_get_plane_payload(vb, 0); dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0); + u32 num_dst_bufs = 0; u32 offset; u32 pad_size; - if (codec_ops->num_pending_bufs) - num_dst_bufs = codec_ops->num_pending_bufs(sess); - - num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); - - if (esparser_vififo_get_free_space(sess) < payload_size || - atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs) + /* + * When max ref frame is held by VP9, this should be -= 3 to prevent a + * shortage of CAPTURE buffers on the decoder side. + * For the future, a good enhancement of the way this is handled could + * be to notify new capture buffers to the decoding modules, so that + * they could pause when there is no capture buffer available and + * resume on this notification. + */ + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) { + if (codec_ops->num_pending_bufs) + num_dst_bufs = codec_ops->num_pending_bufs(sess); + + num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) + num_dst_bufs -= 3; + + if (esparser_vififo_get_free_space(sess) < payload_size || + atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs) + return -EAGAIN; + } else if (esparser_vififo_get_free_space(sess) < payload_size) { return -EAGAIN; + } v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf); offset = esparser_get_offset(sess); - amvdec_add_ts_reorder(sess, vb->timestamp, offset); - dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X\n", - vb->timestamp, payload_size, offset); + amvdec_add_ts(sess, vb->timestamp, vbuf->timecode, offset, vbuf->flags); + dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X flags = %08X\n", + vb->timestamp, payload_size, offset, vbuf->flags); + + vbuf->flags = 0; + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = sess->sequence_out++; + + if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) { + payload_size = vp9_update_header(core, vb); - pad_size = esparser_pad_start_code(vb); + /* If unable to alter buffer to add headers */ + if (payload_size == 0) { + amvdec_remove_ts(sess, vb->timestamp); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + + return 0; + } + } + + pad_size = esparser_pad_start_code(core, vb, payload_size); ret = esparser_write_data(core, phy, payload_size + pad_size); if (ret <= 0) { @@ -216,19 +360,7 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) return 0; } - /* We need to wait until we parse the first keyframe. - * All buffers prior to the first keyframe must be dropped. - */ - if (!sess->keyframe_found) - usleep_range(1000, 2000); - - if (sess->keyframe_found) - atomic_inc(&sess->esparser_queued_bufs); - else - amvdec_remove_ts(sess, vb->timestamp); - - vbuf->flags = 0; - vbuf->field = V4L2_FIELD_NONE; + atomic_inc(&sess->esparser_queued_bufs); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); return 0; diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h new file mode 100644 index 000000000000..0392f41a1eed --- /dev/null +++ b/drivers/staging/media/meson/vdec/hevc_regs.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_VDEC_HEVC_REGS_H_ +#define __MESON_VDEC_HEVC_REGS_H_ + +#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024 + +#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4 +#define HEVC_ASSIST_MBOX1_MASK 0xc1d8 + +#define HEVC_ASSIST_SCRATCH_0 0xc300 +#define HEVC_ASSIST_SCRATCH_1 0xc304 +#define HEVC_ASSIST_SCRATCH_2 0xc308 +#define HEVC_ASSIST_SCRATCH_3 0xc30c +#define HEVC_ASSIST_SCRATCH_4 0xc310 +#define HEVC_ASSIST_SCRATCH_5 0xc314 +#define HEVC_ASSIST_SCRATCH_6 0xc318 +#define HEVC_ASSIST_SCRATCH_7 0xc31c +#define HEVC_ASSIST_SCRATCH_8 0xc320 +#define HEVC_ASSIST_SCRATCH_9 0xc324 +#define HEVC_ASSIST_SCRATCH_A 0xc328 +#define HEVC_ASSIST_SCRATCH_B 0xc32c +#define HEVC_ASSIST_SCRATCH_C 0xc330 +#define HEVC_ASSIST_SCRATCH_D 0xc334 +#define HEVC_ASSIST_SCRATCH_E 0xc338 +#define HEVC_ASSIST_SCRATCH_F 0xc33c +#define HEVC_ASSIST_SCRATCH_G 0xc340 +#define HEVC_ASSIST_SCRATCH_H 0xc344 +#define HEVC_ASSIST_SCRATCH_I 0xc348 +#define HEVC_ASSIST_SCRATCH_J 0xc34c +#define HEVC_ASSIST_SCRATCH_K 0xc350 +#define HEVC_ASSIST_SCRATCH_L 0xc354 +#define HEVC_ASSIST_SCRATCH_M 0xc358 +#define HEVC_ASSIST_SCRATCH_N 0xc35c + +#define HEVC_PARSER_VERSION 0xc400 +#define HEVC_STREAM_CONTROL 0xc404 +#define HEVC_STREAM_START_ADDR 0xc408 +#define HEVC_STREAM_END_ADDR 0xc40c +#define HEVC_STREAM_WR_PTR 0xc410 +#define HEVC_STREAM_RD_PTR 0xc414 +#define HEVC_STREAM_LEVEL 0xc418 +#define HEVC_STREAM_FIFO_CTL 0xc41c +#define HEVC_SHIFT_CONTROL 0xc420 +#define HEVC_SHIFT_STARTCODE 0xc424 +#define HEVC_SHIFT_EMULATECODE 0xc428 +#define HEVC_SHIFT_STATUS 0xc42c +#define HEVC_SHIFTED_DATA 0xc430 +#define HEVC_SHIFT_BYTE_COUNT 0xc434 +#define HEVC_SHIFT_COMMAND 0xc438 +#define HEVC_ELEMENT_RESULT 0xc43c +#define HEVC_CABAC_CONTROL 0xc440 +#define HEVC_PARSER_SLICE_INFO 0xc444 +#define HEVC_PARSER_CMD_WRITE 0xc448 +#define HEVC_PARSER_CORE_CONTROL 0xc44c +#define HEVC_PARSER_CMD_FETCH 0xc450 +#define HEVC_PARSER_CMD_STATUS 0xc454 +#define HEVC_PARSER_LCU_INFO 0xc458 +#define HEVC_PARSER_HEADER_INFO 0xc45c +#define HEVC_PARSER_INT_CONTROL 0xc480 +#define HEVC_PARSER_INT_STATUS 0xc484 +#define HEVC_PARSER_IF_CONTROL 0xc488 +#define HEVC_PARSER_PICTURE_SIZE 0xc48c +#define HEVC_PARSER_LCU_START 0xc490 +#define HEVC_PARSER_HEADER_INFO2 0xc494 +#define HEVC_PARSER_QUANT_READ 0xc498 +#define HEVC_PARSER_RESERVED_27 0xc49c +#define HEVC_PARSER_CMD_SKIP_0 0xc4a0 +#define HEVC_PARSER_CMD_SKIP_1 0xc4a4 +#define HEVC_PARSER_CMD_SKIP_2 0xc4a8 +#define HEVC_SAO_IF_STATUS 0xc4c0 +#define HEVC_SAO_IF_DATA_Y 0xc4c4 +#define HEVC_SAO_IF_DATA_U 0xc4c8 +#define HEVC_SAO_IF_DATA_V 0xc4cc +#define HEVC_STREAM_SWAP_ADDR 0xc4d0 +#define HEVC_STREAM_SWAP_CTRL 0xc4d4 +#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8 +#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc +#define HEVC_SAO_IF_WAIT_CNT 0xc4e0 + +#define HEVC_MPRED_VERSION 0xc800 +#define HEVC_MPRED_CTRL0 0xc804 + #define MPRED_CTRL0_NEW_PIC BIT(2) + #define MPRED_CTRL0_NEW_TILE BIT(3) + #define MPRED_CTRL0_NEW_SLI_SEG BIT(4) + #define MPRED_CTRL0_TMVP BIT(5) + #define MPRED_CTRL0_LDC BIT(6) + #define MPRED_CTRL0_COL_FROM_L0 BIT(7) + #define MPRED_CTRL0_ABOVE_EN BIT(9) + #define MPRED_CTRL0_MV_WR_EN BIT(10) + #define MPRED_CTRL0_MV_RD_EN BIT(11) + #define MPRED_CTRL0_BUF_LINEAR BIT(13) +#define HEVC_MPRED_CTRL1 0xc808 +#define HEVC_MPRED_INT_EN 0xc80c +#define HEVC_MPRED_INT_STATUS 0xc810 +#define HEVC_MPRED_PIC_SIZE 0xc814 +#define HEVC_MPRED_PIC_SIZE_LCU 0xc818 +#define HEVC_MPRED_TILE_START 0xc81c +#define HEVC_MPRED_TILE_SIZE_LCU 0xc820 +#define HEVC_MPRED_REF_NUM 0xc824 +#define HEVC_MPRED_REF_EN_L0 0xc830 +#define HEVC_MPRED_REF_EN_L1 0xc834 +#define HEVC_MPRED_COLREF_EN_L0 0xc838 +#define HEVC_MPRED_COLREF_EN_L1 0xc83c +#define HEVC_MPRED_AXI_WCTRL 0xc840 +#define HEVC_MPRED_AXI_RCTRL 0xc844 +#define HEVC_MPRED_ABV_START_ADDR 0xc848 +#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c +#define HEVC_MPRED_MV_RD_START_ADDR 0xc850 +#define HEVC_MPRED_MV_WPTR 0xc854 +#define HEVC_MPRED_MV_RPTR 0xc858 +#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c +#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860 +#define HEVC_MPRED_CURR_LCU 0xc864 +#define HEVC_MPRED_ABV_WPTR 0xc868 +#define HEVC_MPRED_ABV_RPTR 0xc86c +#define HEVC_MPRED_CTRL2 0xc870 +#define HEVC_MPRED_CTRL3 0xc874 +#define HEVC_MPRED_L0_REF00_POC 0xc880 +#define HEVC_MPRED_L1_REF00_POC 0xc8c0 + +#define HEVC_MPRED_CTRL4 0xc930 + +#define HEVC_MPRED_CUR_POC 0xc980 +#define HEVC_MPRED_COL_POC 0xc984 +#define HEVC_MPRED_MV_RD_END_ADDR 0xc988 + +#define HEVC_MSP 0xcc00 +#define HEVC_MPSR 0xcc04 +#define HEVC_MCPU_INTR_MSK 0xcc10 +#define HEVC_MCPU_INTR_REQ 0xcc14 +#define HEVC_CPSR 0xcc84 + +#define HEVC_IMEM_DMA_CTRL 0xcd00 +#define HEVC_IMEM_DMA_ADR 0xcd04 +#define HEVC_IMEM_DMA_COUNT 0xcd08 + +#define HEVCD_IPP_TOP_CNTL 0xd000 +#define HEVCD_IPP_LINEBUFF_BASE 0xd024 +#define HEVCD_IPP_AXIIF_CONFIG 0xd02c + +#define VP9D_MPP_REF_SCALE_ENBL 0xd104 +#define VP9D_MPP_REFINFO_TBL_ACCCONFIG 0xd108 +#define VP9D_MPP_REFINFO_DATA 0xd10c + +#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180 +#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184 +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190 + +#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300 +#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304 +#define HEVCD_MPP_DECOMP_CTL1 0xd308 +#define HEVCD_MPP_DECOMP_CTL2 0xd30c +#define HEVCD_MCRCC_CTL1 0xd3c0 +#define HEVCD_MCRCC_CTL2 0xd3c4 +#define HEVCD_MCRCC_CTL3 0xd3c8 + +#define HEVC_DBLK_CFG0 0xd400 +#define HEVC_DBLK_CFG1 0xd404 +#define HEVC_DBLK_CFG2 0xd408 +#define HEVC_DBLK_CFG3 0xd40c +#define HEVC_DBLK_CFG4 0xd410 +#define HEVC_DBLK_CFG5 0xd414 +#define HEVC_DBLK_CFG6 0xd418 +#define HEVC_DBLK_CFG7 0xd41c +#define HEVC_DBLK_CFG8 0xd420 +#define HEVC_DBLK_CFG9 0xd424 +#define HEVC_DBLK_CFGA 0xd428 +#define HEVC_DBLK_STS0 0xd42c +#define HEVC_DBLK_CFGB 0xd42c +#define HEVC_DBLK_STS1 0xd430 +#define HEVC_DBLK_CFGE 0xd438 + +#define HEVC_SAO_VERSION 0xd800 +#define HEVC_SAO_CTRL0 0xd804 +#define HEVC_SAO_CTRL1 0xd808 +#define HEVC_SAO_PIC_SIZE 0xd814 +#define HEVC_SAO_PIC_SIZE_LCU 0xd818 +#define HEVC_SAO_TILE_START 0xd81c +#define HEVC_SAO_TILE_SIZE_LCU 0xd820 +#define HEVC_SAO_Y_START_ADDR 0xd82c +#define HEVC_SAO_Y_LENGTH 0xd830 +#define HEVC_SAO_C_START_ADDR 0xd834 +#define HEVC_SAO_C_LENGTH 0xd838 +#define HEVC_SAO_Y_WPTR 0xd83c +#define HEVC_SAO_C_WPTR 0xd840 +#define HEVC_SAO_ABV_START_ADDR 0xd844 +#define HEVC_SAO_VB_WR_START_ADDR 0xd848 +#define HEVC_SAO_VB_RD_START_ADDR 0xd84c +#define HEVC_SAO_ABV_WPTR 0xd850 +#define HEVC_SAO_ABV_RPTR 0xd854 +#define HEVC_SAO_VB_WPTR 0xd858 +#define HEVC_SAO_VB_RPTR 0xd85c +#define HEVC_SAO_CTRL2 0xd880 +#define HEVC_SAO_CTRL3 0xd884 +#define HEVC_SAO_CTRL4 0xd888 +#define HEVC_SAO_CTRL5 0xd88c +#define HEVC_SAO_CTRL6 0xd890 +#define HEVC_SAO_CTRL7 0xd894 +#define HEVC_CM_BODY_START_ADDR 0xd898 +#define HEVC_CM_BODY_LENGTH 0xd89c +#define HEVC_CM_HEADER_START_ADDR 0xd8a0 +#define HEVC_CM_HEADER_LENGTH 0xd8a4 +#define HEVC_CM_HEADER_OFFSET 0xd8ac +#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8 +#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec + +#define HEVC_IQIT_CLK_RST_CTRL 0xdc00 +#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08 +#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c +#define HEVC_IQIT_SCALELUT_DATA 0xdc10 + +#define HEVC_PSCALE_CTRL 0xe444 + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 5c5dabed2f09..3040136ceb77 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -168,7 +168,10 @@ static void process_num_buffers(struct vb2_queue *q, { const struct amvdec_format *fmt_out = sess->fmt_out; unsigned int buffers_total = q->num_buffers + *num_buffers; + u32 min_buf_capture = v4l2_ctrl_g_ctrl(sess->ctrl_min_buf_capture); + if (q->num_buffers + *num_buffers < min_buf_capture) + *num_buffers = min_buf_capture - q->num_buffers; if (is_reqbufs && buffers_total < fmt_out->min_buffers) *num_buffers = fmt_out->min_buffers - q->num_buffers; if (buffers_total > fmt_out->max_buffers) @@ -193,7 +196,8 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, if (*num_planes) { switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (*num_planes != 1 || sizes[0] < output_size) + if (*num_planes != 1 || + sizes[0] < sess->src_buffer_size) return -EINVAL; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -224,7 +228,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - sizes[0] = amvdec_get_output_size(sess); + sizes[0] = sess->src_buffer_size; *num_planes = 1; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -250,6 +254,7 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, return -EINVAL; } + sess->changed_format = 1; return 0; } @@ -261,10 +266,11 @@ static void vdec_vb2_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(m2m_ctx, vbuf); - if (!sess->streamon_out || !sess->streamon_cap) + if (!sess->streamon_out) return; - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + if (sess->streamon_cap && + vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && vdec_codec_needs_recycle(sess)) vdec_queue_recycle(sess, vb); @@ -289,16 +295,22 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) else sess->streamon_cap = 1; - if (!sess->streamon_out || !sess->streamon_cap) + if (!sess->streamon_out) return 0; if (sess->status == STATUS_NEEDS_RESUME && - q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + sess->changed_format) { codec_ops->resume(sess); sess->status = STATUS_RUNNING; return 0; } + if (sess->status == STATUS_RUNNING || + sess->status == STATUS_NEEDS_RESUME || + sess->status == STATUS_INIT) + return 0; + sess->vififo_size = SIZE_VIFIFO; sess->vififo_vaddr = dma_alloc_coherent(sess->core->dev, sess->vififo_size, @@ -323,13 +335,14 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) goto vififo_free; sess->sequence_cap = 0; + sess->sequence_out = 0; if (vdec_codec_needs_recycle(sess)) sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, "vdec_recycle"); - sess->status = STATUS_RUNNING; + sess->status = STATUS_INIT; core->cur_sess = sess; - + schedule_work(&sess->esparser_queue_work); return 0; vififo_free: @@ -382,10 +395,12 @@ static void vdec_reset_bufs_recycle(struct amvdec_session *sess) static void vdec_stop_streaming(struct vb2_queue *q) { struct amvdec_session *sess = vb2_get_drv_priv(q); + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; struct amvdec_core *core = sess->core; struct vb2_v4l2_buffer *buf; if (sess->status == STATUS_RUNNING || + sess->status == STATUS_INIT || (sess->status == STATUS_NEEDS_RESUME && (!sess->streamon_out || !sess->streamon_cap))) { if (vdec_codec_needs_recycle(sess)) @@ -409,6 +424,10 @@ static void vdec_stop_streaming(struct vb2_queue *q) sess->streamon_out = 0; } else { + /* Drain remaining refs if was still running */ + if (sess->status >= STATUS_RUNNING && codec_ops->drain) + codec_ops->drain(sess); + while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); @@ -476,20 +495,34 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct amvdec_format *fmts = sess->core->platform->formats; - const struct amvdec_format *fmt_out; + const struct amvdec_format *fmt_out = NULL; + u32 output_size = 0; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt_out = find_format(fmts, size, pixmp->pixelformat); if (!fmt_out) { pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; fmt_out = find_format(fmts, size, pixmp->pixelformat); } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + fmt_out = sess->fmt_out; + break; + default: + return NULL; + } + + pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); + pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); + output_size = get_output_size(pixmp->width, pixmp->height); - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!pfmt[0].sizeimage) + pfmt[0].sizeimage = sess->src_buffer_size; pfmt[0].bytesperline = 0; pixmp->num_planes = 1; } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { @@ -499,35 +532,25 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved)); if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) { - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); - pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + pfmt[0].sizeimage = output_size; + pfmt[0].bytesperline = ALIGN(pixmp->width, 32); - pfmt[1].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 2; - pfmt[1].bytesperline = ALIGN(pixmp->width, 64); + pfmt[1].sizeimage = output_size / 2; + pfmt[1].bytesperline = ALIGN(pixmp->width, 32); pixmp->num_planes = 2; } else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) { - pfmt[0].sizeimage = - get_output_size(pixmp->width, pixmp->height); - pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + pfmt[0].sizeimage = output_size; + pfmt[0].bytesperline = ALIGN(pixmp->width, 32); - pfmt[1].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 4; - pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2; + pfmt[1].sizeimage = output_size / 4; + pfmt[1].bytesperline = ALIGN(pixmp->width, 32) / 2; - pfmt[2].sizeimage = - get_output_size(pixmp->width, pixmp->height) / 4; - pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2; + pfmt[2].sizeimage = output_size / 2; + pfmt[2].bytesperline = ALIGN(pixmp->width, 32) / 2; pixmp->num_planes = 3; } - } else { - return NULL; } - pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); - pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); - if (pixmp->field == V4L2_FIELD_ANY) pixmp->field = V4L2_FIELD_NONE; @@ -586,6 +609,8 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) orig_pixmp = *pixmp; fmt_out = vdec_try_fmt_common(sess, num_formats, f); + if (!fmt_out) + return -EINVAL; if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { pixfmt_out = pixmp->pixelformat; @@ -610,6 +635,7 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) sess->ycbcr_enc = pixmp->ycbcr_enc; sess->quantization = pixmp->quantization; sess->xfer_func = pixmp->xfer_func; + sess->src_buffer_size = pixmp->plane_fmt[0].sizeimage; } memset(&format, 0, sizeof(format)); @@ -701,29 +727,31 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) if (!(sess->streamon_out & sess->streamon_cap)) return 0; - /* Currently not handled since we do not support dynamic resolution - * for MPEG2. We consider both queues streaming to mean that the - * decoding session is started - */ - if (cmd->cmd == V4L2_DEC_CMD_START) + if (cmd->cmd == V4L2_DEC_CMD_START) { + v4l2_m2m_clear_state(sess->m2m_ctx); + sess->should_stop = 0; return 0; + } /* Should not happen */ if (cmd->cmd != V4L2_DEC_CMD_STOP) return -EINVAL; dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n"); + sess->should_stop = 1; - vdec_wait_inactive(sess); + v4l2_m2m_mark_stopped(sess->m2m_ctx); if (codec_ops->drain) { + vdec_wait_inactive(sess); codec_ops->drain(sess); } else if (codec_ops->eos_sequence) { u32 len; const u8 *data = codec_ops->eos_sequence(&len); esparser_queue_eos(sess->core, data, len); + vdec_wait_inactive(sess); } return ret; @@ -883,6 +911,7 @@ static int vdec_open(struct file *file) sess->height = 720; sess->pixelaspect.numerator = 1; sess->pixelaspect.denominator = 1; + sess->src_buffer_size = SZ_1M; INIT_LIST_HEAD(&sess->timestamps); INIT_LIST_HEAD(&sess->bufs_recycle); @@ -1076,7 +1105,7 @@ static int vdec_probe(struct platform_device *pdev) video_set_drvdata(vdev, core); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(dev, "Failed registering video device\n"); goto err_vdev_release; diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h index 0faa1ec4858e..f95445ac0658 100644 --- a/drivers/staging/media/meson/vdec/vdec.h +++ b/drivers/staging/media/meson/vdec/vdec.h @@ -29,13 +29,19 @@ struct amvdec_buffer { * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset * * @list: used to make lists out of this struct - * @ts: timestamp + * @tc: timecode from the v4l2 buffer + * @ts: timestamp from the VB2 buffer * @offset: offset in the VIFIFO where the associated packet was written + * @flags: flags from the v4l2 buffer + * @used_count: times this timestamp was checked for a match with a dst buffer */ struct amvdec_timestamp { struct list_head list; + struct v4l2_timecode tc; u64 ts; u32 offset; + u32 flags; + u32 used_count; }; struct amvdec_session; @@ -165,6 +171,7 @@ struct amvdec_format { enum amvdec_status { STATUS_STOPPED, + STATUS_INIT, STATUS_RUNNING, STATUS_NEEDS_RESUME, }; @@ -180,6 +187,7 @@ enum amvdec_status { * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE * @fmt_out: vdec pixel format for the OUTPUT queue * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue + * @src_buffer_size: size in bytes of the OUTPUT buffers' only plane * @width: current picture width * @height: current picture height * @colorspace: current colorspace @@ -221,6 +229,7 @@ struct amvdec_session { const struct amvdec_format *fmt_out; u32 pixfmt_cap; + u32 src_buffer_size; u32 width; u32 height; @@ -235,10 +244,11 @@ struct amvdec_session { struct work_struct esparser_queue_work; unsigned int streamon_cap, streamon_out; - unsigned int sequence_cap; + unsigned int sequence_cap, sequence_out; unsigned int should_stop; unsigned int keyframe_found; unsigned int num_dst_bufs; + unsigned int changed_format; u8 canvas_alloc[MAX_CANVAS]; u32 canvas_num; diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c index f16948bdbf2f..7f07a9175815 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.c +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c @@ -50,6 +50,33 @@ void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val) } EXPORT_SYMBOL_GPL(amvdec_write_parser); +/* 4 KiB per 64x32 block */ +u32 amvdec_am21c_body_size(u32 width, u32 height) +{ + u32 width_64 = ALIGN(width, 64) / 64; + u32 height_32 = ALIGN(height, 32) / 32; + + return SZ_4K * width_64 * height_32; +} +EXPORT_SYMBOL_GPL(amvdec_am21c_body_size); + +/* 32 bytes per 128x64 block */ +u32 amvdec_am21c_head_size(u32 width, u32 height) +{ + u32 width_128 = ALIGN(width, 128) / 128; + u32 height_64 = ALIGN(height, 64) / 64; + + return 32 * width_128 * height_64; +} +EXPORT_SYMBOL_GPL(amvdec_am21c_head_size); + +u32 amvdec_am21c_size(u32 width, u32 height) +{ + return ALIGN(amvdec_am21c_body_size(width, height) + + amvdec_am21c_head_size(width, height), SZ_64K); +} +EXPORT_SYMBOL_GPL(amvdec_am21c_size); + static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id) { int ret; @@ -154,8 +181,8 @@ int amvdec_set_canvases(struct amvdec_session *sess, { struct v4l2_m2m_buffer *buf; u32 pixfmt = sess->pixfmt_cap; - u32 width = ALIGN(sess->width, 64); - u32 height = ALIGN(sess->height, 64); + u32 width = ALIGN(sess->width, 32); + u32 height = ALIGN(sess->height, 32); u32 reg_cur = reg_base[0]; u32 reg_num_cur = 0; u32 reg_base_cur = 0; @@ -200,33 +227,23 @@ int amvdec_set_canvases(struct amvdec_session *sess, } EXPORT_SYMBOL_GPL(amvdec_set_canvases); -void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset) +void amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 vbuf_flags) { - struct amvdec_timestamp *new_ts, *tmp; + struct amvdec_timestamp *new_ts; unsigned long flags; - new_ts = kmalloc(sizeof(*new_ts), GFP_KERNEL); + new_ts = kzalloc(sizeof(*new_ts), GFP_KERNEL); new_ts->ts = ts; + new_ts->tc = tc; new_ts->offset = offset; + new_ts->flags = vbuf_flags; spin_lock_irqsave(&sess->ts_spinlock, flags); - - if (list_empty(&sess->timestamps)) - goto add_tail; - - list_for_each_entry(tmp, &sess->timestamps, list) { - if (ts <= tmp->ts) { - list_add_tail(&new_ts->list, &tmp->list); - goto unlock; - } - } - -add_tail: list_add_tail(&new_ts->list, &sess->timestamps); -unlock: spin_unlock_irqrestore(&sess->ts_spinlock, flags); } -EXPORT_SYMBOL_GPL(amvdec_add_ts_reorder); +EXPORT_SYMBOL_GPL(amvdec_add_ts); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts) { @@ -251,8 +268,8 @@ EXPORT_SYMBOL_GPL(amvdec_remove_ts); static void dst_buf_done(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf, - u32 field, - u64 timestamp) + u32 field, u64 timestamp, + struct v4l2_timecode timecode, u32 flags) { struct device *dev = sess->core->dev_dec; u32 output_size = amvdec_get_output_size(sess); @@ -271,19 +288,27 @@ static void dst_buf_done(struct amvdec_session *sess, vbuf->vb2_buf.timestamp = timestamp; vbuf->sequence = sess->sequence_cap++; + vbuf->flags = flags; + vbuf->timecode = timecode; if (sess->should_stop && - atomic_read(&sess->esparser_queued_bufs) <= 2) { + atomic_read(&sess->esparser_queued_bufs) <= 1) { const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; - dev_dbg(dev, "Signaling EOS\n"); + dev_dbg(dev, "Signaling EOS, sequence_cap = %u\n", + sess->sequence_cap - 1); v4l2_event_queue_fh(&sess->fh, &ev); vbuf->flags |= V4L2_BUF_FLAG_LAST; + } else if (sess->status == STATUS_NEEDS_RESUME) { + /* Mark LAST for drained show frames during a source change */ + vbuf->flags |= V4L2_BUF_FLAG_LAST; + sess->sequence_cap = 0; } else if (sess->should_stop) dev_dbg(dev, "should_stop, %u bufs remain\n", atomic_read(&sess->esparser_queued_bufs)); - dev_dbg(dev, "Buffer %u done\n", vbuf->vb2_buf.index); + dev_dbg(dev, "Buffer %u done, ts = %llu, flags = %08X\n", + vbuf->vb2_buf.index, timestamp, flags); vbuf->field = field; v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); @@ -297,7 +322,9 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, struct device *dev = sess->core->dev_dec; struct amvdec_timestamp *tmp; struct list_head *timestamps = &sess->timestamps; + struct v4l2_timecode timecode; u64 timestamp; + u32 vbuf_flags; unsigned long flags; spin_lock_irqsave(&sess->ts_spinlock, flags); @@ -312,11 +339,13 @@ void amvdec_dst_buf_done(struct amvdec_session *sess, tmp = list_first_entry(timestamps, struct amvdec_timestamp, list); timestamp = tmp->ts; + timecode = tmp->tc; + vbuf_flags = tmp->flags; list_del(&tmp->list); kfree(tmp); spin_unlock_irqrestore(&sess->ts_spinlock, flags); - dst_buf_done(sess, vbuf, field, timestamp); + dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags); atomic_dec(&sess->esparser_queued_bufs); } EXPORT_SYMBOL_GPL(amvdec_dst_buf_done); @@ -328,48 +357,43 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, struct device *dev = sess->core->dev_dec; struct amvdec_timestamp *match = NULL; struct amvdec_timestamp *tmp, *n; + struct v4l2_timecode timecode = { 0 }; u64 timestamp = 0; + u32 vbuf_flags = 0; unsigned long flags; spin_lock_irqsave(&sess->ts_spinlock, flags); /* Look for our vififo offset to get the corresponding timestamp. */ list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { - s64 delta = (s64)offset - tmp->offset; - - /* Offsets reported by codecs usually differ slightly, - * so we need some wiggle room. - * 4KiB being the minimum packet size, there is no risk here. - */ - if (delta > (-1 * (s32)SZ_4K) && delta < SZ_4K) { - match = tmp; + if (tmp->offset > offset) { + /* + * Delete any record that remained unused for 32 match + * checks + */ + if (tmp->used_count++ >= 32) { + list_del(&tmp->list); + kfree(tmp); + } break; } - if (!allow_drop) - continue; - - /* Delete any timestamp entry that appears before our target - * (not all src packets/timestamps lead to a frame) - */ - if (delta > 0 || delta < -1 * (s32)sess->vififo_size) { - atomic_dec(&sess->esparser_queued_bufs); - list_del(&tmp->list); - kfree(tmp); - } + match = tmp; } if (!match) { - dev_dbg(dev, "Buffer %u done but can't match offset (%08X)\n", + dev_err(dev, "Buffer %u done but can't match offset (%08X)\n", vbuf->vb2_buf.index, offset); } else { timestamp = match->ts; + timecode = match->tc; + vbuf_flags = match->flags; list_del(&match->list); kfree(match); } spin_unlock_irqrestore(&sess->ts_spinlock, flags); - dst_buf_done(sess, vbuf, field, timestamp); + dst_buf_done(sess, vbuf, field, timestamp, timecode, vbuf_flags); if (match) atomic_dec(&sess->esparser_queued_bufs); } @@ -420,16 +444,19 @@ void amvdec_src_change(struct amvdec_session *sess, u32 width, v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size); - /* Check if the capture queue is already configured well for our + /* + * Check if the capture queue is already configured well for our * usecase. If so, keep decoding with it and do not send the event */ - if (sess->width == width && + if (sess->streamon_cap && + sess->width == width && sess->height == height && dpb_size <= sess->num_dst_bufs) { sess->fmt_out->codec_ops->resume(sess); return; } + sess->changed_format = 0; sess->width = width; sess->height = height; sess->status = STATUS_NEEDS_RESUME; diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.h b/drivers/staging/media/meson/vdec/vdec_helpers.h index a455a9ee1cc2..cfaed52ab526 100644 --- a/drivers/staging/media/meson/vdec/vdec_helpers.h +++ b/drivers/staging/media/meson/vdec/vdec_helpers.h @@ -27,6 +27,10 @@ void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val); u32 amvdec_read_parser(struct amvdec_core *core, u32 reg); void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val); +u32 amvdec_am21c_body_size(u32 width, u32 height); +u32 amvdec_am21c_head_size(u32 width, u32 height); +u32 amvdec_am21c_size(u32 width, u32 height); + /** * amvdec_dst_buf_done_idx() - Signal that a buffer is done decoding * @@ -44,13 +48,15 @@ void amvdec_dst_buf_done_offset(struct amvdec_session *sess, u32 offset, u32 field, bool allow_drop); /** - * amvdec_add_ts_reorder() - Add a timestamp to the list in chronological order + * amvdec_add_ts() - Add a timestamp to the list * * @sess: current session * @ts: timestamp to add * @offset: offset in the VIFIFO where the associated packet was written + * @flags the vb2_v4l2_buffer flags */ -void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset); +void amvdec_add_ts(struct amvdec_session *sess, u64 ts, + struct v4l2_timecode tc, u32 offset, u32 flags); void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); /** diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c new file mode 100644 index 000000000000..9530e580e57a --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + * + * VDEC_HEVC is a video decoding block that allows decoding of + * HEVC, VP9 + */ + +#include <linux/firmware.h> +#include <linux/clk.h> + +#include "vdec_1.h" +#include "vdec_helpers.h" +#include "vdec_hevc.h" +#include "hevc_regs.h" +#include "dos_regs.h" + +/* AO Registers */ +#define AO_RTI_GEN_PWR_SLEEP0 0xe8 +#define AO_RTI_GEN_PWR_ISO0 0xec + #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6)) + #define GEN_PWR_VDEC_HEVC_SM1 (BIT(2)) + +#define MC_SIZE (4096 * 4) + +static int vdec_hevc_load_firmware(struct amvdec_session *sess, + const char *fwname) +{ + struct amvdec_core *core = sess->core; + struct device *dev = core->dev_dec; + const struct firmware *fw; + static void *mc_addr; + static dma_addr_t mc_addr_map; + int ret; + u32 i = 100; + + ret = request_firmware(&fw, fwname, dev); + if (ret < 0) { + dev_err(dev, "Unable to request firmware %s\n", fwname); + return ret; + } + + if (fw->size < MC_SIZE) { + dev_err(dev, "Firmware size %zu is too small. Expected %u.\n", + fw->size, MC_SIZE); + ret = -EINVAL; + goto release_firmware; + } + + mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map, + GFP_KERNEL); + if (!mc_addr) { + ret = -ENOMEM; + goto release_firmware; + } + + memcpy(mc_addr, fw->data, MC_SIZE); + + amvdec_write_dos(core, HEVC_MPSR, 0); + amvdec_write_dos(core, HEVC_CPSR, 0); + + amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map); + amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4); + amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000)) + i--; + + if (i == 0) { + dev_err(dev, "Firmware load fail (DMA hang?)\n"); + ret = -ENODEV; + } + + dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map); +release_firmware: + release_firmware(fw); + return ret; +} + +static void vdec_hevc_stbuf_init(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1); + amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr); + amvdec_write_dos(core, HEVC_STREAM_END_ADDR, + sess->vififo_paddr + sess->vififo_size); + amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr); + amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr); +} + +/* VDEC_HEVC specific ESPARSER configuration */ +static void vdec_hevc_conf_esparser(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + /* set vififo_vbuf_rp_sel=>vdec_hevc */ + amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1); + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3)); + amvdec_write_dos(core, HEVC_STREAM_CONTROL, + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1); + amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL, + amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29)); +} + +static u32 vdec_hevc_vififo_level(struct amvdec_session *sess) +{ + return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL); +} + +static int vdec_hevc_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + /* Disable interrupt */ + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0); + /* Disable firmware processor */ + amvdec_write_dos(core, HEVC_MPSR, 0); + + if (sess->priv) + codec_ops->stop(sess); + + /* Enable VDEC_HEVC Isolation */ + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + GEN_PWR_VDEC_HEVC_SM1, + GEN_PWR_VDEC_HEVC_SM1); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + 0xc00, 0xc00); + + /* VDEC_HEVC Memories */ + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL); + + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC_SM1, + GEN_PWR_VDEC_HEVC_SM1); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC); + + clk_disable_unprepare(core->vdec_hevc_clk); + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) + clk_disable_unprepare(core->vdec_hevcf_clk); + + return 0; +} + +static int vdec_hevc_start(struct amvdec_session *sess) +{ + int ret; + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) { + clk_set_rate(core->vdec_hevcf_clk, 666666666); + ret = clk_prepare_enable(core->vdec_hevcf_clk); + if (ret) + return ret; + } + + clk_set_rate(core->vdec_hevc_clk, 666666666); + ret = clk_prepare_enable(core->vdec_hevc_clk); + if (ret) + return ret; + + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC_SM1, 0); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_HEVC, 0); + usleep_range(10, 20); + + /* Reset VDEC_HEVC*/ + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff); + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000); + + amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff); + + /* VDEC_HEVC Memories */ + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000); + + /* Remove VDEC_HEVC Isolation */ + if (core->platform->revision == VDEC_REVISION_SM1) + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + GEN_PWR_VDEC_HEVC_SM1, 0); + else + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, + 0xc00, 0); + + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff); + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000); + + vdec_hevc_stbuf_init(sess); + + ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path); + if (ret) + goto stop; + + ret = codec_ops->start(sess); + if (ret) + goto stop; + + amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11)); + amvdec_write_dos(core, DOS_SW_RESET3, 0); + amvdec_read_dos(core, DOS_SW_RESET3); + + amvdec_write_dos(core, HEVC_MPSR, 1); + /* Let the firmware settle */ + usleep_range(10, 20); + + return 0; + +stop: + vdec_hevc_stop(sess); + return ret; +} + +struct amvdec_ops vdec_hevc_ops = { + .start = vdec_hevc_start, + .stop = vdec_hevc_stop, + .conf_esparser = vdec_hevc_conf_esparser, + .vififo_level = vdec_hevc_vififo_level, +}; diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h new file mode 100644 index 000000000000..cd576a73a966 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_hevc.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Maxime Jourdan <maxi.jourdan@wanadoo.fr> + */ + +#ifndef __MESON_VDEC_VDEC_HEVC_H_ +#define __MESON_VDEC_VDEC_HEVC_H_ + +#include "vdec.h" + +extern struct amvdec_ops vdec_hevc_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c index ea39f8209ec7..eabbebab2da2 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.c +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -8,10 +8,25 @@ #include "vdec.h" #include "vdec_1.h" +#include "vdec_hevc.h" #include "codec_mpeg12.h" +#include "codec_h264.h" +#include "codec_vp9.h" static const struct amvdec_format vdec_formats_gxbb[] = { { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxbb_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -21,6 +36,7 @@ static const struct amvdec_format vdec_formats_gxbb[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -31,11 +47,36 @@ static const struct amvdec_format vdec_formats_gxbb[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_gxl[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/gxl_vp9.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxl_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -45,6 +86,7 @@ static const struct amvdec_format vdec_formats_gxl[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -55,11 +97,24 @@ static const struct amvdec_format vdec_formats_gxl[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_gxm[] = { { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxm_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -69,6 +124,7 @@ static const struct amvdec_format vdec_formats_gxm[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -79,11 +135,36 @@ static const struct amvdec_format vdec_formats_gxm[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_g12a[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/g12a_vp9.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/g12a_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -93,6 +174,7 @@ static const struct amvdec_format vdec_formats_g12a[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -103,11 +185,36 @@ static const struct amvdec_format vdec_formats_g12a[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; static const struct amvdec_format vdec_formats_sm1[] = { { + .pixfmt = V4L2_PIX_FMT_VP9, + .min_buffers = 16, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_hevc_ops, + .codec_ops = &codec_vp9_ops, + .firmware_path = "meson/vdec/sm1_vp9_mmu.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/g12a_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { .pixfmt = V4L2_PIX_FMT_MPEG1, .min_buffers = 8, .max_buffers = 8, @@ -117,6 +224,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, { .pixfmt = V4L2_PIX_FMT_MPEG2, .min_buffers = 8, @@ -127,6 +235,7 @@ static const struct amvdec_format vdec_formats_sm1[] = { .codec_ops = &codec_mpeg12_ops, .firmware_path = "meson/vdec/gxl_mpeg12.bin", .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, }, }; diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 673aa3a5f2bd..66975a37dc85 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1111,7 +1111,7 @@ static int iss_video_open(struct file *file) goto done; } - ret = v4l2_pipeline_pm_use(&video->video.entity, 1); + ret = v4l2_pipeline_pm_get(&video->video.entity); if (ret < 0) { omap4iss_put(video->iss); goto done; @@ -1160,7 +1160,7 @@ static int iss_video_release(struct file *file) /* Disable streaming and free the buffers queue resources. */ iss_video_streamoff(file, vfh, video->type); - v4l2_pipeline_pm_use(&video->video.entity, 0); + v4l2_pipeline_pm_put(&video->video.entity); /* Release the videobuf2 queue */ vb2_queue_release(&handle->queue); @@ -1242,7 +1242,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name) video->video.fops = &iss_video_fops; snprintf(video->video.name, sizeof(video->video.name), "OMAP4 ISS %s %s", name, direction); - video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.vfl_type = VFL_TYPE_VIDEO; video->video.release = video_device_release_empty; video->video.ioctl_ops = &iss_video_ioctl_ops; video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED; @@ -1270,7 +1270,7 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT; video->video.device_caps |= V4L2_CAP_STREAMING; - ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); if (ret < 0) dev_err(video->iss->dev, "could not register video device (%d)\n", ret); diff --git a/drivers/staging/media/rkisp1/TODO b/drivers/staging/media/rkisp1/TODO index 03cd9a4e70f7..0aa9877dd64a 100644 --- a/drivers/staging/media/rkisp1/TODO +++ b/drivers/staging/media/rkisp1/TODO @@ -1,4 +1,3 @@ -* Fix serialization on subdev ops. * Don't use v4l2_async_notifier_parse_fwnode_endpoints_by_port(). e.g. isp_parse_of_endpoints in drivers/media/platform/omap3isp/isp.c cio2_parse_firmware in drivers/media/pci/intel/ipu3/ipu3-cio2.c. diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 524e0dd38c1b..24fe6a7888aa 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -937,10 +937,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR); - ret = v4l2_pipeline_pm_use(&node->vdev.entity, 0); - if (ret) - dev_err(rkisp1->dev, "pipeline close failed error:%d\n", ret); - + v4l2_pipeline_pm_put(&node->vdev.entity); ret = pm_runtime_put(rkisp1->dev); if (ret) dev_err(rkisp1->dev, "power down failed error:%d\n", ret); @@ -999,7 +996,7 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) dev_err(cap->rkisp1->dev, "power up failed %d\n", ret); goto err_destroy_dummy; } - ret = v4l2_pipeline_pm_use(entity, 1); + ret = v4l2_pipeline_pm_get(entity); if (ret) { dev_err(cap->rkisp1->dev, "open cif pipeline failed %d\n", ret); goto err_pipe_pm_put; @@ -1025,7 +1022,7 @@ err_pipe_disable: rkisp1_pipeline_sink_walk(entity, NULL, rkisp1_pipeline_disable_cb); err_stop_stream: rkisp1_stream_stop(cap); - v4l2_pipeline_pm_use(entity, 0); + v4l2_pipeline_pm_put(entity); err_pipe_pm_put: pm_runtime_put(cap->rkisp1->dev); err_destroy_dummy: @@ -1344,7 +1341,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) q = &node->buf_queue; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = cap; q->ops = &rkisp1_vb2_ops; q->mem_ops = &vb2_dma_contig_memops; @@ -1362,7 +1359,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap) vdev->queue = q; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(cap->rkisp1->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-common.h b/drivers/staging/media/rkisp1/rkisp1-common.h index 369a401b098a..b291cc60de8e 100644 --- a/drivers/staging/media/rkisp1/rkisp1-common.h +++ b/drivers/staging/media/rkisp1/rkisp1-common.h @@ -96,6 +96,7 @@ struct rkisp1_sensor_async { * @sink_crop: crop for sink pad * @src_fmt: output format * @src_crop: output size + * @ops_lock: ops serialization * * @is_dphy_errctrl_disabled : if dphy errctrl is disabled (avoid endless interrupt) * @frame_sequence: used to synchronize frame_id between video devices. @@ -107,6 +108,7 @@ struct rkisp1_isp { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_isp_mbus_info *sink_fmt; const struct rkisp1_isp_mbus_info *src_fmt; + struct mutex ops_lock; bool is_dphy_errctrl_disabled; atomic_t frame_sequence; }; @@ -224,6 +226,7 @@ struct rkisp1_resizer { struct v4l2_subdev_pad_config pad_cfg[RKISP1_ISP_PAD_MAX]; const struct rkisp1_rsz_config *config; enum rkisp1_fmt_pix_type fmt_type; + struct mutex ops_lock; }; struct rkisp1_debug { diff --git a/drivers/staging/media/rkisp1/rkisp1-dev.c b/drivers/staging/media/rkisp1/rkisp1-dev.c index 558126e66465..b1b3c058e957 100644 --- a/drivers/staging/media/rkisp1/rkisp1-dev.c +++ b/drivers/staging/media/rkisp1/rkisp1-dev.c @@ -128,7 +128,7 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode, MEDIA_PAD_FL_SOURCE); - if (ret) { + if (ret < 0) { dev_err(sd->dev, "failed to find src pad for %s\n", sd->name); return ret; @@ -145,14 +145,15 @@ static int rkisp1_create_links(struct rkisp1_device *rkisp1) flags = 0; } - flags = MEDIA_LNK_FL_ENABLED; + flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE; /* create ISP->RSZ->CAP links */ for (i = 0; i < 2; i++) { source = &rkisp1->isp.sd.entity; sink = &rkisp1->resizer_devs[i].sd.entity; ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_VIDEO, - sink, RKISP1_RSZ_PAD_SINK, flags); + sink, RKISP1_RSZ_PAD_SINK, + MEDIA_LNK_FL_ENABLED); if (ret) return ret; @@ -219,19 +220,17 @@ static int rkisp1_subdev_notifier_complete(struct v4l2_async_notifier *notifier) container_of(notifier, struct rkisp1_device, notifier); int ret; - mutex_lock(&rkisp1->media_dev.graph_mutex); ret = rkisp1_create_links(rkisp1); if (ret) - goto unlock; + return ret; + ret = v4l2_device_register_subdev_nodes(&rkisp1->v4l2_dev); if (ret) - goto unlock; + return ret; dev_dbg(rkisp1->dev, "Async subdev notifier completed\n"); -unlock: - mutex_unlock(&rkisp1->media_dev.graph_mutex); - return ret; + return 0; } static int rkisp1_fwnode_parse(struct device *dev, @@ -502,8 +501,7 @@ static int rkisp1_probe(struct platform_device *pdev) strscpy(rkisp1->media_dev.model, RKISP1_DRIVER_NAME, sizeof(rkisp1->media_dev.model)); rkisp1->media_dev.dev = &pdev->dev; - strscpy(rkisp1->media_dev.bus_info, - "platform: " RKISP1_DRIVER_NAME, + strscpy(rkisp1->media_dev.bus_info, RKISP1_BUS_INFO, sizeof(rkisp1->media_dev.bus_info)); media_device_init(&rkisp1->media_dev); diff --git a/drivers/staging/media/rkisp1/rkisp1-isp.c b/drivers/staging/media/rkisp1/rkisp1-isp.c index 328c7ea60971..fa53f05e37d8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-isp.c +++ b/drivers/staging/media/rkisp1/rkisp1-isp.c @@ -28,9 +28,9 @@ #define RKISP1_DIR_SINK_SRC (RKISP1_DIR_SINK | RKISP1_DIR_SRC) /* - * NOTE: MIPI controller and input MUX are also configured in this file, - * because ISP Subdev is not only describe ISP submodule(input size,format, - * output size, format), but also a virtual route device. + * NOTE: MIPI controller and input MUX are also configured in this file. + * This is because ISP Subdev describes not only ISP submodule (input size, + * format, output size, format), but also a virtual route device. */ /* @@ -504,7 +504,7 @@ static int rkisp1_config_cif(struct rkisp1_device *rkisp1) return 0; } -static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) +static void rkisp1_isp_stop(struct rkisp1_device *rkisp1) { u32 val; @@ -540,8 +540,6 @@ static int rkisp1_isp_stop(struct rkisp1_device *rkisp1) RKISP1_CIF_IRCL_MIPI_SW_RST | RKISP1_CIF_IRCL_ISP_SW_RST, RKISP1_CIF_IRCL); rkisp1_write(rkisp1, 0x0, RKISP1_CIF_IRCL); - - return 0; } static void rkisp1_config_clk(struct rkisp1_device *rkisp1) @@ -555,7 +553,7 @@ static void rkisp1_config_clk(struct rkisp1_device *rkisp1) rkisp1_write(rkisp1, val, RKISP1_CIF_ICCL); } -static int rkisp1_isp_start(struct rkisp1_device *rkisp1) +static void rkisp1_isp_start(struct rkisp1_device *rkisp1) { struct rkisp1_sensor_async *sensor = rkisp1->active_sensor; u32 val; @@ -580,8 +578,6 @@ static int rkisp1_isp_start(struct rkisp1_device *rkisp1) * the MIPI interface and before starting the sensor output. */ usleep_range(1000, 1200); - - return 0; } /* ---------------------------------------------------------------------------- @@ -683,7 +679,7 @@ static void rkisp1_isp_set_src_fmt(struct rkisp1_isp *isp, src_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SRC)) { src_fmt->code = RKISP1_DEF_SRC_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(src_fmt->code); } @@ -767,7 +763,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, which); sink_fmt->code = format->code; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); - if (!mbus_info) { + if (!mbus_info || !(mbus_info->direction & RKISP1_DIR_SINK)) { sink_fmt->code = RKISP1_DEF_SINK_PAD_FMT; mbus_info = rkisp1_isp_mbus_info_get(sink_fmt->code); } @@ -795,7 +791,9 @@ static int rkisp1_isp_get_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -805,6 +803,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + mutex_lock(&isp->ops_lock); if (fmt->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_fmt(isp, cfg, &fmt->format, fmt->which); else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) @@ -813,6 +812,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd, fmt->format = *rkisp1_isp_get_pad_fmt(isp, cfg, fmt->pad, fmt->which); + mutex_unlock(&isp->ops_lock); return 0; } @@ -821,11 +821,13 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->pad != RKISP1_ISP_PAD_SOURCE_VIDEO && sel->pad != RKISP1_ISP_PAD_SINK_VIDEO) return -EINVAL; + mutex_lock(&isp->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) { @@ -848,10 +850,10 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, @@ -861,21 +863,23 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd, struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); struct rkisp1_isp *isp = container_of(sd, struct rkisp1_isp, sd); + int ret = 0; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; dev_dbg(rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); - + mutex_lock(&isp->ops_lock); if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO) rkisp1_isp_set_sink_crop(isp, cfg, &sel->r, sel->which); else if (sel->pad == RKISP1_ISP_PAD_SOURCE_VIDEO) rkisp1_isp_set_src_crop(isp, cfg, &sel->r, sel->which); else - return -EINVAL; + ret = -EINVAL; - return 0; + mutex_unlock(&isp->ops_lock); + return ret; } static int rkisp1_subdev_link_validate(struct media_link *link) @@ -936,13 +940,12 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) { struct rkisp1_device *rkisp1 = container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev); + struct rkisp1_isp *isp = &rkisp1->isp; struct v4l2_subdev *sensor_sd; int ret = 0; if (!enable) { - ret = rkisp1_isp_stop(rkisp1); - if (ret) - return ret; + rkisp1_isp_stop(rkisp1); rkisp1_mipi_csi2_stop(rkisp1->active_sensor); return 0; } @@ -953,22 +956,23 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable) rkisp1->active_sensor = container_of(sensor_sd->asd, struct rkisp1_sensor_async, asd); + if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) + return -EINVAL; + atomic_set(&rkisp1->isp.frame_sequence, -1); + mutex_lock(&isp->ops_lock); ret = rkisp1_config_cif(rkisp1); if (ret) - return ret; - - if (rkisp1->active_sensor->mbus.type != V4L2_MBUS_CSI2_DPHY) - return -EINVAL; + goto mutex_unlock; ret = rkisp1_mipi_csi2_start(&rkisp1->isp, rkisp1->active_sensor); if (ret) - return ret; + goto mutex_unlock; - ret = rkisp1_isp_start(rkisp1); - if (ret) - rkisp1_mipi_csi2_stop(rkisp1->active_sensor); + rkisp1_isp_start(rkisp1); +mutex_unlock: + mutex_unlock(&isp->ops_lock); return ret; } @@ -1028,6 +1032,7 @@ int rkisp1_isp_register(struct rkisp1_device *rkisp1, isp->sink_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SINK_PAD_FMT); isp->src_fmt = rkisp1_isp_mbus_info_get(RKISP1_DEF_SRC_PAD_FMT); + mutex_init(&isp->ops_lock); ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX, pads); if (ret) return ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-params.c b/drivers/staging/media/rkisp1/rkisp1-params.c index 781f0ca85af1..44d542caf32b 100644 --- a/drivers/staging/media/rkisp1/rkisp1-params.c +++ b/drivers/staging/media/rkisp1/rkisp1-params.c @@ -1605,7 +1605,7 @@ int rkisp1_params_register(struct rkisp1_params *params, ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/rkisp1/rkisp1-resizer.c b/drivers/staging/media/rkisp1/rkisp1-resizer.c index 8cdc29c1a178..87799fbf0363 100644 --- a/drivers/staging/media/rkisp1/rkisp1-resizer.c +++ b/drivers/staging/media/rkisp1/rkisp1-resizer.c @@ -503,6 +503,8 @@ static void rkisp1_rsz_set_sink_crop(struct rkisp1_resizer *rsz, sink_crop->top = 0; sink_crop->width = sink_fmt->width; sink_crop->height = sink_fmt->height; + + *r = *sink_crop; return; } @@ -537,15 +539,6 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, if (which == V4L2_SUBDEV_FORMAT_ACTIVE) rsz->fmt_type = mbus_info->fmt_type; - if (rsz->id == RKISP1_MAINPATH && - mbus_info->fmt_type == RKISP1_FMT_BAYER) { - sink_crop->left = 0; - sink_crop->top = 0; - sink_crop->width = sink_fmt->width; - sink_crop->height = sink_fmt->height; - return; - } - /* Propagete to source pad */ src_fmt->code = sink_fmt->code; @@ -569,7 +562,9 @@ static int rkisp1_rsz_get_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); fmt->format = *rkisp1_rsz_get_pad_fmt(rsz, cfg, fmt->pad, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -580,11 +575,13 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); + mutex_lock(&rsz->ops_lock); if (fmt->pad == RKISP1_RSZ_PAD_SINK) rkisp1_rsz_set_sink_fmt(rsz, cfg, &fmt->format, fmt->which); else rkisp1_rsz_set_src_fmt(rsz, cfg, &fmt->format, fmt->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -595,10 +592,12 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, struct rkisp1_resizer *rsz = container_of(sd, struct rkisp1_resizer, sd); struct v4l2_mbus_framefmt *mf_sink; + int ret = 0; if (sel->pad == RKISP1_RSZ_PAD_SRC) return -EINVAL; + mutex_lock(&rsz->ops_lock); switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: mf_sink = rkisp1_rsz_get_pad_fmt(rsz, cfg, RKISP1_RSZ_PAD_SINK, @@ -613,10 +612,11 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd, sel->which); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&rsz->ops_lock); + return ret; } static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, @@ -632,7 +632,9 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd, dev_dbg(sd->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height); + mutex_lock(&rsz->ops_lock); rkisp1_rsz_set_sink_crop(rsz, cfg, &sel->r, sel->which); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -672,9 +674,11 @@ static int rkisp1_rsz_s_stream(struct v4l2_subdev *sd, int enable) if (other->is_streaming) when = RKISP1_SHADOW_REGS_ASYNC; + mutex_lock(&rsz->ops_lock); rkisp1_rsz_config(rsz, when); rkisp1_dcrop_config(rsz); + mutex_unlock(&rsz->ops_lock); return 0; } @@ -720,6 +724,7 @@ static int rkisp1_rsz_register(struct rkisp1_resizer *rsz) rsz->fmt_type = RKISP1_DEF_FMT_TYPE; + mutex_init(&rsz->ops_lock); ret = media_entity_pads_init(&sd->entity, 2, pads); if (ret) return ret; diff --git a/drivers/staging/media/rkisp1/rkisp1-stats.c b/drivers/staging/media/rkisp1/rkisp1-stats.c index d98ea15837de..6dfcbdc3deb8 100644 --- a/drivers/staging/media/rkisp1/rkisp1-stats.c +++ b/drivers/staging/media/rkisp1/rkisp1-stats.c @@ -70,8 +70,7 @@ static int rkisp1_stats_querycap(struct file *file, strscpy(cap->driver, RKISP1_DRIVER_NAME, sizeof(cap->driver)); strscpy(cap->card, vdev->name, sizeof(cap->card)); - strscpy(cap->bus_info, "platform: " RKISP1_DRIVER_NAME, - sizeof(cap->bus_info)); + strscpy(cap->bus_info, RKISP1_BUS_INFO, sizeof(cap->bus_info)); return 0; } @@ -487,7 +486,7 @@ int rkisp1_stats_register(struct rkisp1_stats *stats, if (ret) goto err_release_queue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) { dev_err(&vdev->dev, "failed to register %s, ret=%d\n", vdev->name, ret); diff --git a/drivers/staging/media/soc_camera/soc_camera.c b/drivers/staging/media/soc_camera/soc_camera.c index 7b9448e3c9ba..39f513f69b89 100644 --- a/drivers/staging/media/soc_camera/soc_camera.c +++ b/drivers/staging/media/soc_camera/soc_camera.c @@ -2068,7 +2068,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); } - ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(icd->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); return ret; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index c6ddd46eff82..05a85517ff60 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -414,7 +414,7 @@ static int cedrus_probe(struct platform_device *pdev) dev->mdev.ops = &cedrus_m2m_media_ops; dev->v4l2_dev.mdev = &dev->mdev; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); goto err_m2m; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index bfb4a4820a67..54ee2aa423e2 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -610,8 +610,12 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) goto err_mv_col_buf; } + /* + * NOTE: Multiplying by two deviates from CedarX logic, but it + * is for some unknown reason needed for H264 4K decoding on H6. + */ ctx->codec.h264.intra_pred_buf_size = - ALIGN(ctx->src_fmt.width, 64) * 5; + ALIGN(ctx->src_fmt.width, 64) * 5 * 2; ctx->codec.h264.intra_pred_buf = dma_alloc_coherent(dev->dev, ctx->codec.h264.intra_pred_buf_size, diff --git a/drivers/staging/media/tegra-vde/vde.c b/drivers/staging/media/tegra-vde/vde.c index e18fd48981da..d3e63512a765 100644 --- a/drivers/staging/media/tegra-vde/vde.c +++ b/drivers/staging/media/tegra-vde/vde.c @@ -949,7 +949,6 @@ static int tegra_vde_runtime_resume(struct device *dev) static int tegra_vde_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *regs; struct tegra_vde *vde; int irq, err; @@ -959,75 +958,39 @@ static int tegra_vde_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vde); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sxe"); - if (!regs) - return -ENODEV; - - vde->sxe = devm_ioremap_resource(dev, regs); + vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe"); if (IS_ERR(vde->sxe)) return PTR_ERR(vde->sxe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bsev"); - if (!regs) - return -ENODEV; - - vde->bsev = devm_ioremap_resource(dev, regs); + vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev"); if (IS_ERR(vde->bsev)) return PTR_ERR(vde->bsev); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbe"); - if (!regs) - return -ENODEV; - - vde->mbe = devm_ioremap_resource(dev, regs); + vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe"); if (IS_ERR(vde->mbe)) return PTR_ERR(vde->mbe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppe"); - if (!regs) - return -ENODEV; - - vde->ppe = devm_ioremap_resource(dev, regs); + vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe"); if (IS_ERR(vde->ppe)) return PTR_ERR(vde->ppe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mce"); - if (!regs) - return -ENODEV; - - vde->mce = devm_ioremap_resource(dev, regs); + vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce"); if (IS_ERR(vde->mce)) return PTR_ERR(vde->mce); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tfe"); - if (!regs) - return -ENODEV; - - vde->tfe = devm_ioremap_resource(dev, regs); + vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe"); if (IS_ERR(vde->tfe)) return PTR_ERR(vde->tfe); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ppb"); - if (!regs) - return -ENODEV; - - vde->ppb = devm_ioremap_resource(dev, regs); + vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb"); if (IS_ERR(vde->ppb)) return PTR_ERR(vde->ppb); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vdma"); - if (!regs) - return -ENODEV; - - vde->vdma = devm_ioremap_resource(dev, regs); + vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma"); if (IS_ERR(vde->vdma)) return PTR_ERR(vde->vdma); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "frameid"); - if (!regs) - return -ENODEV; - - vde->frameid = devm_ioremap_resource(dev, regs); + vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid"); if (IS_ERR(vde->frameid)) return PTR_ERR(vde->frameid); diff --git a/drivers/media/usb/usbvision/Kconfig b/drivers/staging/media/usbvision/Kconfig index e1039fdfb0ea..c6e1afb5ac48 100644 --- a/drivers/media/usb/usbvision/Kconfig +++ b/drivers/staging/media/usbvision/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config VIDEO_USBVISION - tristate "USB video devices based on Nogatech NT1003/1004/1005" - depends on I2C && VIDEO_V4L2 + tristate "USB video devices based on Nogatech NT1003/1004/1005 (Deprecated)" + depends on MEDIA_USB_SUPPORT && I2C && VIDEO_V4L2 select VIDEO_TUNER select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT help @@ -9,5 +9,10 @@ config VIDEO_USBVISION NT1003/1004/1005 USB Bridges. This driver enables using those devices. + This driver is deprecated and scheduled for removal by the + end of 2020. See the TODO file in drivers/staging/media/usbvision + for a list of actions that have to be done in order to prevent + removal of this driver. + To compile this driver as a module, choose M here: the module will be called usbvision. diff --git a/drivers/media/usb/usbvision/Makefile b/drivers/staging/media/usbvision/Makefile index 4d8541b9d4f8..4d8541b9d4f8 100644 --- a/drivers/media/usb/usbvision/Makefile +++ b/drivers/staging/media/usbvision/Makefile diff --git a/drivers/staging/media/usbvision/TODO b/drivers/staging/media/usbvision/TODO new file mode 100644 index 000000000000..e9fb4d125581 --- /dev/null +++ b/drivers/staging/media/usbvision/TODO @@ -0,0 +1,11 @@ +The driver is deprecated and scheduled for removal by the end +of 2020. + +In order to prevent removal the following actions would have to +be taken: + +- clean up the code +- convert to the vb2 framework +- fix the disconnect and free-on-last-user handling (i.e., add + a release callback for struct v4l2_device and rework the code + to use that correctly). diff --git a/drivers/media/usb/usbvision/usbvision-cards.c b/drivers/staging/media/usbvision/usbvision-cards.c index 5e0cbbfe7c86..5e0cbbfe7c86 100644 --- a/drivers/media/usb/usbvision/usbvision-cards.c +++ b/drivers/staging/media/usbvision/usbvision-cards.c diff --git a/drivers/media/usb/usbvision/usbvision-cards.h b/drivers/staging/media/usbvision/usbvision-cards.h index 07ec83512743..07ec83512743 100644 --- a/drivers/media/usb/usbvision/usbvision-cards.h +++ b/drivers/staging/media/usbvision/usbvision-cards.h diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/staging/media/usbvision/usbvision-core.c index f05a5c84dc18..f05a5c84dc18 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/staging/media/usbvision/usbvision-core.c diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/staging/media/usbvision/usbvision-i2c.c index 6e4df3335b1b..6e4df3335b1b 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/staging/media/usbvision/usbvision-i2c.c diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/staging/media/usbvision/usbvision-video.c index 5ca2c2f35fe2..3ea25fdcf767 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/staging/media/usbvision/usbvision-video.c @@ -1271,7 +1271,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) if (usbvision->have_tuner) usbvision->vdev.device_caps |= V4L2_CAP_TUNER; - if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) + if (video_register_device(&usbvision->vdev, VFL_TYPE_VIDEO, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", usbvision->nr, video_device_node_name(&usbvision->vdev)); diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/staging/media/usbvision/usbvision.h index 11539578e8d2..11539578e8d2 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/staging/media/usbvision/usbvision.h diff --git a/drivers/staging/most/video/video.c b/drivers/staging/most/video/video.c index d32ae49d617b..4cffc8fd62bd 100644 --- a/drivers/staging/most/video/video.c +++ b/drivers/staging/most/video/video.c @@ -74,7 +74,7 @@ static int comp_vdev_open(struct file *filp) struct comp_fh *fh; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: break; default: return -EINVAL; @@ -424,7 +424,7 @@ static int comp_register_videodev(struct most_video_dev *mdev) /* Register the v4l2 device */ video_set_drvdata(mdev->vdev, mdev); - ret = video_register_device(mdev->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(mdev->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&mdev->v4l2_dev, "video_register_device failed (%d)\n", ret); diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 1ef31a984741..597acef35d0b 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1776,7 +1776,7 @@ static int bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, video_set_drvdata(vfd, dev); ret = video_register_device(vfd, - VFL_TYPE_GRABBER, + VFL_TYPE_VIDEO, video_nr[dev->camera_num]); if (ret < 0) return ret; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index fb0a892687c0..0b9712616455 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -428,7 +428,7 @@ uvc_register_video(struct uvc_device *uvc) video_set_drvdata(&uvc->vdev, uvc); - ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(&uvc->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) return ret; diff --git a/include/dt-bindings/display/sdtv-standards.h b/include/dt-bindings/display/sdtv-standards.h new file mode 100644 index 000000000000..fbc1a3db2ea7 --- /dev/null +++ b/include/dt-bindings/display/sdtv-standards.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only or X11 */ +/* + * Copyright 2019 Pengutronix, Marco Felsch <kernel@pengutronix.de> + */ + +#ifndef _DT_BINDINGS_DISPLAY_SDTV_STDS_H +#define _DT_BINDINGS_DISPLAY_SDTV_STDS_H + +/* + * Attention: Keep the SDTV_STD_* bit definitions in sync with + * include/uapi/linux/videodev2.h V4L2_STD_* bit definitions. + */ +/* One bit for each standard */ +#define SDTV_STD_PAL_B 0x00000001 +#define SDTV_STD_PAL_B1 0x00000002 +#define SDTV_STD_PAL_G 0x00000004 +#define SDTV_STD_PAL_H 0x00000008 +#define SDTV_STD_PAL_I 0x00000010 +#define SDTV_STD_PAL_D 0x00000020 +#define SDTV_STD_PAL_D1 0x00000040 +#define SDTV_STD_PAL_K 0x00000080 + +#define SDTV_STD_PAL (SDTV_STD_PAL_B | \ + SDTV_STD_PAL_B1 | \ + SDTV_STD_PAL_G | \ + SDTV_STD_PAL_H | \ + SDTV_STD_PAL_I | \ + SDTV_STD_PAL_D | \ + SDTV_STD_PAL_D1 | \ + SDTV_STD_PAL_K) + +#define SDTV_STD_PAL_M 0x00000100 +#define SDTV_STD_PAL_N 0x00000200 +#define SDTV_STD_PAL_Nc 0x00000400 +#define SDTV_STD_PAL_60 0x00000800 + +#define SDTV_STD_NTSC_M 0x00001000 /* BTSC */ +#define SDTV_STD_NTSC_M_JP 0x00002000 /* EIA-J */ +#define SDTV_STD_NTSC_443 0x00004000 +#define SDTV_STD_NTSC_M_KR 0x00008000 /* FM A2 */ + +#define SDTV_STD_NTSC (SDTV_STD_NTSC_M | \ + SDTV_STD_NTSC_M_JP | \ + SDTV_STD_NTSC_M_KR) + +#define SDTV_STD_SECAM_B 0x00010000 +#define SDTV_STD_SECAM_D 0x00020000 +#define SDTV_STD_SECAM_G 0x00040000 +#define SDTV_STD_SECAM_H 0x00080000 +#define SDTV_STD_SECAM_K 0x00100000 +#define SDTV_STD_SECAM_K1 0x00200000 +#define SDTV_STD_SECAM_L 0x00400000 +#define SDTV_STD_SECAM_LC 0x00800000 + +#define SDTV_STD_SECAM (SDTV_STD_SECAM_B | \ + SDTV_STD_SECAM_D | \ + SDTV_STD_SECAM_G | \ + SDTV_STD_SECAM_H | \ + SDTV_STD_SECAM_K | \ + SDTV_STD_SECAM_K1 | \ + SDTV_STD_SECAM_L | \ + SDTV_STD_SECAM_LC) + +/* Standards for Countries with 60Hz Line frequency */ +#define SDTV_STD_525_60 (SDTV_STD_PAL_M | \ + SDTV_STD_PAL_60 | \ + SDTV_STD_NTSC | \ + SDTV_STD_NTSC_443) + +/* Standards for Countries with 50Hz Line frequency */ +#define SDTV_STD_625_50 (SDTV_STD_PAL | \ + SDTV_STD_PAL_N | \ + SDTV_STD_PAL_Nc | \ + SDTV_STD_SECAM) + +#endif /* _DT_BINDINGS_DISPLAY_SDTV_STDS_H */ diff --git a/include/dt-bindings/media/tvp5150.h b/include/dt-bindings/media/tvp5150.h index 01eedf4985b8..dda00c038530 100644 --- a/include/dt-bindings/media/tvp5150.h +++ b/include/dt-bindings/media/tvp5150.h @@ -14,8 +14,6 @@ #define TVP5150_COMPOSITE1 1 #define TVP5150_SVIDEO 2 -#define TVP5150_INPUT_NUM 3 - /* TVP5150 HW outputs */ #define TVP5150_NORMAL 0 #define TVP5150_BLACK_SCREEN 1 diff --git a/include/linux/atmel-isc-media.h b/include/linux/atmel-isc-media.h new file mode 100644 index 000000000000..79a320fb724e --- /dev/null +++ b/include/linux/atmel-isc-media.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries + * + * Author: Eugen Hristev <eugen.hristev@microchip.com> + */ + +#ifndef __LINUX_ATMEL_ISC_MEDIA_H__ +#define __LINUX_ATMEL_ISC_MEDIA_H__ + +/* + * There are 8 controls available: + * 4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These gains are multipliers for each component, in format unsigned 0:4:9 with + * a default value of 512 (1.0 multiplier). + * 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These offsets are added/substracted from each component, in format signed + * 1:12:0 with a default value of 0 (+/- 0) + * + * To expose this to userspace, added 8 custom controls, in an auto cluster. + * + * To summarize the functionality: + * The auto cluster switch is the auto white balance control, and it works + * like this: + * AWB == 1: autowhitebalance is on, the do_white_balance button is inactive, + * the gains/offsets are inactive, but volatile and readable. + * Thus, the results of the whitebalance algorithm are available to userspace to + * read at any time. + * AWB == 0: autowhitebalance is off, cluster is in manual mode, user can + * configure the gain/offsets directly. + * More than that, if the do_white_balance button is + * pressed, the driver will perform one-time-adjustment, (preferably with color + * checker card) and the userspace can read again the new values. + * + * With this feature, the userspace can save the coefficients and reinstall them + * for example after reboot or reprobing the driver. + */ + +enum atmel_isc_ctrl_id { + /* Red component gain control */ + ISC_CID_R_GAIN = (V4L2_CID_USER_ATMEL_ISC_BASE + 0), + /* Blue component gain control */ + ISC_CID_B_GAIN, + /* Green Red component gain control */ + ISC_CID_GR_GAIN, + /* Green Blue gain control */ + ISC_CID_GB_GAIN, + /* Red component offset control */ + ISC_CID_R_OFFSET, + /* Blue component offset control */ + ISC_CID_B_OFFSET, + /* Green Red component offset control */ + ISC_CID_GR_OFFSET, + /* Green Blue component offset control */ + ISC_CID_GB_OFFSET, +}; + +#endif diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h index 139e93be13b0..38956969fd12 100644 --- a/include/media/cec-notifier.h +++ b/include/media/cec-notifier.h @@ -20,31 +20,14 @@ struct cec_notifier; #if IS_REACHABLE(CONFIG_CEC_CORE) && IS_ENABLED(CONFIG_CEC_NOTIFIER) /** - * cec_notifier_get_conn - find or create a new cec_notifier for the given - * device and connector tuple. - * @dev: device that sends the events. - * @conn: the connector name from which the event occurs - * - * If a notifier for device @dev already exists, then increase the refcount - * and return that notifier. - * - * If it doesn't exist, then allocate a new notifier struct and return a - * pointer to that new struct. - * - * Return NULL if the memory could not be allocated. - */ -struct cec_notifier *cec_notifier_get_conn(struct device *dev, - const char *conn); - -/** * cec_notifier_conn_register - find or create a new cec_notifier for the given * HDMI device and connector tuple. * @hdmi_dev: HDMI device that sends the events. - * @conn_name: the connector name from which the event occurs. May be NULL + * @port_name: the connector name from which the event occurs. May be NULL * if there is always only one HDMI connector created by the HDMI device. * @conn_info: the connector info from which the event occurs (may be NULL) * - * If a notifier for device @dev and connector @conn_name already exists, then + * If a notifier for device @dev and connector @port_name already exists, then * increase the refcount and return that notifier. * * If it doesn't exist, then allocate a new notifier struct and return a @@ -53,7 +36,7 @@ struct cec_notifier *cec_notifier_get_conn(struct device *dev, * Return NULL if the memory could not be allocated. */ struct cec_notifier * -cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, const struct cec_connector_info *conn_info); /** @@ -67,11 +50,11 @@ void cec_notifier_conn_unregister(struct cec_notifier *n); * cec_notifier_cec_adap_register - find or create a new cec_notifier for the * given device. * @hdmi_dev: HDMI device that sends the events. - * @conn_name: the connector name from which the event occurs. May be NULL + * @port_name: the connector name from which the event occurs. May be NULL * if there is always only one HDMI connector created by the HDMI device. * @adap: the cec adapter that registered this notifier. * - * If a notifier for device @dev and connector @conn_name already exists, then + * If a notifier for device @dev and connector @port_name already exists, then * increase the refcount and return that notifier. * * If it doesn't exist, then allocate a new notifier struct and return a @@ -80,7 +63,7 @@ void cec_notifier_conn_unregister(struct cec_notifier *n); * Return NULL if the memory could not be allocated. */ struct cec_notifier * -cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, struct cec_adapter *adap); /** @@ -125,15 +108,9 @@ void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, struct device *cec_notifier_parse_hdmi_phandle(struct device *dev); #else -static inline struct cec_notifier *cec_notifier_get_conn(struct device *dev, - const char *conn) -{ - /* A non-NULL pointer is expected on success */ - return (struct cec_notifier *)0xdeadfeed; -} static inline struct cec_notifier * -cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, const struct cec_connector_info *conn_info) { /* A non-NULL pointer is expected on success */ @@ -145,7 +122,7 @@ static inline void cec_notifier_conn_unregister(struct cec_notifier *n) } static inline struct cec_notifier * -cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name, +cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, struct cec_adapter *adap) { /* A non-NULL pointer is expected on success */ diff --git a/include/media/h264-ctrls.h b/include/media/h264-ctrls.h index e877bf1d537c..1c6ff7d63bca 100644 --- a/include/media/h264-ctrls.h +++ b/include/media/h264-ctrls.h @@ -185,6 +185,8 @@ struct v4l2_ctrl_h264_slice_params { #define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 #define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 #define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 +#define V4L2_H264_DPB_ENTRY_FLAG_FIELD 0x08 +#define V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD 0x10 struct v4l2_h264_dpb_entry { __u64 reference_ts; diff --git a/include/media/i2c/smiapp.h b/include/media/i2c/smiapp.h deleted file mode 100644 index 80f8251d87a3..000000000000 --- a/include/media/i2c/smiapp.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/i2c/smiapp.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus <sakari.ailus@iki.fi> - */ - -#ifndef __SMIAPP_H_ -#define __SMIAPP_H_ - -#include <media/v4l2-subdev.h> - -#define SMIAPP_NAME "smiapp" - -#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ -#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ - -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 -#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 - -/* - * Sometimes due to board layout considerations the camera module can be - * mounted rotated. The typical rotation used is 180 degrees which can be - * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. - * FIXME: rotation also changes the bayer pattern. - */ -enum smiapp_module_board_orient { - SMIAPP_MODULE_BOARD_ORIENT_0 = 0, - SMIAPP_MODULE_BOARD_ORIENT_180, -}; - -struct smiapp_flash_strobe_parms { - u8 mode; - u32 strobe_width_high_us; - u16 strobe_delay; - u16 stobe_start_point; - u8 trigger; -}; - -struct smiapp_hwconfig { - /* - * Change the cci address if i2c_addr_alt is set. - * Both default and alternate cci addr need to be present - */ - unsigned short i2c_addr_dfl; /* Default i2c addr */ - unsigned short i2c_addr_alt; /* Alternate i2c addr */ - - uint32_t ext_clk; /* sensor external clk */ - - unsigned int lanes; /* Number of CSI-2 lanes */ - uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ - uint64_t *op_sys_clock; - - enum smiapp_module_board_orient module_board_orient; - - struct smiapp_flash_strobe_parms *strobe_setup; -}; - -#endif /* __SMIAPP_H_ */ diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 1f695d9c200a..d3f85df64bb2 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -192,7 +192,7 @@ struct rc_dev { struct timer_list timer_repeat; u32 last_keycode; enum rc_proto last_protocol; - u32 last_scancode; + u64 last_scancode; u8 last_toggle; u32 timeout; u32 min_timeout; @@ -284,12 +284,12 @@ int devm_rc_register_device(struct device *parent, struct rc_dev *dev); void rc_unregister_device(struct rc_dev *dev); void rc_repeat(struct rc_dev *dev); -void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode, +void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode, u8 toggle); void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u8 toggle); + u64 scancode, u8 toggle); void rc_keyup(struct rc_dev *dev); -u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode); +u32 rc_g_keycode_from_table(struct rc_dev *dev, u64 scancode); /* * From rc-raw.c diff --git a/include/media/rc-map.h b/include/media/rc-map.h index f99575a0d29c..0ce896f10202 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -85,11 +85,11 @@ /** * struct rc_map_table - represents a scancode/keycode pair * - * @scancode: scan code (u32) + * @scancode: scan code (u64) * @keycode: Linux input keycode */ struct rc_map_table { - u32 scancode; + u64 scancode; u32 keycode; }; @@ -274,6 +274,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_VIDEOMATE_K100 "rc-videomate-k100" #define RC_MAP_VIDEOMATE_S350 "rc-videomate-s350" #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" +#define RC_MAP_KII_PRO "rc-videostrong-kii-pro" #define RC_MAP_WETEK_HUB "rc-wetek-hub" #define RC_MAP_WETEK_PLAY2 "rc-wetek-play2" #define RC_MAP_WINFAST "rc-winfast" diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 48531e57cc5a..4602c15ff878 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -24,7 +24,7 @@ /** * enum vfl_devnode_type - type of V4L2 device node * - * @VFL_TYPE_GRABBER: for video input/output devices + * @VFL_TYPE_VIDEO: for video input/output devices * @VFL_TYPE_VBI: for vertical blank data (i.e. closed captions, teletext) * @VFL_TYPE_RADIO: for radio tuners * @VFL_TYPE_SUBDEV: for V4L2 subdevices @@ -33,7 +33,7 @@ * @VFL_TYPE_MAX: number of VFL types, must always be last in the enum */ enum vfl_devnode_type { - VFL_TYPE_GRABBER = 0, + VFL_TYPE_VIDEO, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 95353ae476a1..7c912b7d2870 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -240,7 +240,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -265,7 +265,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -293,7 +293,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -328,7 +328,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -359,7 +359,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -388,7 +388,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -419,7 +419,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -447,7 +447,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index f6a7bcd13197..dd82d6d9764e 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -113,14 +113,75 @@ struct v4l2_fwnode_endpoint { * struct v4l2_fwnode_link - a link between two endpoints * @local_node: pointer to device_node of this endpoint * @local_port: identifier of the port this endpoint belongs to + * @local_id: identifier of the id this endpoint belongs to * @remote_node: pointer to device_node of the remote endpoint * @remote_port: identifier of the port the remote endpoint belongs to + * @remote_id: identifier of the id the remote endpoint belongs to */ struct v4l2_fwnode_link { struct fwnode_handle *local_node; unsigned int local_port; + unsigned int local_id; struct fwnode_handle *remote_node; unsigned int remote_port; + unsigned int remote_id; +}; + +/** + * enum v4l2_connector_type - connector type + * @V4L2_CONN_UNKNOWN: unknown connector type, no V4L2 connector configuration + * @V4L2_CONN_COMPOSITE: analog composite connector + * @V4L2_CONN_SVIDEO: analog svideo connector + */ +enum v4l2_connector_type { + V4L2_CONN_UNKNOWN, + V4L2_CONN_COMPOSITE, + V4L2_CONN_SVIDEO, +}; + +/** + * struct v4l2_connector_link - connector link data structure + * @head: structure to be used to add the link to the + * &struct v4l2_fwnode_connector + * @fwnode_link: &struct v4l2_fwnode_link link between the connector and the + * device the connector belongs to. + */ +struct v4l2_connector_link { + struct list_head head; + struct v4l2_fwnode_link fwnode_link; +}; + +/** + * struct v4l2_fwnode_connector_analog - analog connector data structure + * @sdtv_stds: sdtv standards this connector supports, set to V4L2_STD_ALL + * if no restrictions are specified. + */ +struct v4l2_fwnode_connector_analog { + v4l2_std_id sdtv_stds; +}; + +/** + * struct v4l2_fwnode_connector - the connector data structure + * @name: the connector device name + * @label: optional connector label + * @type: connector type + * @links: list of all connector &struct v4l2_connector_link links + * @nr_of_links: total number of links + * @connector: connector configuration + * @connector.analog: analog connector configuration + * &struct v4l2_fwnode_connector_analog + */ +struct v4l2_fwnode_connector { + const char *name; + const char *label; + enum v4l2_connector_type type; + struct list_head links; + unsigned int nr_of_links; + + union { + struct v4l2_fwnode_connector_analog analog; + /* future connectors */ + } connector; }; /** @@ -234,6 +295,66 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); /** + * v4l2_fwnode_connector_free() - free the V4L2 connector acquired memory + * @connector: the V4L2 connector resources of which are to be released + * + * Free all allocated memory and put all links acquired by + * v4l2_fwnode_connector_parse() and v4l2_fwnode_connector_add_link(). + * + * It is safe to call this function with NULL argument or on a V4L2 connector + * the parsing of which failed. + */ +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector); + +/** + * v4l2_fwnode_connector_parse() - initialize the 'struct v4l2_fwnode_connector' + * @fwnode: pointer to the subdev endpoint's fwnode handle where the connector + * is connected to or to the connector endpoint fwnode handle. + * @connector: pointer to the V4L2 fwnode connector data structure + * + * Fill the &struct v4l2_fwnode_connector with the connector type, label and + * all &enum v4l2_connector_type specific connector data. The label is optional + * so it is set to %NULL if no one was found. The function initialize the links + * to zero. Adding links to the connector is done by calling + * v4l2_fwnode_connector_add_link(). + * + * The memory allocated for the label must be freed when no longer needed. + * Freeing the memory is done by v4l2_fwnode_connector_free(). + * + * Return: + * * %0 on success or a negative error code on failure: + * * %-EINVAL if @fwnode is invalid + * * %-ENOTCONN if connector type is unknown or connector device can't be found + */ +int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector); + +/** + * v4l2_fwnode_connector_add_link - add a link between a connector node and + * a v4l2-subdev node. + * @fwnode: pointer to the subdev endpoint's fwnode handle where the connector + * is connected to + * @connector: pointer to the V4L2 fwnode connector data structure + * + * Add a new &struct v4l2_connector_link link to the + * &struct v4l2_fwnode_connector connector links list. The link local_node + * points to the connector node, the remote_node to the host v4l2 (sub)dev. + * + * The taken references to remote_node and local_node must be dropped and the + * allocated memory must be freed when no longer needed. Both is done by calling + * v4l2_fwnode_connector_free(). + * + * Return: + * * %0 on success or a negative error code on failure: + * * %-EINVAL if @fwnode or @connector is invalid or @connector type is unknown + * * %-ENOMEM on link memory allocation failure + * * %-ENOTCONN if remote connector device can't be found + * * %-ENOLINK if link parsing between v4l2 (sub)dev and connector fails + */ +int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector); + +/** * typedef parse_endpoint_func - Driver's callback function to be called on * each V4L2 fwnode endpoint. * @@ -406,4 +527,26 @@ v4l2_async_register_fwnode_subdev(struct v4l2_subdev *sd, unsigned int num_ports, parse_endpoint_func parse_endpoint); +/* Helper macros to access the connector links. */ + +/** v4l2_connector_last_link - Helper macro to get the first + * &struct v4l2_fwnode_connector link + * @v4l2c: &struct v4l2_fwnode_connector owning the connector links + * + * This marco returns the first added &struct v4l2_connector_link connector + * link or @NULL if the connector has no links. + */ +#define v4l2_connector_first_link(v4l2c) \ + list_first_entry_or_null(&(v4l2c)->links, \ + struct v4l2_connector_link, head) + +/** v4l2_connector_last_link - Helper macro to get the last + * &struct v4l2_fwnode_connector link + * @v4l2c: &struct v4l2_fwnode_connector owning the connector links + * + * This marco returns the last &struct v4l2_connector_link added connector link. + */ +#define v4l2_connector_last_link(v4l2c) \ + list_last_entry(&(v4l2c)->links, struct v4l2_connector_link, head) + #endif /* _V4L2_FWNODE_H */ diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index 384960249f01..5e73eb8e28f6 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -86,23 +86,30 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q); /** - * v4l2_pipeline_pm_use - Update the use count of an entity - * @entity: The entity - * @use: Use (1) or stop using (0) the entity + * v4l2_pipeline_pm_get - Increase the use count of a pipeline + * @entity: The root entity of a pipeline * - * Update the use count of all entities in the pipeline and power entities on or - * off accordingly. + * Update the use count of all entities in the pipeline and power entities on. * - * This function is intended to be called in video node open (use == - * 1) and release (use == 0). It uses struct media_entity.use_count to - * track the power status. The use of this function should be paired - * with v4l2_pipeline_link_notify(). + * This function is intended to be called in video node open. It uses + * struct media_entity.use_count to track the power status. The use + * of this function should be paired with v4l2_pipeline_link_notify(). * - * Return 0 on success or a negative error code on failure. Powering entities - * off is assumed to never fail. No failure can occur when the use parameter is - * set to 0. + * Return 0 on success or a negative error code on failure. */ -int v4l2_pipeline_pm_use(struct media_entity *entity, int use); +int v4l2_pipeline_pm_get(struct media_entity *entity); + +/** + * v4l2_pipeline_pm_put - Decrease the use count of a pipeline + * @entity: The root entity of a pipeline + * + * Update the use count of all entities in the pipeline and power entities off. + * + * This function is intended to be called in video node release. It uses + * struct media_entity.use_count to track the power status. The use + * of this function should be paired with v4l2_pipeline_link_notify(). + */ +void v4l2_pipeline_pm_put(struct media_entity *entity); /** @@ -114,7 +121,7 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use); * React to link management on powered pipelines by updating the use count of * all entities in the source and sink sides of the link. Entities are powered * on or off accordingly. The use of this function should be paired - * with v4l2_pipeline_pm_use(). + * with v4l2_pipeline_pm_{get,put}(). * * Return 0 on success or a negative error code on failure. Powering entities * off is assumed to never fail. This function will not fail for disconnection @@ -144,11 +151,14 @@ static inline int v4l_vb2q_enable_media_source(struct vb2_queue *q) return 0; } -static inline int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +static inline int v4l2_pipeline_pm_get(struct media_entity *entity) { return 0; } +static inline void v4l2_pipeline_pm_put(struct media_entity *entity) +{} + static inline int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 1d85e24791e4..98753f00df7e 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -80,6 +80,10 @@ struct v4l2_m2m_queue_ctx { * for an existing frame. This is always true unless * V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which * indicates slicing support. + * @is_draining: indicates device is in draining phase + * @last_src_buf: indicate the last source buffer for draining + * @next_buf_last: next capture queud buffer will be tagged as last + * @has_stopped: indicate the device has been stopped * @m2m_dev: opaque pointer to the internal data to handle M2M context * @cap_q_ctx: Capture (output to memory) queue context * @out_q_ctx: Output (input from memory) queue context @@ -98,6 +102,11 @@ struct v4l2_m2m_ctx { bool new_frame; + bool is_draining; + struct vb2_v4l2_buffer *last_src_buf; + bool next_buf_last; + bool has_stopped; + /* internal use only */ struct v4l2_m2m_dev *m2m_dev; @@ -216,6 +225,86 @@ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state) } /** + * v4l2_m2m_clear_state() - clear encoding/decoding state + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void +v4l2_m2m_clear_state(struct v4l2_m2m_ctx *m2m_ctx) +{ + m2m_ctx->next_buf_last = false; + m2m_ctx->is_draining = false; + m2m_ctx->has_stopped = false; +} + +/** + * v4l2_m2m_mark_stopped() - set current encoding/decoding state as stopped + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void +v4l2_m2m_mark_stopped(struct v4l2_m2m_ctx *m2m_ctx) +{ + m2m_ctx->next_buf_last = false; + m2m_ctx->is_draining = false; + m2m_ctx->has_stopped = true; +} + +/** + * v4l2_m2m_dst_buf_is_last() - return the current encoding/decoding session + * draining management state of next queued capture buffer + * + * This last capture buffer should be tagged with V4L2_BUF_FLAG_LAST to notify + * the end of the capture session. + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline bool +v4l2_m2m_dst_buf_is_last(struct v4l2_m2m_ctx *m2m_ctx) +{ + return m2m_ctx->is_draining && m2m_ctx->next_buf_last; +} + +/** + * v4l2_m2m_has_stopped() - return the current encoding/decoding session + * stopped state + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline bool +v4l2_m2m_has_stopped(struct v4l2_m2m_ctx *m2m_ctx) +{ + return m2m_ctx->has_stopped; +} + +/** + * v4l2_m2m_is_last_draining_src_buf() - return the output buffer draining + * state in the current encoding/decoding session + * + * This will identify the last output buffer queued before a session stop + * was required, leading to an actual encoding/decoding session stop state + * in the encoding/decoding process after being processed. + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: pointer to struct &v4l2_buffer + */ +static inline bool +v4l2_m2m_is_last_draining_src_buf(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + return m2m_ctx->is_draining && vbuf == m2m_ctx->last_src_buf; +} + +/** + * v4l2_m2m_last_buffer_done() - marks the buffer with LAST flag and DONE + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: pointer to struct &v4l2_buffer + */ +void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf); + +/** * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer * * @file: pointer to struct &file @@ -313,6 +402,46 @@ int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); /** + * v4l2_m2m_update_start_streaming_state() - update the encoding/decoding + * session state when a start of streaming of a video queue is requested + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @q: queue + */ +void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q); + +/** + * v4l2_m2m_update_stop_streaming_state() - update the encoding/decoding + * session state when a stop of streaming of a video queue is requested + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @q: queue + */ +void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q); + +/** + * v4l2_m2m_encoder_cmd() - execute an encoder command + * + * @file: pointer to struct &file + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @ec: pointer to the encoder command + */ +int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_encoder_cmd *ec); + +/** + * v4l2_m2m_decoder_cmd() - execute a decoder command + * + * @file: pointer to struct &file + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @dc: pointer to the decoder command + */ +int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_decoder_cmd *dc); + +/** * v4l2_m2m_poll() - poll replacement, for destination buffers only * * @file: pointer to struct &file @@ -704,6 +833,10 @@ int v4l2_m2m_ioctl_streamon(struct file *file, void *fh, enum v4l2_buf_type type); int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh, enum v4l2_buf_type type); +int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec); +int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc); int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec); int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 761aa83a3f3c..a4848de59852 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1093,7 +1093,7 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers; * @f: callback function to be called. * The callback functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Example: err = v4l2_subdev_call(sd, video, s_std, norm); */ diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index a2b2208b02da..f11b96514cf7 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -509,8 +509,11 @@ struct vb2_buf_ops { * by the vb2 core. * @buf_struct_size: size of the driver-specific buffer structure; * "0" indicates the driver doesn't want to use a custom buffer - * structure type. for example, ``sizeof(struct vb2_v4l2_buffer)`` - * will be used for v4l2. + * structure type. In that case a subsystem-specific struct + * will be used (in the case of V4L2 that is + * ``sizeof(struct vb2_v4l2_buffer)``). The first field of the + * driver-specific buffer structure must be the subsystem-specific + * struct (vb2_v4l2_buffer in the case of V4L2). * @timestamp_flags: Timestamp flags; ``V4L2_BUF_FLAG_TIMESTAMP_*`` and * ``V4L2_BUF_FLAG_TSTAMP_SRC_*`` * @gfp_flags: additional gfp flags used when allocating the buffers. diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 16c1fa2d89a4..84fa53ffb13f 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -64,7 +64,7 @@ #define MEDIA_BUS_FMT_RGB121212_1X36 0x1019 #define MEDIA_BUS_FMT_RGB161616_1X48 0x101a -/* YUV (including grey) - next is 0x202d */ +/* YUV (including grey) - next is 0x202e */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 #define MEDIA_BUS_FMT_UV8_1X8 0x2015 #define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 @@ -86,6 +86,7 @@ #define MEDIA_BUS_FMT_VYUY12_2X12 0x201d #define MEDIA_BUS_FMT_YUYV12_2X12 0x201e #define MEDIA_BUS_FMT_YVYU12_2X12 0x201f +#define MEDIA_BUS_FMT_Y14_1X14 0x202d #define MEDIA_BUS_FMT_UYVY8_1X16 0x200f #define MEDIA_BUS_FMT_VYUY8_1X16 0x2010 #define MEDIA_BUS_FMT_YUYV8_1X16 0x2011 diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 5a7bedee2b0e..1a58d7cc4ccc 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -192,6 +192,12 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) +/* + * The base for the atmel isc driver controls. + * We reserve 32 controls for this driver. + */ +#define V4L2_CID_USER_ATMEL_ISC_BASE (V4L2_CID_USER_BASE + 0x10c0) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5f9357dcb060..9817b7e2c968 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -565,6 +565,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */ #define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */ #define V4L2_PIX_FMT_Y12 v4l2_fourcc('Y', '1', '2', ' ') /* 12 Greyscale */ +#define V4L2_PIX_FMT_Y14 v4l2_fourcc('Y', '1', '4', ' ') /* 14 Greyscale */ #define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ #define V4L2_PIX_FMT_Y16_BE v4l2_fourcc_be('Y', '1', '6', ' ') /* 16 Greyscale BE */ @@ -662,6 +663,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG12P v4l2_fourcc('p', 'G', 'C', 'C') #define V4L2_PIX_FMT_SGRBG12P v4l2_fourcc('p', 'g', 'C', 'C') #define V4L2_PIX_FMT_SRGGB12P v4l2_fourcc('p', 'R', 'C', 'C') +#define V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4') /* 14 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4') /* 14 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG14 v4l2_fourcc('G', 'R', '1', '4') /* 14 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB14 v4l2_fourcc('R', 'G', '1', '4') /* 14 RGRG.. GBGB.. */ /* 14bit raw bayer packed, 7 bytes for every 4 pixels */ #define V4L2_PIX_FMT_SBGGR14P v4l2_fourcc('p', 'B', 'E', 'E') #define V4L2_PIX_FMT_SGBRG14P v4l2_fourcc('p', 'G', 'E', 'E') @@ -1242,6 +1247,10 @@ struct v4l2_selection { typedef __u64 v4l2_std_id; +/* + * Attention: Keep the V4L2_STD_* bit definitions in sync with + * include/dt-bindings/display/sdtv-standards.h SDTV_STD_* bit definitions. + */ /* one bit for each */ #define V4L2_STD_PAL_B ((v4l2_std_id)0x00000001) #define V4L2_STD_PAL_B1 ((v4l2_std_id)0x00000002) diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index f6a551bd57ef..3fa6582b4a68 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -879,7 +879,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vdev->tvnorms = SKEL_TVNORMS; video_set_drvdata(vdev, skel); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto free_hdl; |