diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-15 18:29:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-15 18:29:14 -0700 |
commit | 71f3a82fab1b631ae9cb1feb677f498d4ca5007d (patch) | |
tree | f3b7fd0a62658d60b491c65cf8ab93378e322024 | |
parent | 54dbe75bbf1e189982516de179147208e90b5e45 (diff) | |
parent | da2048b7348a0be92f706ac019e022139e29495e (diff) |
Merge tag 'media/v4.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- new Socionext MN88443x ISDB-S/T demodulator driver: mn88443x
- new sensor drivers: ak7375, ov2680 and rj54n1cb0c
- an old soc-camera sensor driver converted to the V4L2 framework:
mt9v111
- a new Voice-Coil Motor (VCM) driver: dw9807-vcm
- some cleanups at cx25821, removing legacy unused code
- some improvements at ddbridge driver
- new platform driver: vicodec
- some DVB API cleanups, removing ioctls and compat code for old
out-of-tree drivers that were never merged upstream
- improvements at DVB core to support frontents that support both
Satellite and non-satellite delivery systems
- got rid of the unused VIDIOC_RESERVED V4L2 ioctl
- some cleanups/improvements at gl861 ISDB driver
- several improvements on ov772x, ov7670 and ov5640, imx274, ov5645,
and smiapp sensor drivers
- fixes at em28xx to support dual TS devices
- some cleanups at V4L2/VB2 locking logic
- some API improvements at media controller
- some cec core and drivers improvements
- some uvcvideo improvements
- some improvements at platform drivers: stm32-dcmi, rcar-vin, coda,
reneseas-ceu, imx, vsp1, venus, camss
- lots of other cleanups and fixes
* tag 'media/v4.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (406 commits)
Revert "media: vivid: shut up warnings due to a non-trivial logic"
siano: get rid of an unused return code for debugfs register
media: isp: fix a warning about a wrong struct initializer
media: radio-wl1273: fix return code for the polling routine
media: s3c-camif: fix return code for the polling routine
media: saa7164: fix return codes for the polling routine
media: exynos-gsc: fix return code if mutex was interrupted
media: mt9v111: Fix build error with no VIDEO_V4L2_SUBDEV_API
media: xc4000: get rid of uneeded casts
media: drxj: get rid of uneeded casts
media: tuner-xc2028: don't use casts for printing sizes
media: cleanup fall-through comments
media: vivid: shut up warnings due to a non-trivial logic
media: rtl28xxu: be sure that it won't go past the array size
media: mt9v111: avoid going past the buffer
media: vsp1_dl: add a description for cmdpool field
media: sta2x11: add a missing parameter description
media: v4l2-mem2mem: add descriptions to MC fields
media: i2c: fix warning in Aptina MT9V111
media: imx: shut up a false positive warning
...
480 files changed, 21808 insertions, 9375 deletions
diff --git a/Documentation/devicetree/bindings/media/cec-gpio.txt b/Documentation/devicetree/bindings/media/cec-gpio.txt index 12fcd55ed153..47e8d73d32a3 100644 --- a/Documentation/devicetree/bindings/media/cec-gpio.txt +++ b/Documentation/devicetree/bindings/media/cec-gpio.txt @@ -4,8 +4,8 @@ The HDMI CEC GPIO module supports CEC implementations where the CEC line is hooked up to a pull-up GPIO line and - optionally - the HPD line is hooked up to another GPIO line. -Please note: the maximum voltage for the CEC line is 3.63V, for the HPD -line it is 5.3V. So you may need some sort of level conversion circuitry +Please note: the maximum voltage for the CEC line is 3.63V, for the HPD and +5V lines it is 5.3V. So you may need some sort of level conversion circuitry when connecting them to a GPIO line. Required properties: @@ -19,18 +19,24 @@ following property is also required: - hdmi-phandle - phandle to the HDMI controller, see also cec.txt. If the CEC line is not associated with an HDMI receiver/transmitter, then -the following property is optional: +the following property is optional and can be used for debugging HPD changes: - hpd-gpios: gpio that the HPD line is connected to. +This property is optional and can be used for debugging changes on the 5V line: + + - v5-gpios: gpio that the 5V line is connected to. + Example for the Raspberry Pi 3 where the CEC line is connected to -pin 26 aka BCM7 aka CE1 on the GPIO pin header and the HPD line is -connected to pin 11 aka BCM17 (some level shifter is needed for this!): +pin 26 aka BCM7 aka CE1 on the GPIO pin header, the HPD line is +connected to pin 11 aka BCM17 and the 5V line is connected to pin +15 aka BCM22 (some level shifter is needed for the HPD and 5V lines!): #include <dt-bindings/gpio/gpio.h> cec-gpio { - compatible = "cec-gpio"; - cec-gpios = <&gpio 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; - hpd-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + compatible = "cec-gpio"; + cec-gpios = <&gpio 7 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + hpd-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + v5-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; }; diff --git a/Documentation/devicetree/bindings/media/i2c/ak7375.txt b/Documentation/devicetree/bindings/media/i2c/ak7375.txt new file mode 100644 index 000000000000..aa3e24b41241 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ak7375.txt @@ -0,0 +1,8 @@ +Asahi Kasei Microdevices AK7375 voice coil lens driver + +AK7375 is a camera voice coil lens. + +Mandatory properties: + +- compatible: "asahi-kasei,ak7375" +- reg: I2C slave address diff --git a/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt new file mode 100644 index 000000000000..bd896e9f67d1 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt @@ -0,0 +1,46 @@ +* Aptina MT9V111 CMOS sensor +---------------------------- + +The Aptina MT9V111 is a 1/4-Inch VGA-format digital image sensor with a core +based on Aptina MT9V011 sensor and an integrated Image Flow Processor (IFP). + +The sensor has an active pixel array of 640x480 pixels and can output a number +of image resolution and formats controllable through a simple two-wires +interface. + +Required properties: +-------------------- + +- compatible: shall be "aptina,mt9v111". +- clocks: reference to the system clock input provider. + +Optional properties: +-------------------- + +- enable-gpios: output enable signal, pin name "OE#". Active low. +- standby-gpios: low power state control signal, pin name "STANDBY". + Active high. +- reset-gpios: chip reset signal, pin name "RESET#". Active low. + +The device node must contain one 'port' child node with one 'endpoint' child +sub-node for its digital output video port, in accordance with the video +interface bindings defined in: +Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: +-------- + + &i2c1 { + camera@48 { + compatible = "aptina,mt9v111"; + reg = <0x48>; + + clocks = <&camera_clk>; + + port { + mt9v111_out: endpoint { + remote-endpoint = <&ceu_in>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807.txt b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807.txt new file mode 100644 index 000000000000..c4701f1eaaf6 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807.txt @@ -0,0 +1,9 @@ +Dongwoon Anatech DW9807 voice coil lens driver + +DW9807 is a 10-bit DAC with current sink capability. It is intended for +controlling voice coil lenses. + +Mandatory properties: + +- compatible: "dongwoon,dw9807-vcm" +- reg: I2C slave address diff --git a/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt b/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt index 33f10a94c381..8ee7c7972ac7 100644 --- a/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt +++ b/Documentation/devicetree/bindings/media/i2c/nokia,smia.txt @@ -29,6 +29,9 @@ Optional properties - reset-gpios: XSHUTDOWN GPIO - flash-leds: See ../video-interfaces.txt - lens-focus: See ../video-interfaces.txt +- rotation: Integer property; valid values are 0 (sensor mounted upright) + and 180 (sensor mounted upside down). See + ../video-interfaces.txt . Endpoint node mandatory properties diff --git a/Documentation/devicetree/bindings/media/i2c/ov2680.txt b/Documentation/devicetree/bindings/media/i2c/ov2680.txt new file mode 100644 index 000000000000..11e925ed9dad --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ov2680.txt @@ -0,0 +1,46 @@ +* Omnivision OV2680 MIPI CSI-2 sensor + +Required Properties: +- compatible: should be "ovti,ov2680". +- clocks: reference to the xvclk input clock. +- clock-names: should be "xvclk". +- DOVDD-supply: Digital I/O voltage supply. +- DVDD-supply: Digital core voltage supply. +- AVDD-supply: Analog voltage supply. + +Optional Properties: +- reset-gpios: reference to the GPIO connected to the powerdown/reset pin, + if any. This is an active low signal to the OV2680. + +The device node must contain one 'port' child node for its digital output +video port, and this port must have a single endpoint in accordance with + the video interface bindings defined in +Documentation/devicetree/bindings/media/video-interfaces.txt. + +Endpoint node required properties for CSI-2 connection are: +- remote-endpoint: a phandle to the bus receiver's endpoint node. +- clock-lanes: should be set to <0> (clock lane on hardware lane 0). +- data-lanes: should be set to <1> (one CSI-2 lane supported). + +Example: + +&i2c2 { + ov2680: camera-sensor@36 { + compatible = "ovti,ov2680"; + reg = <0x36>; + clocks = <&osc>; + clock-names = "xvclk"; + reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + DOVDD-supply = <&sw2_reg>; + DVDD-supply = <&sw2_reg>; + AVDD-supply = <®_peri_3p15v>; + + port { + ov2680_to_mipi: endpoint { + remote-endpoint = <&mipi_from_sensor>; + clock-lanes = <0>; + data-lanes = <1>; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt b/Documentation/devicetree/bindings/media/i2c/ov5640.txt index 8e36da0d8406..c97c2f2da12d 100644 --- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt +++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt @@ -13,6 +13,10 @@ Optional Properties: This is an active low signal to the OV5640. - powerdown-gpios: reference to the GPIO connected to the powerdown pin, if any. This is an active high signal to the OV5640. +- rotation: as defined in + Documentation/devicetree/bindings/media/video-interfaces.txt, + valid values are 0 (sensor mounted upright) and 180 (sensor + mounted upside down). The device node must contain one 'port' child node for its digital output video port, in accordance with the video interface bindings defined in @@ -51,6 +55,7 @@ Examples: DVDD-supply = <&vgen2_reg>; /* 1.5v */ powerdown-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; + rotation = <180>; port { /* MIPI CSI-2 bus endpoint */ diff --git a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt index 470237ed6fe5..7302e949e662 100644 --- a/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt +++ b/Documentation/devicetree/bindings/media/nvidia,tegra-vde.txt @@ -27,9 +27,15 @@ Required properties: - sxe - clocks : Must include the following entries: - vde -- resets : Must include the following entries: +- resets : Must contain an entry for each entry in reset-names. +- reset-names : Should include the following entries: - vde +Optional properties: +- resets : Must contain an entry for each entry in reset-names. +- reset-names : Must include the following entries: + - mc + Example: video-codec@6001a000 { @@ -51,5 +57,6 @@ video-codec@6001a000 { <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>; /* SXE interrupt */ interrupt-names = "sync-token", "bsev", "sxe"; clocks = <&tegra_car TEGRA20_CLK_VDE>; - resets = <&tegra_car 61>; + reset-names = "vde", "mc"; + resets = <&tegra_car 61>, <&mc TEGRA20_MC_RESET_VDE>; }; diff --git a/Documentation/devicetree/bindings/media/qcom,camss.txt b/Documentation/devicetree/bindings/media/qcom,camss.txt index cadecebc73f7..09eb6ed99114 100644 --- a/Documentation/devicetree/bindings/media/qcom,camss.txt +++ b/Documentation/devicetree/bindings/media/qcom,camss.txt @@ -5,8 +5,9 @@ Qualcomm Camera Subsystem - compatible: Usage: required Value type: <stringlist> - Definition: Should contain: + Definition: Should contain one of: - "qcom,msm8916-camss" + - "qcom,msm8996-camss" - reg: Usage: required Value type: <prop-encoded-array> @@ -19,11 +20,16 @@ Qualcomm Camera Subsystem - "csiphy0_clk_mux" - "csiphy1" - "csiphy1_clk_mux" + - "csiphy2" (8996 only) + - "csiphy2_clk_mux" (8996 only) - "csid0" - "csid1" + - "csid2" (8996 only) + - "csid3" (8996 only) - "ispif" - "csi_clk_mux" - "vfe0" + - "vfe1" (8996 only) - interrupts: Usage: required Value type: <prop-encoded-array> @@ -34,10 +40,14 @@ Qualcomm Camera Subsystem Definition: Should contain the following entries: - "csiphy0" - "csiphy1" + - "csiphy2" (8996 only) - "csid0" - "csid1" + - "csid2" (8996 only) + - "csid3" (8996 only) - "ispif" - "vfe0" + - "vfe1" (8996 only) - power-domains: Usage: required Value type: <prop-encoded-array> @@ -53,25 +63,42 @@ Qualcomm Camera Subsystem Usage: required Value type: <stringlist> Definition: Should contain the following entries: - - "camss_top_ahb" - - "ispif_ahb" - - "csiphy0_timer" - - "csiphy1_timer" - - "csi0_ahb" - - "csi0" - - "csi0_phy" - - "csi0_pix" - - "csi0_rdi" - - "csi1_ahb" - - "csi1" - - "csi1_phy" - - "csi1_pix" - - "csi1_rdi" - - "camss_ahb" - - "camss_vfe_vfe" - - "camss_csi_vfe" - - "iface" - - "bus" + - "top_ahb" + - "ispif_ahb" + - "csiphy0_timer" + - "csiphy1_timer" + - "csiphy2_timer" (8996 only) + - "csi0_ahb" + - "csi0" + - "csi0_phy" + - "csi0_pix" + - "csi0_rdi" + - "csi1_ahb" + - "csi1" + - "csi1_phy" + - "csi1_pix" + - "csi1_rdi" + - "csi2_ahb" (8996 only) + - "csi2" (8996 only) + - "csi2_phy" (8996 only) + - "csi2_pix" (8996 only) + - "csi2_rdi" (8996 only) + - "csi3_ahb" (8996 only) + - "csi3" (8996 only) + - "csi3_phy" (8996 only) + - "csi3_pix" (8996 only) + - "csi3_rdi" (8996 only) + - "ahb" + - "vfe0" + - "csi_vfe0" + - "vfe0_ahb", (8996 only) + - "vfe0_stream", (8996 only) + - "vfe1", (8996 only) + - "csi_vfe1", (8996 only) + - "vfe1_ahb", (8996 only) + - "vfe1_stream", (8996 only) + - "vfe_ahb" + - "vfe_axi" - vdda-supply: Usage: required Value type: <phandle> @@ -90,22 +117,27 @@ Qualcomm Camera Subsystem - reg: Usage: required Value type: <u32> - Definition: Selects CSI2 PHY interface - PHY0 or PHY1. + Definition: Selects CSI2 PHY interface - PHY0, PHY1 + or PHY2 (8996 only) Endpoint node properties: - clock-lanes: Usage: required Value type: <u32> - Definition: The physical clock lane index. The value - must always be <1> as the physical clock - lane is lane 1. + Definition: The physical clock lane index. On 8916 + the value must always be <1> as the physical + clock lane is lane 1. On 8996 the value must + always be <7> as the hardware supports D-PHY + and C-PHY, indexes are in a common set and + D-PHY physical clock lane is labeled as 7. - data-lanes: Usage: required Value type: <prop-encoded-array> - Definition: An array of physical data lanes indexes. - Position of an entry determines the logical - lane number, while the value of an entry - indicates physical lane index. Lane swapping - is supported. + Definition: An array of physical data lanes indexes. + Position of an entry determines the logical + lane number, while the value of an entry + indicates physical lane index. Lane swapping + is supported. Physical lane indexes for + 8916: 0, 2, 3, 4; for 8996: 0, 1, 2, 3. * An Example @@ -161,25 +193,25 @@ Qualcomm Camera Subsystem <&gcc GCC_CAMSS_CSI_VFE0_CLK>, <&gcc GCC_CAMSS_VFE_AHB_CLK>, <&gcc GCC_CAMSS_VFE_AXI_CLK>; - clock-names = "camss_top_ahb", - "ispif_ahb", - "csiphy0_timer", - "csiphy1_timer", - "csi0_ahb", - "csi0", - "csi0_phy", - "csi0_pix", - "csi0_rdi", - "csi1_ahb", - "csi1", - "csi1_phy", - "csi1_pix", - "csi1_rdi", - "camss_ahb", - "camss_vfe_vfe", - "camss_csi_vfe", - "iface", - "bus"; + clock-names = "top_ahb", + "ispif_ahb", + "csiphy0_timer", + "csiphy1_timer", + "csi0_ahb", + "csi0", + "csi0_phy", + "csi0_pix", + "csi0_rdi", + "csi1_ahb", + "csi1", + "csi1_phy", + "csi1_pix", + "csi1_rdi", + "ahb", + "vfe0", + "csi_vfe0", + "vfe_ahb", + "vfe_axi"; vdda-supply = <&pm8916_l2>; iommus = <&apps_iommu 3>; ports { diff --git a/Documentation/devicetree/bindings/media/qcom,venus.txt b/Documentation/devicetree/bindings/media/qcom,venus.txt index 2693449daf73..00d0d1bf7647 100644 --- a/Documentation/devicetree/bindings/media/qcom,venus.txt +++ b/Documentation/devicetree/bindings/media/qcom,venus.txt @@ -6,6 +6,7 @@ Definition: Value should contain one of: - "qcom,msm8916-venus" - "qcom,msm8996-venus" + - "qcom,sdm845-venus" - reg: Usage: required Value type: <prop-encoded-array> diff --git a/Documentation/devicetree/bindings/media/rcar_vin.txt b/Documentation/devicetree/bindings/media/rcar_vin.txt index a19517e1c669..2f420050d57f 100644 --- a/Documentation/devicetree/bindings/media/rcar_vin.txt +++ b/Documentation/devicetree/bindings/media/rcar_vin.txt @@ -21,7 +21,9 @@ on Gen3 platforms to a CSI-2 receiver. - "renesas,vin-r8a7794" for the R8A7794 device - "renesas,vin-r8a7795" for the R8A7795 device - "renesas,vin-r8a7796" for the R8A7796 device + - "renesas,vin-r8a77965" for the R8A77965 device - "renesas,vin-r8a77970" for the R8A77970 device + - "renesas,vin-r8a77995" for the R8A77995 device - "renesas,rcar-gen2-vin" for a generic R-Car Gen2 or RZ/G1 compatible device. @@ -37,30 +39,51 @@ Additionally, an alias named vinX will need to be created to specify which video input device this is. The per-board settings Gen2 platforms: - - port sub-node describing a single endpoint connected to the vin - as described in video-interfaces.txt[1]. Only the first one will - be considered as each vin interface has one input port. + +- port - sub-node describing a single endpoint connected to the VIN + from external SoC pins as described in video-interfaces.txt[1]. + Only the first one will be considered as each vin interface has one + input port. + + - Optional properties for endpoint nodes: + - hsync-active: see [1] for description. Default is active high. + - vsync-active: see [1] for description. Default is active high. + If both HSYNC and VSYNC polarities are not specified, embedded + synchronization is selected. + - field-active-even: see [1] for description. Default is active high. + - bus-width: see [1] for description. The selected bus width depends on + the SoC type and selected input image format. + Valid values are: 8, 10, 12, 16, 24 and 32. + - data-shift: see [1] for description. Valid values are 0 and 8. + - data-enable-active: polarity of CLKENB signal, see [1] for + description. Default is active high. The per-board settings Gen3 platforms: Gen3 platforms can support both a single connected parallel input source -from external SoC pins (port0) and/or multiple parallel input sources -from local SoC CSI-2 receivers (port1) depending on SoC. +from external SoC pins (port@0) and/or multiple parallel input sources +from local SoC CSI-2 receivers (port@1) depending on SoC. - renesas,id - ID number of the VIN, VINx in the documentation. - ports - - port 0 - sub-node describing a single endpoint connected to the VIN - from external SoC pins described in video-interfaces.txt[1]. - Describing more then one endpoint in port 0 is invalid. Only VIN - instances that are connected to external pins should have port 0. - - port 1 - sub-nodes describing one or more endpoints connected to + - port@0 - sub-node describing a single endpoint connected to the VIN + from external SoC pins as described in video-interfaces.txt[1]. + Describing more than one endpoint in port@0 is invalid. Only VIN + instances that are connected to external pins should have port@0. + + Endpoint nodes of port@0 support the optional properties listed in + the Gen2 per-board settings description. + + - port@1 - sub-nodes describing one or more endpoints connected to the VIN from local SoC CSI-2 receivers. The endpoint numbers must use the following schema. - - Endpoint 0 - sub-node describing the endpoint connected to CSI20 - - Endpoint 1 - sub-node describing the endpoint connected to CSI21 - - Endpoint 2 - sub-node describing the endpoint connected to CSI40 - - Endpoint 3 - sub-node describing the endpoint connected to CSI41 + - endpoint@0 - sub-node describing the endpoint connected to CSI20 + - endpoint@1 - sub-node describing the endpoint connected to CSI21 + - endpoint@2 - sub-node describing the endpoint connected to CSI40 + - endpoint@3 - sub-node describing the endpoint connected to CSI41 + + Endpoint nodes of port@1 do not support any optional endpoint property. Device node example for Gen2 platforms -------------------------------------- @@ -107,9 +130,6 @@ Board setup example for Gen2 platforms (vin1 composite video input) status = "okay"; port { - #address-cells = <1>; - #size-cells = <0>; - vin1ep0: endpoint { remote-endpoint = <&adv7180>; bus-width = <8>; diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index 258b8dfddf48..baf9d9756b3c 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -85,6 +85,10 @@ Optional properties - lens-focus: A phandle to the node of the focus lens controller. +- rotation: The device, typically an image sensor, is not mounted upright, + but a number of degrees counter clockwise. Typical values are 0 and 180 + (upside down). + Optional endpoint properties ---------------------------- @@ -109,6 +113,8 @@ Optional endpoint properties Note, that if HSYNC and VSYNC polarities are not specified, embedded synchronization may be required, where supported. - data-active: similar to HSYNC and VSYNC, specifies data line polarity. +- data-enable-active: similar to HSYNC and VSYNC, specifies the data enable + signal polarity. - field-even-active: field signal level during the even field data transmission. - pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock signal. diff --git a/Documentation/media/audio.h.rst.exceptions b/Documentation/media/audio.h.rst.exceptions index f40f3cbfe4c9..940458774cf6 100644 --- a/Documentation/media/audio.h.rst.exceptions +++ b/Documentation/media/audio.h.rst.exceptions @@ -1,9 +1,6 @@ # Ignore header name ignore define _DVBAUDIO_H_ -# Typedef pointing to structs -replace typedef audio_karaoke_t :c:type:`audio_karaoke` - # Undocumented audio caps, as this is a deprecated API anyway ignore define AUDIO_CAP_DTS ignore define AUDIO_CAP_LPCM diff --git a/Documentation/media/media.h.rst.exceptions b/Documentation/media/media.h.rst.exceptions index 83d7f7c722fb..684fe9c86dee 100644 --- a/Documentation/media/media.h.rst.exceptions +++ b/Documentation/media/media.h.rst.exceptions @@ -6,10 +6,10 @@ ignore define MEDIA_API_VERSION ignore define MEDIA_ENT_F_BASE ignore define MEDIA_ENT_F_OLD_BASE ignore define MEDIA_ENT_F_OLD_SUBDEV_BASE +ignore define MEDIA_ENT_F_DTV_DECODER ignore define MEDIA_INTF_T_DVB_BASE ignore define MEDIA_INTF_T_V4L_BASE ignore define MEDIA_INTF_T_ALSA_BASE - #ignore legacy entity type macros ignore define MEDIA_ENT_TYPE_SHIFT ignore define MEDIA_ENT_TYPE_MASK diff --git a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst index b6fd86424fbb..8d5633e6ae04 100644 --- a/Documentation/media/uapi/cec/cec-ioc-dqevent.rst +++ b/Documentation/media/uapi/cec/cec-ioc-dqevent.rst @@ -179,6 +179,24 @@ it is guaranteed that the state did change in between the two events. capability set. When open() is called, the HPD pin can be read and if the HPD is high, then an initial event will be generated for that filehandle. + * .. _`CEC-EVENT-PIN-5V-LOW`: + + - ``CEC_EVENT_PIN_5V_LOW`` + - 6 + - Generated if the 5V pin goes from a high voltage to a low voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the 5V pin can be read and + if the 5V is low, then an initial event will be generated for that + filehandle. + * .. _`CEC-EVENT-PIN-5V-HIGH`: + + - ``CEC_EVENT_PIN_5V_HIGH`` + - 7 + - Generated if the 5V pin goes from a low voltage to a high voltage. + Only applies to adapters that have the ``CEC_CAP_MONITOR_PIN`` + capability set. When open() is called, the 5V pin can be read and + if the 5V is high, then an initial event will be generated for that + filehandle. .. tabularcolumns:: |p{6.0cm}|p{0.6cm}|p{10.9cm}| diff --git a/Documentation/media/uapi/dvb/audio-get-pts.rst b/Documentation/media/uapi/dvb/audio-get-pts.rst deleted file mode 100644 index 2d1396b003de..000000000000 --- a/Documentation/media/uapi/dvb/audio-get-pts.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _AUDIO_GET_PTS: - -============= -AUDIO_GET_PTS -============= - -Name ----- - -AUDIO_GET_PTS - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int ioctl(int fd, AUDIO_GET_PTS, __u64 *pts) - :name: AUDIO_GET_PTS - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - __u64 \*pts - - - Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / - ISO/IEC 13818-1. - - The PTS should belong to the currently played frame if possible, - but may also be a value close to it like the PTS of the last - decoded frame or the last PTS extracted by the PES parser. - - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. If you need this -functionality, then please contact the linux-media mailing list -(`https://linuxtv.org/lists.php <https://linuxtv.org/lists.php>`__). - -This ioctl call asks the Audio Device to return the current PTS -timestamp. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. diff --git a/Documentation/media/uapi/dvb/audio-set-attributes.rst b/Documentation/media/uapi/dvb/audio-set-attributes.rst deleted file mode 100644 index f0c6153ca80f..000000000000 --- a/Documentation/media/uapi/dvb/audio-set-attributes.rst +++ /dev/null @@ -1,67 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _AUDIO_SET_ATTRIBUTES: - -==================== -AUDIO_SET_ATTRIBUTES -==================== - -Name ----- - -AUDIO_SET_ATTRIBUTES - -.. attention:: This ioctl is deprecated - - -Synopsis --------- - -.. c:function:: int ioctl(fd, AUDIO_SET_ATTRIBUTES, struct audio_attributes *attr ) - :name: AUDIO_SET_ATTRIBUTES - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_attributes_t attr - - - audio attributes according to section ?? - - -Description ------------ - -This ioctl is intended for DVD playback and allows you to set certain -information about the audio stream. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - attr is not a valid or supported attribute setting. diff --git a/Documentation/media/uapi/dvb/audio-set-ext-id.rst b/Documentation/media/uapi/dvb/audio-set-ext-id.rst deleted file mode 100644 index 8503c47f26bd..000000000000 --- a/Documentation/media/uapi/dvb/audio-set-ext-id.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _AUDIO_SET_EXT_ID: - -================ -AUDIO_SET_EXT_ID -================ - -Name ----- - -AUDIO_SET_EXT_ID - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int ioctl(fd, AUDIO_SET_EXT_ID, int id) - :name: AUDIO_SET_EXT_ID - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - int id - - - audio sub_stream_id - - -Description ------------ - -This ioctl can be used to set the extension id for MPEG streams in DVD -playback. Only the first 3 bits are recognized. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - id is not a valid id. diff --git a/Documentation/media/uapi/dvb/audio-set-karaoke.rst b/Documentation/media/uapi/dvb/audio-set-karaoke.rst deleted file mode 100644 index c759952d88aa..000000000000 --- a/Documentation/media/uapi/dvb/audio-set-karaoke.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _AUDIO_SET_KARAOKE: - -================= -AUDIO_SET_KARAOKE -================= - -Name ----- - -AUDIO_SET_KARAOKE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int ioctl(fd, AUDIO_SET_KARAOKE, struct audio_karaoke *karaoke) - :name: AUDIO_SET_KARAOKE - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_karaoke_t \*karaoke - - - karaoke settings according to section ??. - - -Description ------------ - -This ioctl allows one to set the mixer settings for a karaoke DVD. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - karaoke is not a valid or supported karaoke setting. diff --git a/Documentation/media/uapi/dvb/audio_data_types.rst b/Documentation/media/uapi/dvb/audio_data_types.rst index 6b93359d64f7..5bffa2c98a24 100644 --- a/Documentation/media/uapi/dvb/audio_data_types.rst +++ b/Documentation/media/uapi/dvb/audio_data_types.rst @@ -114,40 +114,3 @@ following bits set according to the hardwares capabilities. #define AUDIO_CAP_OGG 64 #define AUDIO_CAP_SDDS 128 #define AUDIO_CAP_AC3 256 - -.. c:type:: audio_karaoke - -The ioctl AUDIO_SET_KARAOKE uses the following format: - - -.. code-block:: c - - typedef - struct audio_karaoke { - int vocal1; - int vocal2; - int melody; - } audio_karaoke_t; - -If Vocal1 or Vocal2 are non-zero, they get mixed into left and right t -at 70% each. If both, Vocal1 and Vocal2 are non-zero, Vocal1 gets mixed -into the left channel and Vocal2 into the right channel at 100% each. Ff -Melody is non-zero, the melody channel gets mixed into left and right. - - -.. c:type:: audio_attributes - -The following attributes can be set by a call to AUDIO_SET_ATTRIBUTES: - - -.. code-block:: c - - typedef uint16_t audio_attributes_t; - /* bits: descr. */ - /* 15-13 audio coding mode (0=ac3, 2=mpeg1, 3=mpeg2ext, 4=LPCM, 6=DTS, */ - /* 12 multichannel extension */ - /* 11-10 audio type (0=not spec, 1=language included) */ - /* 9- 8 audio application mode (0=not spec, 1=karaoke, 2=surround) */ - /* 7- 6 Quantization / DRC (mpeg audio: 1=DRC exists)(lpcm: 0=16bit, */ - /* 5- 4 Sample frequency fs (0=48kHz, 1=96kHz) */ - /* 2- 0 number of audio channels (n+1 channels) */ diff --git a/Documentation/media/uapi/dvb/audio_function_calls.rst b/Documentation/media/uapi/dvb/audio_function_calls.rst index 0bb56f0cfed4..7dba16285dab 100644 --- a/Documentation/media/uapi/dvb/audio_function_calls.rst +++ b/Documentation/media/uapi/dvb/audio_function_calls.rst @@ -22,13 +22,9 @@ Audio Function Calls audio-set-bypass-mode audio-channel-select audio-bilingual-channel-select - audio-get-pts audio-get-status audio-get-capabilities audio-clear-buffer audio-set-id audio-set-mixer audio-set-streamtype - audio-set-ext-id - audio-set-attributes - audio-set-karaoke diff --git a/Documentation/media/uapi/dvb/video-get-frame-rate.rst b/Documentation/media/uapi/dvb/video-get-frame-rate.rst deleted file mode 100644 index 400042a854cf..000000000000 --- a/Documentation/media/uapi/dvb/video-get-frame-rate.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_GET_FRAME_RATE: - -==================== -VIDEO_GET_FRAME_RATE -==================== - -Name ----- - -VIDEO_GET_FRAME_RATE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(int fd, VIDEO_GET_FRAME_RATE, unsigned int *rate) - :name: VIDEO_GET_FRAME_RATE - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_FRAME_RATE for this command. - - - .. row 3 - - - unsigned int \*rate - - - Returns the framerate in number of frames per 1000 seconds. - - -Description ------------ - -This ioctl call asks the Video Device to return the current framerate. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. diff --git a/Documentation/media/uapi/dvb/video-get-navi.rst b/Documentation/media/uapi/dvb/video-get-navi.rst deleted file mode 100644 index 114a9ac48b9e..000000000000 --- a/Documentation/media/uapi/dvb/video-get-navi.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_GET_NAVI: - -============== -VIDEO_GET_NAVI -============== - -Name ----- - -VIDEO_GET_NAVI - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_GET_NAVI , struct video_navi_pack *navipack) - :name: VIDEO_GET_NAVI - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_NAVI for this command. - - - .. row 3 - - - video_navi_pack_t \*navipack - - - PCI or DSI pack (private stream 2) according to section ??. - - -Description ------------ - -This ioctl returns navigational information from the DVD stream. This is -especially needed if an encoded stream has to be decoded by the -hardware. - -.. c:type:: video_navi_pack - -.. code-block::c - - typedef struct video_navi_pack { - int length; /* 0 ... 1024 */ - __u8 data[1024]; - } video_navi_pack_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EFAULT`` - - - driver is not able to return navigational information diff --git a/Documentation/media/uapi/dvb/video-set-attributes.rst b/Documentation/media/uapi/dvb/video-set-attributes.rst deleted file mode 100644 index b2f11a6746e9..000000000000 --- a/Documentation/media/uapi/dvb/video-set-attributes.rst +++ /dev/null @@ -1,93 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_ATTRIBUTES: - -==================== -VIDEO_SET_ATTRIBUTES -==================== - -Name ----- - -VIDEO_SET_ATTRIBUTES - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_SET_ATTRIBUTE ,video_attributes_t vattr) - :name: VIDEO_SET_ATTRIBUTE - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_ATTRIBUTE for this command. - - - .. row 3 - - - video_attributes_t vattr - - - video attributes according to section ??. - - -Description ------------ - -This ioctl is intended for DVD playback and allows you to set certain -information about the stream. Some hardware may not need this -information, but the call also tells the hardware to prepare for DVD -playback. - -.. c:type:: video_attributes_t - -.. code-block::c - - typedef __u16 video_attributes_t; - /* bits: descr. */ - /* 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) */ - /* 13-12 TV system (0=525/60, 1=625/50) */ - /* 11-10 Aspect ratio (0=4:3, 3=16:9) */ - /* 9- 8 permitted display mode on 4:3 monitor (0=both, 1=only pan-sca */ - /* 7 line 21-1 data present in GOP (1=yes, 0=no) */ - /* 6 line 21-2 data present in GOP (1=yes, 0=no) */ - /* 5- 3 source resolution (0=720x480/576, 1=704x480/576, 2=352x480/57 */ - /* 2 source letterboxed (1=yes, 0=no) */ - /* 0 film/camera mode (0=camera, 1=film (625/50 only)) */ - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - input is not a valid attribute setting. diff --git a/Documentation/media/uapi/dvb/video-set-highlight.rst b/Documentation/media/uapi/dvb/video-set-highlight.rst deleted file mode 100644 index 90aeafd923b7..000000000000 --- a/Documentation/media/uapi/dvb/video-set-highlight.rst +++ /dev/null @@ -1,86 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_HIGHLIGHT: - -=================== -VIDEO_SET_HIGHLIGHT -=================== - -Name ----- - -VIDEO_SET_HIGHLIGHT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_SET_HIGHLIGHT, struct video_highlight *vhilite) - :name: VIDEO_SET_HIGHLIGHT - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_HIGHLIGHT for this command. - - - .. row 3 - - - video_highlight_t \*vhilite - - - SPU Highlight information according to section ??. - - -Description ------------ - -This ioctl sets the SPU highlight information for the menu access of a -DVD. - -.. c:type:: video_highlight - -.. code-block:: c - - typedef - struct video_highlight { - int active; /* 1=show highlight, 0=hide highlight */ - __u8 contrast1; /* 7- 4 Pattern pixel contrast */ - /* 3- 0 Background pixel contrast */ - __u8 contrast2; /* 7- 4 Emphasis pixel-2 contrast */ - /* 3- 0 Emphasis pixel-1 contrast */ - __u8 color1; /* 7- 4 Pattern pixel color */ - /* 3- 0 Background pixel color */ - __u8 color2; /* 7- 4 Emphasis pixel-2 color */ - /* 3- 0 Emphasis pixel-1 color */ - __u32 ypos; /* 23-22 auto action mode */ - /* 21-12 start y */ - /* 9- 0 end y */ - __u32 xpos; /* 23-22 button color number */ - /* 21-12 start x */ - /* 9- 0 end x */ - } video_highlight_t; - - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. diff --git a/Documentation/media/uapi/dvb/video-set-id.rst b/Documentation/media/uapi/dvb/video-set-id.rst deleted file mode 100644 index 18f66875ae3f..000000000000 --- a/Documentation/media/uapi/dvb/video-set-id.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_ID: - -============ -VIDEO_SET_ID -============ - -Name ----- - -VIDEO_SET_ID - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(int fd, VIDEO_SET_ID, int id) - :name: VIDEO_SET_ID - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_ID for this command. - - - .. row 3 - - - int id - - - video sub-stream id - - -Description ------------ - -This ioctl selects which sub-stream is to be decoded if a program or -system stream is sent to the video device. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - Invalid sub-stream id. diff --git a/Documentation/media/uapi/dvb/video-set-spu-palette.rst b/Documentation/media/uapi/dvb/video-set-spu-palette.rst deleted file mode 100644 index 51a1913d21d2..000000000000 --- a/Documentation/media/uapi/dvb/video-set-spu-palette.rst +++ /dev/null @@ -1,82 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_SPU_PALETTE: - -===================== -VIDEO_SET_SPU_PALETTE -===================== - -Name ----- - -VIDEO_SET_SPU_PALETTE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_SET_SPU_PALETTE, struct video_spu_palette *palette ) - :name: VIDEO_SET_SPU_PALETTE - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_SPU_PALETTE for this command. - - - .. row 3 - - - video_spu_palette_t \*palette - - - SPU palette according to section ??. - - -Description ------------ - -This ioctl sets the SPU color palette. - -.. c:type:: video_spu_palette - -.. code-block::c - - typedef struct video_spu_palette { /* SPU Palette information */ - int length; - __u8 __user *palette; - } video_spu_palette_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - input is not a valid palette or driver doesn’t handle SPU. diff --git a/Documentation/media/uapi/dvb/video-set-spu.rst b/Documentation/media/uapi/dvb/video-set-spu.rst deleted file mode 100644 index 739e5e7bd133..000000000000 --- a/Documentation/media/uapi/dvb/video-set-spu.rst +++ /dev/null @@ -1,85 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_SPU: - -============= -VIDEO_SET_SPU -============= - -Name ----- - -VIDEO_SET_SPU - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_SET_SPU , struct video_spu *spu) - :name: VIDEO_SET_SPU - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_SPU for this command. - - - .. row 3 - - - video_spu_t \*spu - - - SPU decoding (de)activation and subid setting according to section - ??. - - -Description ------------ - -This ioctl activates or deactivates SPU decoding in a DVD input stream. -It can only be used, if the driver is able to handle a DVD stream. - -.. c:type:: struct video_spu - -.. code-block:: c - - typedef struct video_spu { - int active; - int stream_id; - } video_spu_t; - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - input is not a valid spu setting or driver cannot handle SPU. diff --git a/Documentation/media/uapi/dvb/video-set-system.rst b/Documentation/media/uapi/dvb/video-set-system.rst deleted file mode 100644 index e39cbe080ef7..000000000000 --- a/Documentation/media/uapi/dvb/video-set-system.rst +++ /dev/null @@ -1,77 +0,0 @@ -.. -*- coding: utf-8; mode: rst -*- - -.. _VIDEO_SET_SYSTEM: - -================ -VIDEO_SET_SYSTEM -================ - -Name ----- - -VIDEO_SET_SYSTEM - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int ioctl(fd, VIDEO_SET_SYSTEM , video_system_t system) - :name: VIDEO_SET_SYSTEM - - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_FORMAT for this command. - - - .. row 3 - - - video_system_t system - - - video system of TV output. - - -Description ------------ - -This ioctl sets the television output format. The format (see section -??) may vary from the color format of the displayed MPEG stream. If the -hardware is not able to display the requested format the call will -return an error. - - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes <gen-errors>` chapter. - - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - .. row 1 - - - ``EINVAL`` - - - system is not a valid or supported video system. diff --git a/Documentation/media/uapi/dvb/video_function_calls.rst b/Documentation/media/uapi/dvb/video_function_calls.rst index 68588ac7fecb..3f4f6c9ffad7 100644 --- a/Documentation/media/uapi/dvb/video_function_calls.rst +++ b/Documentation/media/uapi/dvb/video_function_calls.rst @@ -21,7 +21,6 @@ Video Function Calls video-get-status video-get-frame-count video-get-pts - video-get-frame-rate video-get-event video-command video-try-command @@ -31,13 +30,7 @@ Video Function Calls video-fast-forward video-slowmotion video-get-capabilities - video-set-id video-clear-buffer video-set-streamtype video-set-format - video-set-system - video-set-highlight - video-set-spu - video-set-spu-palette - video-get-navi video-set-attributes diff --git a/Documentation/media/uapi/dvb/video_types.rst b/Documentation/media/uapi/dvb/video_types.rst index 640a21de6b8a..a0942171596c 100644 --- a/Documentation/media/uapi/dvb/video_types.rst +++ b/Documentation/media/uapi/dvb/video_types.rst @@ -246,134 +246,3 @@ following bits set according to the hardwares capabilities. #define VIDEO_CAP_SPU 16 #define VIDEO_CAP_NAVI 32 #define VIDEO_CAP_CSS 64 - - -.. _video-system: - -video_system_t -============== - -A call to VIDEO_SET_SYSTEM sets the desired video system for TV -output. The following system types can be set: - - -.. code-block:: c - - typedef enum { - VIDEO_SYSTEM_PAL, - VIDEO_SYSTEM_NTSC, - VIDEO_SYSTEM_PALN, - VIDEO_SYSTEM_PALNc, - VIDEO_SYSTEM_PALM, - VIDEO_SYSTEM_NTSC60, - VIDEO_SYSTEM_PAL60, - VIDEO_SYSTEM_PALM60 - } video_system_t; - - -.. c:type:: video_highlight - -struct video_highlight -====================== - -Calling the ioctl VIDEO_SET_HIGHLIGHTS posts the SPU highlight -information. The call expects the following format for that information: - - -.. code-block:: c - - typedef - struct video_highlight { - boolean active; /* 1=show highlight, 0=hide highlight */ - uint8_t contrast1; /* 7- 4 Pattern pixel contrast */ - /* 3- 0 Background pixel contrast */ - uint8_t contrast2; /* 7- 4 Emphasis pixel-2 contrast */ - /* 3- 0 Emphasis pixel-1 contrast */ - uint8_t color1; /* 7- 4 Pattern pixel color */ - /* 3- 0 Background pixel color */ - uint8_t color2; /* 7- 4 Emphasis pixel-2 color */ - /* 3- 0 Emphasis pixel-1 color */ - uint32_t ypos; /* 23-22 auto action mode */ - /* 21-12 start y */ - /* 9- 0 end y */ - uint32_t xpos; /* 23-22 button color number */ - /* 21-12 start x */ - /* 9- 0 end x */ - } video_highlight_t; - - -.. c:type:: video_spu - -struct video_spu -================ - -Calling VIDEO_SET_SPU deactivates or activates SPU decoding, according -to the following format: - - -.. code-block:: c - - typedef - struct video_spu { - boolean active; - int stream_id; - } video_spu_t; - - -.. c:type:: video_spu_palette - -struct video_spu_palette -======================== - -The following structure is used to set the SPU palette by calling -VIDEO_SPU_PALETTE: - - -.. code-block:: c - - typedef - struct video_spu_palette { - int length; - uint8_t *palette; - } video_spu_palette_t; - - -.. c:type:: video_navi_pack - -struct video_navi_pack -====================== - -In order to get the navigational data the following structure has to be -passed to the ioctl VIDEO_GET_NAVI: - - -.. code-block:: c - - typedef - struct video_navi_pack { - int length; /* 0 ... 1024 */ - uint8_t data[1024]; - } video_navi_pack_t; - - -.. _video-attributes-t: - -video_attributes_t -================== - -The following attributes can be set by a call to VIDEO_SET_ATTRIBUTES: - - -.. code-block:: c - - typedef uint16_t video_attributes_t; - /* bits: descr. */ - /* 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) */ - /* 13-12 TV system (0=525/60, 1=625/50) */ - /* 11-10 Aspect ratio (0=4:3, 3=16:9) */ - /* 9- 8 permitted display mode on 4:3 monitor (0=both, 1=only pan-sca */ - /* 7 line 21-1 data present in GOP (1=yes, 0=no) */ - /* 6 line 21-2 data present in GOP (1=yes, 0=no) */ - /* 5- 3 source resolution (0=720x480/576, 1=704x480/576, 2=352x480/57 */ - /* 2 source letterboxed (1=yes, 0=no) */ - /* 0 film/camera mode (0=camera, 1=film (625/50 only)) */ diff --git a/Documentation/media/uapi/mediactl/media-ioc-device-info.rst b/Documentation/media/uapi/mediactl/media-ioc-device-info.rst index f690f9afc470..649cb3d9e058 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-device-info.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-device-info.rst @@ -48,12 +48,8 @@ ioctl never fails. :widths: 1 1 2 - - .. row 1 - - - char - + * - char - ``driver``\ [16] - - Name of the driver implementing the media API as a NUL-terminated ASCII string. The driver version is stored in the ``driver_version`` field. @@ -62,66 +58,38 @@ ioctl never fails. the driver identity. It is also useful to work around known bugs, or to identify drivers in error reports. - - .. row 2 - - - char - + * - char - ``model``\ [32] - - Device model name as a NUL-terminated UTF-8 string. The device version is stored in the ``device_version`` field and is not be appended to the model name. - - .. row 3 - - - char - + * - char - ``serial``\ [40] - - Serial number as a NUL-terminated ASCII string. - - .. row 4 - - - char - + * - char - ``bus_info``\ [32] - - Location of the device in the system as a NUL-terminated ASCII string. This includes the bus type name (PCI, USB, ...) and a bus-specific identifier. - - .. row 5 - - - __u32 - + * - __u32 - ``media_version`` - - Media API version, formatted with the ``KERNEL_VERSION()`` macro. - - .. row 6 - - - __u32 - + * - __u32 - ``hw_revision`` - - Hardware device revision in a driver-specific format. - - .. row 7 - - - __u32 - + * - __u32 - ``driver_version`` - - Media device driver version, formatted with the ``KERNEL_VERSION()`` macro. Together with the ``driver`` field this identifies a particular driver. - - .. row 8 - - - __u32 - + * - __u32 - ``reserved``\ [31] - - Reserved for future extensions. Drivers and applications must set this array to zero. diff --git a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst index 582fda488810..fc2e39c070c9 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-enum-entities.rst @@ -58,142 +58,90 @@ id's until they get an error. :stub-columns: 0 :widths: 1 1 1 1 8 - - - .. row 1 - - - __u32 - + * - __u32 - ``id`` - - - - - Entity id, set by the application. When the id is or'ed with + - 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. - - - .. row 2 - - - char + the first entity with a larger ID. Do not expect that the ID will + always be the same for each instance of the device. In other words, + do not hardcode entity IDs in an application. + * - char - ``name``\ [32] - - - - - Entity name as an UTF-8 NULL-terminated string. - - - .. row 3 - - - __u32 + - 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. - - .. row 4 - - - __u32 - + * - __u32 - ``revision`` - - - - Entity revision. Always zero (obsolete) - - .. row 5 - - - __u32 - + * - __u32 - ``flags`` - - - - Entity flags, see :ref:`media-entity-flag` for details. - - .. row 6 - - - __u32 - + * - __u32 - ``group_id`` - - - - Entity group ID. Always zero (obsolete) - - .. row 7 - - - __u16 - + * - __u16 - ``pads`` - - - - Number of pads - - .. row 8 - - - __u16 - + * - __u16 - ``links`` - - - - Total number of outbound links. Inbound links are not counted in this field. - - .. row 9 - - - __u32 - + * - __u32 - ``reserved[4]`` - - - - Reserved for future extensions. Drivers and applications must set the array to zero. - - .. row 10 - - - union - - - .. row 11 + * - union - - + * - - struct - - ``dev`` - - - Valid for (sub-)devices that create a single device node. - - .. row 12 - - - + * - - - __u32 - - ``major`` - - Device node major number. - - .. row 13 - - - + * - - - __u32 - - ``minor`` - - Device node minor number. - - .. row 14 - - - + * - - __u8 - - ``raw``\ [184] - - - diff --git a/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst b/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst index 256168b3c3be..f158c134e9b0 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst @@ -62,35 +62,21 @@ returned during the enumeration process. :stub-columns: 0 :widths: 1 1 2 - - - .. row 1 - - - __u32 - + * - __u32 - ``entity`` - - Entity id, set by the application. - - .. row 2 - - - struct :c:type:`media_pad_desc` - + * - struct :c:type:`media_pad_desc` - \*\ ``pads`` - - Pointer to a pads array allocated by the application. Ignored if NULL. - - .. row 3 - - - struct :c:type:`media_link_desc` - + * - struct :c:type:`media_link_desc` - \*\ ``links`` - - Pointer to a links array allocated by the application. Ignored if NULL. - .. c:type:: media_pad_desc .. tabularcolumns:: |p{4.4cm}|p{4.4cm}|p{8.7cm}| @@ -100,37 +86,20 @@ returned during the enumeration process. :stub-columns: 0 :widths: 1 1 2 - - - .. row 1 - - - __u32 - + * - __u32 - ``entity`` - - ID of the entity this pad belongs to. - - .. row 2 - - - __u16 - + * - __u16 - ``index`` + - Pad index, starts at 0. - - 0-based pad index. - - - .. row 3 - - - __u32 - + * - __u32 - ``flags`` - - Pad flags, see :ref:`media-pad-flag` for more details. - - .. row 4 - - - __u32 - + * - __u32 - ``reserved[2]`` - - Reserved for future extensions. Drivers and applications must set the array to zero. @@ -145,37 +114,20 @@ returned during the enumeration process. :stub-columns: 0 :widths: 1 1 2 - - - .. row 1 - - - struct :c:type:`media_pad_desc` - + * - struct :c:type:`media_pad_desc` - ``source`` - - Pad at the origin of this link. - - .. row 2 - - - struct :c:type:`media_pad_desc` - + * - struct :c:type:`media_pad_desc` - ``sink`` - - Pad at the target of this link. - - .. row 3 - - - __u32 - + * - __u32 - ``flags`` - - Link flags, see :ref:`media-link-flag` for more details. - - .. row 4 - - - __u32 - + * - __u32 - ``reserved[4]`` - - Reserved for future extensions. Drivers and applications must set the array to zero. diff --git a/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst b/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst index c4055ddf070a..bac128c7eda9 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-g-topology.rst @@ -55,119 +55,66 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - - .. row 1 - - - __u64 - + * - __u64 - ``topology_version`` - - Version of the media graph topology. When the graph is created, this field starts with zero. Every time a graph element is added or removed, this field is incremented. - - .. row 2 - - - __u32 - + * - __u32 - ``num_entities`` - - Number of entities in the graph - - .. row 3 - - - __u32 - + * - __u32 - ``reserved1`` - - Applications and drivers shall set this to 0. - - .. row 4 - - - __u64 - + * - __u64 - ``ptr_entities`` - - A pointer to a memory area where the entities array will be stored, converted to a 64-bits integer. It can be zero. if zero, the ioctl won't store the entities. It will just update ``num_entities`` - - .. row 5 - - - __u32 - + * - __u32 - ``num_interfaces`` - - Number of interfaces in the graph - - .. row 6 - - - __u32 - + * - __u32 - ``reserved2`` - - Applications and drivers shall set this to 0. - - .. row 7 - - - __u64 - + * - __u64 - ``ptr_interfaces`` - - A pointer to a memory area where the interfaces array will be stored, converted to a 64-bits integer. It can be zero. if zero, the ioctl won't store the interfaces. It will just update ``num_interfaces`` - - .. row 8 - - - __u32 - + * - __u32 - ``num_pads`` - - Total number of pads in the graph - - .. row 9 - - - __u32 - + * - __u32 - ``reserved3`` - - Applications and drivers shall set this to 0. - - .. row 10 - - - __u64 - + * - __u64 - ``ptr_pads`` - - A pointer to a memory area where the pads array will be stored, converted to a 64-bits integer. It can be zero. if zero, the ioctl won't store the pads. It will just update ``num_pads`` - - .. row 11 - - - __u32 - + * - __u32 - ``num_links`` - - Total number of data and interface links in the graph - - .. row 12 - - - __u32 - + * - __u32 - ``reserved4`` - - Applications and drivers shall set this to 0. - - .. row 13 - - - __u64 - + * - __u64 - ``ptr_links`` - - A pointer to a memory area where the links array will be stored, converted to a 64-bits integer. It can be zero. if zero, the ioctl won't store the links. It will just update ``num_links`` @@ -182,37 +129,31 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - - .. row 1 - - - __u32 - + * - __u32 - ``id`` + - Unique ID for the entity. Do not expect that the ID will + always be the same for each instance of the device. In other words, + do not hardcode entity IDs in an application. - - Unique ID for the entity. - - - .. row 2 - - - char - + * - char - ``name``\ [64] + - Entity name as an UTF-8 NULL-terminated string. This name must be unique + within the media topology. - - Entity name as an UTF-8 NULL-terminated string. - - - .. row 3 - - - __u32 - + * - __u32 - ``function`` - - Entity main function, see :ref:`media-entity-functions` for details. - - .. row 4 - - - __u32 - - - ``reserved``\ [6] + * - __u32 + - ``flags`` + - Entity flags, see :ref:`media-entity-flag` for details. + Only valid if ``MEDIA_V2_ENTITY_HAS_FLAGS(media_version)`` + returns true. The ``media_version`` is defined in struct + :c:type:`media_device_info` and can be retrieved using + :ref:`MEDIA_IOC_DEVICE_INFO`. + * - __u32 + - ``reserved``\ [5] - Reserved for future extensions. Drivers and applications must set this array to zero. @@ -226,47 +167,29 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - .. row 1 - - - __u32 - + * - __u32 - ``id`` + - Unique ID for the interface. Do not expect that the ID will + always be the same for each instance of the device. In other words, + do not hardcode interface IDs in an application. - - Unique ID for the interface. - - - .. row 2 - - - __u32 - + * - __u32 - ``intf_type`` - - Interface type, see :ref:`media-intf-type` for details. - - .. row 3 - - - __u32 - + * - __u32 - ``flags`` - - Interface flags. Currently unused. - - .. row 4 - - - __u32 - + * - __u32 - ``reserved``\ [9] - - Reserved for future extensions. Drivers and applications must set this array to zero. - - .. row 5 - - - struct media_v2_intf_devnode - + * - struct media_v2_intf_devnode - ``devnode`` - - Used only for device node interfaces. See - :c:type:`media_v2_intf_devnode` for details.. + :c:type:`media_v2_intf_devnode` for details. .. tabularcolumns:: |p{1.6cm}|p{3.2cm}|p{12.7cm}| @@ -278,24 +201,14 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - - .. row 1 - - - __u32 - + * - __u32 - ``major`` - - Device node major number. - - .. row 2 - - - __u32 - + * - __u32 - ``minor`` - - Device node minor number. - .. tabularcolumns:: |p{1.6cm}|p{3.2cm}|p{12.7cm}| .. c:type:: media_v2_pad @@ -305,37 +218,29 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - - .. row 1 - - - __u32 - + * - __u32 - ``id`` + - Unique ID for the pad. Do not expect that the ID will + always be the same for each instance of the device. In other words, + do not hardcode pad IDs in an application. - - Unique ID for the pad. - - - .. row 2 - - - __u32 - + * - __u32 - ``entity_id`` - - Unique ID for the entity where this pad belongs. - - .. row 3 - - - __u32 - + * - __u32 - ``flags`` - - Pad flags, see :ref:`media-pad-flag` for more details. - - .. row 4 - - - __u32 - - - ``reserved``\ [5] + * - __u32 + - ``index`` + - Pad index, starts at 0. Only valid if ``MEDIA_V2_PAD_HAS_INDEX(media_version)`` + returns true. The ``media_version`` is defined in struct + :c:type:`media_device_info` and can be retrieved using + :ref:`MEDIA_IOC_DEVICE_INFO`. + * - __u32 + - ``reserved``\ [4] - Reserved for future extensions. Drivers and applications must set this array to zero. @@ -349,49 +254,30 @@ desired arrays with the media graph elements. :stub-columns: 0 :widths: 1 2 8 - - - .. row 1 - - - __u32 - + * - __u32 - ``id`` + - Unique ID for the link. Do not expect that the ID will + always be the same for each instance of the device. In other words, + do not hardcode link IDs in an application. - - Unique ID for the link. - - - .. row 2 - - - __u32 - + * - __u32 - ``source_id`` - - On pad to pad links: unique ID for the source pad. On interface to entity links: unique ID for the interface. - - .. row 3 - - - __u32 - + * - __u32 - ``sink_id`` - - On pad to pad links: unique ID for the sink pad. On interface to entity links: unique ID for the entity. - - .. row 4 - - - __u32 - + * - __u32 - ``flags`` - - Link flags, see :ref:`media-link-flag` for more details. - - .. row 5 - - - __u32 - + * - __u32 - ``reserved``\ [6] - - Reserved for future extensions. Drivers and applications must set this array to zero. diff --git a/Documentation/media/uapi/mediactl/media-types.rst b/Documentation/media/uapi/mediactl/media-types.rst index 2dda14bd89b7..e4c57c8f4553 100644 --- a/Documentation/media/uapi/mediactl/media-types.rst +++ b/Documentation/media/uapi/mediactl/media-types.rst @@ -8,6 +8,41 @@ Types and flags used to represent the media graph elements .. tabularcolumns:: |p{8.2cm}|p{10.3cm}| .. _media-entity-functions: +.. _MEDIA-ENT-F-UNKNOWN: +.. _MEDIA-ENT-F-V4L2-SUBDEV-UNKNOWN: +.. _MEDIA-ENT-F-IO-V4L: +.. _MEDIA-ENT-F-IO-VBI: +.. _MEDIA-ENT-F-IO-SWRADIO: +.. _MEDIA-ENT-F-IO-DTV: +.. _MEDIA-ENT-F-DTV-DEMOD: +.. _MEDIA-ENT-F-TS-DEMUX: +.. _MEDIA-ENT-F-DTV-CA: +.. _MEDIA-ENT-F-DTV-NET-DECAP: +.. _MEDIA-ENT-F-CONN-RF: +.. _MEDIA-ENT-F-CONN-SVIDEO: +.. _MEDIA-ENT-F-CONN-COMPOSITE: +.. _MEDIA-ENT-F-CAM-SENSOR: +.. _MEDIA-ENT-F-FLASH: +.. _MEDIA-ENT-F-LENS: +.. _MEDIA-ENT-F-ATV-DECODER: +.. _MEDIA-ENT-F-TUNER: +.. _MEDIA-ENT-F-IF-VID-DECODER: +.. _MEDIA-ENT-F-IF-AUD-DECODER: +.. _MEDIA-ENT-F-AUDIO-CAPTURE: +.. _MEDIA-ENT-F-AUDIO-PLAYBACK: +.. _MEDIA-ENT-F-AUDIO-MIXER: +.. _MEDIA-ENT-F-PROC-VIDEO-COMPOSER: +.. _MEDIA-ENT-F-PROC-VIDEO-PIXEL-FORMATTER: +.. _MEDIA-ENT-F-PROC-VIDEO-PIXEL-ENC-CONV: +.. _MEDIA-ENT-F-PROC-VIDEO-LUT: +.. _MEDIA-ENT-F-PROC-VIDEO-SCALER: +.. _MEDIA-ENT-F-PROC-VIDEO-STATISTICS: +.. _MEDIA-ENT-F-PROC-VIDEO-ENCODER: +.. _MEDIA-ENT-F-PROC-VIDEO-DECODER: +.. _MEDIA-ENT-F-VID-MUX: +.. _MEDIA-ENT-F-VID-IF-BRIDGE: +.. _MEDIA-ENT-F-DV-DECODER: +.. _MEDIA-ENT-F-DV-ENCODER: .. cssclass:: longtable @@ -15,139 +50,56 @@ Types and flags used to represent the media graph elements :header-rows: 0 :stub-columns: 0 - - - .. row 1 - - .. _MEDIA-ENT-F-UNKNOWN: - .. _MEDIA-ENT-F-V4L2-SUBDEV-UNKNOWN: - - - ``MEDIA_ENT_F_UNKNOWN`` and - + * - ``MEDIA_ENT_F_UNKNOWN`` and ``MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN`` - - Unknown entity. That generally indicates that a driver didn't initialize properly the entity, which is a Kernel bug - - .. row 2 - - .. _MEDIA-ENT-F-IO-V4L: - - - ``MEDIA_ENT_F_IO_V4L`` - + * - ``MEDIA_ENT_F_IO_V4L`` - Data streaming input and/or output entity. - - .. row 3 - - .. _MEDIA-ENT-F-IO-VBI: - - - ``MEDIA_ENT_F_IO_VBI`` - + * - ``MEDIA_ENT_F_IO_VBI`` - V4L VBI streaming input or output entity - - .. row 4 - - .. _MEDIA-ENT-F-IO-SWRADIO: - - - ``MEDIA_ENT_F_IO_SWRADIO`` - + * - ``MEDIA_ENT_F_IO_SWRADIO`` - V4L Software Digital Radio (SDR) streaming input or output entity - - .. row 5 - - .. _MEDIA-ENT-F-IO-DTV: - - - ``MEDIA_ENT_F_IO_DTV`` - + * - ``MEDIA_ENT_F_IO_DTV`` - DVB Digital TV streaming input or output entity - - .. row 6 - - .. _MEDIA-ENT-F-DTV-DEMOD: - - - ``MEDIA_ENT_F_DTV_DEMOD`` - + * - ``MEDIA_ENT_F_DTV_DEMOD`` - Digital TV demodulator entity. - - .. row 7 - - .. _MEDIA-ENT-F-TS-DEMUX: - - - ``MEDIA_ENT_F_TS_DEMUX`` - + * - ``MEDIA_ENT_F_TS_DEMUX`` - MPEG Transport stream demux entity. Could be implemented on hardware or in Kernelspace by the Linux DVB subsystem. - - .. row 8 - - .. _MEDIA-ENT-F-DTV-CA: - - - ``MEDIA_ENT_F_DTV_CA`` - + * - ``MEDIA_ENT_F_DTV_CA`` - Digital TV Conditional Access module (CAM) entity - - .. row 9 - - .. _MEDIA-ENT-F-DTV-NET-DECAP: - - - ``MEDIA_ENT_F_DTV_NET_DECAP`` - + * - ``MEDIA_ENT_F_DTV_NET_DECAP`` - Digital TV network ULE/MLE desencapsulation entity. Could be implemented on hardware or in Kernelspace - - .. row 10 - - .. _MEDIA-ENT-F-CONN-RF: - - - ``MEDIA_ENT_F_CONN_RF`` - + * - ``MEDIA_ENT_F_CONN_RF`` - Connector for a Radio Frequency (RF) signal. - - .. row 11 - - .. _MEDIA-ENT-F-CONN-SVIDEO: - - - ``MEDIA_ENT_F_CONN_SVIDEO`` - + * - ``MEDIA_ENT_F_CONN_SVIDEO`` - Connector for a S-Video signal. - - .. row 12 - - .. _MEDIA-ENT-F-CONN-COMPOSITE: - - - ``MEDIA_ENT_F_CONN_COMPOSITE`` - + * - ``MEDIA_ENT_F_CONN_COMPOSITE`` - Connector for a RGB composite signal. - - .. row 13 - - .. _MEDIA-ENT-F-CAM-SENSOR: - - - ``MEDIA_ENT_F_CAM_SENSOR`` - + * - ``MEDIA_ENT_F_CAM_SENSOR`` - Camera video sensor entity. - - .. row 14 - - .. _MEDIA-ENT-F-FLASH: - - - ``MEDIA_ENT_F_FLASH`` - + * - ``MEDIA_ENT_F_FLASH`` - Flash controller entity. - - .. row 15 - - .. _MEDIA-ENT-F-LENS: - - - ``MEDIA_ENT_F_LENS`` - + * - ``MEDIA_ENT_F_LENS`` - Lens controller entity. - - .. row 16 - - .. _MEDIA-ENT-F-ATV-DECODER: - - - ``MEDIA_ENT_F_ATV_DECODER`` - + * - ``MEDIA_ENT_F_ATV_DECODER`` - Analog video decoder, the basic function of the video decoder is to accept analogue video from a wide variety of sources such as broadcast, DVD players, cameras and video cassette recorders, in @@ -155,36 +107,21 @@ Types and flags used to represent the media graph elements its component parts, luminance and chrominance, and output it in some digital video standard, with appropriate timing signals. - - .. row 17 - - .. _MEDIA-ENT-F-TUNER: - - - ``MEDIA_ENT_F_TUNER`` - + * - ``MEDIA_ENT_F_TUNER`` - Digital TV, analog TV, radio and/or software radio tuner, with consists on a PLL tuning stage that converts radio frequency (RF) signal into an Intermediate Frequency (IF). Modern tuners have internally IF-PLL decoders for audio and video, but older models have those stages implemented on separate entities. - - .. row 18 - - .. _MEDIA-ENT-F-IF-VID-DECODER: - - - ``MEDIA_ENT_F_IF_VID_DECODER`` - + * - ``MEDIA_ENT_F_IF_VID_DECODER`` - IF-PLL video decoder. It receives the IF from a PLL and decodes the analog TV video signal. This is commonly found on some very old analog tuners, like Philips MK3 designs. They all contain a tda9887 (or some software compatible similar chip, like tda9885). Those devices use a different I2C address than the tuner PLL. - - .. row 19 - - .. _MEDIA-ENT-F-IF-AUD-DECODER: - - - ``MEDIA_ENT_F_IF_AUD_DECODER`` - + * - ``MEDIA_ENT_F_IF_AUD_DECODER`` - IF-PLL sound decoder. It receives the IF from a PLL and decodes the analog TV audio signal. This is commonly found on some very old analog hardware, like Micronas msp3400, Philips tda9840, @@ -192,36 +129,16 @@ Types and flags used to represent the media graph elements tuner PLL and should be controlled together with the IF-PLL video decoder. - - .. row 20 - - .. _MEDIA-ENT-F-AUDIO-CAPTURE: - - - ``MEDIA_ENT_F_AUDIO_CAPTURE`` - + * - ``MEDIA_ENT_F_AUDIO_CAPTURE`` - Audio Capture Function Entity. - - .. row 21 - - .. _MEDIA-ENT-F-AUDIO-PLAYBACK: - - - ``MEDIA_ENT_F_AUDIO_PLAYBACK`` - + * - ``MEDIA_ENT_F_AUDIO_PLAYBACK`` - Audio Playback Function Entity. - - .. row 22 - - .. _MEDIA-ENT-F-AUDIO-MIXER: - - - ``MEDIA_ENT_F_AUDIO_MIXER`` - + * - ``MEDIA_ENT_F_AUDIO_MIXER`` - Audio Mixer Function Entity. - - .. row 23 - - .. _MEDIA-ENT-F-PROC-VIDEO-COMPOSER: - - - ``MEDIA_ENT_F_PROC_VIDEO_COMPOSER`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_COMPOSER`` - Video composer (blender). An entity capable of video composing must have at least two sink pads and one source pad, and composes input video frames onto output video @@ -229,12 +146,7 @@ Types and flags used to represent the media graph elements color keying, raster operations (ROP), stitching or any other means. - - .. row 24 - - .. _MEDIA-ENT-F-PROC-VIDEO-PIXEL-FORMATTER: - - - ``MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER`` - Video pixel formatter. An entity capable of pixel formatting must have at least one sink pad and one source pad. Read pixel formatters read pixels from memory and perform a subset @@ -243,12 +155,7 @@ Types and flags used to represent the media graph elements a subset of dithering, pixel encoding conversion and packing and write pixels to memory. - - .. row 25 - - .. _MEDIA-ENT-F-PROC-VIDEO-PIXEL-ENC-CONV: - - - ``MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV`` - Video pixel encoding converter. An entity capable of pixel enconding conversion must have at least one sink pad and one source pad, and convert the encoding of pixels received on @@ -257,12 +164,7 @@ Types and flags used to represent the media graph elements to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB conversions. - - .. row 26 - - .. _MEDIA-ENT-F-PROC-VIDEO-LUT: - - - ``MEDIA_ENT_F_PROC_VIDEO_LUT`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_LUT`` - Video look-up table. An entity capable of video lookup table processing must have one sink pad and one source pad. It uses the values of the pixels received on its sink pad to look up @@ -271,12 +173,7 @@ Types and flags used to represent the media graph elements separately or combine them for multi-dimensional table lookups. - - .. row 27 - - .. _MEDIA-ENT-F-PROC-VIDEO-SCALER: - - - ``MEDIA_ENT_F_PROC_VIDEO_SCALER`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_SCALER`` - Video scaler. An entity capable of video scaling must have at least one sink pad and one source pad, and scale the video frame(s) received on its sink pad(s) to a different @@ -287,311 +184,190 @@ Types and flags used to represent the media graph elements sub-sampling (occasionally also referred to as skipping) are considered as scaling. - - .. row 28 - - .. _MEDIA-ENT-F-PROC-VIDEO-STATISTICS: - - - ``MEDIA_ENT_F_PROC_VIDEO_STATISTICS`` - + * - ``MEDIA_ENT_F_PROC_VIDEO_STATISTICS`` - Video statistics computation (histogram, 3A, etc.). An entity capable of statistics computation must have one sink pad and one source pad. It computes statistics over the frames received on its sink pad and outputs the statistics data on its source pad. - - .. row 29 + * - ``MEDIA_ENT_F_PROC_VIDEO_ENCODER`` + - Video (MPEG, HEVC, VPx, etc.) encoder. An entity capable of + compressing video frames. Must have one sink pad and at least + one source pad. - .. _MEDIA-ENT-F-VID-MUX: - - - ``MEDIA_ENT_F_VID_MUX`` + * - ``MEDIA_ENT_F_PROC_VIDEO_DECODER`` + - Video (MPEG, HEVC, VPx, etc.) decoder. An entity capable of + decompressing a compressed video stream into uncompressed video + frames. Must have one sink pad and at least one source pad. + * - ``MEDIA_ENT_F_VID_MUX`` - Video multiplexer. An entity capable of multiplexing must have at least two sink pads and one source pad, and must pass the video frame(s) received from the active sink pad to the source pad. - - .. row 30 - - .. _MEDIA-ENT-F-VID-IF-BRIDGE: - - - ``MEDIA_ENT_F_VID_IF_BRIDGE`` - + * - ``MEDIA_ENT_F_VID_IF_BRIDGE`` - Video interface bridge. A video interface bridge entity must have at least one sink pad and at least one source pad. It receives video frames on its sink pad from an input video bus of one type (HDMI, eDP, MIPI CSI-2, etc.), and outputs them on its source pad to an output video bus of another type (eDP, MIPI CSI-2, parallel, etc.). - - .. row 31 - - .. _MEDIA-ENT-F-DTV-DECODER: - - - ``MEDIA_ENT_F_DTV_DECODER`` - + * - ``MEDIA_ENT_F_DV_DECODER`` - Digital video decoder. The basic function of the video decoder is to accept digital video from a wide variety of sources and output it in some digital video standard, with appropriate timing signals. + * - ``MEDIA_ENT_F_DV_ENCODER`` + - Digital video encoder. The basic function of the video encoder is + to accept digital video from some digital video standard with + appropriate timing signals (usually a parallel video bus with sync + signals) and output this to a digital video output connector such + as HDMI or DisplayPort. + .. tabularcolumns:: |p{5.5cm}|p{12.0cm}| .. _media-entity-flag: +.. _MEDIA-ENT-FL-DEFAULT: +.. _MEDIA-ENT-FL-CONNECTOR: .. flat-table:: Media entity flags :header-rows: 0 :stub-columns: 0 - - - .. row 1 - - .. _MEDIA-ENT-FL-DEFAULT: - - - ``MEDIA_ENT_FL_DEFAULT`` - + * - ``MEDIA_ENT_FL_DEFAULT`` - Default entity for its type. Used to discover the default audio, VBI and video devices, the default camera sensor, etc. - - .. row 2 - - .. _MEDIA-ENT-FL-CONNECTOR: - - - ``MEDIA_ENT_FL_CONNECTOR`` - + * - ``MEDIA_ENT_FL_CONNECTOR`` - The entity represents a connector. .. tabularcolumns:: |p{6.5cm}|p{6.0cm}|p{5.0cm}| .. _media-intf-type: +.. _MEDIA-INTF-T-DVB-FE: +.. _MEDIA-INTF-T-DVB-DEMUX: +.. _MEDIA-INTF-T-DVB-DVR: +.. _MEDIA-INTF-T-DVB-CA: +.. _MEDIA-INTF-T-DVB-NET: +.. _MEDIA-INTF-T-V4L-VIDEO: +.. _MEDIA-INTF-T-V4L-VBI: +.. _MEDIA-INTF-T-V4L-RADIO: +.. _MEDIA-INTF-T-V4L-SUBDEV: +.. _MEDIA-INTF-T-V4L-SWRADIO: +.. _MEDIA-INTF-T-V4L-TOUCH: +.. _MEDIA-INTF-T-ALSA-PCM-CAPTURE: +.. _MEDIA-INTF-T-ALSA-PCM-PLAYBACK: +.. _MEDIA-INTF-T-ALSA-CONTROL: +.. _MEDIA-INTF-T-ALSA-COMPRESS: +.. _MEDIA-INTF-T-ALSA-RAWMIDI: +.. _MEDIA-INTF-T-ALSA-HWDEP: +.. _MEDIA-INTF-T-ALSA-SEQUENCER: +.. _MEDIA-INTF-T-ALSA-TIMER: .. flat-table:: Media interface types :header-rows: 0 :stub-columns: 0 - - - .. row 1 - - .. _MEDIA-INTF-T-DVB-FE: - - - ``MEDIA_INTF_T_DVB_FE`` - + * - ``MEDIA_INTF_T_DVB_FE`` - Device node interface for the Digital TV frontend - - typically, /dev/dvb/adapter?/frontend? - - .. row 2 - - .. _MEDIA-INTF-T-DVB-DEMUX: - - - ``MEDIA_INTF_T_DVB_DEMUX`` - + * - ``MEDIA_INTF_T_DVB_DEMUX`` - Device node interface for the Digital TV demux - - typically, /dev/dvb/adapter?/demux? - - .. row 3 - - .. _MEDIA-INTF-T-DVB-DVR: - - - ``MEDIA_INTF_T_DVB_DVR`` - + * - ``MEDIA_INTF_T_DVB_DVR`` - Device node interface for the Digital TV DVR - - typically, /dev/dvb/adapter?/dvr? - - .. row 4 - - .. _MEDIA-INTF-T-DVB-CA: - - - ``MEDIA_INTF_T_DVB_CA`` - + * - ``MEDIA_INTF_T_DVB_CA`` - Device node interface for the Digital TV Conditional Access - - typically, /dev/dvb/adapter?/ca? - - .. row 5 - - .. _MEDIA-INTF-T-DVB-NET: - - - ``MEDIA_INTF_T_DVB_NET`` - + * - ``MEDIA_INTF_T_DVB_NET`` - Device node interface for the Digital TV network control - - typically, /dev/dvb/adapter?/net? - - .. row 6 - - .. _MEDIA-INTF-T-V4L-VIDEO: - - - ``MEDIA_INTF_T_V4L_VIDEO`` - + * - ``MEDIA_INTF_T_V4L_VIDEO`` - Device node interface for video (V4L) - - typically, /dev/video? - - .. row 7 - - .. _MEDIA-INTF-T-V4L-VBI: - - - ``MEDIA_INTF_T_V4L_VBI`` - + * - ``MEDIA_INTF_T_V4L_VBI`` - Device node interface for VBI (V4L) - - typically, /dev/vbi? - - .. row 8 - - .. _MEDIA-INTF-T-V4L-RADIO: - - - ``MEDIA_INTF_T_V4L_RADIO`` - + * - ``MEDIA_INTF_T_V4L_RADIO`` - Device node interface for radio (V4L) - - typically, /dev/radio? - - .. row 9 - - .. _MEDIA-INTF-T-V4L-SUBDEV: - - - ``MEDIA_INTF_T_V4L_SUBDEV`` - + * - ``MEDIA_INTF_T_V4L_SUBDEV`` - Device node interface for a V4L subdevice - - typically, /dev/v4l-subdev? - - .. row 10 - - .. _MEDIA-INTF-T-V4L-SWRADIO: - - - ``MEDIA_INTF_T_V4L_SWRADIO`` - + * - ``MEDIA_INTF_T_V4L_SWRADIO`` - Device node interface for Software Defined Radio (V4L) - - typically, /dev/swradio? - - .. row 11 - - .. _MEDIA-INTF-T-V4L-TOUCH: - - - ``MEDIA_INTF_T_V4L_TOUCH`` - + * - ``MEDIA_INTF_T_V4L_TOUCH`` - Device node interface for Touch device (V4L) - - typically, /dev/v4l-touch? - - .. row 12 - - .. _MEDIA-INTF-T-ALSA-PCM-CAPTURE: - - - ``MEDIA_INTF_T_ALSA_PCM_CAPTURE`` - + * - ``MEDIA_INTF_T_ALSA_PCM_CAPTURE`` - Device node interface for ALSA PCM Capture - - typically, /dev/snd/pcmC?D?c - - .. row 13 - - .. _MEDIA-INTF-T-ALSA-PCM-PLAYBACK: - - - ``MEDIA_INTF_T_ALSA_PCM_PLAYBACK`` - + * - ``MEDIA_INTF_T_ALSA_PCM_PLAYBACK`` - Device node interface for ALSA PCM Playback - - typically, /dev/snd/pcmC?D?p - - .. row 14 - - .. _MEDIA-INTF-T-ALSA-CONTROL: - - - ``MEDIA_INTF_T_ALSA_CONTROL`` - + * - ``MEDIA_INTF_T_ALSA_CONTROL`` - Device node interface for ALSA Control - - typically, /dev/snd/controlC? - - .. row 15 - - .. _MEDIA-INTF-T-ALSA-COMPRESS: - - - ``MEDIA_INTF_T_ALSA_COMPRESS`` - + * - ``MEDIA_INTF_T_ALSA_COMPRESS`` - Device node interface for ALSA Compress - - typically, /dev/snd/compr? - - .. row 16 - - .. _MEDIA-INTF-T-ALSA-RAWMIDI: - - - ``MEDIA_INTF_T_ALSA_RAWMIDI`` - + * - ``MEDIA_INTF_T_ALSA_RAWMIDI`` - Device node interface for ALSA Raw MIDI - - typically, /dev/snd/midi? - - .. row 17 - - .. _MEDIA-INTF-T-ALSA-HWDEP: - - - ``MEDIA_INTF_T_ALSA_HWDEP`` - + * - ``MEDIA_INTF_T_ALSA_HWDEP`` - Device node interface for ALSA Hardware Dependent - - typically, /dev/snd/hwC?D? - - .. row 18 - - .. _MEDIA-INTF-T-ALSA-SEQUENCER: - - - ``MEDIA_INTF_T_ALSA_SEQUENCER`` - + * - ``MEDIA_INTF_T_ALSA_SEQUENCER`` - Device node interface for ALSA Sequencer - - typically, /dev/snd/seq - - .. row 19 - - .. _MEDIA-INTF-T-ALSA-TIMER: - - - ``MEDIA_INTF_T_ALSA_TIMER`` - + * - ``MEDIA_INTF_T_ALSA_TIMER`` - Device node interface for ALSA Timer - - typically, /dev/snd/timer .. tabularcolumns:: |p{5.5cm}|p{12.0cm}| .. _media-pad-flag: +.. _MEDIA-PAD-FL-SINK: +.. _MEDIA-PAD-FL-SOURCE: +.. _MEDIA-PAD-FL-MUST-CONNECT: .. flat-table:: Media pad flags :header-rows: 0 :stub-columns: 0 - - - .. row 1 - - .. _MEDIA-PAD-FL-SINK: - - - ``MEDIA_PAD_FL_SINK`` - + * - ``MEDIA_PAD_FL_SINK`` - Input pad, relative to the entity. Input pads sink data and are targets of links. - - .. row 2 - - .. _MEDIA-PAD-FL-SOURCE: - - - ``MEDIA_PAD_FL_SOURCE`` - + * - ``MEDIA_PAD_FL_SOURCE`` - Output pad, relative to the entity. Output pads source data and are origins of links. - - .. row 3 - - .. _MEDIA-PAD-FL-MUST-CONNECT: - - - ``MEDIA_PAD_FL_MUST_CONNECT`` - + * - ``MEDIA_PAD_FL_MUST_CONNECT`` - If this flag is set and the pad is linked to any other pad, then at least one of those links must be enabled for the entity to be able to stream. There could be temporary reasons (e.g. device @@ -606,46 +382,29 @@ must be set for every pad. .. tabularcolumns:: |p{5.5cm}|p{12.0cm}| .. _media-link-flag: +.. _MEDIA-LNK-FL-ENABLED: +.. _MEDIA-LNK-FL-IMMUTABLE: +.. _MEDIA-LNK-FL-DYNAMIC: +.. _MEDIA-LNK-FL-LINK-TYPE: .. flat-table:: Media link flags :header-rows: 0 :stub-columns: 0 - - - .. row 1 - - .. _MEDIA-LNK-FL-ENABLED: - - - ``MEDIA_LNK_FL_ENABLED`` - + * - ``MEDIA_LNK_FL_ENABLED`` - The link is enabled and can be used to transfer media data. When two or more links target a sink pad, only one of them can be enabled at a time. - - .. row 2 - - .. _MEDIA-LNK-FL-IMMUTABLE: - - - ``MEDIA_LNK_FL_IMMUTABLE`` - + * - ``MEDIA_LNK_FL_IMMUTABLE`` - The link enabled state can't be modified at runtime. An immutable link is always enabled. - - .. row 3 - - .. _MEDIA-LNK-FL-DYNAMIC: - - - ``MEDIA_LNK_FL_DYNAMIC`` - + * - ``MEDIA_LNK_FL_DYNAMIC`` - The link enabled state can be modified during streaming. This flag is set by drivers and is read-only for applications. - - .. row 4 - - .. _MEDIA-LNK-FL-LINK-TYPE: - - - ``MEDIA_LNK_FL_LINK_TYPE`` - + * - ``MEDIA_LNK_FL_LINK_TYPE`` - This is a bitmask that defines the type of the link. Currently, two types of links are supported: diff --git a/Documentation/media/uapi/v4l/extended-controls.rst b/Documentation/media/uapi/v4l/extended-controls.rst index 03931f9b1285..9f7312bf3365 100644 --- a/Documentation/media/uapi/v4l/extended-controls.rst +++ b/Documentation/media/uapi/v4l/extended-controls.rst @@ -1955,9 +1955,51 @@ enum v4l2_vp8_golden_frame_sel - ``V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (integer)`` Quantization parameter for a P frame for VP8. -``V4L2_CID_MPEG_VIDEO_VPX_PROFILE (integer)`` - Select the desired profile for VPx encoder. Acceptable values are 0, - 1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3. +.. _v4l2-mpeg-video-vp8-profile: + +``V4L2_CID_MPEG_VIDEO_VP8_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp8_profile - + This control allows selecting the profile for VP8 encoder. + This is also used to enumerate supported profiles by VP8 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP8_PROFILE_3`` + - Profile 3 + +.. _v4l2-mpeg-video-vp9-profile: + +``V4L2_CID_MPEG_VIDEO_VP9_PROFILE`` + (enum) + +enum v4l2_mpeg_video_vp9_profile - + This control allows selecting the profile for VP9 encoder. + This is also used to enumerate supported profiles by VP9 encoder or decoder. + Possible values are: + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_0`` + - Profile 0 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_1`` + - Profile 1 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_2`` + - Profile 2 + * - ``V4L2_MPEG_VIDEO_VP9_PROFILE_3`` + - Profile 3 High Efficiency Video Coding (HEVC/H.265) Control Reference diff --git a/Documentation/media/uapi/v4l/pixfmt-compressed.rst b/Documentation/media/uapi/v4l/pixfmt-compressed.rst index abec03937bb3..d382e7a5c38e 100644 --- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst +++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst @@ -95,3 +95,10 @@ Compressed Formats - ``V4L2_PIX_FMT_HEVC`` - 'HEVC' - HEVC/H.265 video elementary stream. + * .. _V4L2-PIX-FMT-FWHT: + + - ``V4L2_PIX_FMT_FWHT`` + - 'FWHT' + - Video elementary stream using a codec based on the Fast Walsh Hadamard + Transform. This codec is implemented by the vicodec ('Virtual Codec') + driver. See the vicodec-codec.h header for more details. diff --git a/Documentation/media/uapi/v4l/pixfmt-rgb.rst b/Documentation/media/uapi/v4l/pixfmt-rgb.rst index cf2ef7df9616..1f9a7e3a07c9 100644 --- a/Documentation/media/uapi/v4l/pixfmt-rgb.rst +++ b/Documentation/media/uapi/v4l/pixfmt-rgb.rst @@ -19,4 +19,5 @@ RGB Formats pixfmt-srggb10-ipu3 pixfmt-srggb12 pixfmt-srggb12p + pixfmt-srggb14p pixfmt-srggb16 diff --git a/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst b/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst new file mode 100644 index 000000000000..88d20c0e4282 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-srggb14p.rst @@ -0,0 +1,127 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-SRGGB14P: +.. _v4l2-pix-fmt-sbggr14p: +.. _v4l2-pix-fmt-sgbrg14p: +.. _v4l2-pix-fmt-sgrbg14p: + +******************************************************************************************************************************* +V4L2_PIX_FMT_SRGGB14P ('pRCC'), V4L2_PIX_FMT_SGRBG14P ('pgCC'), V4L2_PIX_FMT_SGBRG14P ('pGCC'), V4L2_PIX_FMT_SBGGR14P ('pBCC'), +******************************************************************************************************************************* + +*man V4L2_PIX_FMT_SRGGB14P(2)* + +V4L2_PIX_FMT_SGRBG14P +V4L2_PIX_FMT_SGBRG14P +V4L2_PIX_FMT_SBGGR14P +14-bit packed Bayer formats + + +Description +=========== + +These four pixel formats are packed raw sRGB / Bayer formats with 14 +bits per colour. Every four consecutive samples are packed into seven +bytes. Each of the first four bytes contain the eight high order bits +of the pixels, and the three following bytes contains the six least +significants bits of each pixel, in the same order. + +Each n-pixel row contains n/2 green samples and n/2 blue or red samples, +with alternating green-red and green-blue rows. They are conventionally +described as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example +of one of these formats: + +**Byte Order.** +Each cell is one byte. + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 2 1 1 1 1 1 1 1 + + + - .. row 1 + + - start + 0: + + - B\ :sub:`00high` + + - G\ :sub:`01high` + + - B\ :sub:`02high` + + - G\ :sub:`03high` + + - G\ :sub:`01low bits 1--0`\ (bits 7--6) + B\ :sub:`00low bits 5--0`\ (bits 5--0) + + - R\ :sub:`02low bits 3--0`\ (bits 7--4) + G\ :sub:`01low bits 5--2`\ (bits 3--0) + + - G\ :sub:`03low bits 5--0`\ (bits 7--2) + R\ :sub:`02low bits 5--4`\ (bits 1--0) + + - .. row 2 + + - start + 7: + + - G\ :sub:`00high` + + - R\ :sub:`01high` + + - G\ :sub:`02high` + + - R\ :sub:`03high` + + - R\ :sub:`01low bits 1--0`\ (bits 7--6) + G\ :sub:`00low bits 5--0`\ (bits 5--0) + + - G\ :sub:`02low bits 3--0`\ (bits 7--4) + R\ :sub:`01low bits 5--2`\ (bits 3--0) + + - R\ :sub:`03low bits 5--0`\ (bits 7--2) + G\ :sub:`02low bits 5--4`\ (bits 1--0) + + - .. row 3 + + - start + 14 + + - B\ :sub:`20high` + + - G\ :sub:`21high` + + - B\ :sub:`22high` + + - G\ :sub:`23high` + + - G\ :sub:`21low bits 1--0`\ (bits 7--6) + B\ :sub:`20low bits 5--0`\ (bits 5--0) + + - R\ :sub:`22low bits 3--0`\ (bits 7--4) + G\ :sub:`21low bits 5--2`\ (bits 3--0) + + - G\ :sub:`23low bits 5--0`\ (bits 7--2) + R\ :sub:`22low bits 5--4`\ (bits 1--0) + + - .. row 4 + + - start + 21 + + - G\ :sub:`30high` + + - R\ :sub:`31high` + + - G\ :sub:`32high` + + - R\ :sub:`33high` + + - R\ :sub:`31low bits 1--0`\ (bits 7--6) + G\ :sub:`30low bits 5--0`\ (bits 5--0) + + - G\ :sub:`32low bits 3--0`\ (bits 7--4) + R\ :sub:`31low bits 5--2`\ (bits 3--0) + + - R\ :sub:`33low bits 5--0`\ (bits 7--2) + G\ :sub:`32low bits 5--4`\ (bits 1--0) diff --git a/Documentation/media/uapi/v4l/pixfmt-y10p.rst b/Documentation/media/uapi/v4l/pixfmt-y10p.rst new file mode 100644 index 000000000000..13b571306915 --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-y10p.rst @@ -0,0 +1,33 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _V4L2-PIX-FMT-Y10P: + +****************************** +V4L2_PIX_FMT_Y10P ('Y10P') +****************************** + +Grey-scale image as a MIPI RAW10 packed array + + +Description +=========== + +This is a packed grey-scale image format with a depth of 10 bits per +pixel. Every four consecutive pixels are packed into 5 bytes. Each of +the first 4 bytes contain the 8 high order bits of the pixels, and +the 5th byte contains the 2 least significants bits of each pixel, +in the same order. + +**Bit-packed representation.** + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 8 8 8 8 64 + + * - Y'\ :sub:`00[9:2]` + - Y'\ :sub:`01[9:2]` + - Y'\ :sub:`02[9:2]` + - Y'\ :sub:`03[9:2]` + - Y'\ :sub:`03[1:0]`\ (bits 7--6) Y'\ :sub:`02[1:0]`\ (bits 5--4) + Y'\ :sub:`01[1:0]`\ (bits 3--2) Y'\ :sub:`00[1:0]`\ (bits 1--0) diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index 9fcabe7f9367..8e73fcfc6900 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -37,19 +37,22 @@ Media Bus Formats - Image colorspace, from enum :c:type:`v4l2_colorspace`. See :ref:`colorspaces` for details. - * - enum :c:type:`v4l2_ycbcr_encoding` + * - __u16 - ``ycbcr_enc`` - - This information supplements the ``colorspace`` and must be set by + - Y'CbCr encoding, from enum :c:type:`v4l2_ycbcr_encoding`. + 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`. - * - enum :c:type:`v4l2_quantization` + * - __u16 - ``quantization`` - - This information supplements the ``colorspace`` and must be set by + - Quantization range, from enum :c:type:`v4l2_quantization`. + 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`. - * - enum :c:type:`v4l2_xfer_func` + * - __u16 - ``xfer_func`` - - This information supplements the ``colorspace`` and must be set by + - Transfer function, from enum :c:type:`v4l2_xfer_func`. + 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`. * - __u16 @@ -4315,6 +4318,78 @@ the following codes. - y\ :sub:`2` - y\ :sub:`1` - y\ :sub:`0` + * .. _MEDIA-BUS-FMT-Y10-2X8-PADHI_LE: + + - MEDIA_BUS_FMT_Y10_2X8_PADHI_LE + - 0x202c + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - y\ :sub:`9` + - y\ :sub:`8` * .. _MEDIA-BUS-FMT-UYVY10-2X10: - MEDIA_BUS_FMT_UYVY10_2X10 diff --git a/Documentation/media/uapi/v4l/vidioc-enumstd.rst b/Documentation/media/uapi/v4l/vidioc-enumstd.rst index b7fda29f46a1..2644a62acd4b 100644 --- a/Documentation/media/uapi/v4l/vidioc-enumstd.rst +++ b/Documentation/media/uapi/v4l/vidioc-enumstd.rst @@ -2,14 +2,14 @@ .. _VIDIOC_ENUMSTD: -******************** -ioctl VIDIOC_ENUMSTD -******************** +******************************************* +ioctl VIDIOC_ENUMSTD, VIDIOC_SUBDEV_ENUMSTD +******************************************* Name ==== -VIDIOC_ENUMSTD - Enumerate supported video standards +VIDIOC_ENUMSTD - VIDIOC_SUBDEV_ENUMSTD - Enumerate supported video standards Synopsis @@ -18,6 +18,9 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_ENUMSTD, struct v4l2_standard *argp ) :name: VIDIOC_ENUMSTD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_ENUMSTD, struct v4l2_standard *argp ) + :name: VIDIOC_SUBDEV_ENUMSTD + Arguments ========= diff --git a/Documentation/media/uapi/v4l/vidioc-g-std.rst b/Documentation/media/uapi/v4l/vidioc-g-std.rst index 90791ab51a53..8d94f0404df2 100644 --- a/Documentation/media/uapi/v4l/vidioc-g-std.rst +++ b/Documentation/media/uapi/v4l/vidioc-g-std.rst @@ -2,14 +2,14 @@ .. _VIDIOC_G_STD: -******************************** -ioctl VIDIOC_G_STD, VIDIOC_S_STD -******************************** +************************************************************************** +ioctl VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_SUBDEV_G_STD, VIDIOC_SUBDEV_S_STD +************************************************************************** Name ==== -VIDIOC_G_STD - VIDIOC_S_STD - Query or select the video standard of the current input +VIDIOC_G_STD - VIDIOC_S_STD - VIDIOC_SUBDEV_G_STD - VIDIOC_SUBDEV_S_STD - Query or select the video standard of the current input Synopsis @@ -21,6 +21,12 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_S_STD, const v4l2_std_id *argp ) :name: VIDIOC_S_STD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_G_STD, v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_G_STD + +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_S_STD, const v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_S_STD + Arguments ========= diff --git a/Documentation/media/uapi/v4l/vidioc-querystd.rst b/Documentation/media/uapi/v4l/vidioc-querystd.rst index cf40bca19b9f..a8385cc74818 100644 --- a/Documentation/media/uapi/v4l/vidioc-querystd.rst +++ b/Documentation/media/uapi/v4l/vidioc-querystd.rst @@ -2,14 +2,14 @@ .. _VIDIOC_QUERYSTD: -********************* -ioctl VIDIOC_QUERYSTD -********************* +********************************************* +ioctl VIDIOC_QUERYSTD, VIDIOC_SUBDEV_QUERYSTD +********************************************* Name ==== -VIDIOC_QUERYSTD - Sense the video standard received by the current input +VIDIOC_QUERYSTD - VIDIOC_SUBDEV_QUERYSTD - Sense the video standard received by the current input Synopsis @@ -18,6 +18,9 @@ Synopsis .. c:function:: int ioctl( int fd, VIDIOC_QUERYSTD, v4l2_std_id *argp ) :name: VIDIOC_QUERYSTD +.. c:function:: int ioctl( int fd, VIDIOC_SUBDEV_QUERYSTD, v4l2_std_id *argp ) + :name: VIDIOC_SUBDEV_QUERYSTD + Arguments ========= diff --git a/Documentation/media/uapi/v4l/yuv-formats.rst b/Documentation/media/uapi/v4l/yuv-formats.rst index 3334ea445657..9ab0592d08da 100644 --- a/Documentation/media/uapi/v4l/yuv-formats.rst +++ b/Documentation/media/uapi/v4l/yuv-formats.rst @@ -29,6 +29,7 @@ to brightness information. pixfmt-y10 pixfmt-y12 pixfmt-y10b + pixfmt-y10p pixfmt-y16 pixfmt-y16-be pixfmt-y8i diff --git a/Documentation/media/v4l-drivers/qcom_camss.rst b/Documentation/media/v4l-drivers/qcom_camss.rst index 9e66b7b5770f..f27c8df20b2b 100644 --- a/Documentation/media/v4l-drivers/qcom_camss.rst +++ b/Documentation/media/v4l-drivers/qcom_camss.rst @@ -7,34 +7,34 @@ Introduction ------------ This file documents the Qualcomm Camera Subsystem driver located under -drivers/media/platform/qcom/camss-8x16. +drivers/media/platform/qcom/camss. The current version of the driver supports the Camera Subsystem found on -Qualcomm MSM8916 and APQ8016 processors. +Qualcomm MSM8916/APQ8016 and MSM8996/APQ8096 processors. The driver implements V4L2, Media controller and V4L2 subdev interfaces. Camera sensor using V4L2 subdev interface in the kernel is supported. The driver is implemented using as a reference the Qualcomm Camera Subsystem -driver for Android as found in Code Aurora [#f1]_. +driver for Android as found in Code Aurora [#f1]_ [#f2]_. Qualcomm Camera Subsystem hardware ---------------------------------- -The Camera Subsystem hardware found on 8x16 processors and supported by the -driver consists of: +The Camera Subsystem hardware found on 8x16 / 8x96 processors and supported by +the driver consists of: -- 2 CSIPHY modules. They handle the Physical layer of the CSI2 receivers. +- 2 / 3 CSIPHY modules. They handle the Physical layer of the CSI2 receivers. A separate camera sensor can be connected to each of the CSIPHY module; -- 2 CSID (CSI Decoder) modules. They handle the Protocol and Application layer - of the CSI2 receivers. A CSID can decode data stream from any of the CSIPHY. - Each CSID also contains a TG (Test Generator) block which can generate +- 2 / 4 CSID (CSI Decoder) modules. They handle the Protocol and Application + layer of the CSI2 receivers. A CSID can decode data stream from any of the + CSIPHY. Each CSID also contains a TG (Test Generator) block which can generate artificial input data for test purposes; - ISPIF (ISP Interface) module. Handles the routing of the data streams from the CSIDs to the inputs of the VFE; -- VFE (Video Front End) module. Contains a pipeline of image processing hardware - blocks. The VFE has different input interfaces. The PIX (Pixel) input +- 1 / 2 VFE (Video Front End) module(s). Contain a pipeline of image processing + hardware blocks. The VFE has different input interfaces. The PIX (Pixel) input interface feeds the input data to the image processing pipeline. The image processing pipeline contains also a scale and crop module at the end. Three RDI (Raw Dump Interface) input interfaces bypass the image processing @@ -49,18 +49,33 @@ The current version of the driver supports: - Input from camera sensor via CSIPHY; - Generation of test input data by the TG in CSID; -- RDI interface of VFE - raw dump of the input data to memory. +- RDI interface of VFE - Supported formats: + - Raw dump of the input data to memory. - - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2 - V4L2_PIX_FMT_YUYV / - V4L2_PIX_FMT_UYVY / V4L2_PIX_FMT_YVYU / V4L2_PIX_FMT_VYUY); - - MIPI RAW8 (8bit Bayer RAW - V4L2_PIX_FMT_SRGGB8 / - V4L2_PIX_FMT_SGRBG8 / V4L2_PIX_FMT_SGBRG8 / V4L2_PIX_FMT_SBGGR8); - - MIPI RAW10 (10bit packed Bayer RAW - V4L2_PIX_FMT_SBGGR10P / - V4L2_PIX_FMT_SGBRG10P / V4L2_PIX_FMT_SGRBG10P / V4L2_PIX_FMT_SRGGB10P); - - MIPI RAW12 (12bit packed Bayer RAW - V4L2_PIX_FMT_SRGGB12P / - V4L2_PIX_FMT_SGBRG12P / V4L2_PIX_FMT_SGRBG12P / V4L2_PIX_FMT_SRGGB12P). + Supported formats: + + - YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2 - V4L2_PIX_FMT_YUYV / + V4L2_PIX_FMT_UYVY / V4L2_PIX_FMT_YVYU / V4L2_PIX_FMT_VYUY); + - MIPI RAW8 (8bit Bayer RAW - V4L2_PIX_FMT_SRGGB8 / + V4L2_PIX_FMT_SGRBG8 / V4L2_PIX_FMT_SGBRG8 / V4L2_PIX_FMT_SBGGR8); + - MIPI RAW10 (10bit packed Bayer RAW - V4L2_PIX_FMT_SBGGR10P / + V4L2_PIX_FMT_SGBRG10P / V4L2_PIX_FMT_SGRBG10P / V4L2_PIX_FMT_SRGGB10P / + V4L2_PIX_FMT_Y10P); + - MIPI RAW12 (12bit packed Bayer RAW - V4L2_PIX_FMT_SRGGB12P / + V4L2_PIX_FMT_SGBRG12P / V4L2_PIX_FMT_SGRBG12P / V4L2_PIX_FMT_SRGGB12P). + - (8x96 only) MIPI RAW14 (14bit packed Bayer RAW - V4L2_PIX_FMT_SRGGB14P / + V4L2_PIX_FMT_SGBRG14P / V4L2_PIX_FMT_SGRBG14P / V4L2_PIX_FMT_SRGGB14P). + + - (8x96 only) Format conversion of the input data. + + Supported input formats: + + - MIPI RAW10 (10bit packed Bayer RAW - V4L2_PIX_FMT_SBGGR10P / V4L2_PIX_FMT_Y10P). + + Supported output formats: + + - Plain16 RAW10 (10bit unpacked Bayer RAW - V4L2_PIX_FMT_SBGGR10 / V4L2_PIX_FMT_Y10). - PIX interface of VFE @@ -75,14 +90,16 @@ The current version of the driver supports: - NV12/NV21 (two plane YUV 4:2:0 - V4L2_PIX_FMT_NV12 / V4L2_PIX_FMT_NV21); - NV16/NV61 (two plane YUV 4:2:2 - V4L2_PIX_FMT_NV16 / V4L2_PIX_FMT_NV61). + - (8x96 only) YUYV/UYVY/YVYU/VYUY (packed YUV 4:2:2 - V4L2_PIX_FMT_YUYV / + V4L2_PIX_FMT_UYVY / V4L2_PIX_FMT_YVYU / V4L2_PIX_FMT_VYUY). - Scaling support. Configuration of the VFE Encoder Scale module for downscalling with ratio up to 16x. - Cropping support. Configuration of the VFE Encoder Crop module. -- Concurrent and independent usage of two data inputs - could be camera sensors - and/or TG. +- Concurrent and independent usage of two (8x96: three) data inputs - + could be camera sensors and/or TG. Driver Architecture and Design @@ -90,14 +107,14 @@ Driver Architecture and Design The driver implements the V4L2 subdev interface. With the goal to model the hardware links between the modules and to expose a clean, logical and usable -interface, the driver is split into V4L2 sub-devices as follows: +interface, the driver is split into V4L2 sub-devices as follows (8x16 / 8x96): -- 2 CSIPHY sub-devices - each CSIPHY is represented by a single sub-device; -- 2 CSID sub-devices - each CSID is represented by a single sub-device; -- 2 ISPIF sub-devices - ISPIF is represented by a number of sub-devices equal - to the number of CSID sub-devices; -- 4 VFE sub-devices - VFE is represented by a number of sub-devices equal to - the number of the input interfaces (3 RDI and 1 PIX). +- 2 / 3 CSIPHY sub-devices - each CSIPHY is represented by a single sub-device; +- 2 / 4 CSID sub-devices - each CSID is represented by a single sub-device; +- 2 / 4 ISPIF sub-devices - ISPIF is represented by a number of sub-devices + equal to the number of CSID sub-devices; +- 4 / 8 VFE sub-devices - VFE is represented by a number of sub-devices equal to + the number of the input interfaces (3 RDI and 1 PIX for each VFE). The considerations to split the driver in this particular way are as follows: @@ -115,8 +132,8 @@ The considerations to split the driver in this particular way are as follows: Each VFE sub-device is linked to a separate video device node. -The media controller pipeline graph is as follows (with connected two OV5645 -camera sensors): +The media controller pipeline graph is as follows (with connected two / three +OV5645 camera sensors): .. _qcom_camss_graph: @@ -124,7 +141,13 @@ camera sensors): :alt: qcom_camss_graph.dot :align: center - Media pipeline graph + Media pipeline graph 8x16 + +.. kernel-figure:: qcom_camss_8x96_graph.dot + :alt: qcom_camss_8x96_graph.dot + :align: center + + Media pipeline graph 8x96 Implementation @@ -149,8 +172,12 @@ APQ8016 Specification: https://developer.qualcomm.com/download/sd410/snapdragon-410-processor-device-specification.pdf Referenced 2016-11-24. +APQ8096 Specification: +https://developer.qualcomm.com/download/sd820e/qualcomm-snapdragon-820e-processor-apq8096sge-device-specification.pdf +Referenced 2018-06-22. References ---------- .. [#f1] https://source.codeaurora.org/quic/la/kernel/msm-3.10/ +.. [#f2] https://source.codeaurora.org/quic/la/kernel/msm-3.18/ diff --git a/Documentation/media/v4l-drivers/qcom_camss_8x96_graph.dot b/Documentation/media/v4l-drivers/qcom_camss_8x96_graph.dot new file mode 100644 index 000000000000..de34f0a7afdc --- /dev/null +++ b/Documentation/media/v4l-drivers/qcom_camss_8x96_graph.dot @@ -0,0 +1,104 @@ +digraph board { + rankdir=TB + n00000001 [label="{{<port0> 0} | msm_csiphy0\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n0000000a:port0 [style=dashed] + n00000001:port1 -> n0000000d:port0 [style=dashed] + n00000001:port1 -> n00000010:port0 [style=dashed] + n00000001:port1 -> n00000013:port0 [style=dashed] + n00000004 [label="{{<port0> 0} | msm_csiphy1\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000004:port1 -> n0000000a:port0 [style=dashed] + n00000004:port1 -> n0000000d:port0 [style=dashed] + n00000004:port1 -> n00000010:port0 [style=dashed] + n00000004:port1 -> n00000013:port0 [style=dashed] + n00000007 [label="{{<port0> 0} | msm_csiphy2\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a:port0 [style=dashed] + n00000007:port1 -> n0000000d:port0 [style=dashed] + n00000007:port1 -> n00000010:port0 [style=dashed] + n00000007:port1 -> n00000013:port0 [style=dashed] + n0000000a [label="{{<port0> 0} | msm_csid0\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000a:port1 -> n00000016:port0 [style=dashed] + n0000000a:port1 -> n00000019:port0 [style=dashed] + n0000000a:port1 -> n0000001c:port0 [style=dashed] + n0000000a:port1 -> n0000001f:port0 [style=dashed] + n0000000d [label="{{<port0> 0} | msm_csid1\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000000d:port1 -> n00000016:port0 [style=dashed] + n0000000d:port1 -> n00000019:port0 [style=dashed] + n0000000d:port1 -> n0000001c:port0 [style=dashed] + n0000000d:port1 -> n0000001f:port0 [style=dashed] + n00000010 [label="{{<port0> 0} | msm_csid2\n/dev/v4l-subdev5 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000010:port1 -> n00000016:port0 [style=dashed] + n00000010:port1 -> n00000019:port0 [style=dashed] + n00000010:port1 -> n0000001c:port0 [style=dashed] + n00000010:port1 -> n0000001f:port0 [style=dashed] + n00000013 [label="{{<port0> 0} | msm_csid3\n/dev/v4l-subdev6 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000013:port1 -> n00000016:port0 [style=dashed] + n00000013:port1 -> n00000019:port0 [style=dashed] + n00000013:port1 -> n0000001c:port0 [style=dashed] + n00000013:port1 -> n0000001f:port0 [style=dashed] + n00000016 [label="{{<port0> 0} | msm_ispif0\n/dev/v4l-subdev7 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000016:port1 -> n00000022:port0 [style=dashed] + n00000016:port1 -> n0000002b:port0 [style=dashed] + n00000016:port1 -> n00000034:port0 [style=dashed] + n00000016:port1 -> n0000003d:port0 [style=dashed] + n00000016:port1 -> n00000046:port0 [style=dashed] + n00000016:port1 -> n0000004f:port0 [style=dashed] + n00000016:port1 -> n00000058:port0 [style=dashed] + n00000016:port1 -> n00000061:port0 [style=dashed] + n00000019 [label="{{<port0> 0} | msm_ispif1\n/dev/v4l-subdev8 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000019:port1 -> n00000022:port0 [style=dashed] + n00000019:port1 -> n0000002b:port0 [style=dashed] + n00000019:port1 -> n00000034:port0 [style=dashed] + n00000019:port1 -> n0000003d:port0 [style=dashed] + n00000019:port1 -> n00000046:port0 [style=dashed] + n00000019:port1 -> n0000004f:port0 [style=dashed] + n00000019:port1 -> n00000058:port0 [style=dashed] + n00000019:port1 -> n00000061:port0 [style=dashed] + n0000001c [label="{{<port0> 0} | msm_ispif2\n/dev/v4l-subdev9 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000001c:port1 -> n00000022:port0 [style=dashed] + n0000001c:port1 -> n0000002b:port0 [style=dashed] + n0000001c:port1 -> n00000034:port0 [style=dashed] + n0000001c:port1 -> n0000003d:port0 [style=dashed] + n0000001c:port1 -> n00000046:port0 [style=dashed] + n0000001c:port1 -> n0000004f:port0 [style=dashed] + n0000001c:port1 -> n00000058:port0 [style=dashed] + n0000001c:port1 -> n00000061:port0 [style=dashed] + n0000001f [label="{{<port0> 0} | msm_ispif3\n/dev/v4l-subdev10 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000001f:port1 -> n00000022:port0 [style=dashed] + n0000001f:port1 -> n0000002b:port0 [style=dashed] + n0000001f:port1 -> n00000034:port0 [style=dashed] + n0000001f:port1 -> n0000003d:port0 [style=dashed] + n0000001f:port1 -> n00000046:port0 [style=dashed] + n0000001f:port1 -> n0000004f:port0 [style=dashed] + n0000001f:port1 -> n00000058:port0 [style=dashed] + n0000001f:port1 -> n00000061:port0 [style=dashed] + n00000022 [label="{{<port0> 0} | msm_vfe0_rdi0\n/dev/v4l-subdev11 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000022:port1 -> n00000025 [style=bold] + n00000025 [label="msm_vfe0_video0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n0000002b [label="{{<port0> 0} | msm_vfe0_rdi1\n/dev/v4l-subdev12 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000002b:port1 -> n0000002e [style=bold] + n0000002e [label="msm_vfe0_video1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000034 [label="{{<port0> 0} | msm_vfe0_rdi2\n/dev/v4l-subdev13 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000034:port1 -> n00000037 [style=bold] + n00000037 [label="msm_vfe0_video2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n0000003d [label="{{<port0> 0} | msm_vfe0_pix\n/dev/v4l-subdev14 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000003d:port1 -> n00000040 [style=bold] + n00000040 [label="msm_vfe0_video3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000046 [label="{{<port0> 0} | msm_vfe1_rdi0\n/dev/v4l-subdev15 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000046:port1 -> n00000049 [style=bold] + n00000049 [label="msm_vfe1_video0\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000004f [label="{{<port0> 0} | msm_vfe1_rdi1\n/dev/v4l-subdev16 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000004f:port1 -> n00000052 [style=bold] + n00000052 [label="msm_vfe1_video1\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n00000058 [label="{{<port0> 0} | msm_vfe1_rdi2\n/dev/v4l-subdev17 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000058:port1 -> n0000005b [style=bold] + n0000005b [label="msm_vfe1_video2\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n00000061 [label="{{<port0> 0} | msm_vfe1_pix\n/dev/v4l-subdev18 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000061:port1 -> n00000064 [style=bold] + n00000064 [label="msm_vfe1_video3\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n000000e2 [label="{{} | ov5645 3-0039\n/dev/v4l-subdev19 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n000000e2:port0 -> n00000004:port0 [style=bold] + n000000e4 [label="{{} | ov5645 3-003a\n/dev/v4l-subdev20 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n000000e4:port0 -> n00000007:port0 [style=bold] + n000000e6 [label="{{} | ov5645 3-003b\n/dev/v4l-subdev21 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green] + n000000e6:port0 -> n00000001:port0 [style=bold] +} diff --git a/Documentation/media/video.h.rst.exceptions b/Documentation/media/video.h.rst.exceptions index a91aa884ce0e..371cdbd7d062 100644 --- a/Documentation/media/video.h.rst.exceptions +++ b/Documentation/media/video.h.rst.exceptions @@ -34,7 +34,4 @@ replace typedef video_displayformat_t :c:type:`video_displayformat` replace typedef video_size_t :c:type:`video_size` replace typedef video_stream_source_t :c:type:`video_stream_source` replace typedef video_play_state_t :c:type:`video_play_state` -replace typedef video_highlight_t :c:type:`video_highlight` -replace typedef video_spu_t :c:type:`video_spu` -replace typedef video_spu_palette_t :c:type:`video_spu_palette` replace typedef video_navi_pack_t :c:type:`video_navi_pack` diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions index a5cb0a8686ac..ca9f0edc579e 100644 --- a/Documentation/media/videodev2.h.rst.exceptions +++ b/Documentation/media/videodev2.h.rst.exceptions @@ -517,7 +517,6 @@ ignore define V4L2_CTRL_WHICH_DEF_VAL ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS ignore define V4L2_CID_MAX_CTRLS -ignore ioctl VIDIOC_RESERVED ignore define BASE_VIDIOC_PRIVATE # Associate ioctls with their counterparts diff --git a/MAINTAINERS b/MAINTAINERS index 5ca346e6140b..9f89543b2ffd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2295,6 +2295,14 @@ L: linux-leds@vger.kernel.org S: Maintained F: drivers/leds/leds-as3645a.c +ASAHI KASEI AK7375 LENS VOICE COIL DRIVER +M: Tianshu Qiu <tian.shu.qiu@intel.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/ak7375.c +F: Documentation/devicetree/bindings/media/i2c/ak7375.txt + ASAHI KASEI AK8974 DRIVER M: Linus Walleij <linus.walleij@linaro.org> L: linux-iio@vger.kernel.org @@ -4436,6 +4444,13 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/i2c/dw9714.c +DONGWOON DW9807 LENS VOICE COIL DRIVER +M: Sakari Ailus <sakari.ailus@linux.intel.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/dw9807.c + DOUBLETALK DRIVER M: "James R. Van Zandt" <jrv@vanzandt.mv.com> L: blinux-list@redhat.com @@ -5086,6 +5101,18 @@ T: git git://linuxtv.org/anttip/media_tree.git S: Maintained F: drivers/media/tuners/e4000* +EARTH_PT1 MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/pci/pt1/ + +EARTH_PT3 MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/pci/pt3/ + EC100 MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> L: linux-media@vger.kernel.org @@ -7276,6 +7303,9 @@ F: drivers/dma/iop-adma.c INTEL IPU3 CSI-2 CIO2 DRIVER M: Yong Zhi <yong.zhi@intel.com> M: Sakari Ailus <sakari.ailus@linux.intel.com> +M: Bingbu Cao <bingbu.cao@intel.com> +R: Tian Shu Qiu <tian.shu.qiu@intel.com> +R: Jian Xu Zheng <jian.xu.zheng@intel.com> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/pci/intel/ipu3/ @@ -8988,6 +9018,14 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/dvb-frontends/stv6111* +MEDIA DRIVERS FOR STM32 - DCMI +M: Hugues Fruchet <hugues.fruchet@st.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Supported +F: Documentation/devicetree/bindings/media/st,stm32-dcmi.txt +F: drivers/media/platform/stm32/stm32-dcmi.c + MEDIA DRIVERS FOR NVIDIA TEGRA - VDE M: Dmitry Osipenko <digetx@gmail.com> L: linux-media@vger.kernel.org @@ -9678,6 +9716,14 @@ F: Documentation/devicetree/bindings/media/i2c/mt9v032.txt F: drivers/media/i2c/mt9v032.c F: include/media/i2c/mt9v032.h +MT9V111 APTINA CAMERA SENSOR +M: Jacopo Mondi <jacopo@jmondi.org> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.txt +F: drivers/media/i2c/mt9v111.c + MULTIFUNCTION DEVICES (MFD) M: Lee Jones <lee.jones@linaro.org> T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git @@ -9722,6 +9768,12 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/musb/ +MXL301RF MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/tuners/mxl301rf* + MXL5007T MEDIA DRIVER M: Michael Krufky <mkrufky@linuxtv.org> L: linux-media@vger.kernel.org @@ -10504,6 +10556,14 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/i2c/ov13858.c +OMNIVISION OV2680 SENSOR DRIVER +M: Rui Miguel Silva <rmfrfs@gmail.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/ov2680.c +F: Documentation/devicetree/bindings/media/i2c/ov2680.txt + OMNIVISION OV2685 SENSOR DRIVER M: Shunqian Zheng <zhengsq@rock-chips.com> L: linux-media@vger.kernel.org @@ -11807,6 +11867,18 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/qlogic/qlge/ +QM1D1B0004 MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/tuners/qm1d1b0004* + +QM1D1C0042 MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/tuners/qm1d1c0042* + QNX4 FILESYSTEM M: Anders Larsen <al@alarsen.net> W: http://www.alarsen.net/linux/qnx4fs/ @@ -11855,7 +11927,7 @@ L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/qcom,camss.txt F: Documentation/media/v4l-drivers/qcom_camss.rst -F: drivers/media/platform/qcom/camss-8x16/ +F: drivers/media/platform/qcom/camss/ QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin <ilia.lin@gmail.com> @@ -12913,6 +12985,14 @@ W: http://www.ibm.com/developerworks/linux/linux390/ S: Supported F: net/smc/ +SHARP RJ54N1CB0C SENSOR DRIVER +M: Jacopo Mondi <jacopo@jmondi.org> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Odd fixes +F: drivers/media/i2c/rj54n1cb0c.c +F: include/media/i2c/rj54n1cb0c.h + SH_VEU V4L2 MEM2MEM DRIVER L: linux-media@vger.kernel.org S: Orphan @@ -13929,6 +14009,12 @@ F: include/uapi/linux/tc_act/ F: include/uapi/linux/tc_ematch/ F: net/sched/ +TC90522 MEDIA DRIVER +M: Akihiro Tsukada <tskd08@gmail.com> +L: linux-media@vger.kernel.org +S: Odd Fixes +F: drivers/media/dvb-frontends/tc90522* + TCP LOW PRIORITY MODULE M: "Wong Hoi Sing, Edison" <hswong3i@gmail.com> M: "Hung Hing Lun, Mike" <hlhung3i@gmail.com> @@ -15218,6 +15304,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/via/via-velocity.* +VICODEC VIRTUAL CODEC DRIVER +M: Hans Verkuil <hans.verkuil@cisco.com> +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: https://linuxtv.org +S: Maintained +F: drivers/media/platform/vicodec/* + VIDEO MULTIPLEXER DRIVER M: Philipp Zabel <p.zabel@pengutronix.de> L: linux-media@vger.kernel.org diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index de8393cb7313..8f234d0435aa 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -1,40 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas - AP-325RXA * (Compatible with Algo System ., LTD. - AP-320A) * * Copyright (C) 2008 Renesas Solutions Corp. * Author : Yusuke Goda <goda.yuske@renesas.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ -#include <linux/init.h> +#include <asm/clock.h> +#include <asm/io.h> +#include <asm/suspend.h> + +#include <cpu/sh7723.h> + +#include <linux/clkdev.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/i2c.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> +#include <linux/memblock.h> +#include <linux/mfd/tmio.h> #include <linux/mmc/host.h> #include <linux/mtd/physmap.h> #include <linux/mtd/sh_flctl.h> -#include <linux/mfd/tmio.h> -#include <linux/delay.h> -#include <linux/i2c.h> +#include <linux/platform_device.h> #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> +#include <linux/sh_intc.h> #include <linux/smsc911x.h> -#include <linux/gpio.h> #include <linux/videodev2.h> -#include <linux/sh_intc.h> + +#include <media/drv-intf/renesas-ceu.h> #include <media/i2c/ov772x.h> -#include <media/soc_camera.h> -#include <linux/platform_data/media/soc_camera_platform.h> -#include <media/drv-intf/sh_mobile_ceu.h> + #include <video/sh_mobile_lcdc.h> -#include <asm/io.h> -#include <asm/clock.h> -#include <asm/suspend.h> -#include <cpu/sh7723.h> + +#define CEU_BUFFER_MEMORY_SIZE (4 << 20) +static phys_addr_t ceu_dma_membase; /* Dummy supplies, where voltage doesn't matter */ static struct regulator_consumer_supply dummy_supplies[] = { @@ -253,150 +258,25 @@ static struct platform_device lcdc_device = { }, }; -static void camera_power(int val) -{ - gpio_set_value(GPIO_PTZ5, val); /* RST_CAM/RSTB */ - mdelay(10); -} - -#ifdef CONFIG_I2C -/* support for the old ncm03j camera */ -static unsigned char camera_ncm03j_magic[] = -{ - 0x87, 0x00, 0x88, 0x08, 0x89, 0x01, 0x8A, 0xE8, - 0x1D, 0x00, 0x1E, 0x8A, 0x21, 0x00, 0x33, 0x36, - 0x36, 0x60, 0x37, 0x08, 0x3B, 0x31, 0x44, 0x0F, - 0x46, 0xF0, 0x4B, 0x28, 0x4C, 0x21, 0x4D, 0x55, - 0x4E, 0x1B, 0x4F, 0xC7, 0x50, 0xFC, 0x51, 0x12, - 0x58, 0x02, 0x66, 0xC0, 0x67, 0x46, 0x6B, 0xA0, - 0x6C, 0x34, 0x7E, 0x25, 0x7F, 0x25, 0x8D, 0x0F, - 0x92, 0x40, 0x93, 0x04, 0x94, 0x26, 0x95, 0x0A, - 0x99, 0x03, 0x9A, 0xF0, 0x9B, 0x14, 0x9D, 0x7A, - 0xC5, 0x02, 0xD6, 0x07, 0x59, 0x00, 0x5A, 0x1A, - 0x5B, 0x2A, 0x5C, 0x37, 0x5D, 0x42, 0x5E, 0x56, - 0xC8, 0x00, 0xC9, 0x1A, 0xCA, 0x2A, 0xCB, 0x37, - 0xCC, 0x42, 0xCD, 0x56, 0xCE, 0x00, 0xCF, 0x1A, - 0xD0, 0x2A, 0xD1, 0x37, 0xD2, 0x42, 0xD3, 0x56, - 0x5F, 0x68, 0x60, 0x87, 0x61, 0xA3, 0x62, 0xBC, - 0x63, 0xD4, 0x64, 0xEA, 0xD6, 0x0F, -}; - -static int camera_probe(void) -{ - struct i2c_adapter *a = i2c_get_adapter(0); - struct i2c_msg msg; - int ret; - - if (!a) - return -ENODEV; - - camera_power(1); - msg.addr = 0x6e; - msg.buf = camera_ncm03j_magic; - msg.len = 2; - msg.flags = 0; - ret = i2c_transfer(a, &msg, 1); - camera_power(0); - - return ret; -} - -static int camera_set_capture(struct soc_camera_platform_info *info, - int enable) -{ - struct i2c_adapter *a = i2c_get_adapter(0); - struct i2c_msg msg; - int ret = 0; - int i; - - camera_power(0); - if (!enable) - return 0; /* no disable for now */ - - camera_power(1); - for (i = 0; i < ARRAY_SIZE(camera_ncm03j_magic); i += 2) { - u_int8_t buf[8]; - - msg.addr = 0x6e; - msg.buf = buf; - msg.len = 2; - msg.flags = 0; - - buf[0] = camera_ncm03j_magic[i]; - buf[1] = camera_ncm03j_magic[i + 1]; - - ret = (ret < 0) ? ret : i2c_transfer(a, &msg, 1); - } - - return ret; -} - -static int ap325rxa_camera_add(struct soc_camera_device *icd); -static void ap325rxa_camera_del(struct soc_camera_device *icd); - -static struct soc_camera_platform_info camera_info = { - .format_name = "UYVY", - .format_depth = 16, - .format = { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .field = V4L2_FIELD_NONE, - .width = 640, - .height = 480, +/* Powerdown/reset gpios for CEU image sensors */ +static struct gpiod_lookup_table ov7725_gpios = { + .dev_id = "0-0021", + .table = { + GPIO_LOOKUP("sh7723_pfc", GPIO_PTZ5, "reset", GPIO_ACTIVE_LOW), }, - .mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH, - .mbus_type = V4L2_MBUS_PARALLEL, - .set_capture = camera_set_capture, -}; - -static struct soc_camera_link camera_link = { - .bus_id = 0, - .add_device = ap325rxa_camera_add, - .del_device = ap325rxa_camera_del, - .module_name = "soc_camera_platform", - .priv = &camera_info, }; -static struct platform_device *camera_device; - -static void ap325rxa_camera_release(struct device *dev) -{ - soc_camera_platform_release(&camera_device); -} - -static int ap325rxa_camera_add(struct soc_camera_device *icd) -{ - int ret = soc_camera_platform_add(icd, &camera_device, &camera_link, - ap325rxa_camera_release, 0); - if (ret < 0) - return ret; - - ret = camera_probe(); - if (ret < 0) - soc_camera_platform_del(icd, camera_device, &camera_link); - - return ret; -} - -static void ap325rxa_camera_del(struct soc_camera_device *icd) -{ - soc_camera_platform_del(icd, camera_device, &camera_link); -} -#endif /* CONFIG_I2C */ - -static int ov7725_power(struct device *dev, int mode) -{ - camera_power(0); - if (mode) - camera_power(1); - - return 0; -} - -static struct sh_mobile_ceu_info sh_mobile_ceu_info = { - .flags = SH_CEU_FLAG_USE_8BIT_BUS, +static struct ceu_platform_data ceu0_pdata = { + .num_subdevs = 1, + .subdevs = { + { /* [0] = ov7725 */ + .flags = 0, + .bus_width = 8, + .bus_shift = 0, + .i2c_adapter_id = 0, + .i2c_address = 0x21, + }, + }, }; static struct resource ceu_resources[] = { @@ -410,18 +290,15 @@ static struct resource ceu_resources[] = { .start = evt2irq(0x880), .flags = IORESOURCE_IRQ, }, - [2] = { - /* place holder for contiguous memory */ - }, }; -static struct platform_device ceu_device = { - .name = "sh_mobile_ceu", - .id = 0, /* "ceu0" clock */ +static struct platform_device ap325rxa_ceu_device = { + .name = "renesas-ceu", + .id = 0, /* "ceu.0" clock */ .num_resources = ARRAY_SIZE(ceu_resources), .resource = ceu_resources, .dev = { - .platform_data = &sh_mobile_ceu_info, + .platform_data = &ceu0_pdata, }, }; @@ -488,44 +365,18 @@ static struct platform_device sdhi1_cn7_device = { }, }; -static struct i2c_board_info __initdata ap325rxa_i2c_devices[] = { - { - I2C_BOARD_INFO("pcf8563", 0x51), - }, -}; - -static struct i2c_board_info ap325rxa_i2c_camera[] = { - { - I2C_BOARD_INFO("ov772x", 0x21), - }, -}; - static struct ov772x_camera_info ov7725_info = { .flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP, .edgectrl = OV772X_AUTO_EDGECTRL(0xf, 0), }; -static struct soc_camera_link ov7725_link = { - .bus_id = 0, - .power = ov7725_power, - .board_info = &ap325rxa_i2c_camera[0], - .i2c_adapter_id = 0, - .priv = &ov7725_info, -}; - -static struct platform_device ap325rxa_camera[] = { +static struct i2c_board_info ap325rxa_i2c_devices[] __initdata = { { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &ov7725_link, - }, - }, { - .name = "soc-camera-pdrv", - .id = 1, - .dev = { - .platform_data = &camera_link, - }, + I2C_BOARD_INFO("pcf8563", 0x51), + }, + { + I2C_BOARD_INFO("ov772x", 0x21), + .platform_data = &ov7725_info, }, }; @@ -533,12 +384,9 @@ static struct platform_device *ap325rxa_devices[] __initdata = { &smsc9118_device, &ap325rxa_nor_flash_device, &lcdc_device, - &ceu_device, &nand_flash_device, &sdhi0_cn3_device, &sdhi1_cn7_device, - &ap325rxa_camera[0], - &ap325rxa_camera[1], }; extern char ap325rxa_sdram_enter_start; @@ -649,8 +497,6 @@ static int __init ap325rxa_devices_setup(void) __raw_writew(0xFFFF, PORT_DRVCRA); __raw_writew(0xFFFF, PORT_DRVCRB); - platform_resource_setup_memory(&ceu_device, "ceu", 4 << 20); - /* SDHI0 - CN3 - SD CARD */ gpio_request(GPIO_FN_SDHI0CD_PTD, NULL); gpio_request(GPIO_FN_SDHI0WP_PTD, NULL); @@ -670,9 +516,25 @@ static int __init ap325rxa_devices_setup(void) gpio_request(GPIO_FN_SDHI1CMD, NULL); gpio_request(GPIO_FN_SDHI1CLK, NULL); + /* Add a clock alias for ov7725 xclk source. */ + clk_add_alias(NULL, "0-0021", "video_clk", NULL); + + /* Register RSTB gpio for ov7725 camera sensor. */ + gpiod_add_lookup_table(&ov7725_gpios); + i2c_register_board_info(0, ap325rxa_i2c_devices, ARRAY_SIZE(ap325rxa_i2c_devices)); + /* Initialize CEU platform device separately to map memory first */ + device_initialize(&ap325rxa_ceu_device.dev); + arch_setup_pdev_archdata(&ap325rxa_ceu_device); + dma_declare_coherent_memory(&ap325rxa_ceu_device.dev, + ceu_dma_membase, ceu_dma_membase, + ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1, + DMA_MEMORY_EXCLUSIVE); + + platform_device_add(&ap325rxa_ceu_device); + return platform_add_devices(ap325rxa_devices, ARRAY_SIZE(ap325rxa_devices)); } @@ -689,7 +551,21 @@ static int ap325rxa_mode_pins(void) return MODE_PIN5 | MODE_PIN8; } +/* Reserve a portion of memory for CEU buffers */ +static void __init ap325rxa_mv_mem_reserve(void) +{ + phys_addr_t phys; + phys_addr_t size = CEU_BUFFER_MEMORY_SIZE; + + phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE); + memblock_free(phys, size); + memblock_remove(phys, size); + + ceu_dma_membase = phys; +} + static struct sh_machine_vector mv_ap325rxa __initmv = { .mv_name = "AP-325RXA", .mv_mode_pins = ap325rxa_mode_pins, + .mv_mem_reserve = ap325rxa_mv_mem_reserve, }; diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index 6af7777332fc..e59c577ed871 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -1,41 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 /* * KFR2R09 board support code * * Copyright (C) 2009 Magnus Damm - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/mmc/host.h> -#include <linux/mfd/tmio.h> -#include <linux/mtd/physmap.h> -#include <linux/mtd/onenand.h> + +#include <asm/clock.h> +#include <asm/io.h> +#include <asm/machvec.h> +#include <asm/suspend.h> + +#include <cpu/sh7724.h> + +#include <linux/clkdev.h> #include <linux/delay.h> -#include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/gpio.h> +#include <linux/gpio/machine.h> +#include <linux/i2c.h> +#include <linux/init.h> #include <linux/input.h> #include <linux/input/sh_keysc.h> -#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/memblock.h> +#include <linux/mfd/tmio.h> +#include <linux/mmc/host.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/physmap.h> #include <linux/platform_data/lv5207lp.h> +#include <linux/platform_device.h> #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> +#include <linux/sh_intc.h> #include <linux/usb/r8a66597.h> #include <linux/videodev2.h> -#include <linux/sh_intc.h> + +#include <mach/kfr2r09.h> + +#include <media/drv-intf/renesas-ceu.h> #include <media/i2c/rj54n1cb0c.h> -#include <media/soc_camera.h> -#include <media/drv-intf/sh_mobile_ceu.h> + #include <video/sh_mobile_lcdc.h> -#include <asm/suspend.h> -#include <asm/clock.h> -#include <asm/machvec.h> -#include <asm/io.h> -#include <cpu/sh7724.h> -#include <mach/kfr2r09.h> + +#define CEU_BUFFER_MEMORY_SIZE (4 << 20) +static phys_addr_t ceu_dma_membase; + +/* set VIO_CKO clock to 25MHz */ +#define CEU_MCLK_FREQ 25000000 +#define DRVCRB 0xA405018C static struct mtd_partition kfr2r09_nor_flash_partitions[] = { @@ -230,8 +242,17 @@ static struct platform_device kfr2r09_usb0_gadget_device = { .resource = kfr2r09_usb0_gadget_resources, }; -static struct sh_mobile_ceu_info sh_mobile_ceu_info = { - .flags = SH_CEU_FLAG_USE_8BIT_BUS, +static struct ceu_platform_data ceu_pdata = { + .num_subdevs = 1, + .subdevs = { + { /* [0] = rj54n1cb0c */ + .flags = 0, + .bus_width = 8, + .bus_shift = 0, + .i2c_adapter_id = 1, + .i2c_address = 0x50, + }, + }, }; static struct resource kfr2r09_ceu_resources[] = { @@ -246,109 +267,35 @@ static struct resource kfr2r09_ceu_resources[] = { .end = evt2irq(0x880), .flags = IORESOURCE_IRQ, }, - [2] = { - /* place holder for contiguous memory */ - }, }; static struct platform_device kfr2r09_ceu_device = { - .name = "sh_mobile_ceu", + .name = "renesas-ceu", .id = 0, /* "ceu0" clock */ .num_resources = ARRAY_SIZE(kfr2r09_ceu_resources), .resource = kfr2r09_ceu_resources, .dev = { - .platform_data = &sh_mobile_ceu_info, + .platform_data = &ceu_pdata, }, }; -static struct i2c_board_info kfr2r09_i2c_camera = { - I2C_BOARD_INFO("rj54n1cb0c", 0x50), -}; - -static struct clk *camera_clk; - -/* set VIO_CKO clock to 25MHz */ -#define CEU_MCLK_FREQ 25000000 - -#define DRVCRB 0xA405018C -static int camera_power(struct device *dev, int mode) -{ - int ret; - - if (mode) { - long rate; - - camera_clk = clk_get(NULL, "video_clk"); - if (IS_ERR(camera_clk)) - return PTR_ERR(camera_clk); - - rate = clk_round_rate(camera_clk, CEU_MCLK_FREQ); - ret = clk_set_rate(camera_clk, rate); - if (ret < 0) - goto eclkrate; - - /* set DRVCRB - * - * use 1.8 V for VccQ_VIO - * use 2.85V for VccQ_SR - */ - __raw_writew((__raw_readw(DRVCRB) & ~0x0003) | 0x0001, DRVCRB); - - /* reset clear */ - ret = gpio_request(GPIO_PTB4, NULL); - if (ret < 0) - goto eptb4; - ret = gpio_request(GPIO_PTB7, NULL); - if (ret < 0) - goto eptb7; - - ret = gpio_direction_output(GPIO_PTB4, 1); - if (!ret) - ret = gpio_direction_output(GPIO_PTB7, 1); - if (ret < 0) - goto egpioout; - msleep(1); - - ret = clk_enable(camera_clk); /* start VIO_CKO */ - if (ret < 0) - goto eclkon; - - return 0; - } - - ret = 0; - - clk_disable(camera_clk); -eclkon: - gpio_set_value(GPIO_PTB7, 0); -egpioout: - gpio_set_value(GPIO_PTB4, 0); - gpio_free(GPIO_PTB7); -eptb7: - gpio_free(GPIO_PTB4); -eptb4: -eclkrate: - clk_put(camera_clk); - return ret; -} - static struct rj54n1_pdata rj54n1_priv = { .mclk_freq = CEU_MCLK_FREQ, .ioctl_high = false, }; -static struct soc_camera_link rj54n1_link = { - .power = camera_power, - .board_info = &kfr2r09_i2c_camera, - .i2c_adapter_id = 1, - .priv = &rj54n1_priv, +static struct i2c_board_info kfr2r09_i2c_camera = { + I2C_BOARD_INFO("rj54n1cb0c", 0x50), + .platform_data = &rj54n1_priv, }; -static struct platform_device kfr2r09_camera = { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &rj54n1_link, +static struct gpiod_lookup_table rj54n1_gpios = { + .dev_id = "1-0050", + .table = { + GPIO_LOOKUP("sh7724_pfc", GPIO_PTB4, "poweron", + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("sh7724_pfc", GPIO_PTB7, "enable", + GPIO_ACTIVE_HIGH), }, }; @@ -393,8 +340,6 @@ static struct platform_device *kfr2r09_devices[] __initdata = { &kfr2r09_nand_flash_device, &kfr2r09_sh_keysc_device, &kfr2r09_sh_lcdc_device, - &kfr2r09_ceu_device, - &kfr2r09_camera, &kfr2r09_sh_sdhi0_device, }; @@ -533,6 +478,8 @@ extern char kfr2r09_sdram_leave_end; static int __init kfr2r09_devices_setup(void) { + static struct clk *camera_clk; + /* register board specific self-refresh code */ sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF | SUSP_SH_RSTANDBY, @@ -622,8 +569,6 @@ static int __init kfr2r09_devices_setup(void) gpio_request(GPIO_FN_VIO0_D1, NULL); gpio_request(GPIO_FN_VIO0_D0, NULL); - platform_resource_setup_memory(&kfr2r09_ceu_device, "ceu", 4 << 20); - /* SDHI0 connected to yc304 */ gpio_request(GPIO_FN_SDHI0CD, NULL); gpio_request(GPIO_FN_SDHI0D3, NULL); @@ -635,6 +580,36 @@ static int __init kfr2r09_devices_setup(void) i2c_register_board_info(0, &kfr2r09_backlight_board_info, 1); + /* Set camera clock frequency and register and alias for rj54n1. */ + camera_clk = clk_get(NULL, "video_clk"); + if (!IS_ERR(camera_clk)) { + clk_set_rate(camera_clk, + clk_round_rate(camera_clk, CEU_MCLK_FREQ)); + clk_put(camera_clk); + } + clk_add_alias(NULL, "1-0050", "video_clk", NULL); + + /* set DRVCRB + * + * use 1.8 V for VccQ_VIO + * use 2.85V for VccQ_SR + */ + __raw_writew((__raw_readw(DRVCRB) & ~0x0003) | 0x0001, DRVCRB); + + gpiod_add_lookup_table(&rj54n1_gpios); + + i2c_register_board_info(1, &kfr2r09_i2c_camera, 1); + + /* Initialize CEU platform device separately to map memory first */ + device_initialize(&kfr2r09_ceu_device.dev); + arch_setup_pdev_archdata(&kfr2r09_ceu_device); + dma_declare_coherent_memory(&kfr2r09_ceu_device.dev, + ceu_dma_membase, ceu_dma_membase, + ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1, + DMA_MEMORY_EXCLUSIVE); + + platform_device_add(&kfr2r09_ceu_device); + return platform_add_devices(kfr2r09_devices, ARRAY_SIZE(kfr2r09_devices)); } @@ -651,10 +626,24 @@ static int kfr2r09_mode_pins(void) return MODE_PIN0 | MODE_PIN1 | MODE_PIN5 | MODE_PIN8; } +/* Reserve a portion of memory for CEU buffers */ +static void __init kfr2r09_mv_mem_reserve(void) +{ + phys_addr_t phys; + phys_addr_t size = CEU_BUFFER_MEMORY_SIZE; + + phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE); + memblock_free(phys, size); + memblock_remove(phys, size); + + ceu_dma_membase = phys; +} + /* * The Machine Vector */ static struct sh_machine_vector mv_kfr2r09 __initmv = { .mv_name = "kfr2r09", .mv_mode_pins = kfr2r09_mode_pins, + .mv_mem_reserve = kfr2r09_mv_mem_reserve, }; diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 3d7d0046cf49..254f2c662703 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -28,7 +28,6 @@ #include <video/sh_mobile_lcdc.h> #include <media/drv-intf/renesas-ceu.h> #include <media/i2c/ov772x.h> -#include <media/soc_camera.h> #include <media/i2c/tw9910.h> #include <asm/clock.h> #include <asm/machvec.h> @@ -351,8 +350,9 @@ static struct platform_device migor_ceu_device = { static struct gpiod_lookup_table ov7725_gpios = { .dev_id = "0-0021", .table = { - GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "pwdn", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "rstb", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("sh7722_pfc", GPIO_PTT0, "powerdown", + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("sh7722_pfc", GPIO_PTT3, "reset", GPIO_ACTIVE_LOW), }, }; @@ -592,7 +592,7 @@ static int __init migor_devices_setup(void) } /* Add a clock alias for ov7725 xclk source. */ - clk_add_alias("xclk", "0-0021", "video_clk", NULL); + clk_add_alias(NULL, "0-0021", "video_clk", NULL); /* Register GPIOs for video sources. */ gpiod_add_lookup_table(&ov7725_gpios); diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 255952555656..fdbec22ae687 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -1,43 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 /* * linux/arch/sh/boards/se/7724/setup.c * * Copyright (C) 2009 Renesas Solutions Corp. * * Kuninori Morimoto <morimoto.kuninori@renesas.com> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. */ +#include <asm/clock.h> +#include <asm/heartbeat.h> +#include <asm/io.h> +#include <asm/suspend.h> -#include <linux/init.h> +#include <cpu/sh7724.h> + +#include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/input/sh_keysc.h> #include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/mmc/host.h> +#include <linux/memblock.h> #include <linux/mfd/tmio.h> +#include <linux/mmc/host.h> #include <linux/mtd/physmap.h> -#include <linux/delay.h> +#include <linux/platform_device.h> #include <linux/regulator/fixed.h> #include <linux/regulator/machine.h> -#include <linux/smc91x.h> -#include <linux/gpio.h> -#include <linux/input.h> -#include <linux/input/sh_keysc.h> -#include <linux/usb/r8a66597.h> #include <linux/sh_eth.h> #include <linux/sh_intc.h> +#include <linux/smc91x.h> +#include <linux/usb/r8a66597.h> #include <linux/videodev2.h> -#include <video/sh_mobile_lcdc.h> -#include <media/drv-intf/sh_mobile_ceu.h> + +#include <mach-se/mach/se7724.h> +#include <media/drv-intf/renesas-ceu.h> + #include <sound/sh_fsi.h> #include <sound/simple_card.h> -#include <asm/io.h> -#include <asm/heartbeat.h> -#include <asm/clock.h> -#include <asm/suspend.h> -#include <cpu/sh7724.h> -#include <mach-se/mach/se7724.h> + +#include <video/sh_mobile_lcdc.h> + +#define CEU_BUFFER_MEMORY_SIZE (4 << 20) +static phys_addr_t ceu0_dma_membase; +static phys_addr_t ceu1_dma_membase; /* * SWx 1234 5678 @@ -216,8 +222,8 @@ static struct platform_device lcdc_device = { }; /* CEU0 */ -static struct sh_mobile_ceu_info sh_mobile_ceu0_info = { - .flags = SH_CEU_FLAG_USE_8BIT_BUS, +static struct ceu_platform_data ceu0_pdata = { + .num_subdevs = 0, }; static struct resource ceu0_resources[] = { @@ -231,24 +237,21 @@ static struct resource ceu0_resources[] = { .start = evt2irq(0x880), .flags = IORESOURCE_IRQ, }, - [2] = { - /* place holder for contiguous memory */ - }, }; static struct platform_device ceu0_device = { - .name = "sh_mobile_ceu", - .id = 0, /* "ceu0" clock */ + .name = "renesas-ceu", + .id = 0, /* "ceu.0" clock */ .num_resources = ARRAY_SIZE(ceu0_resources), .resource = ceu0_resources, .dev = { - .platform_data = &sh_mobile_ceu0_info, + .platform_data = &ceu0_pdata, }, }; /* CEU1 */ -static struct sh_mobile_ceu_info sh_mobile_ceu1_info = { - .flags = SH_CEU_FLAG_USE_8BIT_BUS, +static struct ceu_platform_data ceu1_pdata = { + .num_subdevs = 0, }; static struct resource ceu1_resources[] = { @@ -262,18 +265,15 @@ static struct resource ceu1_resources[] = { .start = evt2irq(0x9e0), .flags = IORESOURCE_IRQ, }, - [2] = { - /* place holder for contiguous memory */ - }, }; static struct platform_device ceu1_device = { - .name = "sh_mobile_ceu", - .id = 1, /* "ceu1" clock */ + .name = "renesas-ceu", + .id = 1, /* "ceu.1" clock */ .num_resources = ARRAY_SIZE(ceu1_resources), .resource = ceu1_resources, .dev = { - .platform_data = &sh_mobile_ceu1_info, + .platform_data = &ceu1_pdata, }, }; @@ -574,13 +574,16 @@ static struct platform_device vou_device = { }, }; +static struct platform_device *ms7724se_ceu_devices[] __initdata = { + &ceu0_device, + &ceu1_device, +}; + static struct platform_device *ms7724se_devices[] __initdata = { &heartbeat_device, &smc91x_eth_device, &lcdc_device, &nor_flash_device, - &ceu0_device, - &ceu1_device, &keysc_device, &sh_eth_device, &sh7724_usb0_host_device, @@ -797,7 +800,6 @@ static int __init devices_setup(void) gpio_request(GPIO_FN_VIO0_CLK, NULL); gpio_request(GPIO_FN_VIO0_FLD, NULL); gpio_request(GPIO_FN_VIO0_HD, NULL); - platform_resource_setup_memory(&ceu0_device, "ceu0", 4 << 20); /* enable CEU1 */ gpio_request(GPIO_FN_VIO1_D7, NULL); @@ -812,7 +814,6 @@ static int __init devices_setup(void) gpio_request(GPIO_FN_VIO1_HD, NULL); gpio_request(GPIO_FN_VIO1_VD, NULL); gpio_request(GPIO_FN_VIO1_CLK, NULL); - platform_resource_setup_memory(&ceu1_device, "ceu1", 4 << 20); /* KEYSC */ gpio_request(GPIO_FN_KEYOUT5_IN5, NULL); @@ -934,12 +935,49 @@ static int __init devices_setup(void) gpio_request(GPIO_FN_DV_VSYNC, NULL); gpio_request(GPIO_FN_DV_HSYNC, NULL); + /* Initialize CEU platform devices separately to map memory first */ + device_initialize(&ms7724se_ceu_devices[0]->dev); + arch_setup_pdev_archdata(ms7724se_ceu_devices[0]); + dma_declare_coherent_memory(&ms7724se_ceu_devices[0]->dev, + ceu0_dma_membase, ceu0_dma_membase, + ceu0_dma_membase + + CEU_BUFFER_MEMORY_SIZE - 1, + DMA_MEMORY_EXCLUSIVE); + platform_device_add(ms7724se_ceu_devices[0]); + + device_initialize(&ms7724se_ceu_devices[1]->dev); + arch_setup_pdev_archdata(ms7724se_ceu_devices[1]); + dma_declare_coherent_memory(&ms7724se_ceu_devices[1]->dev, + ceu1_dma_membase, ceu1_dma_membase, + ceu1_dma_membase + + CEU_BUFFER_MEMORY_SIZE - 1, + DMA_MEMORY_EXCLUSIVE); + platform_device_add(ms7724se_ceu_devices[1]); + return platform_add_devices(ms7724se_devices, ARRAY_SIZE(ms7724se_devices)); } device_initcall(devices_setup); +/* Reserve a portion of memory for CEU 0 and CEU 1 buffers */ +static void __init ms7724se_mv_mem_reserve(void) +{ + phys_addr_t phys; + phys_addr_t size = CEU_BUFFER_MEMORY_SIZE; + + phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE); + memblock_free(phys, size); + memblock_remove(phys, size); + ceu0_dma_membase = phys; + + phys = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_ALLOC_ANYWHERE); + memblock_free(phys, size); + memblock_remove(phys, size); + ceu1_dma_membase = phys; +} + static struct sh_machine_vector mv_ms7724se __initmv = { .mv_name = "ms7724se", .mv_init_irq = init_se7724_IRQ, + .mv_mem_reserve = ms7724se_mv_mem_reserve, }; diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c index fe844222f3f6..af01664f7b4c 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c @@ -260,7 +260,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("veu1", &mstp_clks[HWBLK_VEU2H1]), CLKDEV_DEV_ID("sh-vou.0", &mstp_clks[HWBLK_VOU]), CLKDEV_CON_ID("beu0", &mstp_clks[HWBLK_BEU]), - CLKDEV_DEV_ID("sh_mobile_ceu.0", &mstp_clks[HWBLK_CEU]), + CLKDEV_DEV_ID("ceu.0", &mstp_clks[HWBLK_CEU]), CLKDEV_CON_ID("veu0", &mstp_clks[HWBLK_VEU2H0]), CLKDEV_CON_ID("vpu0", &mstp_clks[HWBLK_VPU]), diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 14fedbeca724..039e0f91dba8 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -28,6 +28,7 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/slab.h> diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index b7fad0ec5710..030b2602faf0 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -74,7 +74,7 @@ void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { static const u16 max_events[CEC_NUM_EVENTS] = { - 1, 1, 800, 800, 8, 8, + 1, 1, 800, 800, 8, 8, 8, 8 }; struct cec_event_entry *entry; unsigned int ev_idx = new_ev->event - 1; @@ -176,6 +176,22 @@ void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts) } EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event); +/* Notify userspace that the 5V pin changed state at the given time. */ +void cec_queue_pin_5v_event(struct cec_adapter *adap, bool is_high, ktime_t ts) +{ + struct cec_event ev = { + .event = is_high ? CEC_EVENT_PIN_5V_HIGH : + CEC_EVENT_PIN_5V_LOW, + }; + struct cec_fh *fh; + + mutex_lock(&adap->devnode.lock); + list_for_each_entry(fh, &adap->devnode.fhs, list) + cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); + mutex_unlock(&adap->devnode.lock); +} +EXPORT_SYMBOL_GPL(cec_queue_pin_5v_event); + /* * Queue a new message for this filehandle. * diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 10b67fc40318..b6536bbad530 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -579,6 +579,14 @@ static int cec_open(struct inode *inode, struct file *filp) cec_queue_event_fh(fh, &ev, 0); } } + if (adap->pin && adap->pin->ops->read_5v) { + err = adap->pin->ops->read_5v(adap); + if (err >= 0) { + ev.event = err ? CEC_EVENT_PIN_5V_HIGH : + CEC_EVENT_PIN_5V_LOW; + cec_queue_event_fh(fh, &ev, 0); + } + } #endif list_add(&fh->list, &devnode->fhs); diff --git a/drivers/media/common/siano/smsdvb-debugfs.c b/drivers/media/common/siano/smsdvb-debugfs.c index 40891f4f842b..c95d4583498e 100644 --- a/drivers/media/common/siano/smsdvb-debugfs.c +++ b/drivers/media/common/siano/smsdvb-debugfs.c @@ -500,7 +500,7 @@ void smsdvb_debugfs_release(struct smsdvb_client_t *client) client->debugfs = NULL; } -int smsdvb_debugfs_register(void) +void smsdvb_debugfs_register(void) { struct dentry *d; @@ -517,15 +517,15 @@ int smsdvb_debugfs_register(void) d = debugfs_create_dir("smsdvb", usb_debug_root); if (IS_ERR_OR_NULL(d)) { pr_err("Couldn't create sysfs node for smsdvb\n"); - return PTR_ERR(d); - } else { - smsdvb_debugfs_usb_root = d; + return; } - return 0; + smsdvb_debugfs_usb_root = d; } void smsdvb_debugfs_unregister(void) { + if (!smsdvb_debugfs_usb_root) + return; debugfs_remove_recursive(smsdvb_debugfs_usb_root); smsdvb_debugfs_usb_root = NULL; } diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index c0faad1ba428..43cfd1dbda01 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -1047,9 +1047,9 @@ static void smsdvb_release(struct dvb_frontend *fe) static const struct dvb_frontend_ops smsdvb_fe_ops = { .info = { .name = "Siano Mobile Digital MDTV Receiver", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/common/siano/smsdvb.h b/drivers/media/common/siano/smsdvb.h index b15754d95ec0..befeb9817e54 100644 --- a/drivers/media/common/siano/smsdvb.h +++ b/drivers/media/common/siano/smsdvb.h @@ -107,7 +107,7 @@ struct RECEPTION_STATISTICS_PER_SLICES_S { int smsdvb_debugfs_create(struct smsdvb_client_t *client); void smsdvb_debugfs_release(struct smsdvb_client_t *client); -int smsdvb_debugfs_register(void); +void smsdvb_debugfs_register(void); void smsdvb_debugfs_unregister(void); #else @@ -119,10 +119,7 @@ static inline int smsdvb_debugfs_create(struct smsdvb_client_t *client) static inline void smsdvb_debugfs_release(struct smsdvb_client_t *client) {} -static inline int smsdvb_debugfs_register(void) -{ - return 0; -}; +static inline void smsdvb_debugfs_register(void) {} static inline void smsdvb_debugfs_unregister(void) {}; diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index f32ec7342ef0..5653e8eebe2b 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1377,6 +1377,11 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb) struct vb2_buffer *vb; int ret; + if (q->error) { + dprintk(1, "fatal error occurred on queue\n"); + return -EIO; + } + vb = q->bufs[index]; switch (vb->state) { diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 1310526b0d49..4d371cea0d5d 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1391,7 +1391,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, struct dvb_ca_slot *sl; slot = info->num; - if ((slot > ca->slot_count) || (slot < 0)) { + if ((slot >= ca->slot_count) || (slot < 0)) { err = -EINVAL; goto out_unlock; } diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index ce25aef39008..c4e7ebfe4d29 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -894,21 +894,67 @@ static int dvb_frontend_start(struct dvb_frontend *fe) } static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, - u32 *freq_min, u32 *freq_max) + u32 *freq_min, u32 *freq_max, + u32 *tolerance) { - *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 tuner_min = fe->ops.tuner_ops.info.frequency_min_hz; + u32 tuner_max = fe->ops.tuner_ops.info.frequency_max_hz; + u32 frontend_min = fe->ops.info.frequency_min_hz; + u32 frontend_max = fe->ops.info.frequency_max_hz; + + *freq_min = max(frontend_min, tuner_min); - if (fe->ops.info.frequency_max == 0) - *freq_max = fe->ops.tuner_ops.info.frequency_max; - else if (fe->ops.tuner_ops.info.frequency_max == 0) - *freq_max = fe->ops.info.frequency_max; + if (frontend_max == 0) + *freq_max = tuner_max; + else if (tuner_max == 0) + *freq_max = frontend_max; else - *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max); + *freq_max = min(frontend_max, tuner_max); if (*freq_min == 0 || *freq_max == 0) dev_warn(fe->dvb->device, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", fe->dvb->num, fe->id); + + /* If the standard is for satellite, convert frequencies to kHz */ + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + *freq_min /= kHz; + *freq_max /= kHz; + if (tolerance) + *tolerance = fe->ops.info.frequency_tolerance_hz / kHz; + + break; + default: + if (tolerance) + *tolerance = fe->ops.info.frequency_tolerance_hz; + break; + } +} + +static u32 dvb_frontend_get_stepsize(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 fe_step = fe->ops.info.frequency_stepsize_hz; + u32 tuner_step = fe->ops.tuner_ops.info.frequency_step_hz; + u32 step = max(fe_step, tuner_step); + + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + step /= kHz; + break; + default: + break; + } + + return step; } static int dvb_frontend_check_parameters(struct dvb_frontend *fe) @@ -918,7 +964,7 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) u32 freq_max; /* range check: frequency */ - dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max); + dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max, NULL); if ((freq_min && c->frequency < freq_min) || (freq_max && c->frequency > freq_max)) { dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", @@ -2244,8 +2290,8 @@ static int dtv_set_frontend(struct dvb_frontend *fe) case SYS_ISDBT: case SYS_DTMB: fepriv->min_delay = HZ / 20; - fepriv->step_size = fe->ops.info.frequency_stepsize * 2; - fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + fepriv->step_size = dvb_frontend_get_stepsize(fe) * 2; + fepriv->max_drift = (dvb_frontend_get_stepsize(fe) * 2) + 1; break; default: /* @@ -2374,9 +2420,17 @@ static int dvb_frontend_handle_ioctl(struct file *file, case FE_GET_INFO: { struct dvb_frontend_info *info = parg; - - memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); - dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); + memset(info, 0, sizeof(*info)); + + strcpy(info->name, fe->ops.info.name); + info->symbol_rate_min = fe->ops.info.symbol_rate_min; + info->symbol_rate_max = fe->ops.info.symbol_rate_max; + info->symbol_rate_tolerance = fe->ops.info.symbol_rate_tolerance; + info->caps = fe->ops.info.caps; + info->frequency_stepsize = dvb_frontend_get_stepsize(fe); + dvb_frontend_get_frequency_limits(fe, &info->frequency_min, + &info->frequency_max, + &info->frequency_tolerance); /* * Associate the 4 delivery systems supported by DVBv3 @@ -2406,10 +2460,10 @@ static int dvb_frontend_handle_ioctl(struct file *file, dev_err(fe->dvb->device, "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", __func__, c->delivery_system); - fe->ops.info.type = FE_OFDM; + info->type = FE_OFDM; } dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n", - __func__, c->delivery_system, fe->ops.info.type); + __func__, c->delivery_system, info->type); /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */ if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 64d6793674b9..3c8778570331 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -440,8 +440,10 @@ static int dvb_register_media_device(struct dvb_device *dvbdev, if (!dvbdev->entity) return 0; - link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + link = media_create_intf_link(dvbdev->entity, + &dvbdev->intf_devnode->intf, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; #endif @@ -599,7 +601,8 @@ static int dvb_create_io_intf_links(struct dvb_adapter *adap, if (strncmp(entity->name, name, strlen(name))) continue; link = media_create_intf_link(entity, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -754,14 +757,16 @@ int dvb_create_media_graph(struct dvb_adapter *adap, media_device_for_each_intf(intf, mdev) { if (intf->type == MEDIA_INTF_T_DVB_CA && ca) { link = media_create_intf_link(ca, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) { link = media_create_intf_link(tuner, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } @@ -773,7 +778,8 @@ int dvb_create_media_graph(struct dvb_adapter *adap, */ if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) { link = media_create_intf_link(demux, intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) return -ENOMEM; } diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 9ecaa9d0744a..048285134cdf 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -739,6 +739,16 @@ config DVB_TC90522 Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator. Say Y when you want to support this frontend. +config DVB_MN88443X + tristate "Socionext MN88443x" + depends on DVB_CORE && I2C + select REGMAP_I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + A driver for Socionext/Panasonic MN884433 and MN884434 + ISDB-S + ISDB-T demodulator. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 67a783fd5ed0..779dfd027b24 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -125,6 +125,7 @@ obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o obj-$(CONFIG_DVB_GP8PSK_FE) += gp8psk-fe.o obj-$(CONFIG_DVB_TC90522) += tc90522.o +obj-$(CONFIG_DVB_MN88443X) += mn88443x.o obj-$(CONFIG_DVB_HORUS3A) += horus3a.o obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o obj-$(CONFIG_DVB_HELENE) += helene.o diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 482bce49819a..f3acbb57d48c 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1136,10 +1136,9 @@ static const struct dvb_frontend_ops af9013_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Afatech AF9013", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 250000, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index aaed7cfe5f66..0cd57013ea25 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1020,10 +1020,9 @@ static const struct dvb_frontend_ops af9033_ops = { .delsys = {SYS_DVBT}, .info = { .name = "Afatech AF9033 (DVB-T)", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 250000, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/as102_fe.c b/drivers/media/dvb-frontends/as102_fe.c index 9b2f2da1d989..f59a102b0a64 100644 --- a/drivers/media/dvb-frontends/as102_fe.c +++ b/drivers/media/dvb-frontends/as102_fe.c @@ -419,9 +419,9 @@ static const struct dvb_frontend_ops as102_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Abilis AS102 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index 9746c6dd7fb8..52ce0e6e2a15 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -468,9 +468,9 @@ static int ascot2e_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops ascot2e_tuner_ops = { .info = { .name = "Sony ASCOT2E", - .frequency_min = 1000000, - .frequency_max = 1200000000, - .frequency_step = 25000, + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, }, .init = ascot2e_init, .release = ascot2e_release, diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c index 7b0f3239dbba..cbcc65dc9d54 100644 --- a/drivers/media/dvb-frontends/atbm8830.c +++ b/drivers/media/dvb-frontends/atbm8830.c @@ -428,9 +428,9 @@ static const struct dvb_frontend_ops atbm8830_ops = { .delsys = { SYS_DTMB }, .info = { .name = "AltoBeam ATBM8830/8831 DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c index 8f659bd1c79e..076f737aa8c0 100644 --- a/drivers/media/dvb-frontends/au8522_dig.c +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -897,9 +897,9 @@ static const struct dvb_frontend_ops au8522_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Auvitek AU8522 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 05df133dc5be..e92542b92d34 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -840,10 +840,8 @@ static const struct dvb_frontend_ops bcm3510_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Broadcom BCM3510 VSB/QAM frontend", - .frequency_min = 54000000, - .frequency_max = 803000000, - /* stepsize is just a guess */ - .frequency_stepsize = 0, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 803 * MHz, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c index 9ffd2c7ac74a..961380162cdd 100644 --- a/drivers/media/dvb-frontends/cx22700.c +++ b/drivers/media/dvb-frontends/cx22700.c @@ -412,9 +412,9 @@ static const struct dvb_frontend_ops cx22700_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Conexant CX22700 DVB-T", - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c index e8b1e6b7e7e5..ab9b2924bcca 100644 --- a/drivers/media/dvb-frontends/cx22702.c +++ b/drivers/media/dvb-frontends/cx22702.c @@ -622,9 +622,9 @@ static const struct dvb_frontend_ops cx22702_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Conexant CX22702 DVB-T", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c index 2f3a1c237489..9441bdc73097 100644 --- a/drivers/media/dvb-frontends/cx24110.c +++ b/drivers/media/dvb-frontends/cx24110.c @@ -629,10 +629,10 @@ static const struct dvb_frontend_ops cx24110_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Conexant CX24110 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c index 037db3e9d2dd..91a5033b6bd7 100644 --- a/drivers/media/dvb-frontends/cx24113.c +++ b/drivers/media/dvb-frontends/cx24113.c @@ -533,10 +533,10 @@ static void cx24113_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops cx24113_tuner_ops = { .info = { - .name = "Conexant CX24113", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 125, + .name = "Conexant CX24113", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, }, .release = cx24113_release, diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index 2dbc7349d870..220f26663647 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -1465,10 +1465,10 @@ static const struct dvb_frontend_ops cx24116_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24116/CX24118", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index ba55d75d916c..667bc8be848d 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -1622,10 +1622,10 @@ static const struct dvb_frontend_ops cx24117_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24117/CX24132", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index ccbabdae6a69..dd3ec316e7c2 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -1555,10 +1555,10 @@ static const struct dvb_frontend_ops cx24120_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Conexant CX24120/CX24118", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index bf33e7390aaf..e49215020a93 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -1111,10 +1111,10 @@ static const struct dvb_frontend_ops cx24123_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Conexant CX24123/CX24109", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c index c2e7caf9b010..eb1d7478fa8d 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t.c +++ b/drivers/media/dvb-frontends/cxd2820r_t.c @@ -431,8 +431,8 @@ int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c index e641fde75379..f330ec1710b4 100644 --- a/drivers/media/dvb-frontends/cxd2820r_t2.c +++ b/drivers/media/dvb-frontends/cxd2820r_t2.c @@ -426,8 +426,8 @@ int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 1500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 85905d3503ff..c98093ed3dd7 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3942,9 +3942,8 @@ static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Sony CXD2841ER DVB-S/S2 demodulator", - .frequency_min = 500000, - .frequency_max = 2500000, - .frequency_stepsize = 0, + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, @@ -3988,8 +3987,8 @@ static struct dvb_frontend_ops cxd2841er_t_c_ops = { FE_CAN_HIERARCHY_AUTO | FE_CAN_MUTE_TS | FE_CAN_2G_MODULATION, - .frequency_min = 42000000, - .frequency_max = 1002000000, + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 1002 * MHz, .symbol_rate_min = 870000, .symbol_rate_max = 11700000 }, diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c index bd9101e246d5..f87e27481ea7 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -1833,9 +1833,9 @@ static enum dvbfe_algo cxd2880_get_frontend_algo(struct dvb_frontend *fe) static struct dvb_frontend_ops cxd2880_dvbt_t2_ops = { .info = { .name = "Sony CXD2880", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 1000, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 1 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c index 932d235118e2..37ebd5af8fd4 100644 --- a/drivers/media/dvb-frontends/dib0070.c +++ b/drivers/media/dvb-frontends/dib0070.c @@ -726,10 +726,10 @@ static void dib0070_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops dib0070_ops = { .info = { - .name = "DiBcom DiB0070", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .name = "DiBcom DiB0070", + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0070_release, diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index ee7af34979ed..44a074261e69 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -2578,9 +2578,9 @@ static int dib0090_set_params(struct dvb_frontend *fe) static const struct dvb_tuner_ops dib0090_ops = { .info = { .name = "DiBcom DiB0090", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0090_release, @@ -2593,9 +2593,9 @@ static const struct dvb_tuner_ops dib0090_ops = { static const struct dvb_tuner_ops dib0090_fw_ops = { .info = { .name = "DiBcom DiB0090", - .frequency_min = 45000000, - .frequency_max = 860000000, - .frequency_step = 1000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = dib0090_release, diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c index 5861f346db49..bbbd53280477 100644 --- a/drivers/media/dvb-frontends/dib3000mb.c +++ b/drivers/media/dvb-frontends/dib3000mb.c @@ -786,9 +786,9 @@ static const struct dvb_frontend_ops dib3000mb_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 3000M-B DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c index 7e5d474806a5..c9e1db251723 100644 --- a/drivers/media/dvb-frontends/dib3000mc.c +++ b/drivers/media/dvb-frontends/dib3000mc.c @@ -944,9 +944,9 @@ static const struct dvb_frontend_ops dib3000mc_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 3000MC/P", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c index 6a1d357d0c7c..b79358d09de6 100644 --- a/drivers/media/dvb-frontends/dib7000m.c +++ b/drivers/media/dvb-frontends/dib7000m.c @@ -1443,9 +1443,9 @@ static const struct dvb_frontend_ops dib7000m_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 7000MA/MB/PA/PB/MC", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 5a8dbc0b25fb..58387860b62d 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2824,9 +2824,9 @@ static const struct dvb_frontend_ops dib7000p_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 7000PC", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 22eec8f65485..3c3f8cb14845 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4390,9 +4390,9 @@ static const struct dvb_frontend_ops dib8000_ops = { .delsys = { SYS_ISDBT }, .info = { .name = "DiBcom 8000 ISDB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index b8edb55696bb..0183fb1346ef 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -2553,9 +2553,9 @@ static const struct dvb_frontend_ops dib9000_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DiBcom 9000", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 5706898e84cc..9628d4067fe1 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -2841,8 +2841,7 @@ ctrl_set_cfg_mpeg_output(struct drx_demod_instance *demod, struct drx_cfg_mpeg_o /* coef = 188/204 */ max_bit_rate = (ext_attr->curr_symbol_rate / 8) * nr_bits * 188; - /* pass through as b/c Annex A/c need following settings */ - /* fall-through */ + /* fall-through - as b/c Annex A/C need following settings */ case DRX_STANDARD_ITU_B: rc = drxj_dap_write_reg16(dev_addr, FEC_OC_FCT_USAGE__A, FEC_OC_FCT_USAGE__PRE, 0); if (rc != 0) { @@ -11811,8 +11810,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, block_hdr.CRC = be16_to_cpu(*(__be16 *)(mc_data)); mc_data += sizeof(u16); - pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", - (unsigned)(mc_data - mc_data_init), block_hdr.addr, + pr_debug("%zd: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n", + (mc_data - mc_data_init), block_hdr.addr, block_hdr.size, block_hdr.flags, block_hdr.CRC); /* Check block header on: @@ -11842,8 +11841,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, mc_block_nr_bytes, mc_data, 0x0000)) { rc = -EIO; - pr_err("error writing firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error writing firmware at pos %zd\n", + mc_data - mc_data_init); goto release; } break; @@ -11866,8 +11865,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, (u16)bytes_to_comp, (u8 *)mc_data_buffer, 0x0000)) { - pr_err("error reading firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error reading firmware at pos %zd\n", + mc_data - mc_data_init); return -EIO; } @@ -11875,8 +11874,8 @@ static int drx_ctrl_u_code(struct drx_demod_instance *demod, bytes_to_comp); if (result) { - pr_err("error verifying firmware at pos %u\n", - (unsigned)(mc_data - mc_data_init)); + pr_err("error verifying firmware at pos %zd\n", + mc_data - mc_data_init); return -EIO; } @@ -12374,9 +12373,9 @@ static const struct dvb_frontend_ops drx39xxj_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Micronas DRX39xxj family Frontend", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 3b7d31a22d82..684d428efb0d 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -1970,8 +1970,7 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->transmission_mode) { default: /* Not set, detect it automatically */ operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; - /* try first guess DRX_FFTMODE_8K */ - /* fall through */ + /* fall through - try first guess DRX_FFTMODE_8K */ case TRANSMISSION_MODE_8K: transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; if (state->type_A) { @@ -2144,8 +2143,7 @@ static int DRX_Start(struct drxd_state *state, s32 off) switch (p->modulation) { default: operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; - /* try first guess DRX_CONSTELLATION_QAM64 */ - /* fall through */ + /* fall through - try first guess DRX_CONSTELLATION_QAM64 */ case QAM_64: transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; if (state->type_A) { @@ -2912,10 +2910,9 @@ static const struct dvb_frontend_ops drxd_ops = { .delsys = { SYS_DVBT}, .info = { .name = "Micronas DRXD DVB-T", - .frequency_min = 47125000, - .frequency_max = 855250000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 855250 * kHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 5a26ad93be10..f1886945a7bc 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -3270,13 +3270,11 @@ static int dvbt_sc_command(struct drxk_state *state, case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: status |= write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); - /* All commands using 1 parameters */ - /* fall through */ + /* fall through - All commands using 1 parameters */ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: case OFDM_SC_RA_RAM_CMD_USER_IO: status |= write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); - /* All commands using 0 parameters */ - /* fall through */ + /* fall through - All commands using 0 parameters */ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: case OFDM_SC_RA_RAM_CMD_NULL: /* Write command */ @@ -3784,8 +3782,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case TRANSMISSION_MODE_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M; - /* try first guess DRX_FFTMODE_8K */ - /* fall through */ + /* fall through - try first guess DRX_FFTMODE_8K */ case TRANSMISSION_MODE_8K: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; break; @@ -3799,8 +3796,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, default: case GUARD_INTERVAL_AUTO: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M; - /* try first guess DRX_GUARD_1DIV4 */ - /* fall through */ + /* fall through - try first guess DRX_GUARD_1DIV4 */ case GUARD_INTERVAL_1_4: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; break; @@ -3841,8 +3837,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case QAM_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M; - /* try first guess DRX_CONSTELLATION_QAM64 */ - /* fall through */ + /* fall through - try first guess DRX_CONSTELLATION_QAM64 */ case QAM_64: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; break; @@ -3885,8 +3880,7 @@ static int set_dvbt(struct drxk_state *state, u16 intermediate_freqk_hz, case FEC_AUTO: default: operation_mode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M; - /* try first guess DRX_CODERATE_2DIV3 */ - /* fall through */ + /* fall through - try first guess DRX_CODERATE_2DIV3 */ case FEC_2_3: transmission_params |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; break; @@ -6744,13 +6738,13 @@ static const struct dvb_frontend_ops drxk_ops = { /* .delsys will be filled dynamically */ .info = { .name = "DRXK", - .frequency_min = 47000000, - .frequency_max = 865000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, /* For DVB-C */ - .symbol_rate_min = 870000, + .symbol_rate_min = 870000, .symbol_rate_max = 11700000, /* For DVB-T */ - .frequency_stepsize = 166667, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 2ff90e5eabce..46a55146cb07 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -1100,10 +1100,10 @@ static const struct dvb_frontend_ops ds3000_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "Montage Technology DS3000", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1011 * kHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index e3894ff403d7..6d4b2eec67b4 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -799,6 +799,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct dvb_pll_priv *priv = NULL; int ret; const struct dvb_pll_desc *desc; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; b1 = kmalloc(1, GFP_KERNEL); if (!b1) @@ -844,8 +845,19 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, strncpy(fe->ops.tuner_ops.info.name, desc->name, sizeof(fe->ops.tuner_ops.info.name)); - fe->ops.tuner_ops.info.frequency_min = desc->min; - fe->ops.tuner_ops.info.frequency_max = desc->max; + switch (c->delivery_system) { + case SYS_DVBS: + case SYS_DVBS2: + case SYS_TURBO: + case SYS_ISDBS: + fe->ops.tuner_ops.info.frequency_min_hz = desc->min * kHz; + fe->ops.tuner_ops.info.frequency_max_hz = desc->max * kHz; + break; + default: + fe->ops.tuner_ops.info.frequency_min_hz = desc->min; + fe->ops.tuner_ops.info.frequency_max_hz = desc->max; + } + if (!desc->initdata) fe->ops.tuner_ops.init = NULL; if (!desc->sleepdata) @@ -884,6 +896,17 @@ dvb_pll_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id)) return -ENOMEM; + /* + * Unset tuner_ops.release (== dvb_pll_release) + * which has been just set in the above dvb_pll_attach(), + * because if tuner_ops.release was left defined, + * this module would be 'put' twice on exit: + * once by dvb_frontend_detach() and another by dvb_module_release(). + * + * dvb_pll_release is instead executed in the i2c driver's .remove(), + * keeping dvb_pll_attach untouched for legacy (dvb_attach) drivers. + */ + fe->ops.tuner_ops.release = NULL; dev_info(&client->dev, "DVB Simple Tuner attached.\n"); return 0; } diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c index 6650d4f61ef2..a4cbcae7967d 100644 --- a/drivers/media/dvb-frontends/dvb_dummy_fe.c +++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c @@ -170,9 +170,9 @@ static const struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Dummy DVB-T", - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | @@ -201,11 +201,11 @@ static const struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "Dummy DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = (57840000 / 2) / 64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (57840000 / 2) / 4, /* SACLK/4 */ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO @@ -230,10 +230,10 @@ static const struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Dummy DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 250, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 250 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/gp8psk-fe.c b/drivers/media/dvb-frontends/gp8psk-fe.c index a772ef8bfe1c..238f09aa72f2 100644 --- a/drivers/media/dvb-frontends/gp8psk-fe.c +++ b/drivers/media/dvb-frontends/gp8psk-fe.c @@ -355,9 +355,9 @@ static const struct dvb_frontend_ops gp8psk_fe_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Genpix DVB-S", - .frequency_min = 800000, - .frequency_max = 2250000, - .frequency_stepsize = 100, + .frequency_min_hz = 800 * MHz, + .frequency_max_hz = 2250 * MHz, + .frequency_stepsize_hz = 100 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index a0d0b53c91d7..d7790cb98a0c 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -666,7 +666,7 @@ static int helene_set_params_s(struct dvb_frontend *fe) return 0; } -static int helene_set_params(struct dvb_frontend *fe) +static int helene_set_params_t(struct dvb_frontend *fe) { u8 data[MAX_WRITE_REGSIZE]; u32 frequency; @@ -835,6 +835,19 @@ static int helene_set_params(struct dvb_frontend *fe) return 0; } +static int helene_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (p->delivery_system == SYS_DVBT || + p->delivery_system == SYS_DVBT2 || + p->delivery_system == SYS_ISDBT || + p->delivery_system == SYS_DVBC_ANNEX_A) + return helene_set_params_t(fe); + + return helene_set_params_s(fe); +} + static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct helene_priv *priv = fe->tuner_priv; @@ -843,26 +856,26 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } -static const struct dvb_tuner_ops helene_tuner_ops = { +static const struct dvb_tuner_ops helene_tuner_ops_t = { .info = { .name = "Sony HELENE Ter tuner", - .frequency_min = 1000000, - .frequency_max = 1200000000, - .frequency_step = 25000, + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1200 * MHz, + .frequency_step_hz = 25 * kHz, }, .init = helene_init, .release = helene_release, .sleep = helene_sleep, - .set_params = helene_set_params, + .set_params = helene_set_params_t, .get_frequency = helene_get_frequency, }; static const struct dvb_tuner_ops helene_tuner_ops_s = { .info = { .name = "Sony HELENE Sat tuner", - .frequency_min = 500000, - .frequency_max = 2500000, - .frequency_step = 1000, + .frequency_min_hz = 500 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = helene_init, .release = helene_release, @@ -871,6 +884,20 @@ static const struct dvb_tuner_ops helene_tuner_ops_s = { .get_frequency = helene_get_frequency, }; +static const struct dvb_tuner_ops helene_tuner_ops = { + .info = { + .name = "Sony HELENE Sat/Ter tuner", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 2500 * MHz, + .frequency_step_hz = 25 * kHz, + }, + .init = helene_init, + .release = helene_release, + .sleep = helene_sleep, + .set_params = helene_set_params, + .get_frequency = helene_get_frequency, +}; + /* power-on tuner * call once after reset */ @@ -897,7 +924,10 @@ static int helene_x_pon(struct helene_priv *priv) helene_write_regs(priv, 0x99, cdata, sizeof(cdata)); /* 0x81 - 0x94 */ - data[0] = 0x18; /* xtal 24 MHz */ + if (priv->xtal == SONY_HELENE_XTAL_16000) + data[0] = 0x10; /* xtal 16 MHz */ + else + data[0] = 0x18; /* xtal 24 MHz */ data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */ data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */ data[3] = 0x80; /* REFOUT signal output 500mVpp */ @@ -1032,7 +1062,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = priv; dev_info(&priv->i2c->dev, @@ -1042,6 +1072,59 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, } EXPORT_SYMBOL(helene_attach); +static int helene_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct helene_config *config = client->dev.platform_data; + struct dvb_frontend *fe = config->fe; + struct device *dev = &client->dev; + struct helene_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->i2c_address = client->addr; + priv->i2c = client->adapter; + priv->set_tuner_data = config->set_tuner_priv; + priv->set_tuner = config->set_tuner_callback; + priv->xtal = config->xtal; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (helene_x_pon(priv) != 0) + return -EINVAL; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + memcpy(&fe->ops.tuner_ops, &helene_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = priv; + i2c_set_clientdata(client, priv); + + dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n", + priv->i2c_address, priv->i2c); + + return 0; +} + +static const struct i2c_device_id helene_id[] = { + { "helene", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, helene_id); + +static struct i2c_driver helene_driver = { + .driver = { + .name = "helene", + }, + .probe = helene_probe, + .id_table = helene_id, +}; +module_i2c_driver(helene_driver); + MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver"); MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h index c9fc81c7e4e7..8562d01bc93e 100644 --- a/drivers/media/dvb-frontends/helene.h +++ b/drivers/media/dvb-frontends/helene.h @@ -39,6 +39,7 @@ enum helene_xtal { * @set_tuner_callback: Callback function that notifies the parent driver * which tuner is active now * @xtal: Cristal frequency as described by &enum helene_xtal + * @fe: Frontend for which connects this tuner */ struct helene_config { u8 i2c_address; @@ -46,6 +47,8 @@ struct helene_config { void *set_tuner_priv; int (*set_tuner_callback)(void *, int); enum helene_xtal xtal; + + struct dvb_frontend *fe; }; #if IS_REACHABLE(CONFIG_DVB_HELENE) diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 5e7e265a52e6..02bc08081971 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -330,9 +330,9 @@ static int horus3a_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops horus3a_tuner_ops = { .info = { .name = "Sony Horus3a", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = horus3a_init, .release = horus3a_release, diff --git a/drivers/media/dvb-frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c index 04f7f6854f73..c3a6e81ae87f 100644 --- a/drivers/media/dvb-frontends/itd1000.c +++ b/drivers/media/dvb-frontends/itd1000.c @@ -353,10 +353,10 @@ static void itd1000_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops itd1000_tuner_ops = { .info = { - .name = "Integrant ITD1000", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 125, /* kHz for QPSK frontends */ + .name = "Integrant ITD1000", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 125 * kHz, }, .release = itd1000_release, diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index 965012ad5c59..a30707b61b1f 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -135,8 +135,8 @@ static int ix2505v_set_params(struct dvb_frontend *fe) u8 gain, cc, ref, psc, local_osc, lpf; u8 data[4] = {0}; - if ((frequency < fe->ops.info.frequency_min) - || (frequency > fe->ops.info.frequency_max)) + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; if (state->config->tuner_gain) @@ -256,8 +256,8 @@ static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops ix2505v_tuner_ops = { .info = { .name = "Sharp IX2505V (B0017)", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .release = ix2505v_release, .set_params = ix2505v_set_params, diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c index 249c18761e6e..9afb5bf6424b 100644 --- a/drivers/media/dvb-frontends/l64781.c +++ b/drivers/media/dvb-frontends/l64781.c @@ -575,10 +575,9 @@ static const struct dvb_frontend_ops l64781_ops = { .delsys = { SYS_DVBT }, .info = { .name = "LSI L64781 DVB-T", - /* .frequency_min = ???,*/ - /* .frequency_max = ???,*/ - .frequency_stepsize = 166666, - /* .frequency_tolerance = ???,*/ + /* .frequency_min_hz = ???,*/ + /* .frequency_max_hz = ???,*/ + .frequency_stepsize_hz = 166666, /* .symbol_rate_tolerance = ???,*/ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index 9854096839ae..408151e33fa7 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1349,9 +1349,9 @@ static const struct dvb_frontend_ops lg2160_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2160 ATSC/MH Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, }, .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, #if 0 @@ -1375,9 +1375,9 @@ static const struct dvb_frontend_ops lg2161_ops = { .delsys = { SYS_ATSCMH }, .info = { .name = "LG Electronics LG2161 ATSC/MH Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, }, .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, #if 0 diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index 735d73060265..857e9b4d69b4 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -1164,9 +1164,9 @@ static const struct dvb_frontend_ops lgdt3304_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3304 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, @@ -1187,9 +1187,9 @@ static const struct dvb_frontend_ops lgdt3305_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3305 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 32de824476db..0e1f5daaf20c 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2157,9 +2157,9 @@ static const struct dvb_frontend_ops lgdt3306a_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3306A VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, .i2c_gate_ctrl = lgdt3306a_i2c_gate_ctrl, diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index f6731738b073..10d584ce538d 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -944,9 +944,9 @@ static const struct dvb_frontend_ops lgdt3302_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3302 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB @@ -966,9 +966,9 @@ static const struct dvb_frontend_ops lgdt3303_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "LG Electronics LGDT3303 VSB/QAM Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB diff --git a/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c index f47e5a1af16d..07e5bcee9c1e 100644 --- a/drivers/media/dvb-frontends/lgs8gl5.c +++ b/drivers/media/dvb-frontends/lgs8gl5.c @@ -416,10 +416,9 @@ static const struct dvb_frontend_ops lgs8gl5_ops = { .delsys = { SYS_DTMB }, .info = { .name = "Legend Silicon LGS-8GL5 DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, - .frequency_tolerance = 0, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c index 84af8a12f26a..a6bcf1571d10 100644 --- a/drivers/media/dvb-frontends/lgs8gxx.c +++ b/drivers/media/dvb-frontends/lgs8gxx.c @@ -985,9 +985,9 @@ static const struct dvb_frontend_ops lgs8gxx_ops = { .delsys = { SYS_DTMB }, .info = { .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", - .frequency_min = 474000000, - .frequency_max = 858000000, - .frequency_stepsize = 10000, + .frequency_min_hz = 474 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 10 * kHz, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index 65d157fe76d1..dffd2d4bf1c8 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1300,9 +1300,9 @@ static const struct dvb_frontend_ops m88ds3103_ops = { .delsys = {SYS_DVBS, SYS_DVBS2}, .info = { .name = "Montage Technology M88DS3103", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 496ce27fa63a..d5bc85501f9e 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -759,10 +759,10 @@ static const struct dvb_frontend_ops m88rs2000_ops = { .delsys = { SYS_DVBS }, .info = { .name = "M88RS2000 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 377cd984b069..da505a5d035f 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1808,10 +1808,9 @@ static const struct dvb_frontend_ops mb86a16_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Fujitsu MB86A16 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 3000, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 3 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c3b1b88e2e7a..66fc77db0e75 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -2111,9 +2111,9 @@ static const struct dvb_frontend_ops mb86a20s_ops = { FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, /* Actually, those values depend on the used tuner */ - .frequency_min = 45000000, - .frequency_max = 864000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_stepsize_hz = 62500, }, .release = mb86a20s_release, diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c new file mode 100644 index 000000000000..9ec1aeef03d5 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.c @@ -0,0 +1,802 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. +// +// Copyright (c) 2018 Socionext Inc. + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <media/dvb_math.h> + +#include "mn88443x.h" + +/* ISDB-S registers */ +#define ATSIDU_S 0x2f +#define ATSIDL_S 0x30 +#define TSSET_S 0x31 +#define AGCREAD_S 0x5a +#define CPMON1_S 0x5e +#define CPMON1_S_FSYNC BIT(5) +#define CPMON1_S_ERRMON BIT(4) +#define CPMON1_S_SIGOFF BIT(3) +#define CPMON1_S_W2LOCK BIT(2) +#define CPMON1_S_W1LOCK BIT(1) +#define CPMON1_S_DW1LOCK BIT(0) +#define TRMON_S 0x60 +#define BERCNFLG_S 0x68 +#define BERCNFLG_S_BERVRDY BIT(5) +#define BERCNFLG_S_BERVCHK BIT(4) +#define BERCNFLG_S_BERDRDY BIT(3) +#define BERCNFLG_S_BERDCHK BIT(2) +#define CNRDXU_S 0x69 +#define CNRDXL_S 0x6a +#define CNRDYU_S 0x6b +#define CNRDYL_S 0x6c +#define BERVRDU_S 0x71 +#define BERVRDL_S 0x72 +#define DOSET1_S 0x73 + +/* Primary ISDB-T */ +#define PLLASET1 0x00 +#define PLLASET2 0x01 +#define PLLBSET1 0x02 +#define PLLBSET2 0x03 +#define PLLSET 0x04 +#define OUTCSET 0x08 +#define OUTCSET_CHDRV_8MA 0xff +#define OUTCSET_CHDRV_4MA 0x00 +#define PLDWSET 0x09 +#define PLDWSET_NORMAL 0x00 +#define PLDWSET_PULLDOWN 0xff +#define HIZSET1 0x0a +#define HIZSET2 0x0b + +/* Secondary ISDB-T (for MN884434 only) */ +#define RCVSET 0x00 +#define TSSET1_M 0x01 +#define TSSET2_M 0x02 +#define TSSET3_M 0x03 +#define INTACSET 0x08 +#define HIZSET3 0x0b + +/* ISDB-T registers */ +#define TSSET1 0x05 +#define TSSET1_TSASEL_MASK GENMASK(4, 3) +#define TSSET1_TSASEL_ISDBT (0x0 << 3) +#define TSSET1_TSASEL_ISDBS (0x1 << 3) +#define TSSET1_TSASEL_NONE (0x2 << 3) +#define TSSET1_TSBSEL_MASK GENMASK(2, 1) +#define TSSET1_TSBSEL_ISDBS (0x0 << 1) +#define TSSET1_TSBSEL_ISDBT (0x1 << 1) +#define TSSET1_TSBSEL_NONE (0x2 << 1) +#define TSSET2 0x06 +#define TSSET3 0x07 +#define TSSET3_INTASEL_MASK GENMASK(7, 6) +#define TSSET3_INTASEL_T (0x0 << 6) +#define TSSET3_INTASEL_S (0x1 << 6) +#define TSSET3_INTASEL_NONE (0x2 << 6) +#define TSSET3_INTBSEL_MASK GENMASK(5, 4) +#define TSSET3_INTBSEL_S (0x0 << 4) +#define TSSET3_INTBSEL_T (0x1 << 4) +#define TSSET3_INTBSEL_NONE (0x2 << 4) +#define OUTSET2 0x0d +#define PWDSET 0x0f +#define PWDSET_OFDMPD_MASK GENMASK(3, 2) +#define PWDSET_OFDMPD_DOWN BIT(3) +#define PWDSET_PSKPD_MASK GENMASK(1, 0) +#define PWDSET_PSKPD_DOWN BIT(1) +#define CLKSET1_T 0x11 +#define MDSET_T 0x13 +#define MDSET_T_MDAUTO_MASK GENMASK(7, 4) +#define MDSET_T_MDAUTO_AUTO (0xf << 4) +#define MDSET_T_MDAUTO_MANUAL (0x0 << 4) +#define MDSET_T_FFTS_MASK GENMASK(3, 2) +#define MDSET_T_FFTS_MODE1 (0x0 << 2) +#define MDSET_T_FFTS_MODE2 (0x1 << 2) +#define MDSET_T_FFTS_MODE3 (0x2 << 2) +#define MDSET_T_GI_MASK GENMASK(1, 0) +#define MDSET_T_GI_1_32 (0x0 << 0) +#define MDSET_T_GI_1_16 (0x1 << 0) +#define MDSET_T_GI_1_8 (0x2 << 0) +#define MDSET_T_GI_1_4 (0x3 << 0) +#define MDASET_T 0x14 +#define ADCSET1_T 0x20 +#define ADCSET1_T_REFSEL_MASK GENMASK(1, 0) +#define ADCSET1_T_REFSEL_2V (0x3 << 0) +#define ADCSET1_T_REFSEL_1_5V (0x2 << 0) +#define ADCSET1_T_REFSEL_1V (0x1 << 0) +#define NCOFREQU_T 0x24 +#define NCOFREQM_T 0x25 +#define NCOFREQL_T 0x26 +#define FADU_T 0x27 +#define FADM_T 0x28 +#define FADL_T 0x29 +#define AGCSET2_T 0x2c +#define AGCSET2_T_IFPOLINV_INC BIT(0) +#define AGCSET2_T_RFPOLINV_INC BIT(1) +#define AGCV3_T 0x3e +#define MDRD_T 0xa2 +#define MDRD_T_SEGID_MASK GENMASK(5, 4) +#define MDRD_T_SEGID_13 (0x0 << 4) +#define MDRD_T_SEGID_1 (0x1 << 4) +#define MDRD_T_SEGID_3 (0x2 << 4) +#define MDRD_T_FFTS_MASK GENMASK(3, 2) +#define MDRD_T_FFTS_MODE1 (0x0 << 2) +#define MDRD_T_FFTS_MODE2 (0x1 << 2) +#define MDRD_T_FFTS_MODE3 (0x2 << 2) +#define MDRD_T_GI_MASK GENMASK(1, 0) +#define MDRD_T_GI_1_32 (0x0 << 0) +#define MDRD_T_GI_1_16 (0x1 << 0) +#define MDRD_T_GI_1_8 (0x2 << 0) +#define MDRD_T_GI_1_4 (0x3 << 0) +#define SSEQRD_T 0xa3 +#define SSEQRD_T_SSEQSTRD_MASK GENMASK(3, 0) +#define SSEQRD_T_SSEQSTRD_RESET (0x0 << 0) +#define SSEQRD_T_SSEQSTRD_TUNING (0x1 << 0) +#define SSEQRD_T_SSEQSTRD_AGC (0x2 << 0) +#define SSEQRD_T_SSEQSTRD_SEARCH (0x3 << 0) +#define SSEQRD_T_SSEQSTRD_CLOCK_SYNC (0x4 << 0) +#define SSEQRD_T_SSEQSTRD_FREQ_SYNC (0x8 << 0) +#define SSEQRD_T_SSEQSTRD_FRAME_SYNC (0x9 << 0) +#define SSEQRD_T_SSEQSTRD_SYNC (0xa << 0) +#define SSEQRD_T_SSEQSTRD_LOCK (0xb << 0) +#define AGCRDU_T 0xa8 +#define AGCRDL_T 0xa9 +#define CNRDU_T 0xbe +#define CNRDL_T 0xbf +#define BERFLG_T 0xc0 +#define BERFLG_T_BERDRDY BIT(7) +#define BERFLG_T_BERDCHK BIT(6) +#define BERFLG_T_BERVRDYA BIT(5) +#define BERFLG_T_BERVCHKA BIT(4) +#define BERFLG_T_BERVRDYB BIT(3) +#define BERFLG_T_BERVCHKB BIT(2) +#define BERFLG_T_BERVRDYC BIT(1) +#define BERFLG_T_BERVCHKC BIT(0) +#define BERRDU_T 0xc1 +#define BERRDM_T 0xc2 +#define BERRDL_T 0xc3 +#define BERLENRDU_T 0xc4 +#define BERLENRDL_T 0xc5 +#define ERRFLG_T 0xc6 +#define ERRFLG_T_BERDOVF BIT(7) +#define ERRFLG_T_BERVOVFA BIT(6) +#define ERRFLG_T_BERVOVFB BIT(5) +#define ERRFLG_T_BERVOVFC BIT(4) +#define ERRFLG_T_NERRFA BIT(3) +#define ERRFLG_T_NERRFB BIT(2) +#define ERRFLG_T_NERRFC BIT(1) +#define ERRFLG_T_NERRF BIT(0) +#define DOSET1_T 0xcf + +#define CLK_LOW 4000000 +#define CLK_DIRECT 20200000 +#define CLK_MAX 25410000 + +#define S_T_FREQ 8126984 /* 512 / 63 MHz */ + +struct mn88443x_spec { + bool primary; +}; + +struct mn88443x_priv { + const struct mn88443x_spec *spec; + + struct dvb_frontend fe; + struct clk *mclk; + struct gpio_desc *reset_gpio; + u32 clk_freq; + u32 if_freq; + + /* Common */ + bool use_clkbuf; + + /* ISDB-S */ + struct i2c_client *client_s; + struct regmap *regmap_s; + + /* ISDB-T */ + struct i2c_client *client_t; + struct regmap *regmap_t; +}; + +static void mn88443x_cmn_power_on(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + clk_prepare_enable(chip->mclk); + + gpiod_set_value_cansleep(chip->reset_gpio, 1); + usleep_range(100, 1000); + gpiod_set_value_cansleep(chip->reset_gpio, 0); + + if (chip->spec->primary) { + regmap_write(r_t, OUTCSET, OUTCSET_CHDRV_8MA); + regmap_write(r_t, PLDWSET, PLDWSET_NORMAL); + regmap_write(r_t, HIZSET1, 0x80); + regmap_write(r_t, HIZSET2, 0xe0); + } else { + regmap_write(r_t, HIZSET3, 0x8f); + } +} + +static void mn88443x_cmn_power_off(struct mn88443x_priv *chip) +{ + gpiod_set_value_cansleep(chip->reset_gpio, 1); + + clk_disable_unprepare(chip->mclk); +} + +static void mn88443x_s_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, + PWDSET_PSKPD_DOWN); +} + +static void mn88443x_s_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_PSKPD_MASK, 0); +} + +static void mn88443x_s_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_s = chip->regmap_s; + + regmap_write(r_s, ATSIDU_S, c->stream_id >> 8); + regmap_write(r_s, ATSIDL_S, c->stream_id); + regmap_write(r_s, TSSET_S, 0); +} + +static int mn88443x_s_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_s = chip->regmap_s; + u32 cpmon, tmpu, tmpl, flg; + u64 tmp; + + /* Sync detection */ + regmap_read(r_s, CPMON1_S, &cpmon); + + *status = 0; + if (cpmon & CPMON1_S_FSYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (cpmon & CPMON1_S_W2LOCK) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_s, AGCREAD_S, &tmpu); + agc = tmpu << 8; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr = 0, x, y, d; + u64 d_3 = 0; + + regmap_read(r_s, CNRDXU_S, &tmpu); + regmap_read(r_s, CNRDXL_S, &tmpl); + x = (tmpu << 8) | tmpl; + regmap_read(r_s, CNRDYU_S, &tmpu); + regmap_read(r_s, CNRDYL_S, &tmpl); + y = (tmpu << 8) | tmpl; + + /* CNR[dB]: 10 * log10(D) - 30.74 / D^3 - 3 */ + /* D = x^2 / (2^15 * y - x^2) */ + d = (y << 15) - x * x; + if (d > 0) { + /* (2^4 * D)^3 = 2^12 * D^3 */ + /* 3.074 * 2^(12 + 24) = 211243671486 */ + d_3 = div_u64(16 * x * x, d); + d_3 = d_3 * d_3 * d_3; + if (d_3) + d_3 = div_u64(211243671486ULL, d_3); + } + + if (d_3) { + /* 0.3 * 2^24 = 5033164 */ + tmp = (s64)2 * intlog10(x) - intlog10(abs(d)) - d_3 + - 5033164; + cnr = div_u64(tmp * 10000, 1 << 24); + } + + if (cnr) { + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_s, BERCNFLG_S, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERCNFLG_S_BERVRDY)) { + u32 bit_err, bit_cnt; + + regmap_read(r_s, BERVRDU_S, &tmpu); + regmap_read(r_s, BERVRDL_S, &tmpl); + bit_err = (tmpu << 8) | tmpl; + bit_cnt = (1 << 13) * 204; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static void mn88443x_t_sleep(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, + PWDSET_OFDMPD_DOWN); +} + +static void mn88443x_t_wake(struct mn88443x_priv *chip) +{ + struct regmap *r_t = chip->regmap_t; + + regmap_update_bits(r_t, PWDSET, PWDSET_OFDMPD_MASK, 0); +} + +static bool mn88443x_t_is_valid_clk(u32 adckt, u32 if_freq) +{ + if (if_freq == DIRECT_IF_57MHZ) { + if (adckt >= CLK_DIRECT && adckt <= 21000000) + return true; + if (adckt >= 25300000 && adckt <= CLK_MAX) + return true; + } else if (if_freq == DIRECT_IF_44MHZ) { + if (adckt >= 25000000 && adckt <= CLK_MAX) + return true; + } else if (if_freq >= LOW_IF_4MHZ && if_freq < DIRECT_IF_44MHZ) { + if (adckt >= CLK_DIRECT && adckt <= CLK_MAX) + return true; + } + + return false; +} + +static int mn88443x_t_set_freq(struct mn88443x_priv *chip) +{ + struct device *dev = &chip->client_s->dev; + struct regmap *r_t = chip->regmap_t; + s64 adckt, nco, ad_t; + u32 m, v; + + /* Clock buffer (but not supported) or XTAL */ + if (chip->clk_freq >= CLK_LOW && chip->clk_freq < CLK_DIRECT) { + chip->use_clkbuf = true; + regmap_write(r_t, CLKSET1_T, 0x07); + + adckt = 0; + } else { + chip->use_clkbuf = false; + regmap_write(r_t, CLKSET1_T, 0x00); + + adckt = chip->clk_freq; + } + if (!mn88443x_t_is_valid_clk(adckt, chip->if_freq)) { + dev_err(dev, "Invalid clock, CLK:%d, ADCKT:%lld, IF:%d\n", + chip->clk_freq, adckt, chip->if_freq); + return -EINVAL; + } + + /* Direct IF or Low IF */ + if (chip->if_freq == DIRECT_IF_57MHZ || + chip->if_freq == DIRECT_IF_44MHZ) + nco = adckt * 2 - chip->if_freq; + else + nco = -((s64)chip->if_freq); + nco = div_s64(nco << 24, adckt); + ad_t = div_s64(adckt << 22, S_T_FREQ); + + regmap_write(r_t, NCOFREQU_T, nco >> 16); + regmap_write(r_t, NCOFREQM_T, nco >> 8); + regmap_write(r_t, NCOFREQL_T, nco); + regmap_write(r_t, FADU_T, ad_t >> 16); + regmap_write(r_t, FADM_T, ad_t >> 8); + regmap_write(r_t, FADL_T, ad_t); + + /* Level of IF */ + m = ADCSET1_T_REFSEL_MASK; + v = ADCSET1_T_REFSEL_1_5V; + regmap_update_bits(r_t, ADCSET1_T, m, v); + + /* Polarity of AGC */ + v = AGCSET2_T_IFPOLINV_INC | AGCSET2_T_RFPOLINV_INC; + regmap_update_bits(r_t, AGCSET2_T, v, v); + + /* Lower output level of AGC */ + regmap_write(r_t, AGCV3_T, 0x00); + + regmap_write(r_t, MDSET_T, 0xfa); + + return 0; +} + +static void mn88443x_t_tune(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c) +{ + struct regmap *r_t = chip->regmap_t; + u32 m, v; + + m = MDSET_T_MDAUTO_MASK | MDSET_T_FFTS_MASK | MDSET_T_GI_MASK; + v = MDSET_T_MDAUTO_AUTO | MDSET_T_FFTS_MODE3 | MDSET_T_GI_1_8; + regmap_update_bits(r_t, MDSET_T, m, v); + + regmap_write(r_t, MDASET_T, 0); +} + +static int mn88443x_t_read_status(struct mn88443x_priv *chip, + struct dtv_frontend_properties *c, + enum fe_status *status) +{ + struct regmap *r_t = chip->regmap_t; + u32 seqrd, st, flg, tmpu, tmpm, tmpl; + u64 tmp; + + /* Sync detection */ + regmap_read(r_t, SSEQRD_T, &seqrd); + st = seqrd & SSEQRD_T_SSEQSTRD_MASK; + + *status = 0; + if (st >= SSEQRD_T_SSEQSTRD_SYNC) + *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + if (st >= SSEQRD_T_SSEQSTRD_FRAME_SYNC) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + + /* Signal strength */ + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_SIGNAL) { + u32 agc; + + regmap_read(r_t, AGCRDU_T, &tmpu); + regmap_read(r_t, AGCRDL_T, &tmpl); + agc = (tmpu << 8) | tmpl; + + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = agc; + } + + /* C/N rate */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + if (*status & FE_HAS_VITERBI) { + u32 cnr; + + regmap_read(r_t, CNRDU_T, &tmpu); + regmap_read(r_t, CNRDL_T, &tmpl); + + if (tmpu || tmpl) { + /* CNR[dB]: 10 * (log10(65536 / value) + 0.2) */ + /* intlog10(65536) = 80807124, 0.2 * 2^24 = 3355443 */ + tmp = (u64)80807124 - intlog10((tmpu << 8) | tmpl) + + 3355443; + cnr = div_u64(tmp * 10000, 1 << 24); + } else { + cnr = 0; + } + + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].uvalue = cnr; + } + + /* BER */ + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + regmap_read(r_t, BERFLG_T, &flg); + + if ((*status & FE_HAS_VITERBI) && (flg & BERFLG_T_BERVRDYA)) { + u32 bit_err, bit_cnt; + + regmap_read(r_t, BERRDU_T, &tmpu); + regmap_read(r_t, BERRDM_T, &tmpm); + regmap_read(r_t, BERRDL_T, &tmpl); + bit_err = (tmpu << 16) | (tmpm << 8) | tmpl; + + regmap_read(r_t, BERLENRDU_T, &tmpu); + regmap_read(r_t, BERLENRDL_T, &tmpl); + bit_cnt = ((tmpu << 8) | tmpl) * 203 * 8; + + if (bit_cnt) { + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = bit_err; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = bit_cnt; + } + } + + return 0; +} + +static int mn88443x_sleep(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; +} + +static int mn88443x_set_frontend(struct dvb_frontend *fe) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct regmap *r_s = chip->regmap_s; + struct regmap *r_t = chip->regmap_t; + u8 tssel = 0, intsel = 0; + + if (c->delivery_system == SYS_ISDBS) { + mn88443x_s_wake(chip); + mn88443x_t_sleep(chip); + + tssel = TSSET1_TSASEL_ISDBS; + intsel = TSSET3_INTASEL_S; + } else if (c->delivery_system == SYS_ISDBT) { + mn88443x_s_sleep(chip); + mn88443x_t_wake(chip); + + mn88443x_t_set_freq(chip); + + tssel = TSSET1_TSASEL_ISDBT; + intsel = TSSET3_INTASEL_T; + } + + regmap_update_bits(r_t, TSSET1, + TSSET1_TSASEL_MASK | TSSET1_TSBSEL_MASK, + tssel | TSSET1_TSBSEL_NONE); + regmap_write(r_t, TSSET2, 0); + regmap_update_bits(r_t, TSSET3, + TSSET3_INTASEL_MASK | TSSET3_INTBSEL_MASK, + intsel | TSSET3_INTBSEL_NONE); + + regmap_write(r_t, DOSET1_T, 0x95); + regmap_write(r_s, DOSET1_S, 0x80); + + if (c->delivery_system == SYS_ISDBS) + mn88443x_s_tune(chip, c); + else if (c->delivery_system == SYS_ISDBT) + mn88443x_t_tune(chip, c); + + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + return 0; +} + +static int mn88443x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *s) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + s->min_delay_ms = 850; + + if (c->delivery_system == SYS_ISDBS) { + s->max_drift = 30000 * 2 + 1; + s->step_size = 30000; + } else if (c->delivery_system == SYS_ISDBT) { + s->max_drift = 142857 * 2 + 1; + s->step_size = 142857 * 2; + } + + return 0; +} + +static int mn88443x_read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + struct mn88443x_priv *chip = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + if (c->delivery_system == SYS_ISDBS) + return mn88443x_s_read_status(chip, c, status); + + if (c->delivery_system == SYS_ISDBT) + return mn88443x_t_read_status(chip, c, status); + + return -EINVAL; +} + +static const struct dvb_frontend_ops mn88443x_ops = { + .delsys = { SYS_ISDBS, SYS_ISDBT }, + .info = { + .name = "Socionext MN88443x", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 2071 * MHz, + .symbol_rate_min = 28860000, + .symbol_rate_max = 28860000, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + + .sleep = mn88443x_sleep, + .set_frontend = mn88443x_set_frontend, + .get_tune_settings = mn88443x_get_tune_settings, + .read_status = mn88443x_read_status, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, +}; + +static int mn88443x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mn88443x_config *conf = client->dev.platform_data; + struct mn88443x_priv *chip; + struct device *dev = &client->dev; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (dev->of_node) + chip->spec = of_device_get_match_data(dev); + else + chip->spec = (struct mn88443x_spec *)id->driver_data; + if (!chip->spec) + return -EINVAL; + + chip->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(chip->mclk) && !conf) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(chip->mclk)); + return PTR_ERR(chip->mclk); + } + + ret = of_property_read_u32(dev->of_node, "if-frequency", + &chip->if_freq); + if (ret && !conf) { + dev_err(dev, "Failed to load IF frequency: %d.\n", ret); + return ret; + } + + chip->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(chip->reset_gpio)) { + dev_err(dev, "Failed to request reset_gpio: %ld\n", + PTR_ERR(chip->reset_gpio)); + return PTR_ERR(chip->reset_gpio); + } + + if (conf) { + chip->mclk = conf->mclk; + chip->if_freq = conf->if_freq; + chip->reset_gpio = conf->reset_gpio; + + *conf->fe = &chip->fe; + } + + chip->client_s = client; + chip->regmap_s = devm_regmap_init_i2c(chip->client_s, ®map_config); + if (IS_ERR(chip->regmap_s)) + return PTR_ERR(chip->regmap_s); + + /* + * Chip has two I2C addresses for each satellite/terrestrial system. + * ISDB-T uses address ISDB-S + 4, so we register a dummy client. + */ + chip->client_t = i2c_new_dummy(client->adapter, client->addr + 4); + if (!chip->client_t) + return -ENODEV; + + chip->regmap_t = devm_regmap_init_i2c(chip->client_t, ®map_config); + if (IS_ERR(chip->regmap_t)) { + ret = PTR_ERR(chip->regmap_t); + goto err_i2c_t; + } + + chip->clk_freq = clk_get_rate(chip->mclk); + + memcpy(&chip->fe.ops, &mn88443x_ops, sizeof(mn88443x_ops)); + chip->fe.demodulator_priv = chip; + i2c_set_clientdata(client, chip); + + mn88443x_cmn_power_on(chip); + mn88443x_s_sleep(chip); + mn88443x_t_sleep(chip); + + return 0; + +err_i2c_t: + i2c_unregister_device(chip->client_t); + + return ret; +} + +static int mn88443x_remove(struct i2c_client *client) +{ + struct mn88443x_priv *chip = i2c_get_clientdata(client); + + mn88443x_cmn_power_off(chip); + + i2c_unregister_device(chip->client_t); + + return 0; +} + +static const struct mn88443x_spec mn88443x_spec_pri = { + .primary = true, +}; + +static const struct mn88443x_spec mn88443x_spec_sec = { + .primary = false, +}; + +static const struct of_device_id mn88443x_of_match[] = { + { .compatible = "socionext,mn884433", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-0", .data = &mn88443x_spec_pri, }, + { .compatible = "socionext,mn884434-1", .data = &mn88443x_spec_sec, }, + {} +}; +MODULE_DEVICE_TABLE(of, mn88443x_of_match); + +static const struct i2c_device_id mn88443x_i2c_id[] = { + { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri }, + { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id); + +static struct i2c_driver mn88443x_driver = { + .driver = { + .name = "mn88443x", + .of_match_table = of_match_ptr(mn88443x_of_match), + }, + .probe = mn88443x_probe, + .remove = mn88443x_remove, + .id_table = mn88443x_i2c_id, +}; + +module_i2c_driver(mn88443x_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>"); +MODULE_DESCRIPTION("Socionext MN88443x series demodulator driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/dvb-frontends/mn88443x.h b/drivers/media/dvb-frontends/mn88443x.h new file mode 100644 index 000000000000..b19aaf6a1ea3 --- /dev/null +++ b/drivers/media/dvb-frontends/mn88443x.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Socionext MN88443x series demodulator driver for ISDB-S/ISDB-T. + * + * Copyright (c) 2018 Socionext Inc. + */ + +#ifndef MN88443X_H +#define MN88443X_H + +#include <media/dvb_frontend.h> + +/* ISDB-T IF frequency */ +#define DIRECT_IF_57MHZ 57000000 +#define DIRECT_IF_44MHZ 44000000 +#define LOW_IF_4MHZ 4000000 + +struct mn88443x_config { + struct clk *mclk; + u32 if_freq; + struct gpio_desc *reset_gpio; + + /* Everything after that is returned by the driver. */ + struct dvb_frontend **fe; +}; + +#endif /* MN88443X_H */ diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index e2a3fc521620..aad07adda37d 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -559,8 +559,8 @@ static int mt312_set_frontend(struct dvb_frontend *fe) dprintk("%s: Freq %d\n", __func__, p->frequency); - if ((p->frequency < fe->ops.info.frequency_min) - || (p->frequency > fe->ops.info.frequency_max)) + if ((p->frequency < fe->ops.info.frequency_min_hz / kHz) + || (p->frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; if (((int)p->inversion < INVERSION_OFF) @@ -755,10 +755,10 @@ static const struct dvb_frontend_ops mt312_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Zarlink ???? DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, /* FIXME: adjust freq to real used xtal */ - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, + .frequency_stepsize_hz = MT312_PLL_CLK / 128, .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ .symbol_rate_max = MT312_SYS_CLK / 2, .caps = diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c index a440b76444d3..da3e466d50e2 100644 --- a/drivers/media/dvb-frontends/mt352.c +++ b/drivers/media/dvb-frontends/mt352.c @@ -567,10 +567,9 @@ static const struct dvb_frontend_ops mt352_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Zarlink MT352 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c index 274d8fca0763..295f37d5f10e 100644 --- a/drivers/media/dvb-frontends/mxl5xx.c +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -784,10 +784,8 @@ static struct dvb_frontend_ops mxl_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "MaxLinear MxL5xx DVB-S/S2 tuner-demodulator", - .frequency_min = 300000, - .frequency_max = 2350000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 300 * MHz, + .frequency_max_hz = 2350 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index a6cc4952eb74..0961e686ff68 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -1212,9 +1212,9 @@ static const struct dvb_frontend_ops nxt200x_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Nextwave NXT200X VSB/QAM frontend", - .frequency_min = 54000000, - .frequency_max = 860000000, - .frequency_stepsize = 166666, /* stepsize is just a guess */ + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, /* stepsize is just a guess */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256 diff --git a/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c index 109a635d166a..72e447e8ba64 100644 --- a/drivers/media/dvb-frontends/nxt6000.c +++ b/drivers/media/dvb-frontends/nxt6000.c @@ -596,9 +596,9 @@ static const struct dvb_frontend_ops nxt6000_ops = { .delsys = { SYS_DVBT }, .info = { .name = "NxtWave NXT6000 DVB-T", - .frequency_min = 0, - .frequency_max = 863250000, - .frequency_stepsize = 62500, + .frequency_min_hz = 0, + .frequency_max_hz = 863250 * kHz, + .frequency_stepsize_hz = 62500, /*.frequency_tolerance = *//* FIXME: 12% of SR */ .symbol_rate_min = 0, /* FIXME */ .symbol_rate_max = 9360000, /* FIXME */ diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index b4c9aadcb552..fc35f37eb3c0 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -583,9 +583,9 @@ static const struct dvb_frontend_ops or51132_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Oren OR51132 VSB/QAM Frontend", - .frequency_min = 44000000, - .frequency_max = 958000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index b65ba34fd00a..a39bbd8ff1f0 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -530,10 +530,10 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config, static const struct dvb_frontend_ops or51211_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { - .name = "Oren OR51211 VSB Frontend", - .frequency_min = 44000000, - .frequency_max = 958000000, - .frequency_stepsize = 166666, + .name = "Oren OR51211 VSB Frontend", + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 958 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_8VSB diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 7bbfe11d11ed..adc9046d5a90 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -159,8 +159,8 @@ static int rtl2830_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 500; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index fa3b8169c1a5..2f1f5cbaf03c 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -408,8 +408,8 @@ static int rtl2832_get_tune_settings(struct dvb_frontend *fe, dev_dbg(&client->dev, "\n"); s->min_delay_ms = 1000; - s->step_size = fe->ops.info.frequency_stepsize * 2; - s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; + s->step_size = fe->ops.info.frequency_stepsize_hz * 2; + s->max_drift = (fe->ops.info.frequency_stepsize_hz * 2) + 1; return 0; } @@ -841,9 +841,9 @@ static const struct dvb_frontend_ops rtl2832_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Realtek RTL2832 (DVB-T)", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c6e78d870ccd..d448d9d4879c 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -301,7 +301,7 @@ static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev) for (i = 0; i < dev->urbs_initialized; i++) { dev_dbg(&pdev->dev, "submit urb=%d\n", i); - ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); + ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL); if (ret) { dev_err(&pdev->dev, "Could not submit urb no. %d - get them all back\n", @@ -345,7 +345,7 @@ static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev) for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, + BULK_BUFFER_SIZE, GFP_KERNEL, &dev->dma_addr[dev->buf_num]); if (!dev->buf_list[dev->buf_num]) { dev_dbg(&pdev->dev, "alloc buf=%d failed\n", @@ -390,7 +390,7 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { dev_dbg(&pdev->dev, "alloc urb=%d\n", i); - dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); if (!dev->urb_list[i]) { for (j = 0; j < i; j++) usb_free_urb(dev->urb_list[j]); diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index a23ba8727218..ceeb0c3551ce 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -999,9 +999,9 @@ static const struct dvb_frontend_ops s5h1409_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Samsung S5H1409 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index af5962807f2c..98aeed1d2284 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -918,9 +918,9 @@ static const struct dvb_frontend_ops s5h1411_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { .name = "Samsung S5H1411 QAM/8VSB Frontend", - .frequency_min = 54000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 54 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB }, diff --git a/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c index 8b2222530227..a65cdf8e8cd9 100644 --- a/drivers/media/dvb-frontends/s5h1420.c +++ b/drivers/media/dvb-frontends/s5h1420.c @@ -934,10 +934,10 @@ static const struct dvb_frontend_ops s5h1420_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c index 740a60df0455..4dc3febc0e12 100644 --- a/drivers/media/dvb-frontends/s5h1432.c +++ b/drivers/media/dvb-frontends/s5h1432.c @@ -370,9 +370,9 @@ static const struct dvb_frontend_ops s5h1432_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Samsung s5h1432 DVB-T Frontend", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c index 6c9015236655..79276871112a 100644 --- a/drivers/media/dvb-frontends/s921.c +++ b/drivers/media/dvb-frontends/s921.c @@ -510,15 +510,14 @@ static const struct dvb_frontend_ops s921_ops = { /* Use dib8000 values per default */ .info = { .name = "Sharp S921", - .frequency_min = 470000000, + .frequency_min_hz = 470 * MHz, /* * Max should be 770MHz instead, according with Sharp docs, * but Leadership doc says it works up to 806 MHz. This is * required to get channel 69, used in Brazil */ - .frequency_max = 806000000, - .frequency_tolerance = 0, - .caps = FE_CAN_INVERSION_AUTO | + .frequency_max_hz = 806 * MHz, + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 2dd336f95cbf..feacd8da421d 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1120,7 +1120,7 @@ static const struct dvb_frontend_ops si2165_ops = { .symbol_rate_min = 1000000, .symbol_rate_max = 7200000, /* For DVB-T */ - .frequency_stepsize = 166667, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index 9b32a1b3205e..8546a236d452 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -870,10 +870,9 @@ static const struct dvb_frontend_ops si21xx_ops = { .delsys = { SYS_DVBS }, .info = { .name = "SL SI21XX DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c index 1d57a20093fc..8d31cf3f4f07 100644 --- a/drivers/media/dvb-frontends/sp8870.c +++ b/drivers/media/dvb-frontends/sp8870.c @@ -584,9 +584,9 @@ static const struct dvb_frontend_ops sp8870_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Spase SP8870 DVB-T", - .frequency_min = 470000000, - .frequency_max = 860000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c index 57a0d0ae2b48..c02f50995df4 100644 --- a/drivers/media/dvb-frontends/sp887x.c +++ b/drivers/media/dvb-frontends/sp887x.c @@ -594,9 +594,9 @@ static const struct dvb_frontend_ops sp887x_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Spase SP887x DVB-T", - .frequency_min = 50500000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 50500 * kHz, + .frequency_max_hz = 858000 * kHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c index 3c654ae16e78..874e9c9125d6 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.c +++ b/drivers/media/dvb-frontends/stb0899_drv.c @@ -1584,10 +1584,8 @@ static const struct dvb_frontend_ops stb0899_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STB0899 Multistandard", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 5000000, .symbol_rate_max = 45000000, diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c index 69c03892f2da..786b9eccde00 100644 --- a/drivers/media/dvb-frontends/stb6000.c +++ b/drivers/media/dvb-frontends/stb6000.c @@ -188,8 +188,8 @@ static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops stb6000_tuner_ops = { .info = { .name = "ST STB6000", - .frequency_min = 950000, - .frequency_max = 2150000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz }, .release = stb6000_release, .sleep = stb6000_sleep, diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c index 3a851f524b16..30ac584dfab3 100644 --- a/drivers/media/dvb-frontends/stb6100.c +++ b/drivers/media/dvb-frontends/stb6100.c @@ -527,9 +527,8 @@ static int stb6100_set_params(struct dvb_frontend *fe) static const struct dvb_tuner_ops stb6100_ops = { .info = { .name = "STB6100 Silicon Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = stb6100_init, diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c index f947ed947aae..c9a9fa4e2c1b 100644 --- a/drivers/media/dvb-frontends/stv0288.c +++ b/drivers/media/dvb-frontends/stv0288.c @@ -533,10 +533,9 @@ static const struct dvb_frontend_ops stv0288_ops = { .delsys = { SYS_DVBS }, .info = { .name = "ST STV0288 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c index b823c04e24d3..9a9915f71483 100644 --- a/drivers/media/dvb-frontends/stv0297.c +++ b/drivers/media/dvb-frontends/stv0297.c @@ -694,9 +694,9 @@ static const struct dvb_frontend_ops stv0297_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "ST STV0297 DVB-C", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 633b90e6fe86..4f466394a16c 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -717,10 +717,9 @@ static const struct dvb_frontend_ops stv0299_ops = { .delsys = { SYS_DVBS }, .info = { .name = "ST STV0299 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 5435c908e298..5b91e740e135 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -1693,10 +1693,9 @@ static const struct dvb_frontend_ops stv0367ter_ops = { .delsys = { SYS_DVBT }, .info = { .name = "ST STV0367 DVB-T", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 15625, - .frequency_tolerance = 0, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 15625, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | @@ -2867,9 +2866,9 @@ static const struct dvb_frontend_ops stv0367cab_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "ST STV0367 DVB-C", - .frequency_min = 47000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = 0x400 |/* FE_CAN_QAM_4 */ @@ -3273,10 +3272,9 @@ static const struct dvb_frontend_ops stv0367ddb_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBT }, .info = { .name = "ST STV0367 DDB DVB-C/T", - .frequency_min = 47000000, - .frequency_max = 865000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 865 * MHz, + .frequency_stepsize_hz = 166667, .symbol_rate_min = 870000, .symbol_rate_max = 11700000, .caps = /* DVB-C */ diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 72f17b97ca04..254618a06140 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1875,10 +1875,9 @@ static const struct dvb_frontend_ops stv0900_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STV0900 frontend", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 9133f65d4623..a0622bb71803 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -4905,10 +4905,8 @@ static const struct dvb_frontend_ops stv090x_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "STV090x Multistandard", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index 41444fa1c0bb..4c86073f1a8d 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -682,8 +682,8 @@ static int get_bit_error_rate_s(struct stv *state, u32 *bernumerator, return -EINVAL; if ((regs[0] & 0x80) == 0) { - state->last_berdenominator = 1 << ((state->berscale * 2) + - 10 + 3); + state->last_berdenominator = 1ULL << ((state->berscale * 2) + + 10 + 3); state->last_bernumerator = ((u32)(regs[0] & 0x7F) << 16) | ((u32)regs[1] << 8) | regs[2]; if (state->last_bernumerator < 256 && state->berscale < 6) { @@ -1724,10 +1724,8 @@ static const struct dvb_frontend_ops stv0910_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { .name = "ST STV0910", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .symbol_rate_min = 100000, .symbol_rate_max = 70000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c index 6aad0efa3174..7db9a5bceccc 100644 --- a/drivers/media/dvb-frontends/stv6110.c +++ b/drivers/media/dvb-frontends/stv6110.c @@ -371,9 +371,9 @@ static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops stv6110_tuner_ops = { .info = { .name = "ST STV6110", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .init = stv6110_init, .release = stv6110_release, diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index d8950028d021..82c002d3833a 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -347,10 +347,9 @@ static void stv6110x_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops stv6110x_ops = { .info = { - .name = "STV6110(A) Silicon Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0, + .name = "STV6110(A) Silicon Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .release = stv6110x_release }; diff --git a/drivers/media/dvb-frontends/stv6111.c b/drivers/media/dvb-frontends/stv6111.c index 9b715b6fe152..0cf460111acb 100644 --- a/drivers/media/dvb-frontends/stv6111.c +++ b/drivers/media/dvb-frontends/stv6111.c @@ -646,9 +646,8 @@ static int get_rf_strength(struct dvb_frontend *fe, u16 *st) static const struct dvb_tuner_ops tuner_ops = { .info = { .name = "ST STV6111", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .set_params = set_params, .release = release, diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index 7abf6b0916ed..2ad81a438d6a 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -714,8 +714,8 @@ static const struct dvb_frontend_ops tc90522_ops_sat = { .delsys = { SYS_ISDBS }, .info = { .name = "Toshiba TC90522 ISDB-S module", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, @@ -734,9 +734,9 @@ static const struct dvb_frontend_ops tc90522_ops_ter = { .delsys = { SYS_ISDBT }, .info = { .name = "Toshiba TC90522 ISDB-T module", - .frequency_min = 470000000, - .frequency_max = 770000000, - .frequency_stepsize = 142857, + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 770 * MHz, + .frequency_stepsize_hz = 142857, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c index 4f588ebde39d..5cd885d4ea04 100644 --- a/drivers/media/dvb-frontends/tda10021.c +++ b/drivers/media/dvb-frontends/tda10021.c @@ -487,11 +487,11 @@ static const struct dvb_frontend_ops tda10021_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, .info = { .name = "Philips TDA10021 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, - .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = (XIN / 2) / 64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (XIN / 2) / 4, /* SACLK/4 */ #if 0 .frequency_tolerance = ???, .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ diff --git a/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c index 6c84916234e3..0a9a54563ebe 100644 --- a/drivers/media/dvb-frontends/tda10023.c +++ b/drivers/media/dvb-frontends/tda10023.c @@ -575,9 +575,9 @@ static const struct dvb_frontend_ops tda10023_ops = { .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, .info = { .name = "Philips TDA10023 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 0, /* set in tda10023_attach */ .symbol_rate_max = 0, /* set in tda10023_attach */ .caps = 0x400 | //FE_CAN_QAM_4 diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c index de82a2558e15..c01d60a88af2 100644 --- a/drivers/media/dvb-frontends/tda10048.c +++ b/drivers/media/dvb-frontends/tda10048.c @@ -1156,9 +1156,9 @@ static const struct dvb_frontend_ops tda10048_ops = { .delsys = { SYS_DVBT }, .info = { .name = "NXP TDA10048HN DVB-T", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c index 7dcfb4a4b2d0..d402e4b722ca 100644 --- a/drivers/media/dvb-frontends/tda1004x.c +++ b/drivers/media/dvb-frontends/tda1004x.c @@ -1249,9 +1249,9 @@ static const struct dvb_frontend_ops tda10045_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Philips TDA10045H DVB-T", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | @@ -1319,9 +1319,9 @@ static const struct dvb_frontend_ops tda10046_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Philips TDA10046H DVB-T", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 1ed67c08e699..097c42d3f8c2 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -681,8 +681,8 @@ static int tda10071_set_frontend(struct dvb_frontend *fe) cmd.args[5] = (c->frequency >> 0) & 0xff; cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff; cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff; - cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; - cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; + cmd.args[8] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 8) & 0xff; + cmd.args[9] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 0) & 0xff; cmd.args[10] = rolloff; cmd.args[11] = inversion; cmd.args[12] = pilot; @@ -1106,9 +1106,9 @@ static const struct dvb_frontend_ops tda10071_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { .name = "NXP TDA10071", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_tolerance = 5000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_tolerance_hz = 5 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c index 1a95c521e97f..8323e4e53d66 100644 --- a/drivers/media/dvb-frontends/tda10086.c +++ b/drivers/media/dvb-frontends/tda10086.c @@ -710,9 +710,9 @@ static const struct dvb_frontend_ops tda10086_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Philips TDA10086 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index 2e1d36ae943b..5ce58612315d 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -1154,6 +1154,7 @@ static int set_params(struct dvb_frontend *fe) default: return -EINVAL; } + break; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: if (bw <= 6000000) @@ -1214,9 +1215,9 @@ static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops tuner_ops = { .info = { .name = "NXP TDA18271C2D", - .frequency_min = 47125000, - .frequency_max = 865000000, - .frequency_step = 62500 + .frequency_min_hz = 47125 * kHz, + .frequency_max_hz = 865 * MHz, + .frequency_step_hz = 62500 }, .init = init, .sleep = sleep, diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c index 3ef7140ed7f3..8766c9ff6680 100644 --- a/drivers/media/dvb-frontends/tda665x.c +++ b/drivers/media/dvb-frontends/tda665x.c @@ -231,9 +231,9 @@ struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, info = &fe->ops.tuner_ops.info; memcpy(info->name, config->name, sizeof(config->name)); - info->frequency_min = config->frequency_min; - info->frequency_max = config->frequency_max; - info->frequency_step = config->frequency_offst; + info->frequency_min_hz = config->frequency_min; + info->frequency_max_hz = config->frequency_max; + info->frequency_step_hz = config->frequency_offst; printk(KERN_DEBUG "%s: Attaching TDA665x (%s) tuner\n", __func__, info->name); diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c index 29b4f64c030c..53b26060db7e 100644 --- a/drivers/media/dvb-frontends/tda8083.c +++ b/drivers/media/dvb-frontends/tda8083.c @@ -453,10 +453,9 @@ static const struct dvb_frontend_ops tda8083_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Philips TDA8083 DVB-S", - .frequency_min = 920000, /* TDA8060 */ - .frequency_max = 2200000, /* TDA8060 */ - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - /* .frequency_tolerance = ???,*/ + .frequency_min_hz = 920 * MHz, /* TDA8060 */ + .frequency_max_hz = 2200 * MHz, /* TDA8060 */ + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 12000000, .symbol_rate_max = 30000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c index f72a54e7eb23..500f50b81b66 100644 --- a/drivers/media/dvb-frontends/tda8261.c +++ b/drivers/media/dvb-frontends/tda8261.c @@ -163,10 +163,9 @@ static void tda8261_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda8261_ops = { .info = { - .name = "TDA8261", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 0 + .name = "TDA8261", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .set_params = tda8261_set_params, @@ -190,7 +189,7 @@ struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, fe->tuner_priv = state; fe->ops.tuner_ops = tda8261_ops; - fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; + fe->ops.tuner_ops.info.frequency_step_hz = div_tab[config->step_size] * kHz; pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c index da427b4c2aaa..100da5d5fdc5 100644 --- a/drivers/media/dvb-frontends/tda826x.c +++ b/drivers/media/dvb-frontends/tda826x.c @@ -131,8 +131,8 @@ static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tda826x_tuner_ops = { .info = { .name = "Philips TDA826X", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .release = tda826x_release, .sleep = tda826x_sleep, diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index c55882a8da06..3e3e40878633 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -498,8 +498,8 @@ static int ts2020_read_signal_strength(struct dvb_frontend *fe, static const struct dvb_tuner_ops ts2020_tuner_ops = { .info = { .name = "TS2020", - .frequency_min = 950000, - .frequency_max = 2150000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz }, .init = ts2020_init, .release = ts2020_release, diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c index 1d41abd47f04..b233b7be0b84 100644 --- a/drivers/media/dvb-frontends/tua6100.c +++ b/drivers/media/dvb-frontends/tua6100.c @@ -155,9 +155,9 @@ static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tua6100_tuner_ops = { .info = { .name = "Infineon TUA6100", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_step = 1000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_step_hz = 1 * MHz, }, .release = tua6100_release, .sleep = tua6100_sleep, diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c index 17600989f121..eb1249d81310 100644 --- a/drivers/media/dvb-frontends/ves1820.c +++ b/drivers/media/dvb-frontends/ves1820.c @@ -412,9 +412,9 @@ static const struct dvb_frontend_ops ves1820_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "VLSI VES1820 DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min_hz = 47 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | diff --git a/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c index 0c7b3286b04d..ddc5bfd84cd5 100644 --- a/drivers/media/dvb-frontends/ves1x93.c +++ b/drivers/media/dvb-frontends/ves1x93.c @@ -516,10 +516,10 @@ static const struct dvb_frontend_ops ves1x93_ops = { .delsys = { SYS_DVBS }, .info = { .name = "VLSI VES1x93 DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* .symbol_rate_tolerance = ???,*/ diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c index 89dd65ae88ad..f1c92338015d 100644 --- a/drivers/media/dvb-frontends/zl10036.c +++ b/drivers/media/dvb-frontends/zl10036.c @@ -311,8 +311,8 @@ static int zl10036_set_params(struct dvb_frontend *fe) /* ensure correct values * maybe redundant as core already checks this */ - if ((frequency < fe->ops.info.frequency_min) - || (frequency > fe->ops.info.frequency_max)) + if ((frequency < fe->ops.info.frequency_min_hz / kHz) + || (frequency > fe->ops.info.frequency_max_hz / kHz)) return -EINVAL; /* @@ -443,8 +443,8 @@ static int zl10036_init(struct dvb_frontend *fe) static const struct dvb_tuner_ops zl10036_tuner_ops = { .info = { .name = "Zarlink ZL10036", - .frequency_min = 950000, - .frequency_max = 2175000 + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2175 * MHz }, .init = zl10036_init, .release = zl10036_release, diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c index c9901f45deb7..42e63a3fa121 100644 --- a/drivers/media/dvb-frontends/zl10353.c +++ b/drivers/media/dvb-frontends/zl10353.c @@ -635,10 +635,9 @@ static const struct dvb_frontend_ops zl10353_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Zarlink ZL10353 DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, - .frequency_tolerance = 0, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/firewire/firedtv-fe.c b/drivers/media/firewire/firedtv-fe.c index a2ef4ede8ebe..69087ae6c1d0 100644 --- a/drivers/media/firewire/firedtv-fe.c +++ b/drivers/media/firewire/firedtv-fe.c @@ -152,7 +152,7 @@ static int fdtv_set_frontend(struct dvb_frontend *fe) void fdtv_frontend_init(struct firedtv *fdtv, const char *name) { struct dvb_frontend_ops *ops = &fdtv->fe.ops; - struct dvb_frontend_info *fi = &ops->info; + struct dvb_frontend_internal_info *fi = &ops->info; ops->init = fdtv_dvb_init; ops->sleep = fdtv_sleep; @@ -174,9 +174,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_S: ops->delsys[0] = SYS_DVBS; - fi->frequency_min = 950000; - fi->frequency_max = 2150000; - fi->frequency_stepsize = 125; + fi->frequency_min_hz = 950 * MHz; + fi->frequency_max_hz = 2150 * MHz; + fi->frequency_stepsize_hz = 125 * kHz; fi->symbol_rate_min = 1000000; fi->symbol_rate_max = 40000000; @@ -194,9 +194,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) ops->delsys[0] = SYS_DVBS; ops->delsys[1] = SYS_DVBS2; - fi->frequency_min = 950000; - fi->frequency_max = 2150000; - fi->frequency_stepsize = 125; + fi->frequency_min_hz = 950 * MHz; + fi->frequency_max_hz = 2150 * MHz; + fi->frequency_stepsize_hz = 125 * kHz; fi->symbol_rate_min = 1000000; fi->symbol_rate_max = 40000000; @@ -214,9 +214,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_C: ops->delsys[0] = SYS_DVBC_ANNEX_A; - fi->frequency_min = 47000000; - fi->frequency_max = 866000000; - fi->frequency_stepsize = 62500; + fi->frequency_min_hz = 47 * MHz; + fi->frequency_max_hz = 866 * MHz; + fi->frequency_stepsize_hz = 62500; fi->symbol_rate_min = 870000; fi->symbol_rate_max = 6900000; @@ -232,9 +232,9 @@ void fdtv_frontend_init(struct firedtv *fdtv, const char *name) case FIREDTV_DVB_T: ops->delsys[0] = SYS_DVBT; - fi->frequency_min = 49000000; - fi->frequency_max = 861000000; - fi->frequency_stepsize = 62500; + fi->frequency_min_hz = 49 * MHz; + fi->frequency_max_hz = 861 * MHz; + fi->frequency_stepsize_hz = 62500; fi->caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_2_3 | diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 341452fe98df..82af97430e5b 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -326,6 +326,16 @@ config VIDEO_AD5820 This is a driver for the AD5820 camera lens voice coil. It is used for example in Nokia N900 (RX-51). +config VIDEO_AK7375 + tristate "AK7375 lens voice coil support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + help + This is a driver for the AK7375 camera lens voice coil. + AK7375 is a 12 bit DAC with 120mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_DW9714 tristate "DW9714 lens voice coil support" depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER @@ -336,6 +346,16 @@ config VIDEO_DW9714 capability. This is designed for linear control of voice coil motors, controlled via I2C serial interface. +config VIDEO_DW9807_VCM + tristate "DW9807 lens voice coil support" + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + ---help--- + This is a driver for the DW9807 camera lens voice coil. + DW9807 is a 10 bit DAC with 100mA output current sink + capability. This is designed for linear control of + voice coil motors, controlled via I2C serial interface. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L2 && I2C @@ -378,7 +398,7 @@ config VIDEO_TVP514X depends on VIDEO_V4L2 && I2C select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + This is a Video4Linux2 sensor driver for the TI TVP5146/47 decoder. It is currently working with the TI OMAP3 camera controller. @@ -580,7 +600,7 @@ config VIDEO_IMX258 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Sony + This is a Video4Linux2 sensor driver for the Sony IMX258 camera. To compile this driver as a module, choose M here: the @@ -591,7 +611,7 @@ config VIDEO_IMX274 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a V4L2 sensor-level driver for the Sony IMX274 + This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. config VIDEO_OV2640 @@ -599,7 +619,7 @@ config VIDEO_OV2640 depends on VIDEO_V4L2 && I2C depends on MEDIA_CAMERA_SUPPORT help - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2640 camera. To compile this driver as a module, choose M here: the @@ -611,19 +631,31 @@ config VIDEO_OV2659 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2659 camera. To compile this driver as a module, choose M here: the module will be called ov2659. +config VIDEO_OV2680 + tristate "OmniVision OV2680 sensor support" + depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + ---help--- + This is a Video4Linux2 sensor driver for the OmniVision + OV2680 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2680. + config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV2685 camera. To compile this driver as a module, choose M here: the @@ -636,7 +668,7 @@ config VIDEO_OV5640 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the Omnivision + This is a Video4Linux2 sensor driver for the Omnivision OV5640 camera sensor with a MIPI CSI-2 interface. config VIDEO_OV5645 @@ -646,7 +678,7 @@ config VIDEO_OV5645 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5645 camera. To compile this driver as a module, choose M here: the @@ -658,7 +690,7 @@ config VIDEO_OV5647 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5647 camera. To compile this driver as a module, choose M here: the @@ -669,7 +701,7 @@ config VIDEO_OV6650 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV6650 camera. To compile this driver as a module, choose M here: the @@ -682,7 +714,7 @@ config VIDEO_OV5670 depends on MEDIA_CONTROLLER select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5670 camera. To compile this driver as a module, choose M here: the @@ -693,7 +725,7 @@ config VIDEO_OV5695 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV5695 camera. To compile this driver as a module, choose M here: the @@ -705,7 +737,7 @@ config VIDEO_OV7251 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE help - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7251 camera. To compile this driver as a module, choose M here: the @@ -716,7 +748,7 @@ config VIDEO_OV772X depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. To compile this driver as a module, choose M here: the @@ -727,7 +759,7 @@ config VIDEO_OV7640 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7640 camera. To compile this driver as a module, choose M here: the @@ -739,7 +771,7 @@ config VIDEO_OV7670 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7670 VGA camera. It currently only works with the M88ALP01 controller. @@ -748,14 +780,14 @@ config VIDEO_OV7740 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for the Omnivision + This is a V4L2 sensor driver for the Omnivision OV9650 and OV9652 camera sensors. config VIDEO_OV13858 @@ -764,7 +796,7 @@ config VIDEO_OV13858 depends on MEDIA_CAMERA_SUPPORT select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the OmniVision + This is a Video4Linux2 sensor driver for the OmniVision OV13858 camera. config VIDEO_VS6624 @@ -772,7 +804,7 @@ config VIDEO_VS6624 depends on VIDEO_V4L2 && I2C depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the ST VS6624 + This is a Video4Linux2 sensor driver for the ST VS6624 camera. To compile this driver as a module, choose M here: the @@ -800,7 +832,7 @@ config VIDEO_MT9P031 depends on MEDIA_CAMERA_SUPPORT select VIDEO_APTINA_PLL ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) mt9p031 5 Mpixel camera. config VIDEO_MT9T001 @@ -808,7 +840,7 @@ config VIDEO_MT9T001 depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) mt0t001 3 Mpixel camera. config VIDEO_MT9T112 @@ -816,7 +848,7 @@ config VIDEO_MT9T112 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Aptina + This is a Video4Linux2 sensor driver for the Aptina (Micron) MT9T111 and MT9T112 3 Mpixel camera. To compile this driver as a module, choose M here: the @@ -827,7 +859,7 @@ config VIDEO_MT9V011 depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT ---help--- - This is a Video4Linux2 sensor-level driver for the Micron + This is a Video4Linux2 sensor driver for the Micron mt0v011 1.3 Mpixel camera. It currently only works with the em28xx driver. @@ -838,9 +870,20 @@ config VIDEO_MT9V032 select REGMAP_I2C select V4L2_FWNODE ---help--- - This is a Video4Linux2 sensor-level driver for the Micron + This is a Video4Linux2 sensor driver for the Micron MT9V032 752x480 CMOS sensor. +config VIDEO_MT9V111 + tristate "Aptina MT9V111 sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Aptina/Micron + MT9V111 sensor. + + To compile this driver as a module, choose M here: the + module will be called mt9v111. + config VIDEO_SR030PC30 tristate "Siliconfile SR030PC30 sensor support" depends on I2C && VIDEO_V4L2 @@ -857,12 +900,23 @@ config VIDEO_NOON010PC30 source "drivers/media/i2c/m5mols/Kconfig" +config VIDEO_RJ54N1 + tristate "Sharp RJ54N1CB0C sensor support" + depends on I2C && VIDEO_V4L2 + depends on MEDIA_CAMERA_SUPPORT + help + This is a V4L2 sensor driver for Sharp RJ54N1CB0C CMOS image + sensor. + + To compile this driver as a module, choose M here: the + module will be called rj54n1. + config VIDEO_S5K6AA tristate "Samsung S5K6AAFX sensor support" depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K6A3 @@ -870,7 +924,7 @@ config VIDEO_S5K6A3 depends on MEDIA_CAMERA_SUPPORT depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ---help--- - This is a V4L2 sensor-level driver for Samsung S5K6A3 raw + This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. config VIDEO_S5K4ECGX @@ -878,7 +932,7 @@ config VIDEO_S5K4ECGX depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select CRC32 ---help--- - This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M + This is a V4L2 sensor driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. config VIDEO_S5K5BAF @@ -886,7 +940,7 @@ config VIDEO_S5K5BAF depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE ---help--- - This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M + This is a V4L2 sensor driver for Samsung S5K5BAF 2M camera sensor with an embedded SoC image signal processor. source "drivers/media/i2c/smiapp/Kconfig" @@ -897,7 +951,7 @@ config VIDEO_S5C73M3 depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE ---help--- - This is a V4L2 sensor-level driver for Samsung S5C73M3 + This is a V4L2 sensor driver for Samsung S5C73M3 8 Mpixel camera. comment "Flash devices" @@ -1002,6 +1056,7 @@ config VIDEO_I2C tristate "I2C transport video support" depends on VIDEO_V4L2 && I2C select VIDEOBUF2_VMALLOC + imply HWMON ---help--- Enable the I2C transport video support which supports the following: diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index d679d57cd3b3..a94eb03d10d4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -23,7 +23,9 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o +obj-$(CONFIG_VIDEO_AK7375) += ak7375.o obj-$(CONFIG_VIDEO_DW9714) += dw9714.o +obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o @@ -63,6 +65,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV2680) += ov2680.o obj-$(CONFIG_VIDEO_OV2685) += ov2685.o obj-$(CONFIG_VIDEO_OV5640) += ov5640.o obj-$(CONFIG_VIDEO_OV5645) += ov5645.o @@ -84,8 +87,10 @@ obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o +obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o +obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 91ff06088572..5b008b0002c0 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1134,6 +1134,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id * goto err_hdl; } state->pad.flags = MEDIA_PAD_FL_SINK; + sd->entity.function = MEDIA_ENT_F_DV_ENCODER; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 25d24a3f10a7..de10367d550b 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -461,6 +461,22 @@ static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } +static int adv7180_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct adv7180_state *state = to_state(sd); + + if (state->curr_norm & V4L2_STD_525_60) { + fi->interval.numerator = 1001; + fi->interval.denominator = 30000; + } else { + fi->interval.numerator = 1; + fi->interval.denominator = 25; + } + + return 0; +} + static void adv7180_set_power_pin(struct adv7180_state *state, bool on) { if (!state->pwdn_gpio) @@ -644,6 +660,9 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd, fmt->width = 720; fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; + if (state->field == V4L2_FIELD_ALTERNATE) + fmt->height /= 2; + return 0; } @@ -711,11 +730,11 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, switch (format->format.field) { case V4L2_FIELD_NONE: - if (!(state->chip_info->flags & ADV7180_FLAG_I2P)) - format->format.field = V4L2_FIELD_INTERLACED; - break; + if (state->chip_info->flags & ADV7180_FLAG_I2P) + break; + /* fall through */ default: - format->format.field = V4L2_FIELD_INTERLACED; + format->format.field = V4L2_FIELD_ALTERNATE; break; } @@ -817,6 +836,7 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops adv7180_video_ops = { .s_std = adv7180_s_std, .g_std = adv7180_g_std, + .g_frame_interval = adv7180_g_frame_interval, .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, @@ -1291,7 +1311,7 @@ static int adv7180_probe(struct i2c_client *client, return -ENOMEM; state->client = client; - state->field = V4L2_FIELD_INTERLACED; + state->field = V4L2_FIELD_ALTERNATE; state->chip_info = (struct adv7180_chip_info *)id->driver_data; state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", @@ -1335,7 +1355,7 @@ static int adv7180_probe(struct i2c_client *client, goto err_unregister_vpp_client; state->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER; + sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, 1, &state->pad); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 820b44ed56a8..469be87a3761 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -284,7 +284,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) adv748x_csi2_set_virtual_channel(tx, 0); adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, - MEDIA_ENT_F_UNKNOWN, + MEDIA_ENT_F_VID_IF_BRIDGE, is_txa(tx) ? "txa" : "txb"); /* Ensure that matching is based upon the endpoint fwnodes */ diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 5731751d3f2a..55c2ea0720d9 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1847,6 +1847,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id * goto err_hdl; } state->pad.flags = MEDIA_PAD_FL_SINK; + sd->entity.function = MEDIA_ENT_F_DV_ENCODER; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) goto err_hdl; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index cac2081e876e..668be2bca57a 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -3108,12 +3108,9 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), &bus_cfg); - if (ret) { - of_node_put(endpoint); - return ret; - } - of_node_put(endpoint); + if (ret) + return ret; if (!of_property_read_u32(np, "default-input", &v)) state->pdata.default_input = v; @@ -3502,6 +3499,7 @@ static int adv76xx_probe(struct i2c_client *client, for (i = 0; i < state->source_pad; ++i) state->pads[i].flags = MEDIA_PAD_FL_SINK; state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_DV_DECODER; err = media_entity_pads_init(&sd->entity, state->source_pad + 1, state->pads); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index fddac32e5051..4f8fbdd00e35 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3102,7 +3102,7 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) sdp_write(sd, 0x12, 0x00); /* Disable 3D comb, Frame TBC & 3DNR */ io_write(sd, 0xFF, 0x04); /* Reset memory controller */ - mdelay(5); + usleep_range(5000, 6000); sdp_write(sd, 0x12, 0x00); /* Disable 3D Comb, Frame TBC & 3DNR */ sdp_io_write(sd, 0x2A, 0x01); /* Memory BIST Initialisation */ @@ -3116,12 +3116,12 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) sdp_io_write(sd, 0x7d, 0x00); /* Memory BIST Initialisation */ sdp_io_write(sd, 0x7e, 0x1a); /* Memory BIST Initialisation */ - mdelay(5); + usleep_range(5000, 6000); sdp_io_write(sd, 0xd9, 0xd5); /* Enable BIST Test */ sdp_write(sd, 0x12, 0x05); /* Enable FRAME TBC & 3D COMB */ - mdelay(20); + msleep(20); for (i = 0; i < 10; i++) { u8 result = sdp_io_read(sd, 0xdb); @@ -3132,7 +3132,7 @@ static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) else pass++; } - mdelay(20); + msleep(20); } v4l2_dbg(1, debug, sd, @@ -3541,6 +3541,7 @@ static int adv7842_probe(struct i2c_client *client, INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, adv7842_delayed_work_enable_hotplug); + sd->entity.function = MEDIA_ENT_F_DV_DECODER; state->pad.flags = MEDIA_PAD_FL_SOURCE; err = media_entity_pads_init(&sd->entity, 1, &state->pad); if (err) diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c new file mode 100644 index 000000000000..7b14b11605ca --- /dev/null +++ b/drivers/media/i2c/ak7375.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#define AK7375_MAX_FOCUS_POS 4095 +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position + */ +#define AK7375_FOCUS_STEPS 1 +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define AK7375_CTRL_STEPS 64 +#define AK7375_CTRL_DELAY_US 1000 + +#define AK7375_REG_POSITION 0x0 +#define AK7375_REG_CONT 0x2 +#define AK7375_MODE_ACTIVE 0x0 +#define AK7375_MODE_STANDBY 0x40 + +/* ak7375 device structure */ +struct ak7375_device { + struct v4l2_ctrl_handler ctrls_vcm; + struct v4l2_subdev sd; + struct v4l2_ctrl *focus; + /* active or standby mode */ + bool active; +}; + +static inline struct ak7375_device *to_ak7375_vcm(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct ak7375_device, ctrls_vcm); +} + +static inline struct ak7375_device *sd_to_ak7375_vcm(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct ak7375_device, sd); +} + +static int ak7375_i2c_write(struct ak7375_device *ak7375, + u8 addr, u16 data, u8 size) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ak7375->sd); + u8 buf[3]; + int ret; + + if (size != 1 && size != 2) + return -EINVAL; + buf[0] = addr; + buf[size] = data & 0xff; + if (size == 2) + buf[1] = (data >> 8) & 0xff; + ret = i2c_master_send(client, (const char *)buf, size + 1); + if (ret < 0) + return ret; + if (ret != size + 1) + return -EIO; + + return 0; +} + +static int ak7375_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ak7375_device *dev_vcm = to_ak7375_vcm(ctrl); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) + return ak7375_i2c_write(dev_vcm, AK7375_REG_POSITION, + ctrl->val << 4, 2); + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ak7375_vcm_ctrl_ops = { + .s_ctrl = ak7375_set_ctrl, +}; + +static int ak7375_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 ak7375_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops ak7375_int_ops = { + .open = ak7375_open, + .close = ak7375_close, +}; + +static const struct v4l2_subdev_ops ak7375_ops = { }; + +static void ak7375_subdev_cleanup(struct ak7375_device *ak7375_dev) +{ + v4l2_async_unregister_subdev(&ak7375_dev->sd); + v4l2_ctrl_handler_free(&ak7375_dev->ctrls_vcm); + media_entity_cleanup(&ak7375_dev->sd.entity); +} + +static int ak7375_init_controls(struct ak7375_device *dev_vcm) +{ + struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; + const struct v4l2_ctrl_ops *ops = &ak7375_vcm_ctrl_ops; + + v4l2_ctrl_handler_init(hdl, 1); + + dev_vcm->focus = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, + 0, AK7375_MAX_FOCUS_POS, AK7375_FOCUS_STEPS, 0); + + if (hdl->error) + dev_err(dev_vcm->sd.dev, "%s fail error: 0x%x\n", + __func__, hdl->error); + dev_vcm->sd.ctrl_handler = hdl; + + return hdl->error; +} + +static int ak7375_probe(struct i2c_client *client) +{ + struct ak7375_device *ak7375_dev; + int ret; + + ak7375_dev = devm_kzalloc(&client->dev, sizeof(*ak7375_dev), + GFP_KERNEL); + if (!ak7375_dev) + return -ENOMEM; + + v4l2_i2c_subdev_init(&ak7375_dev->sd, client, &ak7375_ops); + ak7375_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ak7375_dev->sd.internal_ops = &ak7375_int_ops; + ak7375_dev->sd.entity.function = MEDIA_ENT_F_LENS; + + ret = ak7375_init_controls(ak7375_dev); + if (ret) + goto err_cleanup; + + ret = media_entity_pads_init(&ak7375_dev->sd.entity, 0, NULL); + if (ret < 0) + goto err_cleanup; + + ret = v4l2_async_register_subdev(&ak7375_dev->sd); + if (ret < 0) + goto err_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +err_cleanup: + v4l2_ctrl_handler_free(&ak7375_dev->ctrls_vcm); + media_entity_cleanup(&ak7375_dev->sd.entity); + + return ret; +} + +static int ak7375_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + + ak7375_subdev_cleanup(ak7375_dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +/* + * This function sets the vcm position, so it consumes least current + * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused ak7375_vcm_suspend(struct device *dev) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + int ret, val; + + if (!ak7375_dev->active) + return 0; + + for (val = ak7375_dev->focus->val & ~(AK7375_CTRL_STEPS - 1); + val >= 0; val -= AK7375_CTRL_STEPS) { + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, + val << 4, 2); + if (ret) + dev_err_once(dev, "%s I2C failure: %d\n", + __func__, ret); + usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + } + + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, + AK7375_MODE_STANDBY, 1); + if (ret) + dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + + ak7375_dev->active = false; + + return 0; +} + +/* + * This function sets the vcm position to the value set by the user + * through v4l2_ctrl_ops s_ctrl handler + * The lens position is gradually moved in units of AK7375_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused ak7375_vcm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ak7375_device *ak7375_dev = sd_to_ak7375_vcm(sd); + int ret, val; + + if (ak7375_dev->active) + return 0; + + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_CONT, + AK7375_MODE_ACTIVE, 1); + if (ret) { + dev_err(dev, "%s I2C failure: %d\n", __func__, ret); + return ret; + } + + for (val = ak7375_dev->focus->val % AK7375_CTRL_STEPS; + val <= ak7375_dev->focus->val; + val += AK7375_CTRL_STEPS) { + ret = ak7375_i2c_write(ak7375_dev, AK7375_REG_POSITION, + val << 4, 2); + if (ret) + dev_err_ratelimited(dev, "%s I2C failure: %d\n", + __func__, ret); + usleep_range(AK7375_CTRL_DELAY_US, AK7375_CTRL_DELAY_US + 10); + } + + ak7375_dev->active = true; + + return 0; +} + +static const struct of_device_id ak7375_of_table[] = { + { .compatible = "asahi-kasei,ak7375" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ak7375_of_table); + +static const struct dev_pm_ops ak7375_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ak7375_vcm_suspend, ak7375_vcm_resume) + SET_RUNTIME_PM_OPS(ak7375_vcm_suspend, ak7375_vcm_resume, NULL) +}; + +static struct i2c_driver ak7375_i2c_driver = { + .driver = { + .name = "ak7375", + .pm = &ak7375_pm_ops, + .of_match_table = ak7375_of_table, + }, + .probe_new = ak7375_probe, + .remove = ak7375_remove, +}; +module_i2c_driver(ak7375_i2c_driver); + +MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>"); +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); +MODULE_DESCRIPTION("AK7375 VCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h index fb13a624d2e3..c323b1af1f83 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.h +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -45,6 +45,35 @@ enum cx25840_media_pads { CX25840_NUM_PADS }; +/** + * struct cx25840_state - a device instance private data + * @c: i2c_client struct representing this device + * @sd: our V4L2 sub-device + * @hdl: our V4L2 control handler + * @volume: audio volume V4L2 control (non-cx2583x devices only) + * @mute: audio mute V4L2 control (non-cx2583x devices only) + * @pvr150_workaround: whether we enable workaround for Hauppauge PVR150 + * hardware bug (audio dropping out) + * @radio: set if we are currently in the radio mode, otherwise + * the current mode is non-radio (that is, video) + * @std: currently set video standard + * @vid_input: currently set video input + * @aud_input: currently set audio input + * @audclk_freq: currently set audio sample rate + * @audmode: currently set audio mode (when in non-radio mode) + * @vbi_line_offset: vbi line number offset + * @id: exact device model + * @rev: raw device id read from the chip + * @is_initialized: whether we have already loaded firmware into the chip + * and initialized it + * @vbi_regs_offset: offset of vbi regs + * @fw_wait: wait queue to wake an initalization function up when + * firmware loading (on a separate workqueue) finishes + * @fw_work: a work that actually loads the firmware on a separate + * workqueue + * @ir_state: a pointer to chip IR controller private data + * @pads: array of supported chip pads (currently only a stub) + */ struct cx25840_state { struct i2c_client *c; struct v4l2_subdev sd; @@ -66,8 +95,8 @@ struct cx25840_state { u32 rev; int is_initialized; unsigned vbi_regs_offset; - wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ - struct work_struct fw_work; /* work entry for fw load */ + wait_queue_head_t fw_wait; + struct work_struct fw_work; struct cx25840_ir_state *ir_state; #if defined(CONFIG_MEDIA_CONTROLLER) struct media_pad pads[CX25840_NUM_PADS]; diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c new file mode 100644 index 000000000000..8ba3920b6e2f --- /dev/null +++ b/drivers/media/i2c/dw9807-vcm.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> + +#define DW9807_MAX_FOCUS_POS 1023 +/* + * This sets the minimum granularity for the focus positions. + * A value of 1 gives maximum accuracy for a desired focus position. + */ +#define DW9807_FOCUS_STEPS 1 +/* + * This acts as the minimum granularity of lens movement. + * Keep this value power of 2, so the control steps can be + * uniformly adjusted for gradual lens movement, with desired + * number of control steps. + */ +#define DW9807_CTRL_STEPS 16 +#define DW9807_CTRL_DELAY_US 1000 + +#define DW9807_CTL_ADDR 0x02 +/* + * DW9807 separates two registers to control the VCM position. + * One for MSB value, another is LSB value. + */ +#define DW9807_MSB_ADDR 0x03 +#define DW9807_LSB_ADDR 0x04 +#define DW9807_STATUS_ADDR 0x05 +#define DW9807_MODE_ADDR 0x06 +#define DW9807_RESONANCE_ADDR 0x07 + +#define MAX_RETRY 10 + +struct dw9807_device { + struct v4l2_ctrl_handler ctrls_vcm; + struct v4l2_subdev sd; + u16 current_val; +}; + +static inline struct dw9807_device *sd_to_dw9807_vcm( + struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct dw9807_device, sd); +} + +static int dw9807_i2c_check(struct i2c_client *client) +{ + const char status_addr = DW9807_STATUS_ADDR; + char status_result; + int ret; + + ret = i2c_master_send(client, &status_addr, sizeof(status_addr)); + if (ret < 0) { + dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n", + ret); + return ret; + } + + ret = i2c_master_recv(client, &status_result, sizeof(status_result)); + if (ret < 0) { + dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n", + ret); + return ret; + } + + return status_result; +} + +static int dw9807_set_dac(struct i2c_client *client, u16 data) +{ + const char tx_data[3] = { + DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff) + }; + int val, ret; + + /* + * According to the datasheet, need to check the bus status before we + * write VCM position. This ensure that we really write the value + * into the register + */ + ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0, + DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US); + + if (ret || val < 0) { + if (ret) { + dev_warn(&client->dev, + "Cannot do the write operation because VCM is busy\n"); + } + + return ret ? -EBUSY : val; + } + + /* Write VCM position to registers */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, + "I2C write MSB fail ret=%d\n", ret); + + return ret; + } + + return 0; +} + +static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct dw9807_device *dev_vcm = container_of(ctrl->handler, + struct dw9807_device, ctrls_vcm); + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + dev_vcm->current_val = ctrl->val; + return dw9807_set_dac(client, ctrl->val); + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = { + .s_ctrl = dw9807_set_ctrl, +}; + +static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rval; + + rval = pm_runtime_get_sync(sd->dev); + if (rval < 0) { + pm_runtime_put_noidle(sd->dev); + return rval; + } + + return 0; +} + +static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + pm_runtime_put(sd->dev); + + return 0; +} + +static const struct v4l2_subdev_internal_ops dw9807_int_ops = { + .open = dw9807_open, + .close = dw9807_close, +}; + +static const struct v4l2_subdev_ops dw9807_ops = { }; + +static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) +{ + v4l2_async_unregister_subdev(&dw9807_dev->sd); + v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); + media_entity_cleanup(&dw9807_dev->sd.entity); +} + +static int dw9807_init_controls(struct dw9807_device *dev_vcm) +{ + struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; + const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; + struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd); + + v4l2_ctrl_handler_init(hdl, 1); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, + 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0); + + dev_vcm->sd.ctrl_handler = hdl; + if (hdl->error) { + dev_err(&client->dev, "%s fail error: 0x%x\n", + __func__, hdl->error); + return hdl->error; + } + + return 0; +} + +static int dw9807_probe(struct i2c_client *client) +{ + struct dw9807_device *dw9807_dev; + int rval; + + dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev), + GFP_KERNEL); + if (dw9807_dev == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops); + dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + dw9807_dev->sd.internal_ops = &dw9807_int_ops; + + rval = dw9807_init_controls(dw9807_dev); + if (rval) + goto err_cleanup; + + rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL); + if (rval < 0) + goto err_cleanup; + + dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; + + rval = v4l2_async_register_subdev(&dw9807_dev->sd); + if (rval < 0) + goto err_cleanup; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +err_cleanup: + dw9807_subdev_cleanup(dw9807_dev); + + return rval; +} + +static int dw9807_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + dw9807_subdev_cleanup(dw9807_dev); + + return 0; +} + +/* + * This function sets the vcm position, so it consumes least current + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; + int ret, val; + + for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); + val >= 0; val -= DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_once(dev, "%s I2C failure: %d", __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + /* Power down */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + return 0; +} + +/* + * This function sets the vcm position to the value set by the user + * through v4l2_ctrl_ops s_ctrl handler + * The lens position is gradually moved in units of DW9807_CTRL_STEPS, + * to make the movements smoothly. + */ +static int __maybe_unused dw9807_vcm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; + int ret, val; + + /* Power on */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; + val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; + val += DW9807_CTRL_STEPS) { + ret = dw9807_set_dac(client, val); + if (ret) + dev_err_ratelimited(dev, "%s I2C failure: %d", + __func__, ret); + usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); + } + + return 0; +} + +static const struct of_device_id dw9807_of_table[] = { + { .compatible = "dongwoon,dw9807-vcm" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw9807_of_table); + +static const struct dev_pm_ops dw9807_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) + SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) +}; + +static struct i2c_driver dw9807_i2c_driver = { + .driver = { + .name = "dw9807", + .pm = &dw9807_pm_ops, + .of_match_table = dw9807_of_table, + }, + .probe_new = dw9807_probe, + .remove = dw9807_remove, +}; + +module_i2c_driver(dw9807_i2c_driver); + +MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>"); +MODULE_DESCRIPTION("DW9807 VCM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index e9eff9039ef5..37ef38947e01 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1446,6 +1446,7 @@ static int et8ek8_probe(struct i2c_client *client, sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sensor->subdev.internal_ops = &et8ek8_internal_ops; + sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) { diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index f3b124723aa0..31a1e2294843 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1221,6 +1221,14 @@ static int imx258_probe(struct i2c_client *client) if (val != 19200000) return -EINVAL; + /* + * Check that the device is mounted upside down. The driver only + * supports a single pixel order right now. + */ + ret = device_property_read_u32(&client->dev, "rotation", &val); + if (ret || val != 180) + return -EINVAL; + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); if (!imx258) return -ENOMEM; diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 63fb94e7da37..f8c70f1a34fe 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -5,6 +5,7 @@ * * Leon Luo <leonl@leopardimaging.com> * Edwin Zou <edwinz@leopardimaging.com> + * Luca Ceresoli <luca@lucaceresoli.net> * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +26,7 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/regmap.h> @@ -74,7 +76,7 @@ */ #define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72) -#define IMX274_DEFAULT_MODE IMX274_MODE_3840X2160 +#define IMX274_DEFAULT_MODE IMX274_BINNING_OFF #define IMX274_MAX_WIDTH (3840) #define IMX274_MAX_HEIGHT (2160) #define IMX274_MAX_FRAME_RATE (120) @@ -111,6 +113,20 @@ #define IMX274_SHR_REG_LSB 0x300C /* SHR */ #define IMX274_SVR_REG_MSB 0x300F /* SVR */ #define IMX274_SVR_REG_LSB 0x300E /* SVR */ +#define IMX274_HTRIM_EN_REG 0x3037 +#define IMX274_HTRIM_START_REG_LSB 0x3038 +#define IMX274_HTRIM_START_REG_MSB 0x3039 +#define IMX274_HTRIM_END_REG_LSB 0x303A +#define IMX274_HTRIM_END_REG_MSB 0x303B +#define IMX274_VWIDCUTEN_REG 0x30DD +#define IMX274_VWIDCUT_REG_LSB 0x30DE +#define IMX274_VWIDCUT_REG_MSB 0x30DF +#define IMX274_VWINPOS_REG_LSB 0x30E0 +#define IMX274_VWINPOS_REG_MSB 0x30E1 +#define IMX274_WRITE_VSIZE_REG_LSB 0x3130 +#define IMX274_WRITE_VSIZE_REG_MSB 0x3131 +#define IMX274_Y_OUT_SIZE_REG_LSB 0x3132 +#define IMX274_Y_OUT_SIZE_REG_MSB 0x3133 #define IMX274_VMAX_REG_1 0x30FA /* VMAX, MSB */ #define IMX274_VMAX_REG_2 0x30F9 /* VMAX */ #define IMX274_VMAX_REG_3 0x30F8 /* VMAX, LSB */ @@ -140,17 +156,35 @@ static const struct regmap_config imx274_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -enum imx274_mode { - IMX274_MODE_3840X2160, - IMX274_MODE_1920X1080, - IMX274_MODE_1280X720, +enum imx274_binning { + IMX274_BINNING_OFF, + IMX274_BINNING_2_1, + IMX274_BINNING_3_1, }; /* - * imx274 format related structure + * Parameters for each imx274 readout mode. + * + * These are the values to configure the sensor in one of the + * implemented modes. + * + * @init_regs: registers to initialize the mode + * @bin_ratio: downscale factor (e.g. 3 for 3:1 binning) + * @min_frame_len: Minimum frame length for each mode (see "Frame Rate + * Adjustment (CSI-2)" in the datasheet) + * @min_SHR: Minimum SHR register value (see "Shutter Setting (CSI-2)" in the + * datasheet) + * @max_fps: Maximum frames per second + * @nocpiop: Number of clocks per internal offset period (see "Integration Time + * in Each Readout Drive Mode (CSI-2)" in the datasheet) */ struct imx274_frmfmt { - struct v4l2_frmsize_discrete size; + const struct reg_8 *init_regs; + unsigned int bin_ratio; + int min_frame_len; + int min_SHR; + int max_fps; + int nocpiop; }; /* @@ -197,31 +231,14 @@ static const struct reg_8 imx274_mode1_3840x2160_raw10[] = { {0x3004, 0x01}, {0x3005, 0x01}, {0x3006, 0x00}, - {0x3007, 0x02}, + {0x3007, 0xa2}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x01}, - {0x30F6, 0x07}, /* HMAX, 263 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30dd, 0x01}, /* crop to 2160 */ - {0x30de, 0x06}, - {0x30df, 0x00}, - {0x30e0, 0x12}, - {0x30e1, 0x00}, - {0x3037, 0x01}, /* to crop to 3840 */ - {0x3038, 0x0c}, - {0x3039, 0x00}, - {0x303a, 0x0c}, - {0x303b, 0x0f}, {0x30EE, 0x01}, - {0x3130, 0x86}, - {0x3131, 0x08}, - {0x3132, 0x7E}, - {0x3133, 0x08}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x16}, @@ -255,32 +272,14 @@ static const struct reg_8 imx274_mode3_1920x1080_raw10[] = { {0x3004, 0x02}, {0x3005, 0x21}, {0x3006, 0x00}, - {0x3007, 0x11}, + {0x3007, 0xb1}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x02}, - {0x30F6, 0x04}, /* HMAX, 260 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30dd, 0x01}, /* to crop to 1920x1080 */ - {0x30de, 0x05}, - {0x30df, 0x00}, - {0x30e0, 0x04}, - {0x30e1, 0x00}, - {0x3037, 0x01}, - {0x3038, 0x0c}, - {0x3039, 0x00}, - {0x303a, 0x0c}, - {0x303b, 0x0f}, - {0x30EE, 0x01}, - {0x3130, 0x4E}, - {0x3131, 0x04}, - {0x3132, 0x46}, - {0x3133, 0x04}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x1A}, @@ -313,31 +312,14 @@ static const struct reg_8 imx274_mode5_1280x720_raw10[] = { {0x3004, 0x03}, {0x3005, 0x31}, {0x3006, 0x00}, - {0x3007, 0x09}, + {0x3007, 0xa9}, {0x3018, 0xA2}, /* output XVS, HVS */ {0x306B, 0x05}, {0x30E2, 0x03}, - {0x30F6, 0x04}, /* HMAX, 260 */ - {0x30F7, 0x01}, /* HMAX */ - - {0x30DD, 0x01}, - {0x30DE, 0x07}, - {0x30DF, 0x00}, - {0x40E0, 0x04}, - {0x30E1, 0x00}, - {0x3030, 0xD4}, - {0x3031, 0x02}, - {0x3032, 0xD0}, - {0x3033, 0x02}, - {0x30EE, 0x01}, - {0x3130, 0xE2}, - {0x3131, 0x02}, - {0x3132, 0xDE}, - {0x3133, 0x02}, {0x3342, 0x0A}, {0x3343, 0x00}, {0x3344, 0x1B}, @@ -476,58 +458,35 @@ static const struct reg_8 imx274_tp_regs[] = { {IMX274_TABLE_END, 0x00} }; -static const struct reg_8 *mode_table[] = { - [IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10, - [IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10, - [IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10, -}; - -/* - * imx274 format related structure - */ +/* nocpiop happens to be the same number for the implemented modes */ static const struct imx274_frmfmt imx274_formats[] = { - { {3840, 2160} }, - { {1920, 1080} }, - { {1280, 720} }, -}; - -/* - * minimal frame length for each mode - * refer to datasheet section "Frame Rate Adjustment (CSI-2)" - */ -static const int min_frame_len[] = { - 4550, /* mode 1, 4K */ - 2310, /* mode 3, 1080p */ - 2310 /* mode 5, 720p */ -}; - -/* - * minimal numbers of SHR register - * refer to datasheet table "Shutter Setting (CSI-2)" - */ -static const int min_SHR[] = { - 12, /* mode 1, 4K */ - 8, /* mode 3, 1080p */ - 8 /* mode 5, 720p */ -}; - -static const int max_frame_rate[] = { - 60, /* mode 1 , 4K */ - 120, /* mode 3, 1080p */ - 120 /* mode 5, 720p */ -}; - -/* - * Number of clocks per internal offset period - * a constant based on mode - * refer to section "Integration Time in Each Readout Drive Mode (CSI-2)" - * in the datasheet - * for the implemented 3 modes, it happens to be the same number - */ -static const int nocpiop[] = { - 112, /* mode 1 , 4K */ - 112, /* mode 3, 1080p */ - 112 /* mode 5, 720p */ + { + /* mode 1, 4K */ + .bin_ratio = 1, + .init_regs = imx274_mode1_3840x2160_raw10, + .min_frame_len = 4550, + .min_SHR = 12, + .max_fps = 60, + .nocpiop = 112, + }, + { + /* mode 3, 1080p */ + .bin_ratio = 2, + .init_regs = imx274_mode3_1920x1080_raw10, + .min_frame_len = 2310, + .min_SHR = 8, + .max_fps = 120, + .nocpiop = 112, + }, + { + /* mode 5, 720p */ + .bin_ratio = 3, + .init_regs = imx274_mode5_1280x720_raw10, + .min_frame_len = 2310, + .min_SHR = 8, + .max_fps = 120, + .nocpiop = 112, + }, }; /* @@ -549,29 +508,40 @@ struct imx274_ctrls { /* * struct stim274 - imx274 device structure * @sd: V4L2 subdevice structure - * @pd: Media pad structure + * @pad: Media pad structure * @client: Pointer to I2C client * @ctrls: imx274 control structure + * @crop: rect to be captured + * @compose: compose rect, i.e. output resolution * @format: V4L2 media bus frame format structure + * (width and height are in sync with the compose rect) * @frame_rate: V4L2 frame rate structure * @regmap: Pointer to regmap structure * @reset_gpio: Pointer to reset gpio * @lock: Mutex structure - * @mode_index: Resolution mode index + * @mode: Parameters for the selected readout mode */ struct stimx274 { struct v4l2_subdev sd; struct media_pad pad; struct i2c_client *client; struct imx274_ctrls ctrls; + struct v4l2_rect crop; struct v4l2_mbus_framefmt format; struct v4l2_fract frame_interval; struct regmap *regmap; struct gpio_desc *reset_gpio; struct mutex lock; /* mutex lock for operations */ - u32 mode_index; + const struct imx274_frmfmt *mode; }; +#define IMX274_ROUND(dim, step, flags) \ + ((flags) & V4L2_SEL_FLAG_GE \ + ? roundup((dim), (step)) \ + : ((flags) & V4L2_SEL_FLAG_LE \ + ? rounddown((dim), (step)) \ + : rounddown((dim) + (step) / 2, (step)))) + /* * Function declaration */ @@ -602,20 +572,18 @@ static inline struct stimx274 *to_imx274(struct v4l2_subdev *sd) } /* - * imx274_regmap_util_write_table_8 - Function for writing register table - * @regmap: Pointer to device reg map structure - * @table: Table containing register values - * @wait_ms_addr: Flag for performing delay - * @end_addr: Flag for incating end of table + * Writing a register table + * + * @priv: Pointer to device + * @table: Table containing register values (with optional delays) * * This is used to write register table into sensor's reg map. * * Return: 0 on success, errors otherwise */ -static int imx274_regmap_util_write_table_8(struct regmap *regmap, - const struct reg_8 table[], - u16 wait_ms_addr, u16 end_addr) +static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) { + struct regmap *regmap = priv->regmap; int err = 0; const struct reg_8 *next; u8 val; @@ -627,8 +595,8 @@ static int imx274_regmap_util_write_table_8(struct regmap *regmap, for (next = table;; next++) { if ((next->addr != range_start + range_count) || - (next->addr == end_addr) || - (next->addr == wait_ms_addr) || + (next->addr == IMX274_TABLE_END) || + (next->addr == IMX274_TABLE_WAIT_MS) || (range_count == max_range_vals)) { if (range_count == 1) err = regmap_write(regmap, @@ -647,10 +615,10 @@ static int imx274_regmap_util_write_table_8(struct regmap *regmap, range_count = 0; /* Handle special address values */ - if (next->addr == end_addr) + if (next->addr == IMX274_TABLE_END) break; - if (next->addr == wait_ms_addr) { + if (next->addr == IMX274_TABLE_WAIT_MS) { msleep_range(next->val); continue; } @@ -697,25 +665,42 @@ static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) return err; } -static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) +/** + * Write a multibyte register. + * + * Uses a bulk write where possible. + * + * @priv: Pointer to device structure + * @addr: Address of the LSB register. Other registers must be + * consecutive, least-to-most significant. + * @val: Value to be written to the register (cpu endianness) + * @nbytes: Number of bits to write (range: [1..3]) + */ +static int imx274_write_mbreg(struct stimx274 *priv, u16 addr, u32 val, + size_t nbytes) { - return imx274_regmap_util_write_table_8(priv->regmap, - table, IMX274_TABLE_WAIT_MS, IMX274_TABLE_END); + __le32 val_le = cpu_to_le32(val); + int err; + + err = regmap_bulk_write(priv->regmap, addr, &val_le, nbytes); + if (err) + dev_err(&priv->client->dev, + "%s : i2c bulk write failed, %x = %x (%zu bytes)\n", + __func__, addr, val, nbytes); + else + dev_dbg(&priv->client->dev, + "%s : addr 0x%x, val=0x%x (%zu bytes)\n", + __func__, addr, val, nbytes); + return err; } /* - * imx274_mode_regs - Function for set mode registers per mode index + * Set mode registers to start stream. * @priv: Pointer to device structure - * @mode: Mode index value - * - * This is used to start steam per mode index. - * mode = 0, start stream for sensor Mode 1: 4K/raw10 - * mode = 1, start stream for sensor Mode 3: 1080p/raw10 - * mode = 2, start stream for sensor Mode 5: 720p/raw10 * * Return: 0 on success, errors otherwise */ -static int imx274_mode_regs(struct stimx274 *priv, int mode) +static int imx274_mode_regs(struct stimx274 *priv) { int err = 0; @@ -727,7 +712,7 @@ static int imx274_mode_regs(struct stimx274 *priv, int mode) if (err) return err; - err = imx274_write_table(priv, mode_table[mode]); + err = imx274_write_table(priv, priv->mode->init_regs); return err; } @@ -830,6 +815,114 @@ static int imx274_s_ctrl(struct v4l2_ctrl *ctrl) return ret; } +static int imx274_binning_goodness(struct stimx274 *imx274, + int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct device *dev = &imx274->client->dev; + const int goodness = 100000; + int val = 0; + + if (flags & V4L2_SEL_FLAG_GE) { + if (w < ask_w) + val -= goodness; + if (h < ask_h) + val -= goodness; + } + + if (flags & V4L2_SEL_FLAG_LE) { + if (w > ask_w) + val -= goodness; + if (h > ask_h) + val -= goodness; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + dev_dbg(dev, "%s: ask %dx%d, size %dx%d, goodness %d\n", + __func__, ask_w, ask_h, w, h, val); + + return val; +} + +/** + * Helper function to change binning and set both compose and format. + * + * We have two entry points to change binning: set_fmt and + * set_selection(COMPOSE). Both have to compute the new output size + * and set it in both the compose rect and the frame format size. We + * also need to do the same things after setting cropping to restore + * 1:1 binning. + * + * This function contains the common code for these three cases, it + * has many arguments in order to accommodate the needs of all of + * them. + * + * Must be called with imx274->lock locked. + * + * @imx274: The device object + * @cfg: The pad config we are editing for TRY requests + * @which: V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY from the caller + * @width: Input-output parameter: set to the desired width before + * the call, contains the chosen value after returning successfully + * @height: Input-output parameter for height (see @width) + * @flags: Selection flags from struct v4l2_subdev_selection, or 0 if not + * available (when called from set_fmt) + */ +static int __imx274_change_compose(struct stimx274 *imx274, + struct v4l2_subdev_pad_config *cfg, + u32 which, + u32 *width, + u32 *height, + u32 flags) +{ + struct device *dev = &imx274->client->dev; + const struct v4l2_rect *cur_crop; + struct v4l2_mbus_framefmt *tgt_fmt; + unsigned int i; + const struct imx274_frmfmt *best_mode = &imx274_formats[0]; + int best_goodness = INT_MIN; + + if (which == V4L2_SUBDEV_FORMAT_TRY) { + cur_crop = &cfg->try_crop; + tgt_fmt = &cfg->try_fmt; + } else { + cur_crop = &imx274->crop; + tgt_fmt = &imx274->format; + } + + for (i = 0; i < ARRAY_SIZE(imx274_formats); i++) { + unsigned int ratio = imx274_formats[i].bin_ratio; + + int goodness = imx274_binning_goodness( + imx274, + cur_crop->width / ratio, *width, + cur_crop->height / ratio, *height, + flags); + + if (goodness >= best_goodness) { + best_goodness = goodness; + best_mode = &imx274_formats[i]; + } + } + + *width = cur_crop->width / best_mode->bin_ratio; + *height = cur_crop->height / best_mode->bin_ratio; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + imx274->mode = best_mode; + + dev_dbg(dev, "%s: selected %u:1 binning\n", + __func__, best_mode->bin_ratio); + + tgt_fmt->width = *width; + tgt_fmt->height = *height; + tgt_fmt->field = V4L2_FIELD_NONE; + + return 0; +} + /** * imx274_get_fmt - Get the pad format * @sd: Pointer to V4L2 Sub device structure @@ -868,45 +961,238 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *fmt = &format->format; struct stimx274 *imx274 = to_imx274(sd); - struct i2c_client *client = imx274->client; - int index; - - dev_dbg(&client->dev, - "%s: width = %d height = %d code = %d\n", - __func__, fmt->width, fmt->height, fmt->code); + int err = 0; mutex_lock(&imx274->lock); - for (index = 0; index < ARRAY_SIZE(imx274_formats); index++) { - if (imx274_formats[index].size.width == fmt->width && - imx274_formats[index].size.height == fmt->height) - break; - } + err = __imx274_change_compose(imx274, cfg, format->which, + &fmt->width, &fmt->height, 0); - if (index >= ARRAY_SIZE(imx274_formats)) { - /* default to first format */ - index = 0; - } - - imx274->mode_index = index; + if (err) + goto out; - if (fmt->width > IMX274_MAX_WIDTH) - fmt->width = IMX274_MAX_WIDTH; - if (fmt->height > IMX274_MAX_HEIGHT) - fmt->height = IMX274_MAX_HEIGHT; - fmt->width = fmt->width & (~IMX274_MASK_LSB_2_BITS); - fmt->height = fmt->height & (~IMX274_MASK_LSB_2_BITS); + /* + * __imx274_change_compose already set width and height in the + * applicable format, but we need to keep all other format + * values, so do a full copy here + */ fmt->field = V4L2_FIELD_NONE; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) cfg->try_fmt = *fmt; else imx274->format = *fmt; +out: + mutex_unlock(&imx274->lock); + + return err; +} + +static int imx274_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct stimx274 *imx274 = to_imx274(sd); + const struct v4l2_rect *src_crop; + const struct v4l2_mbus_framefmt *src_fmt; + int ret = 0; + + if (sel->pad != 0) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX274_MAX_WIDTH; + sel->r.height = IMX274_MAX_HEIGHT; + return 0; + } + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + src_crop = &cfg->try_crop; + src_fmt = &cfg->try_fmt; + } else { + src_crop = &imx274->crop; + src_fmt = &imx274->format; + } + + mutex_lock(&imx274->lock); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *src_crop; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = src_crop->width; + sel->r.height = src_crop->height; + break; + case V4L2_SEL_TGT_COMPOSE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = src_fmt->width; + sel->r.height = src_fmt->height; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&imx274->lock); + + return ret; +} + +static int imx274_set_selection_crop(struct stimx274 *imx274, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct v4l2_rect *tgt_crop; + struct v4l2_rect new_crop; + bool size_changed; + + /* + * h_step could be 12 or 24 depending on the binning. But we + * won't know the binning until we choose the mode later in + * __imx274_change_compose(). Thus let's be safe and use the + * most conservative value in all cases. + */ + const u32 h_step = 24; + + new_crop.width = min_t(u32, + IMX274_ROUND(sel->r.width, h_step, sel->flags), + IMX274_MAX_WIDTH); + + /* Constraint: HTRIMMING_END - HTRIMMING_START >= 144 */ + if (new_crop.width < 144) + new_crop.width = 144; + + new_crop.left = min_t(u32, + IMX274_ROUND(sel->r.left, h_step, 0), + IMX274_MAX_WIDTH - new_crop.width); + + new_crop.height = min_t(u32, + IMX274_ROUND(sel->r.height, 2, sel->flags), + IMX274_MAX_HEIGHT); + + new_crop.top = min_t(u32, IMX274_ROUND(sel->r.top, 2, 0), + IMX274_MAX_HEIGHT - new_crop.height); + + sel->r = new_crop; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + tgt_crop = &cfg->try_crop; + else + tgt_crop = &imx274->crop; + + mutex_lock(&imx274->lock); + + size_changed = (new_crop.width != tgt_crop->width || + new_crop.height != tgt_crop->height); + + /* __imx274_change_compose needs the new size in *tgt_crop */ + *tgt_crop = new_crop; + + /* if crop size changed then reset the output image size */ + if (size_changed) + __imx274_change_compose(imx274, cfg, sel->which, + &new_crop.width, &new_crop.height, + sel->flags); + mutex_unlock(&imx274->lock); + return 0; } +static int imx274_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct stimx274 *imx274 = to_imx274(sd); + + if (sel->pad != 0) + return -EINVAL; + + if (sel->target == V4L2_SEL_TGT_CROP) + return imx274_set_selection_crop(imx274, cfg, sel); + + if (sel->target == V4L2_SEL_TGT_COMPOSE) { + int err; + + mutex_lock(&imx274->lock); + err = __imx274_change_compose(imx274, cfg, sel->which, + &sel->r.width, &sel->r.height, + sel->flags); + mutex_unlock(&imx274->lock); + + /* + * __imx274_change_compose already set width and + * height in set->r, we still need to set top-left + */ + if (!err) { + sel->r.top = 0; + sel->r.left = 0; + } + + return err; + } + + return -EINVAL; +} + +static int imx274_apply_trimming(struct stimx274 *imx274) +{ + u32 h_start; + u32 h_end; + u32 hmax; + u32 v_cut; + s32 v_pos; + u32 write_v_size; + u32 y_out_size; + int err; + + h_start = imx274->crop.left + 12; + h_end = h_start + imx274->crop.width; + + /* Use the minimum allowed value of HMAX */ + /* Note: except in mode 1, (width / 16 + 23) is always < hmax_min */ + /* Note: 260 is the minimum HMAX in all implemented modes */ + hmax = max_t(u32, 260, (imx274->crop.width) / 16 + 23); + + /* invert v_pos if VFLIP */ + v_pos = imx274->ctrls.vflip->cur.val ? + (-imx274->crop.top / 2) : (imx274->crop.top / 2); + v_cut = (IMX274_MAX_HEIGHT - imx274->crop.height) / 2; + write_v_size = imx274->crop.height + 22; + y_out_size = imx274->crop.height + 14; + + err = imx274_write_mbreg(imx274, IMX274_HMAX_REG_LSB, hmax, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_EN_REG, 1, 1); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_START_REG_LSB, + h_start, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_HTRIM_END_REG_LSB, + h_end, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWIDCUTEN_REG, 1, 1); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWIDCUT_REG_LSB, + v_cut, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_VWINPOS_REG_LSB, + v_pos, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_WRITE_VSIZE_REG_LSB, + write_v_size, 2); + if (!err) + err = imx274_write_mbreg(imx274, IMX274_Y_OUT_SIZE_REG_LSB, + y_out_size, 2); + + return err; +} + /** * imx274_g_frame_interval - Get the frame interval * @sd: Pointer to V4L2 Sub device structure @@ -1035,14 +1321,19 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) struct stimx274 *imx274 = to_imx274(sd); int ret = 0; - dev_dbg(&imx274->client->dev, "%s : %s, mode index = %d\n", __func__, - on ? "Stream Start" : "Stream Stop", imx274->mode_index); + dev_dbg(&imx274->client->dev, "%s : %s, mode index = %td\n", __func__, + on ? "Stream Start" : "Stream Stop", + imx274->mode - &imx274_formats[0]); mutex_lock(&imx274->lock); if (on) { /* load mode registers */ - ret = imx274_mode_regs(imx274, imx274->mode_index); + ret = imx274_mode_regs(imx274); + if (ret) + goto fail; + + ret = imx274_apply_trimming(imx274); if (ret) goto fail; @@ -1075,8 +1366,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) } mutex_unlock(&imx274->lock); - dev_dbg(&imx274->client->dev, - "%s : Done: mode = %d\n", __func__, imx274->mode_index); + dev_dbg(&imx274->client->dev, "%s : Done\n", __func__); return 0; fail: @@ -1146,14 +1436,14 @@ static int imx274_clamp_coarse_time(struct stimx274 *priv, u32 *val, if (err) return err; - if (*frame_length < min_frame_len[priv->mode_index]) - *frame_length = min_frame_len[priv->mode_index]; + if (*frame_length < priv->mode->min_frame_len) + *frame_length = priv->mode->min_frame_len; *val = *frame_length - *val; /* convert to raw shr */ if (*val > *frame_length - IMX274_SHR_LIMIT_CONST) *val = *frame_length - IMX274_SHR_LIMIT_CONST; - else if (*val < min_SHR[priv->mode_index]) - *val = min_SHR[priv->mode_index]; + else if (*val < priv->mode->min_SHR) + *val = priv->mode->min_SHR; return 0; } @@ -1182,15 +1472,6 @@ static int imx274_set_digital_gain(struct stimx274 *priv, u32 dgain) reg_val & IMX274_MASK_LSB_4_BITS); } -static inline void imx274_calculate_gain_regs(struct reg_8 regs[2], u16 gain) -{ - regs->addr = IMX274_ANALOG_GAIN_ADDR_MSB; - regs->val = (gain >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_3_BITS; - - (regs + 1)->addr = IMX274_ANALOG_GAIN_ADDR_LSB; - (regs + 1)->val = (gain) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_gain - Function called when setting gain * @priv: Pointer to device structure @@ -1204,10 +1485,8 @@ static inline void imx274_calculate_gain_regs(struct reg_8 regs[2], u16 gain) */ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl) { - struct reg_8 reg_list[2]; int err; u32 gain, analog_gain, digital_gain, gain_reg; - int i; gain = (u32)(ctrl->val); @@ -1248,14 +1527,10 @@ static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl) if (gain_reg > IMX274_GAIN_REG_MAX) gain_reg = IMX274_GAIN_REG_MAX; - imx274_calculate_gain_regs(reg_list, (u16)gain_reg); - - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_ANALOG_GAIN_ADDR_LSB, gain_reg, + 2); + if (err) + goto fail; if (IMX274_GAIN_CONST - gain_reg == 0) { err = -EINVAL; @@ -1277,16 +1552,6 @@ fail: return err; } -static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], - u32 coarse_time) -{ - regs->addr = IMX274_SHR_REG_MSB; - regs->val = (coarse_time >> IMX274_SHIFT_8_BITS) - & IMX274_MASK_LSB_8_BITS; - (regs + 1)->addr = IMX274_SHR_REG_LSB; - (regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_coarse_time - Function called when setting SHR value * @priv: Pointer to device structure @@ -1298,10 +1563,8 @@ static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], */ static int imx274_set_coarse_time(struct stimx274 *priv, u32 *val) { - struct reg_8 reg_list[2]; int err; u32 coarse_time, frame_length; - int i; coarse_time = *val; @@ -1310,16 +1573,9 @@ static int imx274_set_coarse_time(struct stimx274 *priv, u32 *val) if (err) goto fail; - /* prepare SHR registers */ - imx274_calculate_coarse_time_regs(reg_list, coarse_time); - - /* write to SHR registers */ - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_SHR_REG_LSB, coarse_time, 2); + if (err) + goto fail; *val = frame_length - coarse_time; return 0; @@ -1365,7 +1621,7 @@ static int imx274_set_exposure(struct stimx274 *priv, int val) } coarse_time = (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2 * val - - nocpiop[priv->mode_index]) / hmax; + - priv->mode->nocpiop) / hmax; /* step 2: convert exposure_time into SHR value */ @@ -1375,7 +1631,7 @@ static int imx274_set_exposure(struct stimx274 *priv, int val) goto fail; priv->ctrls.exposure->val = - (coarse_time * hmax + nocpiop[priv->mode_index]) + (coarse_time * hmax + priv->mode->nocpiop) / (IMX274_PIXCLK_CONST1 / IMX274_PIXCLK_CONST2); dev_dbg(&priv->client->dev, @@ -1448,19 +1704,6 @@ static int imx274_set_test_pattern(struct stimx274 *priv, int val) return err; } -static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], - u32 frame_length) -{ - regs->addr = IMX274_VMAX_REG_1; - regs->val = (frame_length >> IMX274_SHIFT_16_BITS) - & IMX274_MASK_LSB_4_BITS; - (regs + 1)->addr = IMX274_VMAX_REG_2; - (regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS) - & IMX274_MASK_LSB_8_BITS; - (regs + 2)->addr = IMX274_VMAX_REG_3; - (regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS; -} - /* * imx274_set_frame_length - Function called when setting frame length * @priv: Pointer to device structure @@ -1472,23 +1715,17 @@ static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], */ static int imx274_set_frame_length(struct stimx274 *priv, u32 val) { - struct reg_8 reg_list[3]; int err; u32 frame_length; - int i; dev_dbg(&priv->client->dev, "%s : input length = %d\n", __func__, val); frame_length = (u32)val; - imx274_calculate_frame_length_regs(reg_list, frame_length); - for (i = 0; i < ARRAY_SIZE(reg_list); i++) { - err = imx274_write_reg(priv, reg_list[i].addr, - reg_list[i].val); - if (err) - goto fail; - } + err = imx274_write_mbreg(priv, IMX274_VMAX_REG_3, frame_length, 3); + if (err) + goto fail; return 0; @@ -1529,10 +1766,9 @@ static int imx274_set_frame_interval(struct stimx274 *priv, / frame_interval.numerator); /* boundary check */ - if (req_frame_rate > max_frame_rate[priv->mode_index]) { + if (req_frame_rate > priv->mode->max_fps) { frame_interval.numerator = 1; - frame_interval.denominator = - max_frame_rate[priv->mode_index]; + frame_interval.denominator = priv->mode->max_fps; } else if (req_frame_rate < IMX274_MIN_FRAME_RATE) { frame_interval.numerator = 1; frame_interval.denominator = IMX274_MIN_FRAME_RATE; @@ -1589,6 +1825,8 @@ fail: static const struct v4l2_subdev_pad_ops imx274_pad_ops = { .get_fmt = imx274_get_fmt, .set_fmt = imx274_set_fmt, + .get_selection = imx274_get_selection, + .set_selection = imx274_set_selection, }; static const struct v4l2_subdev_video_ops imx274_video_ops = { @@ -1632,6 +1870,18 @@ static int imx274_probe(struct i2c_client *client, mutex_init(&imx274->lock); + /* initialize format */ + imx274->mode = &imx274_formats[IMX274_DEFAULT_MODE]; + imx274->crop.width = IMX274_MAX_WIDTH; + imx274->crop.height = IMX274_MAX_HEIGHT; + imx274->format.width = imx274->crop.width / imx274->mode->bin_ratio; + imx274->format.height = imx274->crop.height / imx274->mode->bin_ratio; + imx274->format.field = V4L2_FIELD_NONE; + imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + imx274->format.colorspace = V4L2_COLORSPACE_SRGB; + imx274->frame_interval.numerator = 1; + imx274->frame_interval.denominator = IMX274_DEF_FRAME_RATE; + /* initialize regmap */ imx274->regmap = devm_regmap_init_i2c(client, &imx274_regmap_config); if (IS_ERR(imx274->regmap)) { @@ -1720,16 +1970,6 @@ static int imx274_probe(struct i2c_client *client, goto err_ctrls; } - /* initialize format */ - imx274->mode_index = IMX274_MODE_3840X2160; - imx274->format.width = imx274_formats[0].size.width; - imx274->format.height = imx274_formats[0].size.height; - imx274->format.field = V4L2_FIELD_NONE; - imx274->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; - imx274->format.colorspace = V4L2_COLORSPACE_SRGB; - imx274->frame_interval.numerator = 1; - imx274->frame_interval.denominator = IMX274_DEF_FRAME_RATE; - /* load default control values */ ret = imx274_load_default(imx274); if (ret) { diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index b600e03aa94b..49c6644cbba7 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -1,6 +1,6 @@ /* * drivers/media/i2c/lm3560.c - * General device driver for TI lm3560, FLASH LED Driver + * General device driver for TI lm3559, lm3560, FLASH LED Driver * * Copyright (C) 2013 Texas Instruments * @@ -465,6 +465,7 @@ static int lm3560_remove(struct i2c_client *client) } static const struct i2c_device_id lm3560_id_table[] = { + {LM3559_NAME, 0}, {LM3560_NAME, 0}, {} }; diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 6a9e068462fd..b385f2b632ad 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -793,6 +793,7 @@ static int mt9m032_probe(struct i2c_client *client, v4l2_ctrl_cluster(2, &sensor->hflip); sensor->subdev.ctrl_handler = &sensor->ctrls; + sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); if (ret < 0) diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 91d822fc4443..715be3632b01 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -1111,6 +1111,7 @@ static int mt9p031_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; + mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad); if (ret < 0) diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index 9d981d9f5686..f683d2cb0486 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -943,6 +943,7 @@ static int mt9t001_probe(struct i2c_client *client, mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9t001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 4de63b2df334..f74730d24d8f 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -1162,6 +1162,7 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9v032->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); if (ret < 0) diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c new file mode 100644 index 000000000000..b5410aeb5fe2 --- /dev/null +++ b/drivers/media/i2c/mt9v111.c @@ -0,0 +1,1298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 sensor driver for Aptina MT9V111 image sensor + * Copyright (C) 2018 Jacopo Mondi <jacopo@jmondi.org> + * + * Based on mt9v032 driver + * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * Based on mt9v011 driver + * Copyright (c) 2009 Mauro Carvalho Chehab <mchehab@kernel.org> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/v4l2-mediabus.h> +#include <linux/module.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-subdev.h> + +/* + * MT9V111 is a 1/4-Inch CMOS digital image sensor with an integrated + * Image Flow Processing (IFP) engine and a sensor core loosely based on + * MT9V011. + * + * The IFP can produce several output image formats from the sensor core + * output. This driver currently supports only YUYV format permutations. + * + * The driver allows manual frame rate control through s_frame_interval subdev + * operation or V4L2_CID_V/HBLANK controls, but it is known that the + * auto-exposure algorithm might modify the programmed frame rate. While the + * driver initially programs the sensor with auto-exposure and + * auto-white-balancing enabled, it is possible to disable them and more + * precisely control the frame rate. + * + * While it seems possible to instruct the auto-exposure control algorithm to + * respect a programmed frame rate when adjusting the pixel integration time, + * registers controlling this feature are not documented in the public + * available sensor manual used to develop this driver (09005aef80e90084, + * MT9V111_1.fm - Rev. G 1/05 EN). + */ + +#define MT9V111_CHIP_ID_HIGH 0x82 +#define MT9V111_CHIP_ID_LOW 0x3a + +#define MT9V111_R01_ADDR_SPACE 0x01 +#define MT9V111_R01_IFP 0x01 +#define MT9V111_R01_CORE 0x04 + +#define MT9V111_IFP_R06_OPMODE_CTRL 0x06 +#define MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN BIT(1) +#define MT9V111_IFP_R06_OPMODE_CTRL_AE_EN BIT(14) +#define MT9V111_IFP_R07_IFP_RESET 0x07 +#define MT9V111_IFP_R07_IFP_RESET_MASK BIT(0) +#define MT9V111_IFP_R08_OUTFMT_CTRL 0x08 +#define MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER BIT(11) +#define MT9V111_IFP_R08_OUTFMT_CTRL_PCLK BIT(5) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2 0x3a +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR BIT(0) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC BIT(1) +#define MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK GENMASK(2, 0) +#define MT9V111_IFP_RA5_HPAN 0xa5 +#define MT9V111_IFP_RA6_HZOOM 0xa6 +#define MT9V111_IFP_RA7_HOUT 0xa7 +#define MT9V111_IFP_RA8_VPAN 0xa8 +#define MT9V111_IFP_RA9_VZOOM 0xa9 +#define MT9V111_IFP_RAA_VOUT 0xaa +#define MT9V111_IFP_DECIMATION_MASK GENMASK(9, 0) +#define MT9V111_IFP_DECIMATION_FREEZE BIT(15) + +#define MT9V111_CORE_R03_WIN_HEIGHT 0x03 +#define MT9V111_CORE_R03_WIN_V_OFFS 2 +#define MT9V111_CORE_R04_WIN_WIDTH 0x04 +#define MT9V111_CORE_R04_WIN_H_OFFS 114 +#define MT9V111_CORE_R05_HBLANK 0x05 +#define MT9V111_CORE_R05_MIN_HBLANK 0x09 +#define MT9V111_CORE_R05_MAX_HBLANK GENMASK(9, 0) +#define MT9V111_CORE_R05_DEF_HBLANK 0x26 +#define MT9V111_CORE_R06_VBLANK 0x06 +#define MT9V111_CORE_R06_MIN_VBLANK 0x03 +#define MT9V111_CORE_R06_MAX_VBLANK GENMASK(11, 0) +#define MT9V111_CORE_R06_DEF_VBLANK 0x04 +#define MT9V111_CORE_R07_OUT_CTRL 0x07 +#define MT9V111_CORE_R07_OUT_CTRL_SAMPLE BIT(4) +#define MT9V111_CORE_R09_PIXEL_INT 0x09 +#define MT9V111_CORE_R09_PIXEL_INT_MASK GENMASK(11, 0) +#define MT9V111_CORE_R0D_CORE_RESET 0x0d +#define MT9V111_CORE_R0D_CORE_RESET_MASK BIT(0) +#define MT9V111_CORE_RFF_CHIP_VER 0xff + +#define MT9V111_PIXEL_ARRAY_WIDTH 640 +#define MT9V111_PIXEL_ARRAY_HEIGHT 480 + +#define MT9V111_MAX_CLKIN 27000000 + +/* The default sensor configuration at startup time. */ +static struct v4l2_mbus_framefmt mt9v111_def_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, +}; + +struct mt9v111_dev { + struct device *dev; + struct i2c_client *client; + + u8 addr_space; + + struct v4l2_subdev sd; +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + struct media_pad pad; +#endif + + struct v4l2_ctrl *auto_awb; + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl_handler ctrls; + + /* Output image format and sizes. */ + struct v4l2_mbus_framefmt fmt; + unsigned int fps; + + /* Protects power up/down sequences. */ + struct mutex pwr_mutex; + int pwr_count; + + /* Protects stream on/off sequences. */ + struct mutex stream_mutex; + bool streaming; + + /* Flags to mark HW settings as not yet applied. */ + bool pending; + + /* Clock provider and system clock frequency. */ + struct clk *clk; + u32 sysclk; + + struct gpio_desc *oe; + struct gpio_desc *standby; + struct gpio_desc *reset; +}; + +#define sd_to_mt9v111(__sd) container_of((__sd), struct mt9v111_dev, sd) + +/* + * mt9v111_mbus_fmt - List all media bus formats supported by the driver. + * + * Only list the media bus code here. The image sizes are freely configurable + * in the pixel array sizes range. + * + * The desired frame interval, in the supported frame interval range, is + * obtained by configuring blanking as the sensor does not have a PLL but + * only a fixed clock divider that generates the output pixel clock. + */ +static struct mt9v111_mbus_fmt { + u32 code; +} mt9v111_formats[] = { + { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + }, + { + .code = MEDIA_BUS_FMT_YUYV8_2X8, + }, + { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + }, + { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + }, +}; + +static u32 mt9v111_frame_intervals[] = {5, 10, 15, 20, 30}; + +/* + * mt9v111_frame_sizes - List sensor's supported resolutions. + * + * Resolution generated through decimation in the IFP block from the + * full VGA pixel array. + */ +static struct v4l2_rect mt9v111_frame_sizes[] = { + { + .width = 640, + .height = 480, + }, + { + .width = 352, + .height = 288 + }, + { + .width = 320, + .height = 240, + }, + { + .width = 176, + .height = 144, + }, + { + .width = 160, + .height = 120, + }, +}; + +/* --- Device I/O access --- */ + +static int __mt9v111_read(struct i2c_client *c, u8 reg, u16 *val) +{ + struct i2c_msg msg[2]; + __be16 buf; + int ret; + + msg[0].addr = c->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].addr = c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = (char *)&buf; + + ret = i2c_transfer(c->adapter, msg, 2); + if (ret < 0) { + dev_err(&c->dev, "i2c read transfer error: %d\n", ret); + return ret; + } + + *val = be16_to_cpu(buf); + + dev_dbg(&c->dev, "%s: %x=%x\n", __func__, reg, *val); + + return 0; +} + +static int __mt9v111_write(struct i2c_client *c, u8 reg, u16 val) +{ + struct i2c_msg msg; + u8 buf[3] = { 0 }; + int ret; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + + msg.addr = c->addr; + msg.flags = 0; + msg.len = 3; + msg.buf = (char *)buf; + + dev_dbg(&c->dev, "%s: %x = %x%x\n", __func__, reg, buf[1], buf[2]); + + ret = i2c_transfer(c->adapter, &msg, 1); + if (ret < 0) { + dev_err(&c->dev, "i2c write transfer error: %d\n", ret); + return ret; + } + + return 0; +} + +static int __mt9v111_addr_space_select(struct i2c_client *c, u16 addr_space) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + u16 val; + int ret; + + if (mt9v111->addr_space == addr_space) + return 0; + + ret = __mt9v111_write(c, MT9V111_R01_ADDR_SPACE, addr_space); + if (ret) + return ret; + + /* Verify address space has been updated */ + ret = __mt9v111_read(c, MT9V111_R01_ADDR_SPACE, &val); + if (ret) + return ret; + + if (val != addr_space) + return -EINVAL; + + mt9v111->addr_space = addr_space; + + return 0; +} + +static int mt9v111_read(struct i2c_client *c, u8 addr_space, u8 reg, u16 *val) +{ + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + ret = __mt9v111_read(c, reg, val); + if (ret) + return ret; + + return 0; +} + +static int mt9v111_write(struct i2c_client *c, u8 addr_space, u8 reg, u16 val) +{ + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + ret = __mt9v111_write(c, reg, val); + if (ret) + return ret; + + return 0; +} + +static int mt9v111_update(struct i2c_client *c, u8 addr_space, u8 reg, + u16 mask, u16 val) +{ + u16 current_val; + int ret; + + /* Select register address space first. */ + ret = __mt9v111_addr_space_select(c, addr_space); + if (ret) + return ret; + + /* Read the current register value, then update it. */ + ret = __mt9v111_read(c, reg, ¤t_val); + if (ret) + return ret; + + current_val &= ~mask; + current_val |= (val & mask); + ret = __mt9v111_write(c, reg, current_val); + if (ret) + return ret; + + return 0; +} + +/* --- Sensor HW operations --- */ + +static int __mt9v111_power_on(struct v4l2_subdev *sd) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + int ret; + + ret = clk_prepare_enable(mt9v111->clk); + if (ret) + return ret; + + clk_set_rate(mt9v111->clk, mt9v111->sysclk); + + gpiod_set_value(mt9v111->standby, 0); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->oe, 1); + usleep_range(500, 1000); + + return 0; +} + +static int __mt9v111_power_off(struct v4l2_subdev *sd) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + + gpiod_set_value(mt9v111->oe, 0); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->standby, 1); + usleep_range(500, 1000); + + clk_disable_unprepare(mt9v111->clk); + + return 0; +} + +static int __mt9v111_hw_reset(struct mt9v111_dev *mt9v111) +{ + if (!mt9v111->reset) + return -EINVAL; + + gpiod_set_value(mt9v111->reset, 1); + usleep_range(500, 1000); + + gpiod_set_value(mt9v111->reset, 0); + usleep_range(500, 1000); + + return 0; +} + +static int __mt9v111_sw_reset(struct mt9v111_dev *mt9v111) +{ + struct i2c_client *c = mt9v111->client; + int ret; + + /* Software reset core and IFP blocks. */ + + ret = mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R0D_CORE_RESET, + MT9V111_CORE_R0D_CORE_RESET_MASK, 1); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R0D_CORE_RESET, + MT9V111_CORE_R0D_CORE_RESET_MASK, 0); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_IFP, + MT9V111_IFP_R07_IFP_RESET, + MT9V111_IFP_R07_IFP_RESET_MASK, 1); + if (ret) + return ret; + usleep_range(500, 1000); + + ret = mt9v111_update(c, MT9V111_R01_IFP, + MT9V111_IFP_R07_IFP_RESET, + MT9V111_IFP_R07_IFP_RESET_MASK, 0); + if (ret) + return ret; + usleep_range(500, 1000); + + return 0; +} + +static int mt9v111_calc_frame_rate(struct mt9v111_dev *mt9v111, + struct v4l2_fract *tpf) +{ + unsigned int fps = tpf->numerator ? + tpf->denominator / tpf->numerator : + tpf->denominator; + unsigned int best_diff; + unsigned int frm_cols; + unsigned int row_pclk; + unsigned int best_fps; + unsigned int pclk; + unsigned int diff; + unsigned int idx; + unsigned int hb; + unsigned int vb; + unsigned int i; + int ret; + + /* Approximate to the closest supported frame interval. */ + best_diff = ~0L; + for (i = 0, idx = 0; i < ARRAY_SIZE(mt9v111_frame_intervals); i++) { + diff = abs(fps - mt9v111_frame_intervals[i]); + if (diff < best_diff) { + idx = i; + best_diff = diff; + } + } + fps = mt9v111_frame_intervals[idx]; + + /* + * The sensor does not provide a PLL circuitry and pixel clock is + * generated dividing the master clock source by two. + * + * Trow = (W + Hblank + 114) * 2 * (1 / SYSCLK) + * TFrame = Trow * (H + Vblank + 2) + * + * FPS = (SYSCLK / 2) / (Trow * (H + Vblank + 2)) + * + * This boils down to tune H and V blanks to best approximate the + * above equation. + * + * Test all available H/V blank values, until we reach the + * desired frame rate. + */ + best_fps = vb = hb = 0; + pclk = DIV_ROUND_CLOSEST(mt9v111->sysclk, 2); + row_pclk = MT9V111_PIXEL_ARRAY_WIDTH + 7 + MT9V111_CORE_R04_WIN_H_OFFS; + frm_cols = MT9V111_PIXEL_ARRAY_HEIGHT + 7 + MT9V111_CORE_R03_WIN_V_OFFS; + + best_diff = ~0L; + for (vb = MT9V111_CORE_R06_MIN_VBLANK; + vb < MT9V111_CORE_R06_MAX_VBLANK; vb++) { + for (hb = MT9V111_CORE_R05_MIN_HBLANK; + hb < MT9V111_CORE_R05_MAX_HBLANK; hb += 10) { + unsigned int t_frame = (row_pclk + hb) * + (frm_cols + vb); + unsigned int t_fps = DIV_ROUND_CLOSEST(pclk, t_frame); + + diff = abs(fps - t_fps); + if (diff < best_diff) { + best_diff = diff; + best_fps = t_fps; + + if (diff == 0) + break; + } + } + + if (diff == 0) + break; + } + + ret = v4l2_ctrl_s_ctrl_int64(mt9v111->hblank, hb); + if (ret) + return ret; + + ret = v4l2_ctrl_s_ctrl_int64(mt9v111->vblank, vb); + if (ret) + return ret; + + tpf->numerator = 1; + tpf->denominator = best_fps; + + return 0; +} + +static int mt9v111_hw_config(struct mt9v111_dev *mt9v111) +{ + struct i2c_client *c = mt9v111->client; + unsigned int ret; + u16 outfmtctrl2; + + /* Force device reset. */ + ret = __mt9v111_hw_reset(mt9v111); + if (ret == -EINVAL) + ret = __mt9v111_sw_reset(mt9v111); + if (ret) + return ret; + + /* Configure internal clock sample rate. */ + ret = mt9v111->sysclk < DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? + mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R07_OUT_CTRL, + MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 1) : + mt9v111_update(c, MT9V111_R01_CORE, + MT9V111_CORE_R07_OUT_CTRL, + MT9V111_CORE_R07_OUT_CTRL_SAMPLE, 0); + if (ret) + return ret; + + /* + * Configure output image format components ordering. + * + * TODO: IFP block can also output several RGB permutations, we only + * support YUYV permutations at the moment. + */ + switch (mt9v111->fmt.code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + outfmtctrl2 = MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_YC | + MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_CBCR; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + outfmtctrl2 = 0; + break; + } + + ret = mt9v111_update(c, MT9V111_R01_IFP, MT9V111_IFP_R3A_OUTFMT_CTRL2, + MT9V111_IFP_R3A_OUTFMT_CTRL2_SWAP_MASK, + outfmtctrl2); + if (ret) + return ret; + + /* + * Do not change default sensor's core configuration: + * output the whole 640x480 pixel array, skip 18 columns and 6 rows. + * + * Instead, control the output image size through IFP block. + * + * TODO: No zoom&pan support. Currently we control the output image + * size only through decimation, with no zoom support. + */ + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA5_HPAN, + MT9V111_IFP_DECIMATION_FREEZE); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA8_VPAN, + MT9V111_IFP_DECIMATION_FREEZE); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA6_HZOOM, + MT9V111_IFP_DECIMATION_FREEZE | + MT9V111_PIXEL_ARRAY_WIDTH); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA9_VZOOM, + MT9V111_IFP_DECIMATION_FREEZE | + MT9V111_PIXEL_ARRAY_HEIGHT); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RA7_HOUT, + MT9V111_IFP_DECIMATION_FREEZE | + mt9v111->fmt.width); + if (ret) + return ret; + + ret = mt9v111_write(c, MT9V111_R01_IFP, MT9V111_IFP_RAA_VOUT, + mt9v111->fmt.height); + if (ret) + return ret; + + /* Apply controls to set auto exp, auto awb and timings */ + ret = v4l2_ctrl_handler_setup(&mt9v111->ctrls); + if (ret) + return ret; + + /* + * Set pixel integration time to the whole frame time. + * This value controls the the shutter delay when running with AE + * disabled. If longer than frame time, it affects the output + * frame rate. + */ + return mt9v111_write(c, MT9V111_R01_CORE, MT9V111_CORE_R09_PIXEL_INT, + MT9V111_PIXEL_ARRAY_HEIGHT); +} + +/* --- V4L2 subdev operations --- */ + +static int mt9v111_s_power(struct v4l2_subdev *sd, int on) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + int pwr_count; + int ret = 0; + + mutex_lock(&mt9v111->pwr_mutex); + + /* + * Make sure we're transitioning from 0 to 1, or viceversa, + * before actually changing the power state. + */ + pwr_count = mt9v111->pwr_count; + pwr_count += on ? 1 : -1; + if (pwr_count == !!on) { + ret = on ? __mt9v111_power_on(sd) : + __mt9v111_power_off(sd); + if (!ret) + /* All went well, updated power counter. */ + mt9v111->pwr_count = pwr_count; + + mutex_unlock(&mt9v111->pwr_mutex); + + return ret; + } + + /* + * Update power counter to keep track of how many nested calls we + * received. + */ + WARN_ON(pwr_count < 0 || pwr_count > 1); + mt9v111->pwr_count = pwr_count; + + mutex_unlock(&mt9v111->pwr_mutex); + + return ret; +} + +static int mt9v111_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + int ret; + + mutex_lock(&mt9v111->stream_mutex); + + if (mt9v111->streaming == enable) { + mutex_unlock(&mt9v111->stream_mutex); + return 0; + } + + ret = mt9v111_s_power(subdev, enable); + if (ret) + goto error_unlock; + + if (enable && mt9v111->pending) { + ret = mt9v111_hw_config(mt9v111); + if (ret) + goto error_unlock; + + /* + * No need to update control here as far as only H/VBLANK are + * supported and immediately programmed to registers in .s_ctrl + */ + + mt9v111->pending = false; + } + + mt9v111->streaming = enable ? true : false; + mutex_unlock(&mt9v111->stream_mutex); + + return 0; + +error_unlock: + mutex_unlock(&mt9v111->stream_mutex); + + return ret; +} + +static int mt9v111_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + struct v4l2_fract *tpf = &ival->interval; + unsigned int fps = tpf->numerator ? + tpf->denominator / tpf->numerator : + tpf->denominator; + unsigned int max_fps; + + if (!tpf->numerator) + tpf->numerator = 1; + + mutex_lock(&mt9v111->stream_mutex); + + if (mt9v111->streaming) { + mutex_unlock(&mt9v111->stream_mutex); + return -EBUSY; + } + + if (mt9v111->fps == fps) { + mutex_unlock(&mt9v111->stream_mutex); + return 0; + } + + /* Make sure frame rate/image sizes constraints are respected. */ + if (mt9v111->fmt.width < QVGA_WIDTH && + mt9v111->fmt.height < QVGA_HEIGHT) + max_fps = 90; + else if (mt9v111->fmt.width < CIF_WIDTH && + mt9v111->fmt.height < CIF_HEIGHT) + max_fps = 60; + else + max_fps = mt9v111->sysclk < + DIV_ROUND_CLOSEST(MT9V111_MAX_CLKIN, 2) ? 15 : + 30; + + if (fps > max_fps) { + mutex_unlock(&mt9v111->stream_mutex); + return -EINVAL; + } + + mt9v111_calc_frame_rate(mt9v111, tpf); + + mt9v111->fps = fps; + mt9v111->pending = true; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *ival) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + struct v4l2_fract *tpf = &ival->interval; + + mutex_lock(&mt9v111->stream_mutex); + + tpf->numerator = 1; + tpf->denominator = mt9v111->fps; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static struct v4l2_mbus_framefmt *__mt9v111_get_pad_format( + struct mt9v111_dev *mt9v111, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: +#if IS_ENABLED(CONFIG_VIDEO_V4L2_SUBDEV_API) + return v4l2_subdev_get_try_format(&mt9v111->sd, cfg, pad); +#else + return &cfg->try_fmt; +#endif + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &mt9v111->fmt; + default: + return NULL; + } +} + +static int mt9v111_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index > ARRAY_SIZE(mt9v111_formats) - 1) + return -EINVAL; + + code->code = mt9v111_formats[code->index].code; + + return 0; +} + +static int mt9v111_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + unsigned int i; + + if (fie->pad || fie->index >= ARRAY_SIZE(mt9v111_frame_intervals)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) + if (fie->width == mt9v111_frame_sizes[i].width && + fie->height == mt9v111_frame_sizes[i].height) + break; + + if (i == ARRAY_SIZE(mt9v111_frame_sizes)) + return -EINVAL; + + fie->interval.numerator = 1; + fie->interval.denominator = mt9v111_frame_intervals[fie->index]; + + return 0; +} + +static int mt9v111_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->pad || fse->index >= ARRAY_SIZE(mt9v111_frame_sizes)) + return -EINVAL; + + fse->min_width = mt9v111_frame_sizes[fse->index].width; + fse->max_width = mt9v111_frame_sizes[fse->index].width; + fse->min_height = mt9v111_frame_sizes[fse->index].height; + fse->max_height = mt9v111_frame_sizes[fse->index].height; + + return 0; +} + +static int mt9v111_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + + if (format->pad) + return -EINVAL; + + mutex_lock(&mt9v111->stream_mutex); + format->format = *__mt9v111_get_pad_format(mt9v111, cfg, format->pad, + format->which); + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(subdev); + struct v4l2_mbus_framefmt new_fmt; + struct v4l2_mbus_framefmt *__fmt; + unsigned int best_fit = ~0L; + unsigned int idx = 0; + unsigned int i; + + mutex_lock(&mt9v111->stream_mutex); + if (mt9v111->streaming) { + mutex_unlock(&mt9v111->stream_mutex); + return -EBUSY; + } + + if (format->pad) { + mutex_unlock(&mt9v111->stream_mutex); + return -EINVAL; + } + + /* Update mbus format code and sizes. */ + for (i = 0; i < ARRAY_SIZE(mt9v111_formats); i++) { + if (format->format.code == mt9v111_formats[i].code) { + new_fmt.code = mt9v111_formats[i].code; + break; + } + } + if (i == ARRAY_SIZE(mt9v111_formats)) + new_fmt.code = mt9v111_formats[0].code; + + for (i = 0; i < ARRAY_SIZE(mt9v111_frame_sizes); i++) { + unsigned int fit = abs(mt9v111_frame_sizes[i].width - + format->format.width) + + abs(mt9v111_frame_sizes[i].height - + format->format.height); + if (fit < best_fit) { + best_fit = fit; + idx = i; + + if (fit == 0) + break; + } + } + new_fmt.width = mt9v111_frame_sizes[idx].width; + new_fmt.height = mt9v111_frame_sizes[idx].height; + + /* Update the device (or pad) format if it has changed. */ + __fmt = __mt9v111_get_pad_format(mt9v111, cfg, format->pad, + format->which); + + /* Format hasn't changed, stop here. */ + if (__fmt->code == new_fmt.code && + __fmt->width == new_fmt.width && + __fmt->height == new_fmt.height) + goto done; + + /* Update the format and sizes, then mark changes as pending. */ + __fmt->code = new_fmt.code; + __fmt->width = new_fmt.width; + __fmt->height = new_fmt.height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + mt9v111->pending = true; + + dev_dbg(mt9v111->dev, "%s: mbus_code: %x - (%ux%u)\n", + __func__, __fmt->code, __fmt->width, __fmt->height); + +done: + format->format = *__fmt; + + mutex_unlock(&mt9v111->stream_mutex); + + return 0; +} + +static int mt9v111_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + cfg->try_fmt = mt9v111_def_fmt; + + return 0; +} + +static const struct v4l2_subdev_core_ops mt9v111_core_ops = { + .s_power = mt9v111_s_power, +}; + +static const struct v4l2_subdev_video_ops mt9v111_video_ops = { + .s_stream = mt9v111_s_stream, + .s_frame_interval = mt9v111_s_frame_interval, + .g_frame_interval = mt9v111_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops mt9v111_pad_ops = { + .init_cfg = mt9v111_init_cfg, + .enum_mbus_code = mt9v111_enum_mbus_code, + .enum_frame_size = mt9v111_enum_frame_size, + .enum_frame_interval = mt9v111_enum_frame_interval, + .get_fmt = mt9v111_get_format, + .set_fmt = mt9v111_set_format, +}; + +static const struct v4l2_subdev_ops mt9v111_ops = { + .core = &mt9v111_core_ops, + .video = &mt9v111_video_ops, + .pad = &mt9v111_pad_ops, +}; + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) +static const struct media_entity_operations mt9v111_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; +#endif + +/* --- V4L2 ctrl --- */ +static int mt9v111_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v111_dev *mt9v111 = container_of(ctrl->handler, + struct mt9v111_dev, + ctrls); + int ret; + + mutex_lock(&mt9v111->pwr_mutex); + /* + * If sensor is powered down, just cache new control values, + * no actual register access. + */ + if (!mt9v111->pwr_count) { + mt9v111->pending = true; + mutex_unlock(&mt9v111->pwr_mutex); + return 0; + } + mutex_unlock(&mt9v111->pwr_mutex); + + /* + * Flickering control gets disabled if both auto exp and auto awb + * are disabled too. If any of the two is enabled, enable it. + * + * Disabling flickering when ae and awb are off allows a more precise + * control of the programmed frame rate. + */ + if (mt9v111->auto_exp->is_new || mt9v111->auto_awb->is_new) { + if (mt9v111->auto_exp->val == V4L2_EXPOSURE_MANUAL && + mt9v111->auto_awb->val == V4L2_WHITE_BALANCE_MANUAL) + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R08_OUTFMT_CTRL, + MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, + 0); + else + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R08_OUTFMT_CTRL, + MT9V111_IFP_R08_OUTFMT_CTRL_FLICKER, + 1); + if (ret) + return ret; + } + + ret = -EINVAL; + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R06_OPMODE_CTRL, + MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN, + ctrl->val == V4L2_WHITE_BALANCE_AUTO ? + MT9V111_IFP_R06_OPMODE_CTRL_AWB_EN : 0); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_IFP, + MT9V111_IFP_R06_OPMODE_CTRL, + MT9V111_IFP_R06_OPMODE_CTRL_AE_EN, + ctrl->val == V4L2_EXPOSURE_AUTO ? + MT9V111_IFP_R06_OPMODE_CTRL_AE_EN : 0); + break; + case V4L2_CID_HBLANK: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_R05_HBLANK, + MT9V111_CORE_R05_MAX_HBLANK, + mt9v111->hblank->val); + break; + case V4L2_CID_VBLANK: + ret = mt9v111_update(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_R06_VBLANK, + MT9V111_CORE_R06_MAX_VBLANK, + mt9v111->vblank->val); + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops mt9v111_ctrl_ops = { + .s_ctrl = mt9v111_s_ctrl, +}; + +static int mt9v111_chip_probe(struct mt9v111_dev *mt9v111) +{ + int ret; + u16 val; + + ret = __mt9v111_power_on(&mt9v111->sd); + if (ret) + return ret; + + ret = mt9v111_read(mt9v111->client, MT9V111_R01_CORE, + MT9V111_CORE_RFF_CHIP_VER, &val); + if (ret) + goto power_off; + + if ((val >> 8) != MT9V111_CHIP_ID_HIGH && + (val & 0xff) != MT9V111_CHIP_ID_LOW) { + dev_err(mt9v111->dev, + "Unable to identify MT9V111 chip: 0x%2x%2x\n", + val >> 8, val & 0xff); + ret = -EIO; + goto power_off; + } + + dev_dbg(mt9v111->dev, "Chip identified: 0x%2x%2x\n", + val >> 8, val & 0xff); + +power_off: + __mt9v111_power_off(&mt9v111->sd); + + return ret; +} + +static int mt9v111_probe(struct i2c_client *client) +{ + struct mt9v111_dev *mt9v111; + struct v4l2_fract tpf; + int ret; + + mt9v111 = devm_kzalloc(&client->dev, sizeof(*mt9v111), GFP_KERNEL); + if (!mt9v111) + return -ENOMEM; + + mt9v111->dev = &client->dev; + mt9v111->client = client; + + mt9v111->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9v111->clk)) + return PTR_ERR(mt9v111->clk); + + mt9v111->sysclk = clk_get_rate(mt9v111->clk); + if (mt9v111->sysclk > MT9V111_MAX_CLKIN) + return -EINVAL; + + mt9v111->oe = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(mt9v111->oe)) { + dev_err(&client->dev, "Unable to get GPIO \"enable\": %ld\n", + PTR_ERR(mt9v111->oe)); + return PTR_ERR(mt9v111->oe); + } + + mt9v111->standby = devm_gpiod_get_optional(&client->dev, "standby", + GPIOD_OUT_HIGH); + if (IS_ERR(mt9v111->standby)) { + dev_err(&client->dev, "Unable to get GPIO \"standby\": %ld\n", + PTR_ERR(mt9v111->standby)); + return PTR_ERR(mt9v111->standby); + } + + mt9v111->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mt9v111->reset)) { + dev_err(&client->dev, "Unable to get GPIO \"reset\": %ld\n", + PTR_ERR(mt9v111->reset)); + return PTR_ERR(mt9v111->reset); + } + + mutex_init(&mt9v111->pwr_mutex); + mutex_init(&mt9v111->stream_mutex); + + v4l2_ctrl_handler_init(&mt9v111->ctrls, 5); + + mt9v111->auto_awb = v4l2_ctrl_new_std(&mt9v111->ctrls, + &mt9v111_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, + V4L2_WHITE_BALANCE_AUTO); + if (IS_ERR_OR_NULL(mt9v111->auto_awb)) { + ret = PTR_ERR(mt9v111->auto_awb); + goto error_free_ctrls; + } + + mt9v111->auto_exp = v4l2_ctrl_new_std_menu(&mt9v111->ctrls, + &mt9v111_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, + 0, V4L2_EXPOSURE_AUTO); + if (IS_ERR_OR_NULL(mt9v111->auto_exp)) { + ret = PTR_ERR(mt9v111->auto_exp); + goto error_free_ctrls; + } + + /* Initialize timings */ + mt9v111->hblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_HBLANK, + MT9V111_CORE_R05_MIN_HBLANK, + MT9V111_CORE_R05_MAX_HBLANK, 1, + MT9V111_CORE_R05_DEF_HBLANK); + if (IS_ERR_OR_NULL(mt9v111->hblank)) { + ret = PTR_ERR(mt9v111->hblank); + goto error_free_ctrls; + } + + mt9v111->vblank = v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_VBLANK, + MT9V111_CORE_R06_MIN_VBLANK, + MT9V111_CORE_R06_MAX_VBLANK, 1, + MT9V111_CORE_R06_DEF_VBLANK); + if (IS_ERR_OR_NULL(mt9v111->vblank)) { + ret = PTR_ERR(mt9v111->vblank); + goto error_free_ctrls; + } + + /* PIXEL_RATE is fixed: just expose it to user space. */ + v4l2_ctrl_new_std(&mt9v111->ctrls, &mt9v111_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + DIV_ROUND_CLOSEST(mt9v111->sysclk, 2), 1, + DIV_ROUND_CLOSEST(mt9v111->sysclk, 2)); + + mt9v111->sd.ctrl_handler = &mt9v111->ctrls; + + /* Start with default configuration: 640x480 UYVY. */ + mt9v111->fmt = mt9v111_def_fmt; + + /* Re-calculate blankings for 640x480@15fps. */ + mt9v111->fps = 15; + tpf.numerator = 1; + tpf.denominator = mt9v111->fps; + mt9v111_calc_frame_rate(mt9v111, &tpf); + + mt9v111->pwr_count = 0; + mt9v111->addr_space = MT9V111_R01_IFP; + mt9v111->pending = true; + + v4l2_i2c_subdev_init(&mt9v111->sd, client, &mt9v111_ops); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + mt9v111->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9v111->sd.entity.ops = &mt9v111_subdev_entity_ops; + mt9v111->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + mt9v111->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&mt9v111->sd.entity, 1, &mt9v111->pad); + if (ret) + goto error_free_ctrls; +#endif + + ret = mt9v111_chip_probe(mt9v111); + if (ret) + goto error_free_ctrls; + + ret = v4l2_async_register_subdev(&mt9v111->sd); + if (ret) + goto error_free_ctrls; + + return 0; + +error_free_ctrls: + v4l2_ctrl_handler_free(&mt9v111->ctrls); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&mt9v111->sd.entity); +#endif + + mutex_destroy(&mt9v111->pwr_mutex); + mutex_destroy(&mt9v111->stream_mutex); + + return ret; +} + +static int mt9v111_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct mt9v111_dev *mt9v111 = sd_to_mt9v111(sd); + + v4l2_async_unregister_subdev(sd); + + v4l2_ctrl_handler_free(&mt9v111->ctrls); + +#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + + mutex_destroy(&mt9v111->pwr_mutex); + mutex_destroy(&mt9v111->stream_mutex); + + devm_gpiod_put(mt9v111->dev, mt9v111->oe); + devm_gpiod_put(mt9v111->dev, mt9v111->standby); + devm_gpiod_put(mt9v111->dev, mt9v111->reset); + + devm_clk_put(mt9v111->dev, mt9v111->clk); + + return 0; +} + +static const struct of_device_id mt9v111_of_match[] = { + { .compatible = "aptina,mt9v111", }, + { /* sentinel */ }, +}; + +static struct i2c_driver mt9v111_driver = { + .driver = { + .name = "mt9v111", + .of_match_table = mt9v111_of_match, + }, + .probe_new = mt9v111_probe, + .remove = mt9v111_remove, +}; + +module_i2c_driver(mt9v111_driver); + +MODULE_DESCRIPTION("V4L2 sensor driver for Aptina MT9V111"); +MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c new file mode 100644 index 000000000000..f753a1c333ef --- /dev/null +++ b/drivers/media/i2c/ov2680.c @@ -0,0 +1,1186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Omnivision OV2680 CMOS Image Sensor driver + * + * Copyright (C) 2018 Linaro Ltd + * + * Based on OV5640 Sensor Driver + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2014-2017 Mentor Graphics Inc. + * + */ + +#include <asm/unaligned.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#define OV2680_XVCLK_VALUE 24000000 + +#define OV2680_CHIP_ID 0x2680 + +#define OV2680_REG_STREAM_CTRL 0x0100 +#define OV2680_REG_SOFT_RESET 0x0103 + +#define OV2680_REG_CHIP_ID_HIGH 0x300a +#define OV2680_REG_CHIP_ID_LOW 0x300b + +#define OV2680_REG_R_MANUAL 0x3503 +#define OV2680_REG_GAIN_PK 0x350a +#define OV2680_REG_EXPOSURE_PK_HIGH 0x3500 +#define OV2680_REG_TIMING_HTS 0x380c +#define OV2680_REG_TIMING_VTS 0x380e +#define OV2680_REG_FORMAT1 0x3820 +#define OV2680_REG_FORMAT2 0x3821 + +#define OV2680_REG_ISP_CTRL00 0x5080 + +#define OV2680_FRAME_RATE 30 + +#define OV2680_REG_VALUE_8BIT 1 +#define OV2680_REG_VALUE_16BIT 2 +#define OV2680_REG_VALUE_24BIT 3 + +#define OV2680_WIDTH_MAX 1600 +#define OV2680_HEIGHT_MAX 1200 + +enum ov2680_mode_id { + OV2680_MODE_QUXGA_800_600, + OV2680_MODE_720P_1280_720, + OV2680_MODE_UXGA_1600_1200, + OV2680_MODE_MAX, +}; + +struct reg_value { + u16 reg_addr; + u8 val; +}; + +static const char * const ov2680_supply_name[] = { + "DOVDD", + "DVDD", + "AVDD", +}; + +#define OV2680_NUM_SUPPLIES ARRAY_SIZE(ov2680_supply_name) + +struct ov2680_mode_info { + const char *name; + enum ov2680_mode_id id; + u32 width; + u32 height; + const struct reg_value *reg_data; + u32 reg_data_size; +}; + +struct ov2680_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + }; + struct { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *test_pattern; +}; + +struct ov2680_dev { + struct i2c_client *i2c_client; + struct v4l2_subdev sd; + + struct media_pad pad; + struct clk *xvclk; + u32 xvclk_freq; + struct regulator_bulk_data supplies[OV2680_NUM_SUPPLIES]; + + struct gpio_desc *reset_gpio; + struct mutex lock; /* protect members */ + + bool mode_pending_changes; + bool is_enabled; + bool is_streaming; + + struct ov2680_ctrls ctrls; + struct v4l2_mbus_framefmt fmt; + struct v4l2_fract frame_interval; + + const struct ov2680_mode_info *current_mode; +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Random Data", + "Square", + "Black Image", +}; + +static const int ov2680_hv_flip_bayer_order[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + +static const struct reg_value ov2680_setting_30fps_QUXGA_800_600[] = { + {0x3086, 0x01}, {0x370a, 0x23}, {0x3808, 0x03}, {0x3809, 0x20}, + {0x380a, 0x02}, {0x380b, 0x58}, {0x380c, 0x06}, {0x380d, 0xac}, + {0x380e, 0x02}, {0x380f, 0x84}, {0x3811, 0x04}, {0x3813, 0x04}, + {0x3814, 0x31}, {0x3815, 0x31}, {0x3820, 0xc0}, {0x4008, 0x00}, + {0x4009, 0x03}, {0x4837, 0x1e}, {0x3501, 0x4e}, {0x3502, 0xe0}, +}; + +static const struct reg_value ov2680_setting_30fps_720P_1280_720[] = { + {0x3086, 0x00}, {0x3808, 0x05}, {0x3809, 0x00}, {0x380a, 0x02}, + {0x380b, 0xd0}, {0x380c, 0x06}, {0x380d, 0xa8}, {0x380e, 0x05}, + {0x380f, 0x0e}, {0x3811, 0x08}, {0x3813, 0x06}, {0x3814, 0x11}, + {0x3815, 0x11}, {0x3820, 0xc0}, {0x4008, 0x00}, +}; + +static const struct reg_value ov2680_setting_30fps_UXGA_1600_1200[] = { + {0x3086, 0x00}, {0x3501, 0x4e}, {0x3502, 0xe0}, {0x3808, 0x06}, + {0x3809, 0x40}, {0x380a, 0x04}, {0x380b, 0xb0}, {0x380c, 0x06}, + {0x380d, 0xa8}, {0x380e, 0x05}, {0x380f, 0x0e}, {0x3811, 0x00}, + {0x3813, 0x00}, {0x3814, 0x11}, {0x3815, 0x11}, {0x3820, 0xc0}, + {0x4008, 0x00}, {0x4837, 0x18} +}; + +static const struct ov2680_mode_info ov2680_mode_init_data = { + "mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, 800, 600, + ov2680_setting_30fps_QUXGA_800_600, + ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600), +}; + +static const struct ov2680_mode_info ov2680_mode_data[OV2680_MODE_MAX] = { + {"mode_quxga_800_600", OV2680_MODE_QUXGA_800_600, + 800, 600, ov2680_setting_30fps_QUXGA_800_600, + ARRAY_SIZE(ov2680_setting_30fps_QUXGA_800_600)}, + {"mode_720p_1280_720", OV2680_MODE_720P_1280_720, + 1280, 720, ov2680_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov2680_setting_30fps_720P_1280_720)}, + {"mode_uxga_1600_1200", OV2680_MODE_UXGA_1600_1200, + 1600, 1200, ov2680_setting_30fps_UXGA_1600_1200, + ARRAY_SIZE(ov2680_setting_30fps_UXGA_1600_1200)}, +}; + +static struct ov2680_dev *to_ov2680_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov2680_dev, sd); +} + +static struct device *ov2680_to_dev(struct ov2680_dev *sensor) +{ + return &sensor->i2c_client->dev; +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov2680_dev, + ctrls.handler)->sd; +} + +static int __ov2680_write_reg(struct ov2680_dev *sensor, u16 reg, + unsigned int len, u32 val) +{ + struct i2c_client *client = sensor->i2c_client; + u8 buf[6]; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + ret = i2c_master_send(client, buf, len + 2); + if (ret != len + 2) { + dev_err(&client->dev, "write error: reg=0x%4x: %d\n", reg, ret); + return -EIO; + } + + return 0; +} + +#define ov2680_write_reg(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_8BIT, v) + +#define ov2680_write_reg16(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_16BIT, v) + +#define ov2680_write_reg24(s, r, v) \ + __ov2680_write_reg(s, r, OV2680_REG_VALUE_24BIT, v) + +static int __ov2680_read_reg(struct ov2680_dev *sensor, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_client *client = sensor->i2c_client; + 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; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error: reg=0x%4x: %d\n", reg, ret); + return -EIO; + } + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +#define ov2680_read_reg(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_8BIT, v) + +#define ov2680_read_reg16(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_16BIT, v) + +#define ov2680_read_reg24(s, r, v) \ + __ov2680_read_reg(s, r, OV2680_REG_VALUE_24BIT, v) + +static int ov2680_mod_reg(struct ov2680_dev *sensor, u16 reg, u8 mask, u8 val) +{ + u32 readval; + int ret; + + ret = ov2680_read_reg(sensor, reg, &readval); + if (ret < 0) + return ret; + + readval &= ~mask; + val &= mask; + val |= readval; + + return ov2680_write_reg(sensor, reg, val); +} + +static int ov2680_load_regs(struct ov2680_dev *sensor, + const struct ov2680_mode_info *mode) +{ + const struct reg_value *regs = mode->reg_data; + unsigned int i; + int ret = 0; + u16 reg_addr; + u8 val; + + for (i = 0; i < mode->reg_data_size; ++i, ++regs) { + reg_addr = regs->reg_addr; + val = regs->val; + + ret = ov2680_write_reg(sensor, reg_addr, val); + if (ret) + break; + } + + return ret; +} + +static void ov2680_power_up(struct ov2680_dev *sensor) +{ + if (!sensor->reset_gpio) + return; + + gpiod_set_value(sensor->reset_gpio, 0); + usleep_range(5000, 10000); +} + +static void ov2680_power_down(struct ov2680_dev *sensor) +{ + if (!sensor->reset_gpio) + return; + + gpiod_set_value(sensor->reset_gpio, 1); + usleep_range(5000, 10000); +} + +static int ov2680_bayer_order(struct ov2680_dev *sensor) +{ + u32 format1; + u32 format2; + u32 hv_flip; + int ret; + + ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT1, &format1); + if (ret < 0) + return ret; + + ret = ov2680_read_reg(sensor, OV2680_REG_FORMAT2, &format2); + if (ret < 0) + return ret; + + hv_flip = (format2 & BIT(2) << 1) | (format1 & BIT(2)); + + sensor->fmt.code = ov2680_hv_flip_bayer_order[hv_flip]; + + return 0; +} + +static int ov2680_vflip_enable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(2)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_vflip_disable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT1, BIT(2), BIT(0)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_hflip_enable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(2)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_hflip_disable(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_FORMAT2, BIT(2), BIT(0)); + if (ret < 0) + return ret; + + return ov2680_bayer_order(sensor); +} + +static int ov2680_test_pattern_set(struct ov2680_dev *sensor, int value) +{ + int ret; + + if (!value) + return ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), 0); + + ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, 0x03, value - 1); + if (ret < 0) + return ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_ISP_CTRL00, BIT(7), BIT(7)); + if (ret < 0) + return ret; + + return 0; +} + +static int ov2680_gain_set(struct ov2680_dev *sensor, bool auto_gain) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + u32 gain; + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(1), + auto_gain ? 0 : BIT(1)); + if (ret < 0) + return ret; + + if (auto_gain || !ctrls->gain->is_new) + return 0; + + gain = ctrls->gain->val; + + ret = ov2680_write_reg16(sensor, OV2680_REG_GAIN_PK, gain); + + return 0; +} + +static int ov2680_gain_get(struct ov2680_dev *sensor) +{ + u32 gain; + int ret; + + ret = ov2680_read_reg16(sensor, OV2680_REG_GAIN_PK, &gain); + if (ret) + return ret; + + return gain; +} + +static int ov2680_exposure_set(struct ov2680_dev *sensor, bool auto_exp) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + u32 exp; + int ret; + + ret = ov2680_mod_reg(sensor, OV2680_REG_R_MANUAL, BIT(0), + auto_exp ? 0 : BIT(0)); + if (ret < 0) + return ret; + + if (auto_exp || !ctrls->exposure->is_new) + return 0; + + exp = (u32)ctrls->exposure->val; + exp <<= 4; + + return ov2680_write_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, exp); +} + +static int ov2680_exposure_get(struct ov2680_dev *sensor) +{ + int ret; + u32 exp; + + ret = ov2680_read_reg24(sensor, OV2680_REG_EXPOSURE_PK_HIGH, &exp); + if (ret) + return ret; + + return exp >> 4; +} + +static int ov2680_stream_enable(struct ov2680_dev *sensor) +{ + return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 1); +} + +static int ov2680_stream_disable(struct ov2680_dev *sensor) +{ + return ov2680_write_reg(sensor, OV2680_REG_STREAM_CTRL, 0); +} + +static int ov2680_mode_set(struct ov2680_dev *sensor) +{ + struct ov2680_ctrls *ctrls = &sensor->ctrls; + int ret; + + ret = ov2680_gain_set(sensor, false); + if (ret < 0) + return ret; + + ret = ov2680_exposure_set(sensor, false); + if (ret < 0) + return ret; + + ret = ov2680_load_regs(sensor, sensor->current_mode); + if (ret < 0) + return ret; + + if (ctrls->auto_gain->val) { + ret = ov2680_gain_set(sensor, true); + if (ret < 0) + return ret; + } + + if (ctrls->auto_exp->val == V4L2_EXPOSURE_AUTO) { + ret = ov2680_exposure_set(sensor, true); + if (ret < 0) + return ret; + } + + sensor->mode_pending_changes = false; + + return 0; +} + +static int ov2680_mode_restore(struct ov2680_dev *sensor) +{ + int ret; + + ret = ov2680_load_regs(sensor, &ov2680_mode_init_data); + if (ret < 0) + return ret; + + return ov2680_mode_set(sensor); +} + +static int ov2680_power_off(struct ov2680_dev *sensor) +{ + if (!sensor->is_enabled) + return 0; + + clk_disable_unprepare(sensor->xvclk); + ov2680_power_down(sensor); + regulator_bulk_disable(OV2680_NUM_SUPPLIES, sensor->supplies); + sensor->is_enabled = false; + + return 0; +} + +static int ov2680_power_on(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + int ret; + + if (sensor->is_enabled) + return 0; + + ret = regulator_bulk_enable(OV2680_NUM_SUPPLIES, sensor->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + if (!sensor->reset_gpio) { + ret = ov2680_write_reg(sensor, OV2680_REG_SOFT_RESET, 0x01); + if (ret != 0) { + dev_err(dev, "sensor soft reset failed\n"); + return ret; + } + usleep_range(1000, 2000); + } else { + ov2680_power_down(sensor); + ov2680_power_up(sensor); + } + + ret = clk_prepare_enable(sensor->xvclk); + if (ret < 0) + return ret; + + ret = ov2680_mode_restore(sensor); + if (ret < 0) + goto disable; + + sensor->is_enabled = true; + + /* Set clock lane into LP-11 state */ + ov2680_stream_enable(sensor); + usleep_range(1000, 2000); + ov2680_stream_disable(sensor); + + return 0; + +disable: + dev_err(dev, "failed to enable sensor: %d\n", ret); + ov2680_power_off(sensor); + + return ret; +} + +static int ov2680_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + if (on) + ret = ov2680_power_on(sensor); + else + ret = ov2680_power_off(sensor); + + mutex_unlock(&sensor->lock); + + if (on && ret == 0) { + ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret < 0) + return ret; + } + + return ret; +} + +static int ov2680_s_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + mutex_lock(&sensor->lock); + fi->interval = sensor->frame_interval; + mutex_unlock(&sensor->lock); + + return 0; +} + +static int ov2680_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + if (sensor->is_streaming == !!enable) + goto unlock; + + if (enable && sensor->mode_pending_changes) { + ret = ov2680_mode_set(sensor); + if (ret < 0) + goto unlock; + } + + if (enable) + ret = ov2680_stream_enable(sensor); + else + ret = ov2680_stream_disable(sensor); + + sensor->is_streaming = !!enable; + +unlock: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + if (code->pad != 0 || code->index != 0) + return -EINVAL; + + code->code = sensor->fmt.code; + + return 0; +} + +static int ov2680_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *fmt = NULL; + int ret = 0; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg, format->pad); +#else + ret = -ENOTTY; +#endif + } else { + fmt = &sensor->fmt; + } + + if (fmt) + format->format = *fmt; + + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + struct v4l2_mbus_framefmt *try_fmt; +#endif + const struct ov2680_mode_info *mode; + int ret = 0; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&sensor->lock); + + if (sensor->is_streaming) { + ret = -EBUSY; + goto unlock; + } + + mode = v4l2_find_nearest_size(ov2680_mode_data, + ARRAY_SIZE(ov2680_mode_data), width, + height, fmt->width, fmt->height); + if (!mode) { + ret = -EINVAL; + goto unlock; + } + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + try_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + format->format = *try_fmt; +#else + ret = -ENOTTY; +#endif + + goto unlock; + } + + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = sensor->fmt.code; + fmt->colorspace = sensor->fmt.colorspace; + + sensor->current_mode = mode; + sensor->fmt = format->format; + sensor->mode_pending_changes = true; + +unlock: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int ov2680_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, + .format = { + .width = 800, + .height = 600, + } + }; + + return ov2680_set_fmt(sd, cfg, &fmt); +} + +static int ov2680_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + int index = fse->index; + + if (index >= OV2680_MODE_MAX || index < 0) + return -EINVAL; + + fse->min_width = ov2680_mode_data[index].width; + fse->min_height = ov2680_mode_data[index].height; + fse->max_width = ov2680_mode_data[index].width; + fse->max_height = ov2680_mode_data[index].height; + + return 0; +} + +static int ov2680_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct v4l2_fract tpf; + + if (fie->index >= OV2680_MODE_MAX || fie->width > OV2680_WIDTH_MAX || + fie->height > OV2680_HEIGHT_MAX || + fie->which > V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + tpf.denominator = OV2680_FRAME_RATE; + tpf.numerator = 1; + + fie->interval = tpf; + + return 0; +} + +static int ov2680_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct ov2680_ctrls *ctrls = &sensor->ctrls; + int val; + + if (!sensor->is_enabled) + return 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + val = ov2680_gain_get(sensor); + if (val < 0) + return val; + ctrls->gain->val = val; + break; + case V4L2_CID_EXPOSURE: + val = ov2680_exposure_get(sensor); + if (val < 0) + return val; + ctrls->exposure->val = val; + break; + } + + return 0; +} + +static int ov2680_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + struct ov2680_ctrls *ctrls = &sensor->ctrls; + + if (!sensor->is_enabled) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + return ov2680_gain_set(sensor, !!ctrl->val); + case V4L2_CID_GAIN: + return ov2680_gain_set(sensor, !!ctrls->auto_gain->val); + case V4L2_CID_EXPOSURE_AUTO: + return ov2680_exposure_set(sensor, !!ctrl->val); + case V4L2_CID_EXPOSURE: + return ov2680_exposure_set(sensor, !!ctrls->auto_exp->val); + case V4L2_CID_VFLIP: + if (sensor->is_streaming) + return -EBUSY; + if (ctrl->val) + return ov2680_vflip_enable(sensor); + else + return ov2680_vflip_disable(sensor); + case V4L2_CID_HFLIP: + if (sensor->is_streaming) + return -EBUSY; + if (ctrl->val) + return ov2680_hflip_enable(sensor); + else + return ov2680_hflip_disable(sensor); + case V4L2_CID_TEST_PATTERN: + return ov2680_test_pattern_set(sensor, ctrl->val); + default: + break; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops ov2680_ctrl_ops = { + .g_volatile_ctrl = ov2680_g_volatile_ctrl, + .s_ctrl = ov2680_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops ov2680_core_ops = { + .s_power = ov2680_s_power, +}; + +static const struct v4l2_subdev_video_ops ov2680_video_ops = { + .g_frame_interval = ov2680_s_g_frame_interval, + .s_frame_interval = ov2680_s_g_frame_interval, + .s_stream = ov2680_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov2680_pad_ops = { + .init_cfg = ov2680_init_cfg, + .enum_mbus_code = ov2680_enum_mbus_code, + .get_fmt = ov2680_get_fmt, + .set_fmt = ov2680_set_fmt, + .enum_frame_size = ov2680_enum_frame_size, + .enum_frame_interval = ov2680_enum_frame_interval, +}; + +static const struct v4l2_subdev_ops ov2680_subdev_ops = { + .core = &ov2680_core_ops, + .video = &ov2680_video_ops, + .pad = &ov2680_pad_ops, +}; + +static int ov2680_mode_init(struct ov2680_dev *sensor) +{ + const struct ov2680_mode_info *init_mode; + + /* set initial mode */ + sensor->fmt.code = MEDIA_BUS_FMT_SBGGR10_1X10; + sensor->fmt.width = 800; + sensor->fmt.height = 600; + sensor->fmt.field = V4L2_FIELD_NONE; + sensor->fmt.colorspace = V4L2_COLORSPACE_SRGB; + + sensor->frame_interval.denominator = OV2680_FRAME_RATE; + sensor->frame_interval.numerator = 1; + + init_mode = &ov2680_mode_init_data; + + sensor->current_mode = init_mode; + + sensor->mode_pending_changes = true; + + return 0; +} + +static int ov2680_v4l2_init(struct ov2680_dev *sensor) +{ + const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; + struct ov2680_ctrls *ctrls = &sensor->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret = 0; + + v4l2_i2c_subdev_init(&sensor->sd, sensor->i2c_client, + &ov2680_subdev_ops); + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sensor->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(hdl, 7); + + hdl->lock = &sensor->lock; + + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + + ctrls->test_pattern = v4l2_ctrl_new_std_menu_items(hdl, + &ov2680_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, + 0, 0, test_pattern_menu); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 32767, 1, 0); + + ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, + 0, 1, 1, 1); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 0, 2047, 1, 0); + + if (hdl->error) { + ret = hdl->error; + goto cleanup_entity; + } + + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true); + + sensor->sd.ctrl_handler = hdl; + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret < 0) + goto cleanup_entity; + + return 0; + +cleanup_entity: + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(hdl); + + return ret; +} + +static int ov2680_get_regulators(struct ov2680_dev *sensor) +{ + int i; + + for (i = 0; i < OV2680_NUM_SUPPLIES; i++) + sensor->supplies[i].supply = ov2680_supply_name[i]; + + return devm_regulator_bulk_get(&sensor->i2c_client->dev, + OV2680_NUM_SUPPLIES, + sensor->supplies); +} + +static int ov2680_check_id(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + u32 chip_id; + int ret; + + ov2680_power_on(sensor); + + ret = ov2680_read_reg16(sensor, OV2680_REG_CHIP_ID_HIGH, &chip_id); + if (ret < 0) { + dev_err(dev, "failed to read chip id high\n"); + return -ENODEV; + } + + if (chip_id != OV2680_CHIP_ID) { + dev_err(dev, "chip id: 0x%04x does not match expected 0x%04x\n", + chip_id, OV2680_CHIP_ID); + return -ENODEV; + } + + return 0; +} + +static int ov2860_parse_dt(struct ov2680_dev *sensor) +{ + struct device *dev = ov2680_to_dev(sensor); + int ret; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(sensor->reset_gpio); + if (ret < 0) { + dev_dbg(dev, "error while getting reset gpio: %d\n", ret); + return ret; + } + + sensor->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(sensor->xvclk)) { + dev_err(dev, "xvclk clock missing or invalid\n"); + return PTR_ERR(sensor->xvclk); + } + + sensor->xvclk_freq = clk_get_rate(sensor->xvclk); + if (sensor->xvclk_freq != OV2680_XVCLK_VALUE) { + dev_err(dev, "wrong xvclk frequency %d HZ, expected: %d Hz\n", + sensor->xvclk_freq, OV2680_XVCLK_VALUE); + return -EINVAL; + } + + return 0; +} + +static int ov2680_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ov2680_dev *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->i2c_client = client; + + ret = ov2860_parse_dt(sensor); + if (ret < 0) + return -EINVAL; + + ret = ov2680_mode_init(sensor); + if (ret < 0) + return ret; + + ret = ov2680_get_regulators(sensor); + if (ret < 0) { + dev_err(dev, "failed to get regulators\n"); + return ret; + } + + mutex_init(&sensor->lock); + + ret = ov2680_v4l2_init(sensor); + if (ret < 0) + goto lock_destroy; + + ret = ov2680_check_id(sensor); + if (ret < 0) + goto error_cleanup; + + dev_info(dev, "ov2680 init correctly\n"); + + return 0; + +error_cleanup: + dev_err(dev, "ov2680 init fail: %d\n", ret); + + media_entity_cleanup(&sensor->sd.entity); + v4l2_async_unregister_subdev(&sensor->sd); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + +lock_destroy: + mutex_destroy(&sensor->lock); + + return ret; +} + +static int ov2680_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + mutex_destroy(&sensor->lock); + media_entity_cleanup(&sensor->sd.entity); + v4l2_ctrl_handler_free(&sensor->ctrls.handler); + + return 0; +} + +static int __maybe_unused ov2680_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + + if (sensor->is_streaming) + ov2680_stream_disable(sensor); + + return 0; +} + +static int __maybe_unused ov2680_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov2680_dev *sensor = to_ov2680_dev(sd); + int ret; + + if (sensor->is_streaming) { + ret = ov2680_stream_enable(sensor); + if (ret < 0) + goto stream_disable; + } + + return 0; + +stream_disable: + ov2680_stream_disable(sensor); + sensor->is_streaming = false; + + return ret; +} + +static const struct dev_pm_ops ov2680_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ov2680_suspend, ov2680_resume) +}; + +static const struct of_device_id ov2680_dt_ids[] = { + { .compatible = "ovti,ov2680" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov2680_dt_ids); + +static struct i2c_driver ov2680_i2c_driver = { + .driver = { + .name = "ov2680", + .pm = &ov2680_pm_ops, + .of_match_table = of_match_ptr(ov2680_dt_ids), + }, + .probe_new = ov2680_probe, + .remove = ov2680_remove, +}; +module_i2c_driver(ov2680_i2c_driver); + +MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>"); +MODULE_DESCRIPTION("OV2680 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index f6e40cc9745c..071f4bc240ca 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -30,7 +30,7 @@ /* min/typical/max system clock (xclk) frequencies */ #define OV5640_XCLK_MIN 6000000 -#define OV5640_XCLK_MAX 24000000 +#define OV5640_XCLK_MAX 54000000 #define OV5640_DEFAULT_SLAVE_ID 0x3c @@ -64,6 +64,7 @@ #define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e +#define OV5640_REG_TIMING_TC_REG20 0x3820 #define OV5640_REG_TIMING_TC_REG21 0x3821 #define OV5640_REG_AEC_CTRL00 0x3a00 #define OV5640_REG_AEC_B50_STEP 0x3a08 @@ -199,6 +200,8 @@ struct ov5640_ctrls { struct v4l2_ctrl *contrast; struct v4l2_ctrl *hue; struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; }; struct ov5640_dev { @@ -212,6 +215,7 @@ struct ov5640_dev { struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; struct gpio_desc *pwdn_gpio; + bool upside_down; /* lock to protect all members below */ struct mutex lock; @@ -341,7 +345,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -360,7 +364,7 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -379,7 +383,7 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -399,7 +403,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -418,7 +422,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -437,7 +441,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -456,7 +460,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -475,7 +479,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -494,7 +498,7 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -513,7 +517,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -532,7 +536,7 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -551,7 +555,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, @@ -571,7 +575,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, @@ -591,7 +595,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, + {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, @@ -611,7 +615,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -644,7 +648,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -673,10 +677,9 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { }; static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0}, + {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, @@ -1340,6 +1343,27 @@ static int ov5640_binning_on(struct ov5640_dev *sensor) return temp ? 1 : 0; } +static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable) +{ + int ret; + + /* + * TIMING TC REG21: + * - [0]: Horizontal binning enable + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, + BIT(0), enable ? BIT(0) : 0); + if (ret) + return ret; + /* + * TIMING TC REG20: + * - [0]: Undocumented, but hardcoded init sequences + * are always setting REG21/REG20 bit 0 to same value... + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20, + BIT(0), enable ? BIT(0) : 0); +} + static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) { struct i2c_client *client = sensor->i2c_client; @@ -1389,24 +1413,16 @@ static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) { - const struct ov5640_mode_info *mode = NULL; - int i; - - for (i = OV5640_NUM_MODES - 1; i >= 0; i--) { - mode = &ov5640_mode_data[fr][i]; - - if (!mode->reg_data) - continue; + const struct ov5640_mode_info *mode; - if ((nearest && mode->hact <= width && - mode->vact <= height) || - (!nearest && mode->hact == width && - mode->vact == height)) - break; - } + mode = v4l2_find_nearest_size(ov5640_mode_data[fr], + ARRAY_SIZE(ov5640_mode_data[fr]), + hact, vact, + width, height); - if (nearest && i < 0) - mode = &ov5640_mode_data[fr][0]; + if (!mode || + (!nearest && (mode->hact != width || mode->vact != height))) + return NULL; return mode; } @@ -1640,6 +1656,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, if (ret < 0) return ret; + ret = ov5640_set_binning(sensor, dn_mode != SCALING); + if (ret < 0) + return ret; ret = ov5640_set_ae_target(sensor, sensor->ae_target); if (ret < 0) return ret; @@ -1947,9 +1966,11 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, goto out; } - sensor->current_mode = new_mode; - sensor->fmt = *mbus_fmt; - sensor->pending_mode_change = true; + if (new_mode != sensor->current_mode) { + sensor->current_mode = new_mode; + sensor->fmt = *mbus_fmt; + sensor->pending_mode_change = true; + } out: mutex_unlock(&sensor->lock); return ret; @@ -2193,6 +2214,43 @@ static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) BIT(2) : 0); } +static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value) +{ + /* + * If sensor is mounted upside down, mirror logic is inversed. + * + * Sensor is a BSI (Back Side Illuminated) one, + * so image captured is physically mirrored. + * This is why mirror logic is inversed in + * order to cancel this mirror effect. + */ + + /* + * TIMING TC REG21: + * - [2]: ISP mirror + * - [1]: Sensor mirror + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21, + BIT(2) | BIT(1), + (!(value ^ sensor->upside_down)) ? + (BIT(2) | BIT(1)) : 0); +} + +static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) +{ + /* If sensor is mounted upside down, flip logic is inversed */ + + /* + * TIMING TC REG20: + * - [2]: ISP vflip + * - [1]: Sensor vflip + */ + return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20, + BIT(2) | BIT(1), + (value ^ sensor->upside_down) ? + (BIT(2) | BIT(1)) : 0); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2264,6 +2322,12 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_POWER_LINE_FREQUENCY: ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val); break; + case V4L2_CID_HFLIP: + ret = ov5640_set_ctrl_hflip(sensor, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov5640_set_ctrl_vflip(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2325,6 +2389,10 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops, @@ -2435,9 +2503,17 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; - sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact, - mode->vact, true); - sensor->pending_mode_change = true; + mode = ov5640_find_mode(sensor, frame_rate, mode->hact, + mode->vact, true); + if (!mode) { + ret = -EINVAL; + goto out; + } + + if (mode != sensor->current_mode) { + sensor->current_mode = mode; + sensor->pending_mode_change = true; + } out: mutex_unlock(&sensor->lock); return ret; @@ -2558,6 +2634,7 @@ static int ov5640_probe(struct i2c_client *client, struct fwnode_handle *endpoint; struct ov5640_dev *sensor; struct v4l2_mbus_framefmt *fmt; + u32 rotation; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); @@ -2583,6 +2660,22 @@ static int ov5640_probe(struct i2c_client *client, sensor->ae_target = 52; + /* optional indication of physical rotation of sensor */ + ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", + &rotation); + if (!ret) { + switch (rotation) { + case 180: + sensor->upside_down = true; + /* fall through */ + case 0: + break; + default: + dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n", + rotation); + } + } + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!endpoint) { diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index b3f762578f7f..1722cdab0daf 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -510,8 +510,8 @@ static const struct reg_value ov5645_setting_full[] = { }; static const s64 link_freq[] = { - 222880000, - 334320000 + 224000000, + 336000000 }; static const struct ov5645_mode_info ov5645_mode_info_data[] = { @@ -520,7 +520,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 960, .data = ov5645_setting_sxga, .data_size = ARRAY_SIZE(ov5645_setting_sxga), - .pixel_clock = 111440000, + .pixel_clock = 112000000, .link_freq = 0 /* an index in link_freq[] */ }, { @@ -528,7 +528,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 1080, .data = ov5645_setting_1080p, .data_size = ARRAY_SIZE(ov5645_setting_1080p), - .pixel_clock = 167160000, + .pixel_clock = 168000000, .link_freq = 1 /* an index in link_freq[] */ }, { @@ -536,7 +536,7 @@ static const struct ov5645_mode_info ov5645_mode_info_data[] = { .height = 1944, .data = ov5645_setting_full, .data_size = ARRAY_SIZE(ov5645_setting_full), - .pixel_clock = 167160000, + .pixel_clock = 168000000, .link_freq = 1 /* an index in link_freq[] */ }, }; @@ -1145,7 +1145,8 @@ static int ov5645_probe(struct i2c_client *client, return ret; } - if (xclk_freq != 23880000) { + /* external clock must be 24MHz, allow 1% tolerance */ + if (xclk_freq < 23760000 || xclk_freq > 24240000) { dev_err(dev, "external clock frequency %u is not supported\n", xclk_freq); return -EINVAL; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 3474ef832c1e..31bf577b0bd3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1744,14 +1744,12 @@ static int ov7670_parse_dt(struct device *dev, return -EINVAL; ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg); - if (ret) { - fwnode_handle_put(ep); + fwnode_handle_put(ep); + if (ret) return ret; - } if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) { dev_err(dev, "Unsupported media bus type\n"); - fwnode_handle_put(ep); return ret; } info->mbus_config = bus_cfg.bus.parallel.flags; diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index e2550708abc8..7158c31d8403 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -419,11 +419,18 @@ struct ov772x_priv { struct gpio_desc *rstb_gpio; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; - unsigned short flag_vflip:1; - unsigned short flag_hflip:1; + struct v4l2_ctrl *vflip_ctrl; + struct v4l2_ctrl *hflip_ctrl; /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ - unsigned short band_filter; + struct v4l2_ctrl *band_filter_ctrl; unsigned int fps; + /* lock to protect power_count and streaming */ + struct mutex lock; + int power_count; + int streaming; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_pad pad; +#endif }; /* @@ -542,9 +549,19 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) return container_of(sd, struct ov772x_priv, subdev); } -static inline int ov772x_read(struct i2c_client *client, u8 addr) +static int ov772x_read(struct i2c_client *client, u8 addr) { - return i2c_smbus_read_byte_data(client, addr); + int ret; + u8 val; + + ret = i2c_master_send(client, &addr, 1); + if (ret < 0) + return ret; + ret = i2c_master_recv(client, &val, 1); + if (ret < 0) + return ret; + + return val; } static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) @@ -587,39 +604,40 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov772x_priv *priv = to_ov772x(sd); + int ret = 0; - if (!enable) { - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; - } + mutex_lock(&priv->lock); - ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); + if (priv->streaming == enable) + goto done; - dev_dbg(&client->dev, "format %d, win %s\n", - priv->cfmt->code, priv->win->name); + ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, + enable ? 0 : SOFT_SLEEP_MODE); + if (ret) + goto done; - return 0; + if (enable) { + dev_dbg(&client->dev, "format %d, win %s\n", + priv->cfmt->code, priv->win->name); + } + priv->streaming = enable; + +done: + mutex_unlock(&priv->lock); + + return ret; } -static int ov772x_set_frame_rate(struct ov772x_priv *priv, - struct v4l2_fract *tpf, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) +static unsigned int ov772x_select_fps(struct ov772x_priv *priv, + struct v4l2_fract *tpf) { - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - unsigned long fin = clk_get_rate(priv->clk); unsigned int fps = tpf->numerator ? tpf->denominator / tpf->numerator : tpf->denominator; unsigned int best_diff; - unsigned int fsize; - unsigned int pclk; unsigned int diff; unsigned int idx; unsigned int i; - u8 clkrc = 0; - u8 com4 = 0; - int ret; /* Approximate to the closest supported frame interval. */ best_diff = ~0L; @@ -630,7 +648,25 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, best_diff = diff; } } - fps = ov772x_frame_intervals[idx]; + + return ov772x_frame_intervals[idx]; +} + +static int ov772x_set_frame_rate(struct ov772x_priv *priv, + unsigned int fps, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + unsigned long fin = clk_get_rate(priv->clk); + unsigned int best_diff; + unsigned int fsize; + unsigned int pclk; + unsigned int diff; + unsigned int i; + u8 clkrc = 0; + u8 com4 = 0; + int ret; /* Use image size (with blankings) to calculate desired pixel clock. */ switch (cfmt->com7 & OFMT_MASK) { @@ -695,10 +731,6 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, if (ret < 0) return ret; - tpf->numerator = 1; - tpf->denominator = fps; - priv->fps = tpf->denominator; - return 0; } @@ -719,8 +751,37 @@ static int ov772x_s_frame_interval(struct v4l2_subdev *sd, { struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_fract *tpf = &ival->interval; + unsigned int fps; + int ret = 0; + + mutex_lock(&priv->lock); - return ov772x_set_frame_rate(priv, tpf, priv->cfmt, priv->win); + if (priv->streaming) { + ret = -EBUSY; + goto error; + } + + fps = ov772x_select_fps(priv, tpf); + + /* + * If the device is not powered up by the host driver do + * not apply any changes to H/W at this time. Instead + * the frame rate will be restored right after power-up. + */ + if (priv->power_count > 0) { + ret = ov772x_set_frame_rate(priv, fps, priv->cfmt, priv->win); + if (ret) + goto error; + } + + tpf->numerator = 1; + tpf->denominator = fps; + priv->fps = fps; + +error: + mutex_unlock(&priv->lock); + + return ret; } static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) @@ -732,17 +793,25 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) int ret = 0; u8 val; + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (priv->power_count == 0) + return 0; + switch (ctrl->id) { case V4L2_CID_VFLIP: val = ctrl->val ? VFLIP_IMG : 0x00; - priv->flag_vflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_VFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val ^= VFLIP_IMG; return ov772x_mask_set(client, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP: val = ctrl->val ? HFLIP_IMG : 0x00; - priv->flag_hflip = ctrl->val; - if (priv->info->flags & OV772X_FLAG_HFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val ^= HFLIP_IMG; return ov772x_mask_set(client, COM3, HFLIP_IMG, val); case V4L2_CID_BAND_STOP_FILTER: @@ -761,8 +830,7 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) ret = ov772x_mask_set(client, BDBASE, 0xff, val); } - if (!ret) - priv->band_filter = ctrl->val; + return ret; } @@ -824,10 +892,10 @@ static int ov772x_power_on(struct ov772x_priv *priv) * available to handle this cleanly, request the GPIO temporarily * to avoid conflicts. */ - priv->rstb_gpio = gpiod_get_optional(&client->dev, "rstb", + priv->rstb_gpio = gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"rstb\""); + dev_info(&client->dev, "Unable to get GPIO \"reset\""); return PTR_ERR(priv->rstb_gpio); } @@ -855,12 +923,45 @@ static int ov772x_power_off(struct ov772x_priv *priv) return 0; } +static int ov772x_set_params(struct ov772x_priv *priv, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win); + static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct ov772x_priv *priv = to_ov772x(sd); + int ret = 0; + + mutex_lock(&priv->lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (priv->power_count == !on) { + if (on) { + ret = ov772x_power_on(priv); + /* + * Restore the format, the frame rate, and + * the controls + */ + if (!ret) + ret = ov772x_set_params(priv, priv->cfmt, + priv->win); + } else { + ret = ov772x_power_off(priv); + } + } - return on ? ov772x_power_on(priv) : - ov772x_power_off(priv); + if (!ret) { + /* Update the power count. */ + priv->power_count += on ? 1 : -1; + WARN(priv->power_count < 0, "Unbalanced power count\n"); + WARN(priv->power_count > 1, "Duplicated s_power call\n"); + } + + mutex_unlock(&priv->lock); + + return ret; } static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) @@ -901,19 +1002,14 @@ static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, *win = ov772x_select_win(mf->width, mf->height); } -static int ov772x_set_params(struct ov772x_priv *priv, - const struct ov772x_color_format *cfmt, - const struct ov772x_win_size *win) +static int ov772x_edgectrl(struct ov772x_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - struct v4l2_fract tpf; int ret; - u8 val; - /* Reset hardware. */ - ov772x_reset(client); + if (!priv->info) + return 0; - /* Edge Ctrl. */ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { /* * Manual Edge Control Mode. @@ -924,19 +1020,19 @@ static int ov772x_set_params(struct ov772x_priv *priv, ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { /* @@ -948,15 +1044,34 @@ static int ov772x_set_params(struct ov772x_priv *priv, EDGE_UPPER, OV772X_EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; ret = ov772x_mask_set(client, EDGE_LOWER, OV772X_EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) - goto ov772x_set_fmt_error; + return ret; } + return 0; +} + +static int ov772x_set_params(struct ov772x_priv *priv, + const struct ov772x_color_format *cfmt, + const struct ov772x_win_size *win) +{ + struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + int ret; + u8 val; + + /* Reset hardware. */ + ov772x_reset(client); + + /* Edge Ctrl. */ + ret = ov772x_edgectrl(priv); + if (ret < 0) + return ret; + /* Format and window size. */ ret = ov772x_write(client, HSTART, win->rect.left >> 2); if (ret < 0) @@ -1007,13 +1122,13 @@ static int ov772x_set_params(struct ov772x_priv *priv, /* Set COM3. */ val = cfmt->com3; - if (priv->info->flags & OV772X_FLAG_VFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val |= VFLIP_IMG; - if (priv->info->flags & OV772X_FLAG_HFLIP) + if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val |= HFLIP_IMG; - if (priv->flag_vflip) + if (priv->vflip_ctrl->val) val ^= VFLIP_IMG; - if (priv->flag_hflip) + if (priv->hflip_ctrl->val) val ^= HFLIP_IMG; ret = ov772x_mask_set(client, @@ -1027,18 +1142,18 @@ static int ov772x_set_params(struct ov772x_priv *priv, goto ov772x_set_fmt_error; /* COM4, CLKRC: Set pixel clock and framerate. */ - tpf.numerator = 1; - tpf.denominator = priv->fps; - ret = ov772x_set_frame_rate(priv, &tpf, cfmt, win); + ret = ov772x_set_frame_rate(priv, priv->fps, cfmt, win); if (ret < 0) goto ov772x_set_fmt_error; /* Set COM8. */ - if (priv->band_filter) { + if (priv->band_filter_ctrl->val) { + unsigned short band_filter = priv->band_filter_ctrl->val; + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, - 0xff, 256 - priv->band_filter); + 0xff, 256 - band_filter); if (ret < 0) goto ov772x_set_fmt_error; } @@ -1102,7 +1217,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &format->format; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; - int ret; + int ret = 0; if (format->pad) return -EINVAL; @@ -1123,30 +1238,50 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, return 0; } - ret = ov772x_set_params(priv, cfmt, win); - if (ret < 0) - return ret; + mutex_lock(&priv->lock); + + if (priv->streaming) { + ret = -EBUSY; + goto error; + } + /* + * If the device is not powered up by the host driver do + * not apply any changes to H/W at this time. Instead + * the format will be restored right after power-up. + */ + if (priv->power_count > 0) { + ret = ov772x_set_params(priv, cfmt, win); + if (ret < 0) + goto error; + } priv->win = win; priv->cfmt = cfmt; - return 0; +error: + mutex_unlock(&priv->lock); + + return ret; } static int ov772x_video_probe(struct ov772x_priv *priv) { struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); - u8 pid, ver; + int pid, ver, midh, midl; const char *devname; int ret; - ret = ov772x_s_power(&priv->subdev, 1); + ret = ov772x_power_on(priv); if (ret < 0) return ret; /* Check and show product ID and manufacturer ID. */ pid = ov772x_read(client, PID); + if (pid < 0) + return pid; ver = ov772x_read(client, VER); + if (ver < 0) + return ver; switch (VERSION(pid, ver)) { case OV7720: @@ -1162,17 +1297,21 @@ static int ov772x_video_probe(struct ov772x_priv *priv) goto done; } + midh = ov772x_read(client, MIDH); + if (midh < 0) + return midh; + midl = ov772x_read(client, MIDL); + if (midl < 0) + return midl; + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", - devname, - pid, - ver, - ov772x_read(client, MIDH), - ov772x_read(client, MIDL)); + devname, pid, ver, midh, midl); + ret = v4l2_ctrl_handler_setup(&priv->hdl); done: - ov772x_s_power(&priv->subdev, 0); + ov772x_power_off(priv); return ret; } @@ -1250,48 +1389,54 @@ static int ov772x_probe(struct i2c_client *client, struct i2c_adapter *adapter = client->adapter; int ret; - if (!client->dev.platform_data) { - dev_err(&client->dev, "Missing ov772x platform data\n"); + if (!client->dev.of_node && !client->dev.platform_data) { + dev_err(&client->dev, + "Missing ov772x platform data for non-DT device\n"); return -EINVAL; } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_PROTOCOL_MANGLING)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, - "I2C-Adapter doesn't support SMBUS_BYTE_DATA or PROTOCOL_MANGLING\n"); + "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n"); return -EIO; } - client->flags |= I2C_CLIENT_SCCB; priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->info = client->dev.platform_data; + mutex_init(&priv->lock); v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&priv->hdl, 3); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, - V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); + /* Use our mutex for the controls */ + priv->hdl.lock = &priv->lock; + priv->vflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + priv->hflip_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, + V4L2_CID_BAND_STOP_FILTER, + 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) - return priv->hdl.error; + if (priv->hdl.error) { + ret = priv->hdl.error; + goto error_mutex_destroy; + } - priv->clk = clk_get(&client->dev, "xclk"); + priv->clk = clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { dev_err(&client->dev, "Unable to get xclk clock\n"); ret = PTR_ERR(priv->clk); goto error_ctrl_free; } - priv->pwdn_gpio = gpiod_get_optional(&client->dev, "pwdn", + priv->pwdn_gpio = gpiod_get_optional(&client->dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(priv->pwdn_gpio)) { - dev_info(&client->dev, "Unable to get GPIO \"pwdn\""); + dev_info(&client->dev, "Unable to get GPIO \"powerdown\""); ret = PTR_ERR(priv->pwdn_gpio); goto error_clk_put; } @@ -1300,16 +1445,26 @@ static int ov772x_probe(struct i2c_client *client, if (ret < 0) goto error_gpio_put; +#ifdef CONFIG_MEDIA_CONTROLLER + priv->pad.flags = MEDIA_PAD_FL_SOURCE; + priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); + if (ret < 0) + goto error_gpio_put; +#endif + priv->cfmt = &ov772x_cfmts[0]; priv->win = &ov772x_win_sizes[0]; priv->fps = 15; ret = v4l2_async_register_subdev(&priv->subdev); if (ret) - goto error_gpio_put; + goto error_entity_cleanup; return 0; +error_entity_cleanup: + media_entity_cleanup(&priv->subdev.entity); error_gpio_put: if (priv->pwdn_gpio) gpiod_put(priv->pwdn_gpio); @@ -1317,6 +1472,8 @@ error_clk_put: clk_put(priv->clk); error_ctrl_free: v4l2_ctrl_handler_free(&priv->hdl); +error_mutex_destroy: + mutex_destroy(&priv->lock); return ret; } @@ -1325,11 +1482,13 @@ static int ov772x_remove(struct i2c_client *client) { struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); + media_entity_cleanup(&priv->subdev.entity); clk_put(priv->clk); if (priv->pwdn_gpio) gpiod_put(priv->pwdn_gpio); v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); return 0; } @@ -1340,9 +1499,17 @@ static const struct i2c_device_id ov772x_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov772x_id); +static const struct of_device_id ov772x_of_match[] = { + { .compatible = "ovti,ov7725", }, + { .compatible = "ovti,ov7720", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov772x_of_match); + static struct i2c_driver ov772x_i2c_driver = { .driver = { .name = "ov772x", + .of_match_table = ov772x_of_match, }, .probe = ov772x_probe, .remove = ov772x_remove, diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c new file mode 100644 index 000000000000..6ad998ad1b16 --- /dev/null +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -0,0 +1,1437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp + * + * Copyright (C) 2018, Jacopo Mondi <jacopo@jmondi.org> + * + * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/v4l2-mediabus.h> +#include <linux/videodev2.h> + +#include <media/i2c/rj54n1cb0c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#define RJ54N1_DEV_CODE 0x0400 +#define RJ54N1_DEV_CODE2 0x0401 +#define RJ54N1_OUT_SEL 0x0403 +#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 +#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 +#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 +#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 +#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 +#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 +#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a +#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b +#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c +#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d +#define RJ54N1_RESIZE_N 0x040e +#define RJ54N1_RESIZE_N_STEP 0x040f +#define RJ54N1_RESIZE_STEP 0x0410 +#define RJ54N1_RESIZE_HOLD_H 0x0411 +#define RJ54N1_RESIZE_HOLD_L 0x0412 +#define RJ54N1_H_OBEN_OFS 0x0413 +#define RJ54N1_V_OBEN_OFS 0x0414 +#define RJ54N1_RESIZE_CONTROL 0x0415 +#define RJ54N1_STILL_CONTROL 0x0417 +#define RJ54N1_INC_USE_SEL_H 0x0425 +#define RJ54N1_INC_USE_SEL_L 0x0426 +#define RJ54N1_MIRROR_STILL_MODE 0x0427 +#define RJ54N1_INIT_START 0x0428 +#define RJ54N1_SCALE_1_2_LEV 0x0429 +#define RJ54N1_SCALE_4_LEV 0x042a +#define RJ54N1_Y_GAIN 0x04d8 +#define RJ54N1_APT_GAIN_UP 0x04fa +#define RJ54N1_RA_SEL_UL 0x0530 +#define RJ54N1_BYTE_SWAP 0x0531 +#define RJ54N1_OUT_SIGPO 0x053b +#define RJ54N1_WB_SEL_WEIGHT_I 0x054e +#define RJ54N1_BIT8_WB 0x0569 +#define RJ54N1_HCAPS_WB 0x056a +#define RJ54N1_VCAPS_WB 0x056b +#define RJ54N1_HCAPE_WB 0x056c +#define RJ54N1_VCAPE_WB 0x056d +#define RJ54N1_EXPOSURE_CONTROL 0x058c +#define RJ54N1_FRAME_LENGTH_S_H 0x0595 +#define RJ54N1_FRAME_LENGTH_S_L 0x0596 +#define RJ54N1_FRAME_LENGTH_P_H 0x0597 +#define RJ54N1_FRAME_LENGTH_P_L 0x0598 +#define RJ54N1_PEAK_H 0x05b7 +#define RJ54N1_PEAK_50 0x05b8 +#define RJ54N1_PEAK_60 0x05b9 +#define RJ54N1_PEAK_DIFF 0x05ba +#define RJ54N1_IOC 0x05ef +#define RJ54N1_TG_BYPASS 0x0700 +#define RJ54N1_PLL_L 0x0701 +#define RJ54N1_PLL_N 0x0702 +#define RJ54N1_PLL_EN 0x0704 +#define RJ54N1_RATIO_TG 0x0706 +#define RJ54N1_RATIO_T 0x0707 +#define RJ54N1_RATIO_R 0x0708 +#define RJ54N1_RAMP_TGCLK_EN 0x0709 +#define RJ54N1_OCLK_DSP 0x0710 +#define RJ54N1_RATIO_OP 0x0711 +#define RJ54N1_RATIO_O 0x0712 +#define RJ54N1_OCLK_SEL_EN 0x0713 +#define RJ54N1_CLK_RST 0x0717 +#define RJ54N1_RESET_STANDBY 0x0718 +#define RJ54N1_FWFLG 0x07fe + +#define E_EXCLK (1 << 7) +#define SOFT_STDBY (1 << 4) +#define SEN_RSTX (1 << 2) +#define TG_RSTX (1 << 1) +#define DSP_RSTX (1 << 0) + +#define RESIZE_HOLD_SEL (1 << 2) +#define RESIZE_GO (1 << 1) + +/* + * When cropping, the camera automatically centers the cropped region, there + * doesn't seem to be a way to specify an explicit location of the rectangle. + */ +#define RJ54N1_COLUMN_SKIP 0 +#define RJ54N1_ROW_SKIP 0 +#define RJ54N1_MAX_WIDTH 1600 +#define RJ54N1_MAX_HEIGHT 1200 + +#define PLL_L 2 +#define PLL_N 0x31 + +/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ + +/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ +struct rj54n1_datafmt { + u32 code; + enum v4l2_colorspace colorspace; +}; + +/* Find a data format by a pixel code in an array */ +static const struct rj54n1_datafmt *rj54n1_find_datafmt( + u32 code, const struct rj54n1_datafmt *fmt, + int n) +{ + int i; + for (i = 0; i < n; i++) + if (fmt[i].code == code) + return fmt + i; + + return NULL; +} + +static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { + {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, + {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, + {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, +}; + +struct rj54n1_clock_div { + u8 ratio_tg; /* can be 0 or an odd number */ + u8 ratio_t; + u8 ratio_r; + u8 ratio_op; + u8 ratio_o; +}; + +struct rj54n1 { + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler hdl; + struct clk *clk; + struct gpio_desc *pwup_gpio; + struct gpio_desc *enable_gpio; + struct rj54n1_clock_div clk_div; + const struct rj54n1_datafmt *fmt; + struct v4l2_rect rect; /* Sensor window */ + unsigned int tgclk_mhz; + bool auto_wb; + unsigned short width; /* Output window */ + unsigned short height; + unsigned short resize; /* Sensor * 1024 / resize = Output */ + unsigned short scale; + u8 bank; +}; + +struct rj54n1_reg_val { + u16 reg; + u8 val; +}; + +static const struct rj54n1_reg_val bank_4[] = { + {0x417, 0}, + {0x42c, 0}, + {0x42d, 0xf0}, + {0x42e, 0}, + {0x42f, 0x50}, + {0x430, 0xf5}, + {0x431, 0x16}, + {0x432, 0x20}, + {0x433, 0}, + {0x434, 0xc8}, + {0x43c, 8}, + {0x43e, 0x90}, + {0x445, 0x83}, + {0x4ba, 0x58}, + {0x4bb, 4}, + {0x4bc, 0x20}, + {0x4db, 4}, + {0x4fe, 2}, +}; + +static const struct rj54n1_reg_val bank_5[] = { + {0x514, 0}, + {0x516, 0}, + {0x518, 0}, + {0x51a, 0}, + {0x51d, 0xff}, + {0x56f, 0x28}, + {0x575, 0x40}, + {0x5bc, 0x48}, + {0x5c1, 6}, + {0x5e5, 0x11}, + {0x5e6, 0x43}, + {0x5e7, 0x33}, + {0x5e8, 0x21}, + {0x5e9, 0x30}, + {0x5ea, 0x0}, + {0x5eb, 0xa5}, + {0x5ec, 0xff}, + {0x5fe, 2}, +}; + +static const struct rj54n1_reg_val bank_7[] = { + {0x70a, 0}, + {0x714, 0xff}, + {0x715, 0xff}, + {0x716, 0x1f}, + {0x7FE, 2}, +}; + +static const struct rj54n1_reg_val bank_8[] = { + {0x800, 0x00}, + {0x801, 0x01}, + {0x802, 0x61}, + {0x805, 0x00}, + {0x806, 0x00}, + {0x807, 0x00}, + {0x808, 0x00}, + {0x809, 0x01}, + {0x80A, 0x61}, + {0x80B, 0x00}, + {0x80C, 0x01}, + {0x80D, 0x00}, + {0x80E, 0x00}, + {0x80F, 0x00}, + {0x810, 0x00}, + {0x811, 0x01}, + {0x812, 0x61}, + {0x813, 0x00}, + {0x814, 0x11}, + {0x815, 0x00}, + {0x816, 0x41}, + {0x817, 0x00}, + {0x818, 0x51}, + {0x819, 0x01}, + {0x81A, 0x1F}, + {0x81B, 0x00}, + {0x81C, 0x01}, + {0x81D, 0x00}, + {0x81E, 0x11}, + {0x81F, 0x00}, + {0x820, 0x41}, + {0x821, 0x00}, + {0x822, 0x51}, + {0x823, 0x00}, + {0x824, 0x00}, + {0x825, 0x00}, + {0x826, 0x47}, + {0x827, 0x01}, + {0x828, 0x4F}, + {0x829, 0x00}, + {0x82A, 0x00}, + {0x82B, 0x00}, + {0x82C, 0x30}, + {0x82D, 0x00}, + {0x82E, 0x40}, + {0x82F, 0x00}, + {0x830, 0xB3}, + {0x831, 0x00}, + {0x832, 0xE3}, + {0x833, 0x00}, + {0x834, 0x00}, + {0x835, 0x00}, + {0x836, 0x00}, + {0x837, 0x00}, + {0x838, 0x00}, + {0x839, 0x01}, + {0x83A, 0x61}, + {0x83B, 0x00}, + {0x83C, 0x01}, + {0x83D, 0x00}, + {0x83E, 0x00}, + {0x83F, 0x00}, + {0x840, 0x00}, + {0x841, 0x01}, + {0x842, 0x61}, + {0x843, 0x00}, + {0x844, 0x1D}, + {0x845, 0x00}, + {0x846, 0x00}, + {0x847, 0x00}, + {0x848, 0x00}, + {0x849, 0x01}, + {0x84A, 0x1F}, + {0x84B, 0x00}, + {0x84C, 0x05}, + {0x84D, 0x00}, + {0x84E, 0x19}, + {0x84F, 0x01}, + {0x850, 0x21}, + {0x851, 0x01}, + {0x852, 0x5D}, + {0x853, 0x00}, + {0x854, 0x00}, + {0x855, 0x00}, + {0x856, 0x19}, + {0x857, 0x01}, + {0x858, 0x21}, + {0x859, 0x00}, + {0x85A, 0x00}, + {0x85B, 0x00}, + {0x85C, 0x00}, + {0x85D, 0x00}, + {0x85E, 0x00}, + {0x85F, 0x00}, + {0x860, 0xB3}, + {0x861, 0x00}, + {0x862, 0xE3}, + {0x863, 0x00}, + {0x864, 0x00}, + {0x865, 0x00}, + {0x866, 0x00}, + {0x867, 0x00}, + {0x868, 0x00}, + {0x869, 0xE2}, + {0x86A, 0x00}, + {0x86B, 0x01}, + {0x86C, 0x06}, + {0x86D, 0x00}, + {0x86E, 0x00}, + {0x86F, 0x00}, + {0x870, 0x60}, + {0x871, 0x8C}, + {0x872, 0x10}, + {0x873, 0x00}, + {0x874, 0xE0}, + {0x875, 0x00}, + {0x876, 0x27}, + {0x877, 0x01}, + {0x878, 0x00}, + {0x879, 0x00}, + {0x87A, 0x00}, + {0x87B, 0x03}, + {0x87C, 0x00}, + {0x87D, 0x00}, + {0x87E, 0x00}, + {0x87F, 0x00}, + {0x880, 0x00}, + {0x881, 0x00}, + {0x882, 0x00}, + {0x883, 0x00}, + {0x884, 0x00}, + {0x885, 0x00}, + {0x886, 0xF8}, + {0x887, 0x00}, + {0x888, 0x03}, + {0x889, 0x00}, + {0x88A, 0x64}, + {0x88B, 0x00}, + {0x88C, 0x03}, + {0x88D, 0x00}, + {0x88E, 0xB1}, + {0x88F, 0x00}, + {0x890, 0x03}, + {0x891, 0x01}, + {0x892, 0x1D}, + {0x893, 0x00}, + {0x894, 0x03}, + {0x895, 0x01}, + {0x896, 0x4B}, + {0x897, 0x00}, + {0x898, 0xE5}, + {0x899, 0x00}, + {0x89A, 0x01}, + {0x89B, 0x00}, + {0x89C, 0x01}, + {0x89D, 0x04}, + {0x89E, 0xC8}, + {0x89F, 0x00}, + {0x8A0, 0x01}, + {0x8A1, 0x01}, + {0x8A2, 0x61}, + {0x8A3, 0x00}, + {0x8A4, 0x01}, + {0x8A5, 0x00}, + {0x8A6, 0x00}, + {0x8A7, 0x00}, + {0x8A8, 0x00}, + {0x8A9, 0x00}, + {0x8AA, 0x7F}, + {0x8AB, 0x03}, + {0x8AC, 0x00}, + {0x8AD, 0x00}, + {0x8AE, 0x00}, + {0x8AF, 0x00}, + {0x8B0, 0x00}, + {0x8B1, 0x00}, + {0x8B6, 0x00}, + {0x8B7, 0x01}, + {0x8B8, 0x00}, + {0x8B9, 0x00}, + {0x8BA, 0x02}, + {0x8BB, 0x00}, + {0x8BC, 0xFF}, + {0x8BD, 0x00}, + {0x8FE, 2}, +}; + +static const struct rj54n1_reg_val bank_10[] = { + {0x10bf, 0x69} +}; + +/* Clock dividers - these are default register values, divider = register + 1 */ +static const struct rj54n1_clock_div clk_div = { + .ratio_tg = 3 /* default: 5 */, + .ratio_t = 4 /* default: 1 */, + .ratio_r = 4 /* default: 0 */, + .ratio_op = 1 /* default: 5 */, + .ratio_o = 9 /* default: 0 */, +}; + +static struct rj54n1 *to_rj54n1(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); +} + +static int reg_read(struct i2c_client *client, const u16 reg) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + return i2c_smbus_read_byte_data(client, reg & 0xff); +} + +static int reg_write(struct i2c_client *client, const u16 reg, + const u8 data) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); + return i2c_smbus_write_byte_data(client, reg & 0xff, data); +} + +static int reg_set(struct i2c_client *client, const u16 reg, + const u8 data, const u8 mask) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static int reg_write_multiple(struct i2c_client *client, + const struct rj54n1_reg_val *rv, const int n) +{ + int i, ret; + + for (i = 0; i < n; i++) { + ret = reg_write(client, rv->reg, rv->val); + if (ret < 0) + return ret; + rv++; + } + + return 0; +} + +static int rj54n1_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(rj54n1_colour_fmts)) + return -EINVAL; + + code->code = rj54n1_colour_fmts[code->index].code; + return 0; +} + +static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* Switch between preview and still shot modes */ + return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); +} + +static int rj54n1_set_rect(struct i2c_client *client, + u16 reg_x, u16 reg_y, u16 reg_xy, + u32 width, u32 height) +{ + int ret; + + ret = reg_write(client, reg_xy, + ((width >> 4) & 0x70) | + ((height >> 8) & 7)); + + if (!ret) + ret = reg_write(client, reg_x, width & 0xff); + if (!ret) + ret = reg_write(client, reg_y, height & 0xff); + + return ret; +} + +/* + * Some commands, specifically certain initialisation sequences, require + * a commit operation. + */ +static int rj54n1_commit(struct i2c_client *client) +{ + int ret = reg_write(client, RJ54N1_INIT_START, 1); + msleep(10); + if (!ret) + ret = reg_write(client, RJ54N1_INIT_START, 0); + return ret; +} + +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h); + +static int rj54n1_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + const struct v4l2_rect *rect = &sel->r; + int output_w, output_h, input_w = rect->width, input_h = rect->height; + int ret; + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + /* arbitrary minimum width and height, edges unimportant */ + v4l_bound_align_image(&input_w, 8, RJ54N1_MAX_WIDTH, 0, + &input_h, 8, RJ54N1_MAX_HEIGHT, 0, 0); + + output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; + output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; + + dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", + input_w, input_h, rj54n1->resize, output_w, output_h); + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + rj54n1->width = output_w; + rj54n1->height = output_h; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + + return 0; +} + +static int rj54n1_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = RJ54N1_COLUMN_SKIP; + sel->r.top = RJ54N1_ROW_SKIP; + sel->r.width = RJ54N1_MAX_WIDTH; + sel->r.height = RJ54N1_MAX_HEIGHT; + return 0; + case V4L2_SEL_TGT_CROP: + sel->r = rj54n1->rect; + return 0; + default: + return -EINVAL; + } +} + +static int rj54n1_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (format->pad) + return -EINVAL; + + mf->code = rj54n1->fmt->code; + mf->colorspace = rj54n1->fmt->colorspace; + mf->ycbcr_enc = V4L2_YCBCR_ENC_601; + mf->xfer_func = V4L2_XFER_FUNC_SRGB; + mf->quantization = V4L2_QUANTIZATION_DEFAULT; + mf->field = V4L2_FIELD_NONE; + mf->width = rj54n1->width; + mf->height = rj54n1->height; + + return 0; +} + +/* + * The actual geometry configuration routine. It scales the input window into + * the output one, updates the window sizes and returns an error or the resize + * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. + */ +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, + s32 *out_w, s32 *out_h) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + unsigned int skip, resize, input_w = *in_w, input_h = *in_h, + output_w = *out_w, output_h = *out_h; + u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; + unsigned int peak, peak_50, peak_60; + int ret; + + /* + * We have a problem with crops, where the window is larger than 512x384 + * and output window is larger than a half of the input one. In this + * case we have to either reduce the input window to equal or below + * 512x384 or the output window to equal or below 1/2 of the input. + */ + if (output_w > max(512U, input_w / 2)) { + if (2 * output_w > RJ54N1_MAX_WIDTH) { + input_w = RJ54N1_MAX_WIDTH; + output_w = RJ54N1_MAX_WIDTH / 2; + } else { + input_w = output_w * 2; + } + + dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", + input_w, output_w); + } + + if (output_h > max(384U, input_h / 2)) { + if (2 * output_h > RJ54N1_MAX_HEIGHT) { + input_h = RJ54N1_MAX_HEIGHT; + output_h = RJ54N1_MAX_HEIGHT / 2; + } else { + input_h = output_h * 2; + } + + dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", + input_h, output_h); + } + + /* Idea: use the read mode for snapshots, handle separate geometries */ + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, + RJ54N1_Y_OUTPUT_SIZE_S_L, + RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); + if (!ret) + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, + RJ54N1_Y_OUTPUT_SIZE_P_L, + RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); + + if (ret < 0) + return ret; + + if (output_w > input_w && output_h > input_h) { + input_w = output_w; + input_h = output_h; + + resize = 1024; + } else { + unsigned int resize_x, resize_y; + resize_x = (input_w * 1024 + output_w / 2) / output_w; + resize_y = (input_h * 1024 + output_h / 2) / output_h; + + /* We want max(resize_x, resize_y), check if it still fits */ + if (resize_x > resize_y && + (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) + resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / + output_h; + else if (resize_y > resize_x && + (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) + resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / + output_w; + else + resize = max(resize_x, resize_y); + + /* Prohibited value ranges */ + switch (resize) { + case 2040 ... 2047: + resize = 2039; + break; + case 4080 ... 4095: + resize = 4079; + break; + case 8160 ... 8191: + resize = 8159; + break; + case 16320 ... 16384: + resize = 16319; + } + } + + /* Set scaling */ + ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); + + if (ret < 0) + return ret; + + /* + * Configure a skipping bitmask. The sensor will select a skipping value + * among set bits automatically. This is very unclear in the datasheet + * too. I was told, in this register one enables all skipping values, + * that are required for a specific resize, and the camera selects + * automatically, which ones to use. But it is unclear how to identify, + * which cropping values are needed. Secondly, why don't we just set all + * bits and let the camera choose? Would it increase processing time and + * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to + * improve the image quality or stability for larger frames (see comment + * above), but I didn't check the framerate. + */ + skip = min(resize / 1024, 15U); + + inc_sel = 1 << skip; + + if (inc_sel <= 2) + inc_sel = 0xc; + else if (resize & 1023 && skip < 15) + inc_sel |= 1 << (skip + 1); + + ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); + if (!ret) + ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); + + if (!rj54n1->auto_wb) { + /* Auto white balance window */ + wb_left = output_w / 16; + wb_right = (3 * output_w / 4 - 3) / 4; + wb_top = output_h / 16; + wb_bottom = (3 * output_h / 4 - 3) / 4; + wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | + ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); + + if (!ret) + ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); + if (!ret) + ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); + if (!ret) + ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); + if (!ret) + ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); + if (!ret) + ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); + } + + /* Antiflicker */ + peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / + 10000; + peak_50 = peak / 6; + peak_60 = peak / 5; + + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_H, + ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_50, peak_50); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_60, peak_60); + if (!ret) + ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); + + /* Start resizing */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | RESIZE_GO | 1); + + if (ret < 0) + return ret; + + /* Constant taken from manufacturer's example */ + msleep(230); + + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); + if (ret < 0) + return ret; + + *in_w = (output_w * resize + 512) / 1024; + *in_h = (output_h * resize + 512) / 1024; + *out_w = output_w; + *out_h = output_h; + + dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", + *in_w, *in_h, resize, output_w, output_h, skip); + + return resize; +} + +static int rj54n1_set_clock(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* Enable external clock */ + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); + /* Leave stand-by. Note: use this when implementing suspend / resume */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); + + if (!ret) + ret = reg_write(client, RJ54N1_PLL_L, PLL_L); + if (!ret) + ret = reg_write(client, RJ54N1_PLL_N, PLL_N); + + /* TGCLK dividers */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_TG, + rj54n1->clk_div.ratio_tg); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_T, + rj54n1->clk_div.ratio_t); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_R, + rj54n1->clk_div.ratio_r); + + /* Enable TGCLK & RAMP */ + if (!ret) + ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); + + /* Disable clock output */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_DSP, 0); + + /* Set divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_OP, + rj54n1->clk_div.ratio_op); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_O, + rj54n1->clk_div.ratio_o); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + /* Use PLL for Timing Generator, write 2 to reserved bits */ + if (!ret) + ret = reg_write(client, RJ54N1_TG_BYPASS, 2); + + /* Take sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | SEN_RSTX); + /* Enable PLL */ + if (!ret) + ret = reg_write(client, RJ54N1_PLL_EN, 1); + + /* Wait for PLL to stabilise */ + msleep(10); + + /* Enable clock to frequency divider */ + if (!ret) + ret = reg_write(client, RJ54N1_CLK_RST, 1); + + if (!ret) + ret = reg_read(client, RJ54N1_CLK_RST); + if (ret != 1) { + dev_err(&client->dev, + "Resetting RJ54N1CB0C clock failed: %d!\n", ret); + return -EIO; + } + + /* Start the PLL */ + ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + return ret; +} + +static int rj54n1_reg_init(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret = rj54n1_set_clock(client); + + if (!ret) + ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); + if (!ret) + ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); + + /* Set binning divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); + + /* Switch to fixed resize mode */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | 1); + + /* Set gain */ + if (!ret) + ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); + + /* + * Mirror the image back: default is upside down and left-to-right... + * Set manual preview / still shot switching + */ + if (!ret) + ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); + + if (!ret) + ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); + + /* Auto exposure area */ + if (!ret) + ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); + /* Check current auto WB config */ + if (!ret) + ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); + if (ret >= 0) { + rj54n1->auto_wb = ret & 0x80; + ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); + } + if (!ret) + ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); + + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | SEN_RSTX); + + /* Commit init */ + if (!ret) + ret = rj54n1_commit(client); + + /* Take DSP, TG, sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); + + /* Start register update? Same register as 0x?FE in many bank_* sets */ + if (!ret) + ret = reg_write(client, RJ54N1_FWFLG, 2); + + /* Constant taken from manufacturer's example */ + msleep(700); + + return ret; +} + +static int rj54n1_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *mf = &format->format; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + const struct rj54n1_datafmt *fmt; + int output_w, output_h, max_w, max_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; + int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE || + mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE; + int ret; + + if (format->pad) + return -EINVAL; + + dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", + __func__, mf->code, mf->width, mf->height); + + fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, + ARRAY_SIZE(rj54n1_colour_fmts)); + if (!fmt) { + fmt = rj54n1->fmt; + mf->code = fmt->code; + } + + mf->field = V4L2_FIELD_NONE; + mf->colorspace = fmt->colorspace; + + v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, + &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + /* + * Verify if the sensor has just been powered on. TODO: replace this + * with proper PM, when a suitable API is available. + */ + ret = reg_read(client, RJ54N1_RESET_STANDBY); + if (ret < 0) + return ret; + + if (!(ret & E_EXCLK)) { + ret = rj54n1_reg_init(client); + if (ret < 0) + return ret; + } + + /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ + switch (mf->code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + break; + case MEDIA_BUS_FMT_RGB565_2X8_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case MEDIA_BUS_FMT_RGB565_2X8_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); + break; + case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: + ret = reg_write(client, RJ54N1_OUT_SEL, 4); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); + if (!ret) + ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + ret = reg_write(client, RJ54N1_OUT_SEL, 5); + break; + default: + ret = -EINVAL; + } + + /* Special case: a raw mode with 10 bits of data per clock tick */ + if (!ret) + ret = reg_set(client, RJ54N1_OCLK_SEL_EN, + (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2); + + if (ret < 0) + return ret; + + /* Supported scales 1:1 >= scale > 1:16 */ + max_w = mf->width * (16 * 1024 - 1) / 1024; + if (input_w > max_w) + input_w = max_w; + max_h = mf->height * (16 * 1024 - 1) / 1024; + if (input_h > max_h) + input_h = max_h; + + output_w = mf->width; + output_h = mf->height; + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, + ARRAY_SIZE(rj54n1_colour_fmts)); + + rj54n1->fmt = fmt; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + rj54n1->width = output_w; + rj54n1->height = output_h; + + mf->width = output_w; + mf->height = output_h; + mf->field = V4L2_FIELD_NONE; + mf->colorspace = fmt->colorspace; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int rj54n1_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers > 0x0800 are only available from Sharp support */ + return -EINVAL; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xff) + return -EIO; + + return 0; +} + +static int rj54n1_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers >= 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static int rj54n1_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (on) { + if (rj54n1->pwup_gpio) + gpiod_set_value(rj54n1->pwup_gpio, 1); + if (rj54n1->enable_gpio) + gpiod_set_value(rj54n1->enable_gpio, 1); + + msleep(1); + + return clk_prepare_enable(rj54n1->clk); + } + + clk_disable_unprepare(rj54n1->clk); + + if (rj54n1->enable_gpio) + gpiod_set_value(rj54n1->enable_gpio, 0); + if (rj54n1->pwup_gpio) + gpiod_set_value(rj54n1->pwup_gpio, 0); + + return 0; +} + +static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); + struct v4l2_subdev *sd = &rj54n1->subdev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->val) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_HFLIP: + if (ctrl->val) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); + if (data < 0) + return -EIO; + return 0; + case V4L2_CID_GAIN: + if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) + return -EIO; + return 0; + case V4L2_CID_AUTO_WHITE_BALANCE: + /* Auto WB area - whole image */ + if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, + 0x80) < 0) + return -EIO; + rj54n1->auto_wb = ctrl->val; + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { + .s_ctrl = rj54n1_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = rj54n1_g_register, + .s_register = rj54n1_s_register, +#endif + .s_power = rj54n1_s_power, +}; + +static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { + .s_stream = rj54n1_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { + .enum_mbus_code = rj54n1_enum_mbus_code, + .get_selection = rj54n1_get_selection, + .set_selection = rj54n1_set_selection, + .get_fmt = rj54n1_get_fmt, + .set_fmt = rj54n1_set_fmt, +}; + +static const struct v4l2_subdev_ops rj54n1_subdev_ops = { + .core = &rj54n1_subdev_core_ops, + .video = &rj54n1_subdev_video_ops, + .pad = &rj54n1_subdev_pad_ops, +}; + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int rj54n1_video_probe(struct i2c_client *client, + struct rj54n1_pdata *priv) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int data1, data2; + int ret; + + ret = rj54n1_s_power(&rj54n1->subdev, 1); + if (ret < 0) + return ret; + + /* Read out the chip version register */ + data1 = reg_read(client, RJ54N1_DEV_CODE); + data2 = reg_read(client, RJ54N1_DEV_CODE2); + + if (data1 != 0x51 || data2 != 0x10) { + ret = -ENODEV; + dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", + data1, data2); + goto done; + } + + /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ + ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); + if (ret < 0) + goto done; + + dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", + data1, data2); + + ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); + +done: + rj54n1_s_power(&rj54n1->subdev, 0); + return ret; +} + +static int rj54n1_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct rj54n1 *rj54n1; + struct i2c_adapter *adapter = client->adapter; + struct rj54n1_pdata *rj54n1_priv; + int ret; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); + return -EINVAL; + } + + rj54n1_priv = client->dev.platform_data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); + if (!rj54n1) + return -ENOMEM; + + v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + v4l2_ctrl_handler_init(&rj54n1->hdl, 4); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_GAIN, 0, 127, 1, 66); + v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + rj54n1->subdev.ctrl_handler = &rj54n1->hdl; + if (rj54n1->hdl.error) + return rj54n1->hdl.error; + + rj54n1->clk_div = clk_div; + rj54n1->rect.left = RJ54N1_COLUMN_SKIP; + rj54n1->rect.top = RJ54N1_ROW_SKIP; + rj54n1->rect.width = RJ54N1_MAX_WIDTH; + rj54n1->rect.height = RJ54N1_MAX_HEIGHT; + rj54n1->width = RJ54N1_MAX_WIDTH; + rj54n1->height = RJ54N1_MAX_HEIGHT; + rj54n1->fmt = &rj54n1_colour_fmts[0]; + rj54n1->resize = 1024; + rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / + (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); + + rj54n1->clk = clk_get(&client->dev, NULL); + if (IS_ERR(rj54n1->clk)) { + ret = PTR_ERR(rj54n1->clk); + goto err_free_ctrl; + } + + rj54n1->pwup_gpio = gpiod_get_optional(&client->dev, "powerup", + GPIOD_OUT_LOW); + if (IS_ERR(rj54n1->pwup_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"powerup\": %ld\n", + PTR_ERR(rj54n1->pwup_gpio)); + ret = PTR_ERR(rj54n1->pwup_gpio); + goto err_clk_put; + } + + rj54n1->enable_gpio = gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(rj54n1->enable_gpio)) { + dev_info(&client->dev, "Unable to get GPIO \"enable\": %ld\n", + PTR_ERR(rj54n1->enable_gpio)); + ret = PTR_ERR(rj54n1->enable_gpio); + goto err_gpio_put; + } + + ret = rj54n1_video_probe(client, rj54n1_priv); + if (ret < 0) + goto err_gpio_put; + + ret = v4l2_async_register_subdev(&rj54n1->subdev); + if (ret) + goto err_gpio_put; + + return 0; + +err_gpio_put: + if (rj54n1->enable_gpio) + gpiod_put(rj54n1->enable_gpio); + + if (rj54n1->pwup_gpio) + gpiod_put(rj54n1->pwup_gpio); + +err_clk_put: + clk_put(rj54n1->clk); + +err_free_ctrl: + v4l2_ctrl_handler_free(&rj54n1->hdl); + + return ret; +} + +static int rj54n1_remove(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + + if (rj54n1->enable_gpio) + gpiod_put(rj54n1->enable_gpio); + if (rj54n1->pwup_gpio) + gpiod_put(rj54n1->pwup_gpio); + + clk_put(rj54n1->clk); + v4l2_ctrl_handler_free(&rj54n1->hdl); + v4l2_async_unregister_subdev(&rj54n1->subdev); + + return 0; +} + +static const struct i2c_device_id rj54n1_id[] = { + { "rj54n1cb0c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rj54n1_id); + +static struct i2c_driver rj54n1_i2c_driver = { + .driver = { + .name = "rj54n1cb0c", + }, + .probe = rj54n1_probe, + .remove = rj54n1_remove, + .id_table = rj54n1_id, +}; + +module_i2c_driver(rj54n1_i2c_driver); + +MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index e1f8208581aa..1236683da8f7 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1892,7 +1892,7 @@ static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, val -= SCALING_GOODNESS_EXTREME; dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", - w, ask_h, h, ask_h, val); + w, ask_w, h, ask_h, val); return val; } @@ -2764,6 +2764,7 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) struct v4l2_fwnode_endpoint *bus_cfg; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); + u32 rotation; int i; int rval; @@ -2800,6 +2801,21 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) dev_dbg(dev, "lanes %u\n", hwcfg->lanes); + rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); + if (!rval) { + switch (rotation) { + case 180: + hwcfg->module_board_orient = + SMIAPP_MODULE_BOARD_ORIENT_180; + /* Fall through */ + case 0: + break; + default: + dev_err(dev, "invalid rotation %u\n", rotation); + goto out_err; + } + } + /* NVM size is not mandatory */ fwnode_property_read_u32(fwnode, "nokia,nvm-size", &hwcfg->nvm_size); @@ -3171,4 +3187,4 @@ module_i2c_driver(smiapp_i2c_driver); MODULE_AUTHOR("Sakari Ailus <sakari.ailus@iki.fi>"); MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 806383500313..14377af7c888 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -834,7 +834,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, * set COM8 */ if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, 0xff, 256 - priv->band_filter); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 393bbbbbaad7..44c41933415a 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1918,7 +1918,8 @@ static int tc358743_probe_of(struct tc358743_state *state) endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); if (IS_ERR(endpoint)) { dev_err(dev, "failed to parse endpoint\n"); - return PTR_ERR(endpoint); + ret = PTR_ERR(endpoint); + goto put_node; } if (endpoint->bus_type != V4L2_MBUS_CSI2 || @@ -2013,6 +2014,8 @@ disable_clk: clk_disable_unprepare(refclk); free_endpoint: v4l2_fwnode_endpoint_free(endpoint); +put_node: + of_node_put(ep); return ret; } #else diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 039a92c3294a..d114ac5243ec 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2570,7 +2570,7 @@ static int tda1997x_probe(struct i2c_client *client, id->name, i2c_adapter_id(client->adapter), client->addr); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - sd->entity.function = MEDIA_ENT_F_DTV_DECODER; + sd->entity.function = MEDIA_ENT_F_DV_DECODER; sd->entity.ops = &tda1997x_media_ops; /* set allowed mbus modes based on chip, bus-type, and bus-width */ diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 6a9890531d01..675b9ae212ab 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -1084,7 +1084,7 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) decoder->pad.flags = MEDIA_PAD_FL_SOURCE; decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - decoder->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; + decoder->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&decoder->sd.entity, 1, &decoder->pad); if (ret < 0) { diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index b162c2fe62c3..76e6bed5a1da 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -872,7 +872,7 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, f = &format->format; f->width = decoder->rect.width; - f->height = decoder->rect.height; + f->height = decoder->rect.height / 2; f->code = MEDIA_BUS_FMT_UYVY8_2X8; f->field = V4L2_FIELD_ALTERNATE; diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 4599b7e28a8d..4f5c627579c7 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1010,7 +1010,7 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) #if defined(CONFIG_MEDIA_CONTROLLER) device->pad.flags = MEDIA_PAD_FL_SOURCE; device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - device->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER; + device->sd.entity.function = MEDIA_ENT_F_ATV_DECODER; error = media_entity_pads_init(&device->sd.entity, 1, &device->pad); if (error < 0) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 0b347cc19aa5..06d29d8f6be8 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/freezer.h> +#include <linux/hwmon.h> #include <linux/kthread.h> #include <linux/i2c.h> #include <linux/list.h> @@ -77,6 +78,9 @@ struct video_i2c_chip { /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); + + /* hwmon init function */ + int (*hwmon_init)(struct video_i2c_data *data); }; static int amg88xx_xfer(struct video_i2c_data *data, char *buf) @@ -101,6 +105,74 @@ static int amg88xx_xfer(struct video_i2c_data *data, char *buf) return (ret == 2) ? 0 : -EIO; } +#if IS_ENABLED(CONFIG_HWMON) + +static const u32 amg88xx_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info amg88xx_temp = { + .type = hwmon_temp, + .config = amg88xx_temp_config, +}; + +static const struct hwmon_channel_info *amg88xx_info[] = { + &amg88xx_temp, + NULL +}; + +static umode_t amg88xx_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct video_i2c_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int tmp = i2c_smbus_read_word_data(client, 0x0e); + + if (tmp < 0) + return tmp; + + /* + * Check for sign bit, this isn't a two's complement value but an + * absolute temperature that needs to be inverted in the case of being + * negative. + */ + if (tmp & BIT(11)) + tmp = -(tmp & 0x7ff); + + *val = (tmp * 625) / 10; + + return 0; +} + +static const struct hwmon_ops amg88xx_hwmon_ops = { + .is_visible = amg88xx_is_visible, + .read = amg88xx_read, +}; + +static const struct hwmon_chip_info amg88xx_chip_info = { + .ops = &amg88xx_hwmon_ops, + .info = amg88xx_info, +}; + +static int amg88xx_hwmon_init(struct video_i2c_data *data) +{ + void *hwmon = devm_hwmon_device_register_with_info(&data->client->dev, + "amg88xx", data, &amg88xx_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon); +} +#else +#define amg88xx_hwmon_init NULL +#endif + #define AMG88XX 0 static const struct video_i2c_chip video_i2c_chip[] = { @@ -111,6 +183,7 @@ static const struct video_i2c_chip video_i2c_chip[] = { .buffer_size = 128, .bpp = 16, .xfer = &amg88xx_xfer, + .hwmon_init = amg88xx_hwmon_init, }, }; @@ -505,6 +578,14 @@ static int video_i2c_probe(struct i2c_client *client, video_set_drvdata(&data->vdev, data); i2c_set_clientdata(client, data); + if (data->chip->hwmon_init) { + ret = data->chip->hwmon_init(data); + if (ret < 0) { + dev_warn(&client->dev, + "failed to register hwmon device\n"); + } + } + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) goto error_unregister_device; diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index 1658816a9844..bc9825f4a73d 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -770,7 +770,7 @@ static int vs6624_probe(struct i2c_client *client, return ret; } /* wait 100ms before any further i2c writes are performed */ - mdelay(100); + msleep(100); sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (sensor == NULL) @@ -782,7 +782,7 @@ static int vs6624_probe(struct i2c_client *client, vs6624_writeregs(sd, vs6624_p1); vs6624_write(sd, VS6624_MICRO_EN, 0x2); vs6624_write(sd, VS6624_DIO_EN, 0x1); - mdelay(10); + usleep_range(10000, 11000); vs6624_writeregs(sd, vs6624_p2); vs6624_writeregs(sd, vs6624_default); diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index ae59c3177555..fcdf3d5dc4b6 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -16,9 +16,6 @@ * GNU General Public License for more details. */ -/* We need to access legacy defines from linux/media.h */ -#define __NEED_MEDIA_LEGACY_API - #include <linux/compat.h> #include <linux/export.h> #include <linux/idr.h> @@ -28,6 +25,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/usb.h> +#include <linux/version.h> #include <media/media-device.h> #include <media/media-devnode.h> @@ -35,6 +33,16 @@ #ifdef CONFIG_MEDIA_CONTROLLER +/* + * Legacy defines from linux/media.h. This is the only place we need this + * so we just define it here. The media.h header doesn't expose it to the + * kernel to prevent it from being used by drivers, but here (and only here!) + * we need it to handle the legacy behavior. + */ +#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff +#define MEDIA_ENT_T_DEVNODE_UNKNOWN (MEDIA_ENT_F_OLD_BASE | \ + MEDIA_ENT_SUBTYPE_MASK) + /* ----------------------------------------------------------------------------- * Userspace API */ @@ -259,6 +267,7 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) memset(&kentity, 0, sizeof(kentity)); kentity.id = entity->graph_obj.id; kentity.function = entity->function; + kentity.flags = entity->flags; strlcpy(kentity.name, entity->name, sizeof(kentity.name)); @@ -324,6 +333,7 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) kpad.id = pad->graph_obj.id; kpad.entity_id = pad->entity->graph_obj.id; kpad.flags = pad->flags; + kpad.index = pad->index; if (copy_to_user(upad, &kpad, sizeof(kpad))) ret = -EFAULT; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index de3f44b8dec6..cf05e11da01b 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3511,7 +3511,7 @@ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) } pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n", btv->c.nr); - pr_notice("%d: Lets try to catch the culpit red-handed ...\n", + pr_notice("%d: Lets try to catch the culprit red-handed ...\n", btv->c.nr); dump_stack(); } diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c index 2e33b7236672..b98de2a22f78 100644 --- a/drivers/media/pci/bt8xx/dst.c +++ b/drivers/media/pci/bt8xx/dst.c @@ -1739,9 +1739,9 @@ static const struct dvb_frontend_ops dst_dvbt_ops = { .delsys = { SYS_DVBT }, .info = { .name = "DST DVB-T", - .frequency_min = 137000000, - .frequency_max = 858000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 137 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_16 | @@ -1768,10 +1768,10 @@ static const struct dvb_frontend_ops dst_dvbs_ops = { .delsys = { SYS_DVBS }, .info = { .name = "DST DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 29500, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, + .frequency_tolerance_hz = 29500 * kHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, /* . symbol_rate_tolerance = ???,*/ @@ -1797,9 +1797,9 @@ static const struct dvb_frontend_ops dst_dvbc_ops = { .delsys = { SYS_DVBC_ANNEX_A }, .info = { .name = "DST DVB-C", - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_FEC_AUTO | @@ -1826,9 +1826,9 @@ static const struct dvb_frontend_ops dst_atsc_ops = { .delsys = { SYS_ATSC }, .info = { .name = "DST ATSC", - .frequency_stepsize = 62500, - .frequency_min = 510000000, - .frequency_max = 858000000, + .frequency_min_hz = 510 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 5ef6e2051d45..2f810b7130e6 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -386,10 +386,6 @@ static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, bs = 0x02; else if (c->frequency < 470000000) bs = 0x02; - else if (c->frequency < 600000000) - bs = 0x08; - else if (c->frequency < 730000000) - bs = 0x08; else bs = 0x08; @@ -606,8 +602,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; + card->fe->ops.info.frequency_min_hz = 174 * MHz; + card->fe->ops.info.frequency_max_hz = 862 * MHz; } break; @@ -659,8 +655,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); if (card->fe != NULL) { card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; - card->fe->ops.info.frequency_min = 174000000; - card->fe->ops.info.frequency_max = 862000000; + card->fe->ops.info.frequency_min_hz = 174 * MHz; + card->fe->ops.info.frequency_max_hz = 862 * MHz; } break; diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index c8b1a6206c65..4885e833c052 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -670,7 +670,7 @@ static int cobalt_probe(struct pci_dev *pci_dev, /* FIXME - module parameter arrays constrain max instances */ i = atomic_inc_return(&cobalt_instance) - 1; - cobalt = kzalloc(sizeof(struct cobalt), GFP_ATOMIC); + cobalt = kzalloc(sizeof(struct cobalt), GFP_KERNEL); if (cobalt == NULL) return -ENOMEM; cobalt->pci_dev = pci_dev; diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 8f314ca320c7..0c389a3fb4e5 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -1134,8 +1134,6 @@ free_mem: free_workqueues: destroy_workqueue(cx->in_work_queue); err: - if (retval == 0) - retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); v4l2_device_unregister(&cx->v4l2_dev); diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c index 70aec9bb7e95..62bc8049b320 100644 --- a/drivers/media/pci/cx23885/altera-ci.c +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -346,7 +346,7 @@ static int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) mutex_unlock(&inter->fpga_mutex); for (;;) { - mdelay(50); + msleep(50); mutex_lock(&inter->fpga_mutex); diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 9f50748fdf56..ed3210dc50bc 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -1497,20 +1497,20 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the demod into reset and protect the eeprom */ mc417_gpio_clear(dev, GPIO_15 | GPIO_14); - mdelay(100); + msleep(100); /* Bring the demod and blaster out of reset */ mc417_gpio_set(dev, GPIO_15 | GPIO_14); - mdelay(100); + msleep(100); /* Force the TDA8295A into reset and back */ cx23885_gpio_enable(dev, GPIO_2, 1); cx23885_gpio_set(dev, GPIO_2); - mdelay(20); + msleep(20); cx23885_gpio_clear(dev, GPIO_2); - mdelay(20); + msleep(20); cx23885_gpio_set(dev, GPIO_2); - mdelay(20); + msleep(20); break; case CX23885_BOARD_HAUPPAUGE_HVR1200: /* GPIO-0 tda10048 demodulator reset */ @@ -1518,9 +1518,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_HAUPPAUGE_HVR1700: @@ -1539,9 +1539,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_HAUPPAUGE_HVR1400: @@ -1551,9 +1551,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00050000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000005); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00050005); break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: @@ -1564,9 +1564,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x000f0000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x0000000f); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -1578,9 +1578,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x000f0000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x0000000f); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: @@ -1596,9 +1596,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the parts into reset and back */ cx_set(GP0_IO, 0x00040000); - mdelay(20); + msleep(20); cx_clear(GP0_IO, 0x00000004); - mdelay(20); + msleep(20); cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: @@ -1608,11 +1608,11 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); cx_set(MC417_RWD, 0x00000002); - mdelay(200); + msleep(200); cx_clear(MC417_RWD, 0x00000800); - mdelay(200); + msleep(200); cx_set(MC417_RWD, 0x00000800); - mdelay(200); + msleep(200); break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: /* GPIO-0 INTA from CiMax1 @@ -1630,7 +1630,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040000); /* GPIO as out */ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */ cx_clear(GP0_IO, 0x00030004); - mdelay(100);/* reset delay */ + msleep(100);/* reset delay */ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ /* GPIO-15 IN as ~ACK, rest as OUT */ @@ -1653,7 +1653,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1); cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5); cx23885_gpio_clear(dev, GPIO_9); - mdelay(20); + msleep(20); cx23885_gpio_set(dev, GPIO_9); break; case CX23885_BOARD_MYGICA_X8506: @@ -1664,18 +1664,18 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* GPIO-2 demod reset */ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); - mdelay(100); + msleep(100); break; case CX23885_BOARD_MYGICA_X8558PRO: /* GPIO-0 reset first ATBM8830 */ /* GPIO-1 reset second ATBM8830 */ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_0 | GPIO_1); - mdelay(100); + msleep(100); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: @@ -1699,11 +1699,11 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* Put the demod into reset and protect the eeprom */ mc417_gpio_clear(dev, GPIO_14 | GPIO_13); - mdelay(100); + msleep(100); /* Bring the demod out of reset */ mc417_gpio_set(dev, GPIO_14); - mdelay(100); + msleep(100); /* CX24228 GPIO */ /* Connected to IF / Mux */ @@ -1728,7 +1728,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */ /* GPIO-0 as INT, reset & TMS low */ cx_clear(GP0_IO, 0x00010006); - mdelay(100);/* reset delay */ + msleep(100);/* reset delay */ cx_set(GP0_IO, 0x00000004); /* reset high */ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */ @@ -1747,36 +1747,36 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_8 | GPIO_9); - mdelay(100); + msleep(100); break; case CX23885_BOARD_AVERMEDIA_HC81R: cx_clear(MC417_CTL, 1); /* GPIO-0,1,2 setup direction as output */ cx_set(GP0_IO, 0x00070000); - mdelay(10); + usleep_range(10000, 11000); /* AF9013 demod reset */ cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_clear(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); /* demod tune? */ cx_clear(GP0_IO, 0x00030003); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00020002); - mdelay(10); + usleep_range(10000, 11000); cx_set(GP0_IO, 0x00010001); - mdelay(10); + usleep_range(10000, 11000); cx_clear(GP0_IO, 0x00020002); /* XC3028L tuner reset */ cx_set(GP0_IO, 0x00040004); cx_clear(GP0_IO, 0x00040004); cx_set(GP0_IO, 0x00040004); - mdelay(60); + msleep(60); break; case CX23885_BOARD_DVBSKY_T9580: case CX23885_BOARD_DVBSKY_S952: @@ -1785,7 +1785,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(MC417_CTL, 0x00000037); cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1); cx23885_gpio_clear(dev, GPIO_2 | GPIO_11); - mdelay(100); + msleep(100); cx23885_gpio_set(dev, GPIO_2 | GPIO_11); break; case CX23885_BOARD_DVBSKY_T980C: @@ -1807,7 +1807,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */ cx_clear(GP0_IO, 0x00010004); /* GPIO 0 as input */ - mdelay(100); /* reset delay */ + msleep(100); /* reset delay */ cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */ cx_clear(GP0_IO, 0x00010002); cx_write(MC417_CTL, 0x00000037); /* enable GPIO3-18 pins */ diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 94b996ff12a9..39804d830305 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -667,7 +667,7 @@ static void cx23885_reset(struct cx23885_dev *dev) /* clear dma in progress */ cx23885_clear_bridge_error(dev); - mdelay(100); + msleep(100); cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], 720*4, 0); diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c deleted file mode 100644 index ada26d4acfb4..000000000000 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "cx25821-video.h" -#include "cx25821-audio-upstream.h" - -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/syscalls.h> -#include <linux/file.h> -#include <linux/fcntl.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/uaccess.h> - -MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); -MODULE_LICENSE("GPL"); - -static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | - FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; - -static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc) -{ - unsigned int i, lines; - u32 cdt; - - if (ch->cmds_start == 0) { - cx_write(ch->ptr1_reg, 0); - cx_write(ch->ptr2_reg, 0); - cx_write(ch->cnt2_reg, 0); - cx_write(ch->cnt1_reg, 0); - return 0; - } - - bpl = (bpl + 7) & ~7; /* alignment */ - cdt = ch->cdt; - lines = ch->fifo_size / bpl; - - if (lines > 3) - lines = 3; - - BUG_ON(lines < 2); - - /* write CDT */ - for (i = 0; i < lines; i++) { - cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); - cx_write(cdt + 16 * i + 4, 0); - cx_write(cdt + 16 * i + 8, 0); - cx_write(cdt + 16 * i + 12, 0); - } - - /* write CMDS */ - cx_write(ch->cmds_start + 0, risc); - - cx_write(ch->cmds_start + 4, 0); - cx_write(ch->cmds_start + 8, cdt); - cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); - cx_write(ch->cmds_start + 16, ch->ctrl_start); - - /* IQ size */ - cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); - - for (i = 24; i < 80; i += 4) - cx_write(ch->cmds_start + i, 0); - - /* fill registers */ - cx_write(ch->ptr1_reg, ch->fifo_start); - cx_write(ch->ptr2_reg, cdt); - cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); - cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); - - return 0; -} - -static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, - __le32 *rp, - dma_addr_t databuf_phys_addr, - unsigned int bpl, - int fifo_enable) -{ - unsigned int line; - const struct sram_channel *sram_ch = - dev->channels[dev->_audio_upstream_channel].sram_channels; - int offset = 0; - - /* scan lines */ - for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(databuf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - /* Check if we need to enable the FIFO - * after the first 3 lines. - * For the upstream audio channel, - * the risc engine will enable the FIFO */ - if (fifo_enable && line == 2) { - *(rp++) = RISC_WRITECR; - *(rp++) = sram_ch->dma_ctl; - *(rp++) = sram_ch->fld_aud_fifo_en; - *(rp++) = 0x00000020; - } - - offset += AUDIO_LINE_SIZE; - } - - return rp; -} - -static int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, - struct pci_dev *pci, - unsigned int bpl, unsigned int lines) -{ - __le32 *rp; - int fifo_enable = 0; - int frame = 0, i = 0; - int frame_size = AUDIO_DATA_BUF_SZ; - int databuf_offset = 0; - int risc_flag = RISC_CNT_INC; - dma_addr_t risc_phys_jump_addr; - - /* Virtual address of Risc buffer program */ - rp = dev->_risc_virt_addr; - - /* sync instruction */ - *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); - - for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { - databuf_offset = frame_size * frame; - - if (frame == 0) { - fifo_enable = 1; - risc_flag = RISC_CNT_RESET; - } else { - fifo_enable = 0; - risc_flag = RISC_CNT_INC; - } - - /* Calculate physical jump address */ - if ((frame + 1) == NUM_AUDIO_FRAMES) { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE; - } else { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE + - AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); - } - - rp = cx25821_risc_field_upstream_audio(dev, rp, - dev->_audiodata_buf_phys_addr + databuf_offset, - bpl, fifo_enable); - - if (USE_RISC_NOOP_AUDIO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* Loop to (Nth)FrameRISC or to Start of Risc program & - * generate IRQ */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - - /* Recalculate virtual address based on frame index */ - rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + - (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); - } - - return 0; -} - -static void cx25821_free_memory_audio(struct cx25821_dev *dev) -{ - if (dev->_risc_virt_addr) { - pci_free_consistent(dev->pci, dev->_audiorisc_size, - dev->_risc_virt_addr, dev->_risc_phys_addr); - dev->_risc_virt_addr = NULL; - } - - if (dev->_audiodata_buf_virt_addr) { - pci_free_consistent(dev->pci, dev->_audiodata_buf_size, - dev->_audiodata_buf_virt_addr, - dev->_audiodata_buf_phys_addr); - dev->_audiodata_buf_virt_addr = NULL; - } -} - -void cx25821_stop_upstream_audio(struct cx25821_dev *dev) -{ - const struct sram_channel *sram_ch = - dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; - u32 tmp = 0; - - if (!dev->_audio_is_running) { - printk(KERN_DEBUG - pr_fmt("No audio file is currently running so return!\n")); - return; - } - /* Disable RISC interrupts */ - cx_write(sram_ch->int_msk, 0); - - /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ - tmp = cx_read(sram_ch->dma_ctl); - cx_write(sram_ch->dma_ctl, - tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); - - /* Clear data buffer memory */ - if (dev->_audiodata_buf_virt_addr) - memset(dev->_audiodata_buf_virt_addr, 0, - dev->_audiodata_buf_size); - - dev->_audio_is_running = 0; - dev->_is_first_audio_frame = 0; - dev->_audioframe_count = 0; - dev->_audiofile_status = END_OF_FILE; - - flush_work(&dev->_audio_work_entry); - - kfree(dev->_audiofilename); -} - -void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) -{ - if (dev->_audio_is_running) - cx25821_stop_upstream_audio(dev); - - cx25821_free_memory_audio(dev); -} - -static int cx25821_get_audio_data(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - struct file *file; - int frame_index_temp = dev->_audioframe_index; - int i = 0; - int frame_size = AUDIO_DATA_BUF_SZ; - int frame_offset = frame_size * frame_index_temp; - char mybuf[AUDIO_LINE_SIZE]; - loff_t file_offset = dev->_audioframe_count * frame_size; - char *p = NULL; - - if (dev->_audiofile_status == END_OF_FILE) - return 0; - - file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(file)) { - pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", - __func__, dev->_audiofilename, -PTR_ERR(file)); - return PTR_ERR(file); - } - - if (dev->_audiodata_buf_virt_addr) - p = (char *)dev->_audiodata_buf_virt_addr + frame_offset; - - for (i = 0; i < dev->_audio_lines_count; i++) { - int n = kernel_read(file, mybuf, AUDIO_LINE_SIZE, &file_offset); - if (n < AUDIO_LINE_SIZE) { - pr_info("Done: exit %s() since no more bytes to read from Audio file\n", - __func__); - dev->_audiofile_status = END_OF_FILE; - fput(file); - return 0; - } - dev->_audiofile_status = IN_PROGRESS; - if (p) { - memcpy(p, mybuf, n); - p += n; - } - } - dev->_audioframe_count++; - fput(file); - - return 0; -} - -static void cx25821_audioups_handler(struct work_struct *work) -{ - struct cx25821_dev *dev = container_of(work, struct cx25821_dev, - _audio_work_entry); - - if (!dev) { - pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", - __func__); - return; - } - - cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel]. - sram_channels); -} - -static int cx25821_openfile_audio(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - char *p = (void *)dev->_audiodata_buf_virt_addr; - struct file *file; - loff_t file_offset = 0; - int i, j; - - file = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); - if (IS_ERR(file)) { - pr_err("%s(): ERROR opening file(%s) with errno = %ld!\n", - __func__, dev->_audiofilename, PTR_ERR(file)); - return PTR_ERR(file); - } - - for (j = 0; j < NUM_AUDIO_FRAMES; j++) { - for (i = 0; i < dev->_audio_lines_count; i++) { - char buf[AUDIO_LINE_SIZE]; - loff_t offset = file_offset; - int n = kernel_read(file, buf, AUDIO_LINE_SIZE, &file_offset); - - if (n < AUDIO_LINE_SIZE) { - pr_info("Done: exit %s() since no more bytes to read from Audio file\n", - __func__); - dev->_audiofile_status = END_OF_FILE; - fput(file); - return 0; - } - - if (p) - memcpy(p + offset, buf, n); - } - dev->_audioframe_count++; - } - dev->_audiofile_status = IN_PROGRESS; - fput(file); - return 0; -} - -static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, - const struct sram_channel *sram_ch, - int bpl) -{ - int ret = 0; - dma_addr_t dma_addr; - dma_addr_t data_dma_addr; - - cx25821_free_memory_audio(dev); - - dev->_risc_virt_addr = pci_alloc_consistent(dev->pci, - dev->audio_upstream_riscbuf_size, &dma_addr); - dev->_risc_virt_start_addr = dev->_risc_virt_addr; - dev->_risc_phys_start_addr = dma_addr; - dev->_risc_phys_addr = dma_addr; - dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; - - if (!dev->_risc_virt_addr) { - printk(KERN_DEBUG - pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n")); - return -ENOMEM; - } - /* Clear out memory at address */ - memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); - - /* For Audio Data buffer allocation */ - dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, - dev->audio_upstream_databuf_size, &data_dma_addr); - dev->_audiodata_buf_phys_addr = data_dma_addr; - dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; - - if (!dev->_audiodata_buf_virt_addr) { - printk(KERN_DEBUG - pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n")); - return -ENOMEM; - } - /* Clear out memory at address */ - memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); - - ret = cx25821_openfile_audio(dev, sram_ch); - if (ret < 0) - return ret; - - /* Creating RISC programs */ - ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, - dev->_audio_lines_count); - if (ret < 0) { - printk(KERN_DEBUG - pr_fmt("ERROR creating audio upstream RISC programs!\n")); - goto error; - } - - return 0; - -error: - return ret; -} - -static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, - u32 status) -{ - int i = 0; - u32 int_msk_tmp; - const struct sram_channel *channel = dev->channels[chan_num].sram_channels; - dma_addr_t risc_phys_jump_addr; - __le32 *rp; - - if (status & FLD_AUD_SRC_RISCI1) { - /* Get interrupt_index of the program that interrupted */ - u32 prog_cnt = cx_read(channel->gpcnt); - - /* Since we've identified our IRQ, clear our bits from the - * interrupt mask and interrupt status registers */ - cx_write(channel->int_msk, 0); - cx_write(channel->int_stat, cx_read(channel->int_stat)); - - spin_lock(&dev->slock); - - while (prog_cnt != dev->_last_index_irq) { - /* Update _last_index_irq */ - if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) - dev->_last_index_irq++; - else - dev->_last_index_irq = 0; - - dev->_audioframe_index = dev->_last_index_irq; - - schedule_work(&dev->_audio_work_entry); - } - - if (dev->_is_first_audio_frame) { - dev->_is_first_audio_frame = 0; - - if (dev->_risc_virt_start_addr != NULL) { - risc_phys_jump_addr = - dev->_risc_phys_start_addr + - RISC_SYNC_INSTRUCTION_SIZE + - AUDIO_RISC_DMA_BUF_SIZE; - - rp = cx25821_risc_field_upstream_audio(dev, - dev->_risc_virt_start_addr + 1, - dev->_audiodata_buf_phys_addr, - AUDIO_LINE_SIZE, FIFO_DISABLE); - - if (USE_RISC_NOOP_AUDIO) { - for (i = 0; i < NUM_NO_OPS; i++) { - *(rp++) = - cpu_to_le32(RISC_NOOP); - } - } - /* Jump to 2nd Audio Frame */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | - RISC_CNT_RESET); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - } - - spin_unlock(&dev->slock); - } else { - if (status & FLD_AUD_SRC_OF) - pr_warn("%s(): Audio Received Overflow Error Interrupt!\n", - __func__); - - if (status & FLD_AUD_SRC_SYNC) - pr_warn("%s(): Audio Received Sync Error Interrupt!\n", - __func__); - - if (status & FLD_AUD_SRC_OPC_ERR) - pr_warn("%s(): Audio Received OpCode Error Interrupt!\n", - __func__); - - /* Read and write back the interrupt status register to clear - * our bits */ - cx_write(channel->int_stat, cx_read(channel->int_stat)); - } - - if (dev->_audiofile_status == END_OF_FILE) { - pr_warn("EOF Channel Audio Framecount = %d\n", - dev->_audioframe_count); - return -1; - } - /* ElSE, set the interrupt mask register, re-enable irq. */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); - - return 0; -} - -static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) -{ - struct cx25821_dev *dev = dev_id; - u32 audio_status; - int handled = 0; - const struct sram_channel *sram_ch; - - if (!dev) - return -1; - - sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; - - audio_status = cx_read(sram_ch->int_stat); - - /* Only deal with our interrupt */ - if (audio_status) { - handled = cx25821_audio_upstream_irq(dev, - dev->_audio_upstream_channel, audio_status); - } - - if (handled < 0) - cx25821_stop_upstream_audio(dev); - else - handled += handled; - - return IRQ_RETVAL(handled); -} - -static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - int count = 0; - u32 tmp; - - do { - /* Wait 10 microsecond before checking to see if the FIFO is - * turned ON. */ - udelay(10); - - tmp = cx_read(sram_ch->dma_ctl); - - /* 10 millisecond timeout */ - if (count++ > 1000) { - pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n", - __func__); - return; - } - - } while (!(tmp & sram_ch->fld_aud_fifo_en)); - -} - -static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, - const struct sram_channel *sram_ch) -{ - u32 tmp = 0; - int err = 0; - - /* Set the physical start address of the RISC program in the initial - * program counter(IPC) member of the CMDS. */ - cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); - /* Risc IPC High 64 bits 63-32 */ - cx_write(sram_ch->cmds_start + 4, 0); - - /* reset counter */ - cx_write(sram_ch->gpcnt_ctl, 3); - - /* Set the line length (It looks like we do not need to set the - * line length) */ - cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); - - /* Set the input mode to 16-bit */ - tmp = cx_read(sram_ch->aud_cfg); - tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | - FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | - FLD_AUD_SONY_MODE; - cx_write(sram_ch->aud_cfg, tmp); - - /* Read and write back the interrupt status register to clear it */ - tmp = cx_read(sram_ch->int_stat); - cx_write(sram_ch->int_stat, tmp); - - /* Clear our bits from the interrupt status register. */ - cx_write(sram_ch->int_stat, _intr_msk); - - /* Set the interrupt mask register, enable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp |= _intr_msk); - - err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - pr_err("%s: can't get upstream IRQ %d\n", dev->name, - dev->pci->irq); - goto fail_irq; - } - - /* Start the DMA engine */ - tmp = cx_read(sram_ch->dma_ctl); - cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); - - dev->_audio_is_running = 1; - dev->_is_first_audio_frame = 1; - - /* The fifo_en bit turns on by the first Risc program */ - cx25821_wait_fifo_enable(dev, sram_ch); - - return 0; - -fail_irq: - cx25821_dev_unregister(dev); - return err; -} - -int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) -{ - const struct sram_channel *sram_ch; - int err = 0; - - if (dev->_audio_is_running) { - pr_warn("Audio Channel is still running so return!\n"); - return 0; - } - - dev->_audio_upstream_channel = channel_select; - sram_ch = dev->channels[channel_select].sram_channels; - - /* Work queue */ - INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); - - dev->_last_index_irq = 0; - dev->_audio_is_running = 0; - dev->_audioframe_count = 0; - dev->_audiofile_status = RESET_STATUS; - dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; - _line_size = AUDIO_LINE_SIZE; - - if ((dev->input_audiofilename) && - (strcmp(dev->input_audiofilename, "") != 0)) - dev->_audiofilename = kstrdup(dev->input_audiofilename, - GFP_KERNEL); - else - dev->_audiofilename = kstrdup(_defaultAudioName, - GFP_KERNEL); - - if (!dev->_audiofilename) { - err = -ENOMEM; - goto error; - } - - cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, - _line_size, 0); - - dev->audio_upstream_riscbuf_size = - AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + - RISC_SYNC_INSTRUCTION_SIZE; - dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; - - /* Allocating buffers and prepare RISC program */ - err = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, - _line_size); - if (err < 0) { - pr_err("%s: Failed to set up Audio upstream buffers!\n", - dev->name); - goto error; - } - /* Start RISC engine */ - cx25821_start_audio_dma_upstream(dev, sram_ch); - - return 0; - -error: - cx25821_dev_unregister(dev); - - return err; -} diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.h b/drivers/media/pci/cx25821/cx25821-audio-upstream.h deleted file mode 100644 index 2bc875d1ec9f..000000000000 --- a/drivers/media/pci/cx25821/cx25821-audio-upstream.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#include <linux/mutex.h> -#include <linux/workqueue.h> - -#define NUM_AUDIO_PROGS 8 -#define NUM_AUDIO_FRAMES 8 -#define END_OF_FILE 0 -#define IN_PROGRESS 1 -#define RESET_STATUS -1 -#define FIFO_DISABLE 0 -#define FIFO_ENABLE 1 -#define NUM_NO_OPS 4 - -#define RISC_READ_INSTRUCTION_SIZE 12 -#define RISC_JUMP_INSTRUCTION_SIZE 12 -#define RISC_WRITECR_INSTRUCTION_SIZE 16 -#define RISC_SYNC_INSTRUCTION_SIZE 4 -#define DWORD_SIZE 4 -#define AUDIO_SYNC_LINE 4 - -#define LINES_PER_AUDIO_BUFFER 15 -#define AUDIO_LINE_SIZE 128 -#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER) - -#define USE_RISC_NOOP_AUDIO 1 - -#ifdef USE_RISC_NOOP_AUDIO -#define AUDIO_RISC_DMA_BUF_SIZE \ - (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \ - RISC_JUMP_INSTRUCTION_SIZE) -#endif - -#ifndef USE_RISC_NOOP_AUDIO -#define AUDIO_RISC_DMA_BUF_SIZE \ - (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE) -#endif - -static int _line_size; -char *_defaultAudioName = "/root/audioGOOD.wav"; diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 040c6c251d3a..2f0171134f7e 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -428,7 +428,7 @@ static void cx25821_registers_init(struct cx25821_dev *dev) tmp |= FLD_USE_ALT_PLL_REF; cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); - mdelay(100); + msleep(100); } int cx25821_sram_channel_setup(struct cx25821_dev *dev, @@ -803,7 +803,7 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); cx_write(PAD_CTRL, 0x12); /* for I2C */ cx25821_registers_init(dev); /* init Pecos registers */ - mdelay(100); + msleep(100); for (i = 0; i < VID_CHANNEL_NUM; i++) { cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); diff --git a/drivers/media/pci/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c index 76b8f619e55a..f5ffaf880e5f 100644 --- a/drivers/media/pci/cx25821/cx25821-gpio.c +++ b/drivers/media/pci/cx25821/cx25821-gpio.c @@ -88,7 +88,7 @@ void cx25821_gpio_init(struct cx25821_dev *dev) default: /* set GPIO 5 to select the path for Medusa/Athena */ cx25821_set_gpiopin_logicvalue(dev, 5, 1); - mdelay(20); + msleep(20); break; } diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c deleted file mode 100644 index 6c838c8a7924..000000000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.c +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "cx25821-video.h" -#include "cx25821-video-upstream.h" - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> - -MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); -MODULE_LICENSE("GPL"); - -static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | - FLD_VID_SRC_OPC_ERR; - -int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc) -{ - unsigned int i, lines; - u32 cdt; - - if (ch->cmds_start == 0) { - cx_write(ch->ptr1_reg, 0); - cx_write(ch->ptr2_reg, 0); - cx_write(ch->cnt2_reg, 0); - cx_write(ch->cnt1_reg, 0); - return 0; - } - - bpl = (bpl + 7) & ~7; /* alignment */ - cdt = ch->cdt; - lines = ch->fifo_size / bpl; - - if (lines > 4) - lines = 4; - - BUG_ON(lines < 2); - - /* write CDT */ - for (i = 0; i < lines; i++) { - cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); - cx_write(cdt + 16 * i + 4, 0); - cx_write(cdt + 16 * i + 8, 0); - cx_write(cdt + 16 * i + 12, 0); - } - - /* write CMDS */ - cx_write(ch->cmds_start + 0, risc); - - cx_write(ch->cmds_start + 4, 0); - cx_write(ch->cmds_start + 8, cdt); - cx_write(ch->cmds_start + 12, (lines * 16) >> 3); - cx_write(ch->cmds_start + 16, ch->ctrl_start); - - cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW); - - for (i = 24; i < 80; i += 4) - cx_write(ch->cmds_start + i, 0); - - /* fill registers */ - cx_write(ch->ptr1_reg, ch->fifo_start); - cx_write(ch->ptr2_reg, cdt); - cx_write(ch->cnt2_reg, (lines * 16) >> 3); - cx_write(ch->cnt1_reg, (bpl >> 3) - 1); - - return 0; -} - -static __le32 *cx25821_update_riscprogram(struct cx25821_channel *chan, - __le32 *rp, unsigned int offset, - unsigned int bpl, u32 sync_line, - unsigned int lines, int fifo_enable, - int field_type) -{ - struct cx25821_video_out_data *out = chan->out; - unsigned int line, i; - int dist_betwn_starts = bpl * 2; - - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(out->_data_buf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) { - offset += dist_betwn_starts; - } - } - - return rp; -} - -static __le32 *cx25821_risc_field_upstream(struct cx25821_channel *chan, __le32 *rp, - dma_addr_t databuf_phys_addr, - unsigned int offset, u32 sync_line, - unsigned int bpl, unsigned int lines, - int fifo_enable, int field_type) -{ - struct cx25821_video_out_data *out = chan->out; - unsigned int line, i; - const struct sram_channel *sram_ch = chan->sram_channels; - int dist_betwn_starts = bpl * 2; - - /* sync instruction */ - if (sync_line != NO_SYNC_LINE) - *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); - - if (USE_RISC_NOOP_VIDEO) { - for (i = 0; i < NUM_NO_OPS; i++) - *(rp++) = cpu_to_le32(RISC_NOOP); - } - - /* scan lines */ - for (line = 0; line < lines; line++) { - *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); - *(rp++) = cpu_to_le32(databuf_phys_addr + offset); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - - if ((lines <= NTSC_FIELD_HEIGHT) - || (line < (NTSC_FIELD_HEIGHT - 1)) || !(out->is_60hz)) - /* to skip the other field line */ - offset += dist_betwn_starts; - - /* check if we need to enable the FIFO after the first 4 lines - * For the upstream video channel, the risc engine will enable - * the FIFO. */ - if (fifo_enable && line == 3) { - *(rp++) = cpu_to_le32(RISC_WRITECR); - *(rp++) = cpu_to_le32(sram_ch->dma_ctl); - *(rp++) = cpu_to_le32(FLD_VID_FIFO_EN); - *(rp++) = cpu_to_le32(0x00000001); - } - } - - return rp; -} - -static int cx25821_risc_buffer_upstream(struct cx25821_channel *chan, - struct pci_dev *pci, - unsigned int top_offset, - unsigned int bpl, unsigned int lines) -{ - struct cx25821_video_out_data *out = chan->out; - __le32 *rp; - int fifo_enable = 0; - /* get line count for single field */ - int singlefield_lines = lines >> 1; - int odd_num_lines = singlefield_lines; - int frame = 0; - int frame_size = 0; - int databuf_offset = 0; - int risc_program_size = 0; - int risc_flag = RISC_CNT_RESET; - unsigned int bottom_offset = bpl; - dma_addr_t risc_phys_jump_addr; - - if (out->is_60hz) { - odd_num_lines = singlefield_lines + 1; - risc_program_size = FRAME1_VID_PROG_SIZE; - frame_size = (bpl == Y411_LINE_SZ) ? - FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; - } else { - risc_program_size = PAL_VID_PROG_SIZE; - frame_size = (bpl == Y411_LINE_SZ) ? - FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - } - - /* Virtual address of Risc buffer program */ - rp = out->_dma_virt_addr; - - for (frame = 0; frame < NUM_FRAMES; frame++) { - databuf_offset = frame_size * frame; - - if (UNSET != top_offset) { - fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; - rp = cx25821_risc_field_upstream(chan, rp, - out->_data_buf_phys_addr + - databuf_offset, top_offset, 0, bpl, - odd_num_lines, fifo_enable, ODD_FIELD); - } - - fifo_enable = FIFO_DISABLE; - - /* Even Field */ - rp = cx25821_risc_field_upstream(chan, rp, - out->_data_buf_phys_addr + - databuf_offset, bottom_offset, - 0x200, bpl, singlefield_lines, - fifo_enable, EVEN_FIELD); - - if (frame == 0) { - risc_flag = RISC_CNT_RESET; - risc_phys_jump_addr = out->_dma_phys_start_addr + - risc_program_size; - } else { - risc_phys_jump_addr = out->_dma_phys_start_addr; - risc_flag = RISC_CNT_INC; - } - - /* Loop to 2ndFrameRISC or to Start of Risc - * program & generate IRQ - */ - *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - - return 0; -} - -void cx25821_stop_upstream_video(struct cx25821_channel *chan) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch = chan->sram_channels; - u32 tmp = 0; - - if (!out->_is_running) { - pr_info("No video file is currently running so return!\n"); - return; - } - - /* Set the interrupt mask register, disable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) & ~(1 << sram_ch->irq_bit)); - - /* Disable RISC interrupts */ - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp & ~_intr_msk); - - /* Turn OFF risc and fifo enable */ - tmp = cx_read(sram_ch->dma_ctl); - cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - - free_irq(dev->pci->irq, chan); - - /* Clear data buffer memory */ - if (out->_data_buf_virt_addr) - memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); - - out->_is_running = 0; - out->_is_first_frame = 0; - out->_frame_count = 0; - out->_file_status = END_OF_FILE; - - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); -} - -void cx25821_free_mem_upstream(struct cx25821_channel *chan) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - - if (out->_is_running) - cx25821_stop_upstream_video(chan); - - if (out->_dma_virt_addr) { - pci_free_consistent(dev->pci, out->_risc_size, - out->_dma_virt_addr, out->_dma_phys_addr); - out->_dma_virt_addr = NULL; - } - - if (out->_data_buf_virt_addr) { - pci_free_consistent(dev->pci, out->_data_buf_size, - out->_data_buf_virt_addr, - out->_data_buf_phys_addr); - out->_data_buf_virt_addr = NULL; - } -} - -int cx25821_write_frame(struct cx25821_channel *chan, - const char __user *data, size_t count) -{ - struct cx25821_video_out_data *out = chan->out; - int line_size = (out->_pixel_format == PIXEL_FRMT_411) ? - Y411_LINE_SZ : Y422_LINE_SZ; - int frame_size = 0; - int frame_offset = 0; - int curpos = out->curpos; - - if (out->is_60hz) - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; - else - frame_size = (line_size == Y411_LINE_SZ) ? - FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; - - if (curpos == 0) { - out->cur_frame_index = out->_frame_index; - if (wait_event_interruptible(out->waitq, out->cur_frame_index != out->_frame_index)) - return -EINTR; - out->cur_frame_index = out->_frame_index; - } - - frame_offset = out->cur_frame_index ? frame_size : 0; - - if (frame_size - curpos < count) - count = frame_size - curpos; - if (copy_from_user((__force char *)out->_data_buf_virt_addr + frame_offset + curpos, - data, count)) - return -EFAULT; - curpos += count; - if (curpos == frame_size) { - out->_frame_count++; - curpos = 0; - } - out->curpos = curpos; - - return count; -} - -static int cx25821_upstream_buffer_prepare(struct cx25821_channel *chan, - const struct sram_channel *sram_ch, - int bpl) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - int ret = 0; - dma_addr_t dma_addr; - dma_addr_t data_dma_addr; - - if (out->_dma_virt_addr != NULL) - pci_free_consistent(dev->pci, out->upstream_riscbuf_size, - out->_dma_virt_addr, out->_dma_phys_addr); - - out->_dma_virt_addr = pci_alloc_consistent(dev->pci, - out->upstream_riscbuf_size, &dma_addr); - out->_dma_virt_start_addr = out->_dma_virt_addr; - out->_dma_phys_start_addr = dma_addr; - out->_dma_phys_addr = dma_addr; - out->_risc_size = out->upstream_riscbuf_size; - - if (!out->_dma_virt_addr) { - pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); - return -ENOMEM; - } - - /* Clear memory at address */ - memset(out->_dma_virt_addr, 0, out->_risc_size); - - if (out->_data_buf_virt_addr != NULL) - pci_free_consistent(dev->pci, out->upstream_databuf_size, - out->_data_buf_virt_addr, - out->_data_buf_phys_addr); - /* For Video Data buffer allocation */ - out->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, - out->upstream_databuf_size, &data_dma_addr); - out->_data_buf_phys_addr = data_dma_addr; - out->_data_buf_size = out->upstream_databuf_size; - - if (!out->_data_buf_virt_addr) { - pr_err("FAILED to allocate memory for data buffer! Returning\n"); - return -ENOMEM; - } - - /* Clear memory at address */ - memset(out->_data_buf_virt_addr, 0, out->_data_buf_size); - - /* Create RISC programs */ - ret = cx25821_risc_buffer_upstream(chan, dev->pci, 0, bpl, - out->_lines_count); - if (ret < 0) { - pr_info("Failed creating Video Upstream Risc programs!\n"); - goto error; - } - - return 0; - -error: - return ret; -} - -static int cx25821_video_upstream_irq(struct cx25821_channel *chan, u32 status) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - u32 int_msk_tmp; - const struct sram_channel *channel = chan->sram_channels; - int singlefield_lines = NTSC_FIELD_HEIGHT; - int line_size_in_bytes = Y422_LINE_SZ; - int odd_risc_prog_size = 0; - dma_addr_t risc_phys_jump_addr; - __le32 *rp; - - if (status & FLD_VID_SRC_RISC1) { - /* We should only process one program per call */ - u32 prog_cnt = cx_read(channel->gpcnt); - - /* Since we've identified our IRQ, clear our bits from the - * interrupt mask and interrupt status registers */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); - cx_write(channel->int_stat, _intr_msk); - - wake_up(&out->waitq); - - spin_lock(&dev->slock); - - out->_frame_index = prog_cnt; - - if (out->_is_first_frame) { - out->_is_first_frame = 0; - - if (out->is_60hz) { - singlefield_lines += 1; - odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; - } else { - singlefield_lines = PAL_FIELD_HEIGHT; - odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; - } - - if (out->_dma_virt_start_addr != NULL) { - line_size_in_bytes = - (out->_pixel_format == - PIXEL_FRMT_411) ? Y411_LINE_SZ : - Y422_LINE_SZ; - risc_phys_jump_addr = - out->_dma_phys_start_addr + - odd_risc_prog_size; - - rp = cx25821_update_riscprogram(chan, - out->_dma_virt_start_addr, TOP_OFFSET, - line_size_in_bytes, 0x0, - singlefield_lines, FIFO_DISABLE, - ODD_FIELD); - - /* Jump to Even Risc program of 1st Frame */ - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc_phys_jump_addr); - *(rp++) = cpu_to_le32(0); - } - } - - spin_unlock(&dev->slock); - } else { - if (status & FLD_VID_SRC_UF) - pr_err("%s(): Video Received Underflow Error Interrupt!\n", - __func__); - - if (status & FLD_VID_SRC_SYNC) - pr_err("%s(): Video Received Sync Error Interrupt!\n", - __func__); - - if (status & FLD_VID_SRC_OPC_ERR) - pr_err("%s(): Video Received OpCode Error Interrupt!\n", - __func__); - } - - if (out->_file_status == END_OF_FILE) { - pr_err("EOF Channel 1 Framecount = %d\n", out->_frame_count); - return -1; - } - /* ElSE, set the interrupt mask register, re-enable irq. */ - int_msk_tmp = cx_read(channel->int_msk); - cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); - - return 0; -} - -static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) -{ - struct cx25821_channel *chan = dev_id; - struct cx25821_dev *dev = chan->dev; - u32 vid_status; - int handled = 0; - const struct sram_channel *sram_ch; - - if (!dev) - return -1; - - sram_ch = chan->sram_channels; - - vid_status = cx_read(sram_ch->int_stat); - - /* Only deal with our interrupt */ - if (vid_status) - handled = cx25821_video_upstream_irq(chan, vid_status); - - return IRQ_RETVAL(handled); -} - -static void cx25821_set_pixelengine(struct cx25821_channel *chan, - const struct sram_channel *ch, - int pix_format) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - int width = WIDTH_D1; - int height = out->_lines_count; - int num_lines, odd_num_lines; - u32 value; - int vip_mode = OUTPUT_FRMT_656; - - value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); - value &= 0xFFFFFFEF; - value |= out->is_60hz ? 0 : 0x10; - cx_write(ch->vid_fmt_ctl, value); - - /* set number of active pixels in each line. - * Default is 720 pixels in both NTSC and PAL format */ - cx_write(ch->vid_active_ctl1, width); - - num_lines = (height / 2) & 0x3FF; - odd_num_lines = num_lines; - - if (out->is_60hz) - odd_num_lines += 1; - - value = (num_lines << 16) | odd_num_lines; - - /* set number of active lines in field 0 (top) and field 1 (bottom) */ - cx_write(ch->vid_active_ctl2, value); - - cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); -} - -static int cx25821_start_video_dma_upstream(struct cx25821_channel *chan, - const struct sram_channel *sram_ch) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - u32 tmp = 0; - int err = 0; - - /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for - * channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - /* Set the physical start address of the RISC program in the initial - * program counter(IPC) member of the cmds. - */ - cx_write(sram_ch->cmds_start + 0, out->_dma_phys_addr); - /* Risc IPC High 64 bits 63-32 */ - cx_write(sram_ch->cmds_start + 4, 0); - - /* reset counter */ - cx_write(sram_ch->gpcnt_ctl, 3); - - /* Clear our bits from the interrupt status register. */ - cx_write(sram_ch->int_stat, _intr_msk); - - /* Set the interrupt mask register, enable irq. */ - cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); - tmp = cx_read(sram_ch->int_msk); - cx_write(sram_ch->int_msk, tmp |= _intr_msk); - - err = request_irq(dev->pci->irq, cx25821_upstream_irq, - IRQF_SHARED, dev->name, chan); - if (err < 0) { - pr_err("%s: can't get upstream IRQ %d\n", - dev->name, dev->pci->irq); - goto fail_irq; - } - - /* Start the DMA engine */ - tmp = cx_read(sram_ch->dma_ctl); - cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); - - out->_is_running = 1; - out->_is_first_frame = 1; - - return 0; - -fail_irq: - cx25821_dev_unregister(dev); - return err; -} - -int cx25821_vidupstream_init(struct cx25821_channel *chan, - int pixel_format) -{ - struct cx25821_video_out_data *out = chan->out; - struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch; - u32 tmp; - int err = 0; - int data_frame_size = 0; - int risc_buffer_size = 0; - - if (out->_is_running) { - pr_info("Video Channel is still running so return!\n"); - return 0; - } - - sram_ch = chan->sram_channels; - - out->is_60hz = dev->tvnorm & V4L2_STD_525_60; - - /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for - * channel A-C - */ - tmp = cx_read(VID_CH_MODE_SEL); - cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - - out->_is_running = 0; - out->_frame_count = 0; - out->_file_status = RESET_STATUS; - out->_lines_count = out->is_60hz ? 480 : 576; - out->_pixel_format = pixel_format; - out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? - (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - data_frame_size = out->is_60hz ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; - risc_buffer_size = out->is_60hz ? - NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; - - out->_is_running = 0; - out->_frame_count = 0; - out->_file_status = RESET_STATUS; - out->_lines_count = out->is_60hz ? 480 : 576; - out->_pixel_format = pixel_format; - out->_line_size = (out->_pixel_format == PIXEL_FRMT_422) ? - (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; - out->curpos = 0; - init_waitqueue_head(&out->waitq); - - err = cx25821_sram_channel_setup_upstream(dev, sram_ch, - out->_line_size, 0); - - /* setup fifo + format */ - cx25821_set_pixelengine(chan, sram_ch, out->_pixel_format); - - out->upstream_riscbuf_size = risc_buffer_size * 2; - out->upstream_databuf_size = data_frame_size * 2; - - /* Allocating buffers and prepare RISC program */ - err = cx25821_upstream_buffer_prepare(chan, sram_ch, out->_line_size); - if (err < 0) { - pr_err("%s: Failed to set up Video upstream buffers!\n", - dev->name); - goto error; - } - - cx25821_start_video_dma_upstream(chan, sram_ch); - - return 0; - -error: - cx25821_dev_unregister(dev); - - return err; -} diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.h b/drivers/media/pci/cx25821/cx25821-video-upstream.h deleted file mode 100644 index b6cf95f2d11b..000000000000 --- a/drivers/media/pci/cx25821/cx25821-video-upstream.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * - * GNU General Public License for more details. - */ - -#include <linux/mutex.h> -#include <linux/workqueue.h> - -#define OUTPUT_FRMT_656 0 -#define OPEN_FILE_1 0 -#define NUM_PROGS 8 -#define NUM_FRAMES 2 -#define ODD_FIELD 0 -#define EVEN_FIELD 1 -#define TOP_OFFSET 0 -#define FIFO_DISABLE 0 -#define FIFO_ENABLE 1 -#define TEST_FRAMES 5 -#define END_OF_FILE 0 -#define IN_PROGRESS 1 -#define RESET_STATUS -1 -#define NUM_NO_OPS 5 - -/* PAL and NTSC line sizes and number of lines. */ -#define WIDTH_D1 720 -#define NTSC_LINES_PER_FRAME 480 -#define PAL_LINES_PER_FRAME 576 -#define PAL_LINE_SZ 1440 -#define Y422_LINE_SZ 1440 -#define Y411_LINE_SZ 1080 -#define NTSC_FIELD_HEIGHT 240 -#define NTSC_ODD_FLD_LINES 241 -#define PAL_FIELD_HEIGHT 288 - -#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) -#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) -#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) - -#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) -#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) - -#define RISC_WRITECR_INSTRUCTION_SIZE 16 -#define RISC_SYNC_INSTRUCTION_SIZE 4 -#define JUMP_INSTRUCTION_SIZE 12 -#define MAXSIZE_NO_OPS 36 -#define DWORD_SIZE 4 - -#define USE_RISC_NOOP_VIDEO 1 - -#ifdef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ - NUM_NO_OPS * DWORD_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) - -#endif - -#ifndef USE_RISC_NOOP_VIDEO -#define PAL_US_VID_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) - -#define PAL_VID_PROG_SIZE \ - ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#define ODD_FLD_PAL_PROG_SIZE \ - (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define ODD_FLD_NTSC_PROG_SIZE \ - (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ - RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) - -#define NTSC_US_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ - RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) - -#define NTSC_RISC_BUF_SIZE \ - (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) - -#define FRAME1_VID_PROG_SIZE \ - ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ - 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ - JUMP_INSTRUCTION_SIZE) - -#endif diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index b3eb2dabb30b..25eba4ac4499 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -432,18 +432,6 @@ extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, const struct sram_channel *ch, unsigned int bpl, u32 risc); -extern int cx25821_vidupstream_init(struct cx25821_channel *chan, int pixel_format); -extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, - int channel_select); -extern int cx25821_write_frame(struct cx25821_channel *chan, - const char __user *data, size_t count); -extern void cx25821_free_mem_upstream(struct cx25821_channel *chan); -extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); -extern void cx25821_stop_upstream_video(struct cx25821_channel *chan); -extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); -extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, - const struct sram_channel *ch, - unsigned int bpl, u32 risc); extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, u32 format); diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index e5c3387cd1e8..89a65478ae36 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -962,8 +962,11 @@ static int cx88_audio_initdev(struct pci_dev *pci, goto error; /* If there's a wm8775 then add a Line-In ALC switch */ - if (core->sd_wm8775) - snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); + if (core->sd_wm8775) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); + if (err < 0) + goto error; + } strcpy(card->driver, "CX88x"); sprintf(card->shortname, "Conexant CX%x", pci->device); diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 4c92d2388c26..07e1483e987d 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3307,9 +3307,9 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: cx_write(MO_GP2_IO, 0xcf7); - mdelay(50); + msleep(50); cx_write(MO_GP2_IO, 0xef5); - mdelay(50); + msleep(50); cx_write(MO_GP2_IO, 0xcf7); usleep_range(10000, 20000); break; diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index ccfde28d4af2..90b208747e0f 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1226,9 +1226,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); /* Select RF connector callback */ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; @@ -1248,9 +1248,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 9); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, 0x0e, @@ -1267,9 +1267,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_5_gold, 0x0e, @@ -1289,9 +1289,9 @@ static int dvb_register(struct cx8802_dev *dev) /* Do a hardware reset of chip before using it. */ cx_clear(MO_GP0_IO, 1); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 1); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &pchdtv_hd5500, 0x59, @@ -1583,9 +1583,9 @@ static int dvb_register(struct cx8802_dev *dev) cx_set(MO_GP0_IO, 0x0101); cx_clear(MO_GP0_IO, 0x01); - mdelay(100); + msleep(100); cx_set(MO_GP0_IO, 0x01); - mdelay(200); + msleep(200); fe0->dvb.frontend = dvb_attach(stv0299_attach, &samsung_stv0299_config, diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index 9b9e35f171b7..5b6d5bbc38af 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -4,7 +4,8 @@ # ddbridge-objs := ddbridge-main.o ddbridge-core.o ddbridge-ci.o \ - ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o + ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o \ + ddbridge-sx8.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index d5b0d1eaf3ad..c1b982e8e6c9 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -1191,6 +1191,13 @@ static const struct lnbh25_config lnbh25_cfg = { .data2_config = LNBH25_TEN }; +static int has_lnbh25(struct i2c_adapter *i2c, u8 adr) +{ + u8 val; + + return i2c_read_reg(i2c, adr, 0, &val) ? 0 : 1; +} + static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast) { struct i2c_adapter *i2c = &input->port->i2c->adap; @@ -1224,14 +1231,15 @@ static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast) /* attach lnbh25 - leftshift by one as the lnbh25 driver expects 8bit * i2c addresses */ - lnbcfg.i2c_address = (((input->nr & 1) ? 0x0d : 0x0c) << 1); - if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { + if (has_lnbh25(i2c, 0x0d)) + lnbcfg.i2c_address = (((input->nr & 1) ? 0x0d : 0x0c) << 1); + else lnbcfg.i2c_address = (((input->nr & 1) ? 0x09 : 0x08) << 1); - if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { - dev_err(dev, "No LNBH25 found!\n"); - dvb_frontend_detach(dvb->fe); - return -ENODEV; - } + + if (!dvb_attach(lnbh25_attach, dvb->fe, &lnbcfg, i2c)) { + dev_err(dev, "No LNBH25 found!\n"); + dvb_frontend_detach(dvb->fe); + return -ENODEV; } return 0; @@ -1584,8 +1592,8 @@ static int dvb_input_attach(struct ddb_input *input) if (demod_attach_dummy(input) < 0) goto err_detach; break; - case DDB_TUNER_MCI: - if (ddb_fe_attach_mci(input) < 0) + case DDB_TUNER_MCI_SX8: + if (ddb_fe_attach_mci(input, port->type) < 0) goto err_detach; break; default: @@ -1842,6 +1850,7 @@ static void ddb_port_probe(struct ddb_port *port) { struct ddb *dev = port->dev; u32 l = port->lnr; + struct ddb_link *link = &dev->link[l]; u8 id, type; port->name = "NO MODULE"; @@ -1851,7 +1860,7 @@ static void ddb_port_probe(struct ddb_port *port) /* Handle missing ports and ports without I2C */ if (dummy_tuner && !port->nr && - dev->link[0].ids.device == 0x0005) { + link->ids.device == 0x0005) { port->name = "DUMMY"; port->class = DDB_PORT_TUNER; port->type = DDB_TUNER_DUMMY; @@ -1865,14 +1874,14 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (port->nr == 1 && dev->link[l].info->type == DDB_OCTOPUS_CI && - dev->link[l].info->i2c_mask == 1) { + if (port->nr == 1 && link->info->type == DDB_OCTOPUS_CI && + link->info->i2c_mask == 1) { port->name = "NO TAB"; port->class = DDB_PORT_NONE; return; } - if (dev->link[l].info->type == DDB_OCTOPUS_MAX) { + if (link->info->type == DDB_OCTOPUS_MAX) { port->name = "DUAL DVB-S2 MAX"; port->type_name = "MXL5XX"; port->class = DDB_PORT_TUNER; @@ -1883,17 +1892,17 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (dev->link[l].info->type == DDB_OCTOPUS_MCI) { - if (port->nr >= dev->link[l].info->mci) + if (link->info->type == DDB_OCTOPUS_MCI) { + if (port->nr >= link->info->mci_ports) return; port->name = "DUAL MCI"; port->type_name = "MCI"; port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_MCI; + port->type = DDB_TUNER_MCI + link->info->mci_type; return; } - if (port->nr > 1 && dev->link[l].info->type == DDB_OCTOPUS_CI) { + if (port->nr > 1 && link->info->type == DDB_OCTOPUS_CI) { port->name = "CI internal"; port->type_name = "INTERNAL"; port->class = DDB_PORT_CI; @@ -1978,7 +1987,7 @@ static void ddb_port_probe(struct ddb_port *port) port->class = DDB_PORT_TUNER; if (id == 0x51) { if (port->nr == 0 && - dev->link[l].info->ts_quirks & TS_QUIRK_REVERSED) + link->info->ts_quirks & TS_QUIRK_REVERSED) port->type = DDB_TUNER_DVBS_STV0910_PR; else port->type = DDB_TUNER_DVBS_STV0910_P; diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.c b/drivers/media/pci/ddbridge/ddbridge-hw.c index 1d3ee6accdd5..f3cbac07b41f 100644 --- a/drivers/media/pci/ddbridge/ddbridge-hw.c +++ b/drivers/media/pci/ddbridge/ddbridge-hw.c @@ -318,7 +318,8 @@ static const struct ddb_info ddb_s2x_48 = { .port_num = 4, .i2c_mask = 0x00, .tempmon_irq = 24, - .mci = 4 + .mci_ports = 4, + .mci_type = 0, }; /****************************************************************************/ diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c index 667340c86ea7..5a28d7611713 100644 --- a/drivers/media/pci/ddbridge/ddbridge-i2c.c +++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c @@ -73,7 +73,10 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) } return -EIO; } - if (val & 0x70000) + val &= 0x70000; + if (val == 0x20000) + dev_err(dev->dev, "I2C bus error\n"); + if (val) return -EIO; return 0; } diff --git a/drivers/media/pci/ddbridge/ddbridge-max.c b/drivers/media/pci/ddbridge/ddbridge-max.c index 739e4b444cf4..8da1c7b91577 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.c +++ b/drivers/media/pci/ddbridge/ddbridge-max.c @@ -457,21 +457,29 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input) /******************************************************************************/ /* MAX MCI related functions */ -int ddb_fe_attach_mci(struct ddb_input *input) +int ddb_fe_attach_mci(struct ddb_input *input, u32 type) { struct ddb *dev = input->port->dev; struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; struct ddb_port *port = input->port; struct ddb_link *link = &dev->link[port->lnr]; int demod, tuner; + struct mci_cfg cfg; demod = input->nr; tuner = demod & 3; - if (fmode == 3) - tuner = 0; - dvb->fe = ddb_mci_attach(input, 0, demod, &dvb->set_input); + switch (type) { + case DDB_TUNER_MCI_SX8: + cfg = ddb_max_sx8_cfg; + if (fmode == 3) + tuner = 0; + break; + default: + return -EINVAL; + } + dvb->fe = ddb_mci_attach(input, &cfg, demod, &dvb->set_input); if (!dvb->fe) { - dev_err(dev->dev, "No MAXSX8 found!\n"); + dev_err(dev->dev, "No MCI card found!\n"); return -ENODEV; } if (!dvb->set_input) { diff --git a/drivers/media/pci/ddbridge/ddbridge-max.h b/drivers/media/pci/ddbridge/ddbridge-max.h index 82efc53baa94..9838c73973b6 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.h +++ b/drivers/media/pci/ddbridge/ddbridge-max.h @@ -25,6 +25,6 @@ int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm); int ddb_fe_attach_mxl5xx(struct ddb_input *input); -int ddb_fe_attach_mci(struct ddb_input *input); +int ddb_fe_attach_mci(struct ddb_input *input, u32 type); #endif /* _DDBRIDGE_MAX_H */ diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c index 4ac634fc96e4..97384ae9ad27 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.c +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -2,9 +2,9 @@ /* * ddbridge-mci.c: Digital Devices microcode interface * - * Copyright (C) 2017 Digital Devices GmbH - * Ralph Metzler <rjkm@metzlerbros.de> - * Marcus Metzler <mocm@metzlerbros.de> + * Copyright (C) 2017-2018 Digital Devices GmbH + * Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,42 +22,6 @@ static LIST_HEAD(mci_list); -static const u32 MCLK = (1550000000 / 12); -static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); -static const u32 MAX_LDPC_BITRATE = (720000000); - -struct mci_base { - struct list_head mci_list; - void *key; - struct ddb_link *link; - struct completion completion; - - struct device *dev; - struct mutex tuner_lock; /* concurrent tuner access lock */ - u8 adr; - struct mutex mci_lock; /* concurrent MCI access lock */ - int count; - - u8 tuner_use_count[MCI_TUNER_MAX]; - u8 assigned_demod[MCI_DEMOD_MAX]; - u32 used_ldpc_bitrate[MCI_DEMOD_MAX]; - u8 demod_in_use[MCI_DEMOD_MAX]; - u32 iq_mode; -}; - -struct mci { - struct mci_base *base; - struct dvb_frontend fe; - int nr; - int demod; - int tuner; - int first_time_lock; - int started; - struct mci_result signal_info; - - u32 bb_mode; -}; - static int mci_reset(struct mci *state) { struct ddb_link *link = state->base->link; @@ -84,7 +48,7 @@ static int mci_reset(struct mci *state) return 0; } -static int mci_config(struct mci *state, u32 config) +int ddb_mci_config(struct mci *state, u32 config) { struct ddb_link *link = state->base->link; @@ -122,16 +86,16 @@ static int _mci_cmd_unlocked(struct mci *state, return 0; } -static int mci_cmd(struct mci *state, - struct mci_command *command, - struct mci_result *result) +int ddb_mci_cmd(struct mci *state, + struct mci_command *command, + struct mci_result *result) { int stat; mutex_lock(&state->base->mci_lock); stat = _mci_cmd_unlocked(state, (u32 *)command, sizeof(*command) / sizeof(u32), - (u32 *)result, sizeof(*result) / sizeof(u32)); + (u32 *)result, sizeof(*result) / sizeof(u32)); mutex_unlock(&state->base->mci_lock); return stat; } @@ -143,344 +107,6 @@ static void mci_handler(void *priv) complete(&base->completion); } -static void release(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - - state->base->count--; - if (state->base->count == 0) { - list_del(&state->base->mci_list); - kfree(state->base); - } - kfree(state); -} - -static int read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - int stat; - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - struct mci_result res; - - cmd.command = MCI_CMD_GETSTATUS; - cmd.demod = state->demod; - stat = mci_cmd(state, &cmd, &res); - if (stat) - return stat; - *status = 0x00; - if (res.status == SX8_DEMOD_WAIT_MATYPE) - *status = 0x0f; - if (res.status == SX8_DEMOD_LOCKED) - *status = 0x1f; - return stat; -} - -static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.tuner = state->tuner; - cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; - return mci_cmd(state, &cmd, NULL); -} - -static int stop(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - u32 input = state->tuner; - - memset(&cmd, 0, sizeof(cmd)); - if (state->demod != DEMOD_UNUSED) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - mci_cmd(state, &cmd, NULL); - if (state->base->iq_mode) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, SX8_TSCONFIG_MODE_NORMAL); - } - } - mutex_lock(&state->base->tuner_lock); - state->base->tuner_use_count[input]--; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 0); - if (state->demod < MCI_DEMOD_MAX) - state->base->demod_in_use[state->demod] = 0; - state->base->used_ldpc_bitrate[state->nr] = 0; - state->demod = DEMOD_UNUSED; - state->base->assigned_demod[state->nr] = DEMOD_UNUSED; - state->base->iq_mode = 0; - mutex_unlock(&state->base->tuner_lock); - state->started = 0; - return 0; -} - -static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - u32 bits_per_symbol = 0; - int i, stat = 0; - - if (p->symbol_rate >= (MCLK / 2)) - flags &= ~1; - if ((flags & 3) == 0) - return -EINVAL; - - if (flags & 2) { - u32 tmp = modmask; - - bits_per_symbol = 1; - while (tmp & 1) { - tmp >>= 1; - bits_per_symbol++; - } - } - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < MCI_DEMOD_MAX; i++) { - used_ldpc_bitrate += state->base->used_ldpc_bitrate[i]; - if (state->base->demod_in_use[i]) - used_demods++; - } - if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || - ((ts_config & SX8_TSCONFIG_MODE_MASK) > - SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { - stat = -EBUSY; - goto unlock; - } - free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; - if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) - free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; - - while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) - bits_per_symbol--; - - if (bits_per_symbol < 2) { - stat = -EBUSY; - goto unlock; - } - i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; - while (i >= 0 && state->base->demod_in_use[i]) - i--; - if (i < 0) { - stat = -EBUSY; - goto unlock; - } - state->base->demod_in_use[i] = 1; - state->base->used_ldpc_bitrate[state->nr] = p->symbol_rate - * bits_per_symbol; - state->demod = i; - state->base->assigned_demod[state->nr] = i; - - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - memset(&cmd, 0, sizeof(cmd)); - - if (state->base->iq_mode) { - cmd.command = SX8_CMD_SELECT_IQOUT; - cmd.demod = state->demod; - cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, ts_config); - } - if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) - flags |= 0x80; - dev_dbg(state->base->dev, "MCI-%d: tuner=%d demod=%d\n", - state->nr, state->tuner, state->demod); - cmd.command = MCI_CMD_SEARCH_DVBS; - cmd.dvbs2_search.flags = flags; - cmd.dvbs2_search.s2_modulation_mask = - modmask & ((1 << (bits_per_symbol - 1)) - 1); - cmd.dvbs2_search.retry = 2; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.dvbs2_search.scrambling_sequence_index = - p->scrambling_sequence_index; - cmd.dvbs2_search.input_stream_id = - (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = state->nr; - if (p->stream_id == 0x80000000) - cmd.output |= 0x80; - stat = mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - -static int start_iq(struct dvb_frontend *fe, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - int i, stat = 0; - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < MCI_DEMOD_MAX; i++) - if (state->base->demod_in_use[i]) - used_demods++; - if (used_demods > 0) { - stat = -EBUSY; - goto unlock; - } - state->demod = 0; - state->base->assigned_demod[state->nr] = 0; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - - memset(&cmd, 0, sizeof(cmd)); - cmd.command = SX8_CMD_START_IQ; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = 7; - mci_config(state, ts_config); - stat = mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - -static int set_parameters(struct dvb_frontend *fe) -{ - int stat = 0; - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 ts_config, iq_mode = 0, isi; - - if (state->started) - stop(fe); - - isi = p->stream_id; - if (isi != NO_STREAM_ID_FILTER) - iq_mode = (isi & 0x30000000) >> 28; - - switch (iq_mode) { - case 1: - ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); - break; - case 2: - ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); - break; - default: - ts_config = SX8_TSCONFIG_MODE_NORMAL; - break; - } - - if (iq_mode != 2) { - u32 flags = 3; - u32 mask = 3; - - if (p->modulation == APSK_16 || - p->modulation == APSK_32) { - flags = 2; - mask = 15; - } - stat = start(fe, flags, mask, ts_config); - } else { - stat = start_iq(fe, ts_config); - } - - if (!stat) { - state->started = 1; - state->first_time_lock = 1; - state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; - } - - return stat; -} - -static int tune(struct dvb_frontend *fe, bool re_tune, - unsigned int mode_flags, - unsigned int *delay, enum fe_status *status) -{ - int r; - - if (re_tune) { - r = set_parameters(fe); - if (r) - return r; - } - r = read_status(fe, status); - if (r) - return r; - - if (*status & FE_HAS_LOCK) - return 0; - *delay = HZ / 10; - return 0; -} - -static enum dvbfe_algo get_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int set_input(struct dvb_frontend *fe, int input) -{ - struct mci *state = fe->demodulator_priv; - - state->tuner = input; - dev_dbg(state->base->dev, "MCI-%d: input=%d\n", state->nr, input); - return 0; -} - -static struct dvb_frontend_ops mci_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2 }, - .info = { - .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, - .symbol_rate_min = 100000, - .symbol_rate_max = 100000000, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_2G_MODULATION | - FE_CAN_MULTISTREAM, - }, - .get_frontend_algo = get_algo, - .tune = tune, - .release = release, - .read_status = read_status, -}; - static struct mci_base *match_base(void *key) { struct mci_base *p; @@ -498,8 +124,7 @@ static int probe(struct mci *state) } struct dvb_frontend -*ddb_mci_attach(struct ddb_input *input, - int mci_type, int nr, +*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, int (**fn_set_input)(struct dvb_frontend *fe, int input)) { struct ddb_port *port = input->port; @@ -507,9 +132,9 @@ struct dvb_frontend struct ddb_link *link = &dev->link[port->lnr]; struct mci_base *base; struct mci *state; - void *key = mci_type ? (void *)port : (void *)link; + void *key = cfg->type ? (void *)port : (void *)link; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc(cfg->state_size, GFP_KERNEL); if (!state) return NULL; @@ -518,7 +143,7 @@ struct dvb_frontend base->count++; state->base = base; } else { - base = kzalloc(sizeof(*base), GFP_KERNEL); + base = kzalloc(cfg->base_size, GFP_KERNEL); if (!base) goto fail; base->key = key; @@ -535,15 +160,17 @@ struct dvb_frontend goto fail; } list_add(&base->mci_list, &mci_list); + if (cfg->base_init) + cfg->base_init(base); } - state->fe.ops = mci_ops; + memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops)); state->fe.demodulator_priv = state; state->nr = nr; - *fn_set_input = set_input; - + *fn_set_input = cfg->set_input; state->tuner = nr; state->demod = nr; - + if (cfg->init) + cfg->init(state); return &state->fe; fail: kfree(state); diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h index 209cc2b92dff..24241111c634 100644 --- a/drivers/media/pci/ddbridge/ddbridge-mci.h +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -2,9 +2,9 @@ /* * ddbridge-mci.h: Digital Devices micro code interface * - * Copyright (C) 2017 Digital Devices GmbH - * Marcus Metzler <mocm@metzlerbros.de> - * Ralph Metzler <rjkm@metzlerbros.de> + * Copyright (C) 2017-2018 Digital Devices GmbH + * Marcus Metzler <mocm@metzlerbros.de> + * Ralph Metzler <rjkm@metzlerbros.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,6 +42,22 @@ #define SX8_TSCONFIG_MODE_NORMAL (0x00000001) #define SX8_TSCONFIG_MODE_IQ (0x00000003) +/* + * IQMode is only available on MaxSX8 on a single tuner + * + * IQ_MODE_SAMPLES + * sampling rate is 1550/24 MHz (64.583 MHz) + * channel agc is frozen, to allow stitching the FFT results together + * + * IQ_MODE_VTM + * sampling rate is the supplied symbolrate + * channel agc is active + * + * in both cases down sampling is done with a RRC Filter (currently fixed to + * alpha = 0.05) which causes some (ca 5%) aliasing at the edges from + * outside the spectrum + */ + #define SX8_TSCONFIG_TSHEADER (0x00000004) #define SX8_TSCONFIG_BURST (0x00000008) @@ -51,55 +67,65 @@ #define SX8_TSCONFIG_BURSTSIZE_8K (0x00000020) #define SX8_TSCONFIG_BURSTSIZE_16K (0x00000030) -#define SX8_DEMOD_STOPPED (0) -#define SX8_DEMOD_IQ_MODE (1) -#define SX8_DEMOD_WAIT_SIGNAL (2) -#define SX8_DEMOD_WAIT_MATYPE (3) -#define SX8_DEMOD_TIMEOUT (14) -#define SX8_DEMOD_LOCKED (15) - -#define MCI_CMD_STOP (0x01) -#define MCI_CMD_GETSTATUS (0x02) -#define MCI_CMD_GETSIGNALINFO (0x03) -#define MCI_CMD_RFPOWER (0x04) - -#define MCI_CMD_SEARCH_DVBS (0x10) +#define SX8_DEMOD_STOPPED (0) +#define SX8_DEMOD_IQ_MODE (1) +#define SX8_DEMOD_WAIT_SIGNAL (2) +#define SX8_DEMOD_WAIT_MATYPE (3) +#define SX8_DEMOD_TIMEOUT (14) +#define SX8_DEMOD_LOCKED (15) -#define MCI_CMD_GET_IQSYMBOL (0x30) +#define MCI_CMD_STOP (0x01) +#define MCI_CMD_GETSTATUS (0x02) +#define MCI_CMD_GETSIGNALINFO (0x03) +#define MCI_CMD_RFPOWER (0x04) -#define SX8_CMD_INPUT_ENABLE (0x40) -#define SX8_CMD_INPUT_DISABLE (0x41) -#define SX8_CMD_START_IQ (0x42) -#define SX8_CMD_STOP_IQ (0x43) -#define SX8_CMD_SELECT_IQOUT (0x44) -#define SX8_CMD_SELECT_TSOUT (0x45) +#define MCI_CMD_SEARCH_DVBS (0x10) -#define SX8_ERROR_UNSUPPORTED (0x80) +#define MCI_CMD_GET_IQSYMBOL (0x30) -#define SX8_SUCCESS(status) (status < SX8_ERROR_UNSUPPORTED) +#define SX8_CMD_INPUT_ENABLE (0x40) +#define SX8_CMD_INPUT_DISABLE (0x41) +#define SX8_CMD_START_IQ (0x42) +#define SX8_CMD_STOP_IQ (0x43) +#define SX8_CMD_ENABLE_IQOUTPUT (0x44) +#define SX8_CMD_DISABLE_IQOUTPUT (0x45) -#define SX8_CMD_DIAG_READ8 (0xE0) -#define SX8_CMD_DIAG_READ32 (0xE1) -#define SX8_CMD_DIAG_WRITE8 (0xE2) -#define SX8_CMD_DIAG_WRITE32 (0xE3) +#define MCI_STATUS_OK (0x00) +#define MCI_STATUS_UNSUPPORTED (0x80) +#define MCI_STATUS_RETRY (0xFD) +#define MCI_STATUS_NOT_READY (0xFE) +#define MCI_STATUS_ERROR (0xFF) -#define SX8_CMD_DIAG_READRF (0xE8) -#define SX8_CMD_DIAG_WRITERF (0xE9) +#define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) struct mci_command { union { u32 command_word; struct { - u8 command; - u8 tuner; - u8 demod; - u8 output; + u8 command; + u8 tuner; + u8 demod; + u8 output; }; }; union { u32 params[31]; struct { + /* + * Bit 0: DVB-S Enabled + * Bit 1: DVB-S2 Enabled + * Bit 7: InputStreamID + */ u8 flags; + /* + * Bit 0: QPSK, + * Bit 1: 8PSK/8APSK + * Bit 2: 16APSK + * Bit 3: 32APSK + * Bit 4: 64APSK + * Bit 5: 128APSK + * Bit 6: 256APSK + */ u8 s2_modulation_mask; u8 rsvd1; u8 retry; @@ -108,7 +134,36 @@ struct mci_command { u8 input_stream_id; u8 rsvd2[3]; u32 scrambling_sequence_index; + u32 frequency_range; } dvbs2_search; + + struct { + u8 tap; + u8 rsvd; + u16 point; + } get_iq_symbol; + + struct { + /* + * Bit 0: 0=VTM/1=SCAN + * Bit 1: Set Gain + */ + u8 flags; + u8 roll_off; + u8 rsvd1; + u8 rsvd2; + u32 frequency; + u32 symbol_rate; /* Only in VTM mode */ + u16 gain; + } sx8_start_iq; + + struct { + /* + * Bit 1:0 = STVVGLNA Gain. + * 0 = AGC, 1 = 0dB, 2 = Minimum, 3 = Maximum + */ + u8 flags; + } sx8_input_enable; }; }; @@ -116,41 +171,92 @@ struct mci_result { union { u32 status_word; struct { - u8 status; - u8 rsvd; + u8 status; + u8 mode; u16 time; }; }; union { u32 result[27]; struct { + /* 1 = DVB-S, 2 = DVB-S2X */ u8 standard; /* puncture rate for DVB-S */ u8 pls_code; - /* 7-6: rolloff, 5-2: rsrvd, 1:short, 0:pilots */ + /* 2-0: rolloff */ u8 roll_off; u8 rsvd; + /* actual frequency in Hz */ u32 frequency; + /* actual symbolrate in Hz */ u32 symbol_rate; + /* channel power in dBm x 100 */ s16 channel_power; + /* band power in dBm x 100 */ s16 band_power; + /* + * SNR in dB x 100 + * Note: negative values are valid in DVB-S2 + */ s16 signal_to_noise; s16 rsvd2; + /* + * Counter for packet errors + * (set to 0 on start command) + */ u32 packet_errors; + /* Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X */ u32 ber_numerator; u32 ber_denominator; } dvbs2_signal_info; + struct { - u8 i_symbol; - u8 q_symbol; - } dvbs2_signal_iq; + s16 i; + s16 q; + } iq_symbol; }; u32 version[4]; }; +struct mci_base { + struct list_head mci_list; + void *key; + struct ddb_link *link; + struct completion completion; + struct device *dev; + struct mutex tuner_lock; /* concurrent tuner access lock */ + struct mutex mci_lock; /* concurrent MCI access lock */ + int count; + int type; +}; + +struct mci { + struct mci_base *base; + struct dvb_frontend fe; + int nr; + int demod; + int tuner; +}; + +struct mci_cfg { + int type; + struct dvb_frontend_ops *fe_ops; + u32 base_size; + u32 state_size; + int (*init)(struct mci *mci); + int (*base_init)(struct mci_base *mci_base); + int (*set_input)(struct dvb_frontend *fe, int input); +}; + +/* defined in ddbridge-sx8.c */ +extern const struct mci_cfg ddb_max_sx8_cfg; + +int ddb_mci_cmd(struct mci *state, struct mci_command *command, + struct mci_result *result); +int ddb_mci_config(struct mci *state, u32 config); + struct dvb_frontend -*ddb_mci_attach(struct ddb_input *input, - int mci_type, int nr, +*ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, int (**fn_set_input)(struct dvb_frontend *fe, int input)); #endif /* _DDBRIDGE_MCI_H_ */ diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h index b978b5991940..f9e1cbb99b53 100644 --- a/drivers/media/pci/ddbridge/ddbridge-regs.h +++ b/drivers/media/pci/ddbridge/ddbridge-regs.h @@ -34,14 +34,6 @@ #define GPIO_DIRECTION 0x28 /* ------------------------------------------------------------------------- */ -/* MDIO */ - -#define MDIO_CTRL 0x20 -#define MDIO_ADR 0x24 -#define MDIO_REG 0x28 -#define MDIO_VAL 0x2C - -/* ------------------------------------------------------------------------- */ #define BOARD_CONTROL 0x30 diff --git a/drivers/media/pci/ddbridge/ddbridge-sx8.c b/drivers/media/pci/ddbridge/ddbridge-sx8.c new file mode 100644 index 000000000000..64f05f5ee039 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-sx8.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ddbridge-sx8.c: Digital Devices MAX SX8 driver + * + * Copyright (C) 2018 Digital Devices GmbH + * Marcus Metzler <mocm@metzlerbros.de> + * Ralph Metzler <rjkm@metzlerbros.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ddbridge.h" +#include "ddbridge-io.h" +#include "ddbridge-mci.h" + +static const u32 MCLK = (1550000000 / 12); +static const u32 MAX_LDPC_BITRATE = (720000000); +static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); + +#define SX8_TUNER_NUM 4 +#define SX8_DEMOD_NUM 8 +#define SX8_DEMOD_NONE 0xff + +struct sx8_base { + struct mci_base mci_base; + + u8 tuner_use_count[SX8_TUNER_NUM]; + u32 gain_mode[SX8_TUNER_NUM]; + + u32 used_ldpc_bitrate[SX8_DEMOD_NUM]; + u8 demod_in_use[SX8_DEMOD_NUM]; + u32 iq_mode; + u32 burst_size; + u32 direct_mode; +}; + +struct sx8 { + struct mci mci; + + int first_time_lock; + int started; + struct mci_result signal_info; + + u32 bb_mode; + u32 local_frequency; +}; + +static void release(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + + mci_base->count--; + if (mci_base->count == 0) { + list_del(&mci_base->mci_list); + kfree(mci_base); + } + kfree(state); +} + +static int get_info(struct dvb_frontend *fe) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = MCI_CMD_GETSIGNALINFO; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info); + return stat; +} + +static int get_snr(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = + (s64)state->signal_info.dvbs2_signal_info.signal_to_noise + * 10; + return 0; +} + +static int get_strength(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 str; + + str = 100000 - + (state->signal_info.dvbs2_signal_info.channel_power + * 10 + 108750); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = str; + return 0; +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + struct mci_result res; + + cmd.command = MCI_CMD_GETSTATUS; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, &res); + if (stat) + return stat; + *status = 0x00; + get_info(fe); + get_strength(fe); + if (res.status == SX8_DEMOD_WAIT_MATYPE) + *status = 0x0f; + if (res.status == SX8_DEMOD_LOCKED) { + *status = 0x1f; + get_snr(fe); + } + return stat; +} + +static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tuner = state->mci.tuner; + cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; + cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner]; + return ddb_mci_cmd(&state->mci, &cmd, NULL); +} + +static int stop(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct mci_command cmd; + u32 input = state->mci.tuner; + + memset(&cmd, 0, sizeof(cmd)); + if (state->mci.demod != SX8_DEMOD_NONE) { + cmd.command = MCI_CMD_STOP; + cmd.demod = state->mci.demod; + ddb_mci_cmd(&state->mci, &cmd, NULL); + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_DISABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); + } + } + mutex_lock(&mci_base->tuner_lock); + sx8_base->tuner_use_count[input]--; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 0); + if (state->mci.demod < SX8_DEMOD_NUM) { + sx8_base->demod_in_use[state->mci.demod] = 0; + state->mci.demod = SX8_DEMOD_NONE; + } + sx8_base->used_ldpc_bitrate[state->mci.nr] = 0; + sx8_base->iq_mode = 0; + mutex_unlock(&mci_base->tuner_lock); + state->started = 0; + return 0; +} + +static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + u32 bits_per_symbol = 0; + int i = -1, stat = 0; + + if (p->symbol_rate >= (MCLK / 2)) + flags &= ~1; + if ((flags & 3) == 0) + return -EINVAL; + + if (flags & 2) { + u32 tmp = modmask; + + bits_per_symbol = 1; + while (tmp & 1) { + tmp >>= 1; + bits_per_symbol++; + } + } + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + + if (sx8_base->direct_mode) { + if (p->symbol_rate >= MCLK / 2) { + if (state->mci.nr < 4) + i = state->mci.nr; + } else { + i = state->mci.nr; + } + } else { + for (i = 0; i < SX8_DEMOD_NUM; i++) { + used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i]; + if (sx8_base->demod_in_use[i]) + used_demods++; + } + if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || + ((ts_config & SX8_TSCONFIG_MODE_MASK) > + SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { + stat = -EBUSY; + goto unlock; + } + free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; + if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) + free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; + + while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) + bits_per_symbol--; + if (bits_per_symbol < 2) { + stat = -EBUSY; + goto unlock; + } + + modmask &= ((1 << (bits_per_symbol - 1)) - 1); + if (((flags & 0x02) != 0) && modmask == 0) { + stat = -EBUSY; + goto unlock; + } + + i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; + while (i >= 0 && sx8_base->demod_in_use[i]) + i--; + } + + if (i < 0) { + stat = -EBUSY; + goto unlock; + } + sx8_base->demod_in_use[i] = 1; + sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate + * bits_per_symbol; + state->mci.demod = i; + + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + memset(&cmd, 0, sizeof(cmd)); + + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_ENABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, ts_config); + } + if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) + flags |= 0x80; + dev_dbg(mci_base->dev, "MCI-%d: tuner=%d demod=%d\n", + state->mci.nr, state->mci.tuner, state->mci.demod); + cmd.command = MCI_CMD_SEARCH_DVBS; + cmd.dvbs2_search.flags = flags; + cmd.dvbs2_search.s2_modulation_mask = modmask; + cmd.dvbs2_search.retry = 2; + cmd.dvbs2_search.frequency = p->frequency * 1000; + cmd.dvbs2_search.symbol_rate = p->symbol_rate; + cmd.dvbs2_search.scrambling_sequence_index = + p->scrambling_sequence_index | 0x80000000; + cmd.dvbs2_search.input_stream_id = + (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + cmd.output = state->mci.nr; + if (p->stream_id == 0x80000000) + cmd.output |= 0x80; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + return stat; +} + +static int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off, + u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *)mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + int i, stat = 0; + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + for (i = 0; i < SX8_DEMOD_NUM; i++) + if (sx8_base->demod_in_use[i]) + used_demods++; + if (used_demods > 0) { + stat = -EBUSY; + goto unlock; + } + state->mci.demod = 0; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = SX8_CMD_START_IQ; + cmd.sx8_start_iq.flags = flags; + cmd.sx8_start_iq.roll_off = roll_off; + cmd.sx8_start_iq.frequency = p->frequency * 1000; + cmd.sx8_start_iq.symbol_rate = p->symbol_rate; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + ddb_mci_config(&state->mci, ts_config); + return stat; +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi; + + if (state->started) + stop(fe); + + isi = p->stream_id; + if (isi != NO_STREAM_ID_FILTER) + iq_mode = (isi & 0x30000000) >> 28; + + if (iq_mode) + ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + if (iq_mode < 3) { + u32 mask; + + switch (p->modulation) { + /* uncomment whenever these modulations hit the DVB API + * case APSK_256: + * mask = 0x7f; + * break; + * case APSK_128: + * mask = 0x3f; + * break; + * case APSK_64: + * mask = 0x1f; + * break; + */ + case APSK_32: + mask = 0x0f; + break; + case APSK_16: + mask = 0x07; + break; + default: + mask = 0x03; + break; + } + stat = start(fe, 3, mask, ts_config); + } else { + u32 flags = (iq_mode == 2) ? 1 : 0; + + stat = start_iq(fe, flags, 4, ts_config); + } + if (!stat) { + state->started = 1; + state->first_time_lock = 1; + state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; + } + + return stat; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + int r; + + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + } + r = read_status(fe, status); + if (r) + return r; + + if (*status & FE_HAS_LOCK) + return 0; + *delay = HZ / 10; + return 0; +} + +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int set_input(struct dvb_frontend *fe, int input) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + + if (input >= SX8_TUNER_NUM) + return -EINVAL; + + state->mci.tuner = input; + dev_dbg(mci_base->dev, "MCI-%d: input=%d\n", state->mci.nr, input); + return 0; +} + +static struct dvb_frontend_ops sx8_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .symbol_rate_min = 100000, + .symbol_rate_max = 100000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM, + }, + .get_frontend_algo = get_algo, + .tune = tune, + .release = release, + .read_status = read_status, +}; + +static int init(struct mci *mci) +{ + struct sx8 *state = (struct sx8 *)mci; + + state->mci.demod = SX8_DEMOD_NONE; + return 0; +} + +const struct mci_cfg ddb_max_sx8_cfg = { + .type = 0, + .fe_ops = &sx8_ops, + .base_size = sizeof(struct sx8_base), + .state_size = sizeof(struct sx8), + .init = init, + .set_input = set_input, +}; diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index a66b1125cc74..8a354dfb6c22 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -120,21 +120,23 @@ struct ddb_info { #define DDB_OCTOPUS_MCI 9 char *name; u32 i2c_mask; + u32 board_control; + u32 board_control_2; + u8 port_num; u8 led_num; u8 fan_num; u8 temp_num; u8 temp_bus; - u32 board_control; - u32 board_control_2; - u8 mdio_num; u8 con_clock; /* use a continuous clock */ u8 ts_quirks; #define TS_QUIRK_SERIAL 1 #define TS_QUIRK_REVERSED 2 #define TS_QUIRK_ALT_OSC 8 + u8 mci_ports; + u8 mci_type; + u32 tempmon_irq; - u8 mci; const struct ddb_regmap *regmap; }; @@ -255,7 +257,6 @@ struct ddb_port { #define DDB_CI_EXTERNAL_XO2_B 13 #define DDB_TUNER_DVBS_STV0910_PR 14 #define DDB_TUNER_DVBC2T2I_SONY_P 15 -#define DDB_TUNER_MCI 16 #define DDB_TUNER_XO2 32 #define DDB_TUNER_DVBS_STV0910 (DDB_TUNER_XO2 + 0) @@ -265,6 +266,9 @@ struct ddb_port { #define DDB_TUNER_ATSC_ST (DDB_TUNER_XO2 + 4) #define DDB_TUNER_DVBC2T2I_SONY (DDB_TUNER_XO2 + 5) +#define DDB_TUNER_MCI 48 +#define DDB_TUNER_MCI_SX8 (DDB_TUNER_MCI + 0) + struct ddb_input *input[2]; struct ddb_output *output; struct dvb_ca_en50221 *en; diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index c9db108751a7..1ddb0576fb7b 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -986,6 +986,9 @@ static int dm1105_probe(struct pci_dev *pdev, int ret = -ENOMEM; int i; + if (dm1105_devcount >= ARRAY_SIZE(card)) + return -ENODEV; + dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); if (!dev) return -ENOMEM; diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 6b2ffdc96961..dd727098daf4 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -999,7 +999,7 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) int vbi_buf_size; struct ivtv *itv; - itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); + itv = kzalloc(sizeof(struct ivtv), GFP_KERNEL); if (itv == NULL) return -ENOMEM; itv->pdev = pdev; diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 522cd111e399..e9ce54dd5e01 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -293,6 +293,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) .platform_data = &pdata, }; + memset(&pdata, 0, sizeof(pdata)); pdata.pvr150_workaround = itv->pvr150_workaround; sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, &cx25840_info, NULL); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index b19058e36853..5ddaa8ed11a5 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -1178,7 +1178,7 @@ static int ivtvfb_init_card(struct ivtv *itv) } itv->osd_info = kzalloc(sizeof(struct osd_info), - GFP_ATOMIC|__GFP_NOWARN); + GFP_KERNEL|__GFP_NOWARN); if (itv->osd_info == NULL) { IVTVFB_ERR("Failed to allocate memory for osd_info\n"); return -ENOMEM; diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c index 14f6e153000c..9797c9fd8259 100644 --- a/drivers/media/pci/mantis/mantis_vp3030.c +++ b/drivers/media/pci/mantis/mantis_vp3030.c @@ -42,8 +42,8 @@ static struct zl10353_config mantis_vp3030_config = { static struct tda665x_config env57h12d5_config = { .name = "ENV57H12D5 (ET-50DT)", .addr = 0x60, - .frequency_min = 47000000, - .frequency_max = 862000000, + .frequency_min = 47 * MHz, + .frequency_max = 862 * MHz, .frequency_offst = 3616667, .ref_multiplier = 6, /* 1/6 MHz */ .ref_divider = 100000, /* 1/6 MHz */ diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c index b13e319d24b7..5f1613aec93c 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_i2c.c @@ -214,11 +214,6 @@ static int netup_i2c_xfer(struct i2c_adapter *adap, struct netup_i2c *i2c = i2c_get_adapdata(adap); u16 reg; - if (num <= 0) { - dev_dbg(i2c->adap.dev.parent, - "%s(): num == %d\n", __func__, num); - return -EINVAL; - } spin_lock_irqsave(&i2c->lock, flags); if (i2c->state != STATE_DONE) { dev_dbg(i2c->adap.dev.parent, diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c index fda969a85684..7f878fc41b7e 100644 --- a/drivers/media/pci/pt1/pt1.c +++ b/drivers/media/pci/pt1/pt1.c @@ -1443,9 +1443,7 @@ static struct pci_driver pt1_driver = { .probe = pt1_probe, .remove = pt1_remove, .id_table = pt1_id_table, -#ifdef CONFIG_PM_SLEEP .driver.pm = &pt1_pm_ops, -#endif }; module_pci_driver(pt1_driver); diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c index 64ab91c24c18..221de91a8bae 100644 --- a/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/drivers/media/pci/saa7164/saa7164-vbi.c @@ -629,12 +629,12 @@ static __poll_t fops_poll(struct file *file, poll_table *wait) port->last_poll_msecs_diff); if (!video_is_registered(port->v4l_device)) - return -EIO; + return EPOLLERR; if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { if (atomic_inc_return(&port->v4l_reader_count) == 1) { if (saa7164_vbi_initialize(port) < 0) - return -EINVAL; + return EPOLLERR; saa7164_vbi_start_streaming(port); msleep(200); } @@ -644,7 +644,7 @@ static __poll_t fops_poll(struct file *file, poll_table *wait) if ((file->f_flags & O_NONBLOCK) == 0) { if (wait_event_interruptible(port->wait_read, saa7164_vbi_next_buf(port))) { - return -ERESTARTSYS; + return EPOLLERR; } } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 069c4a853346..1858efedaf1a 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -116,6 +116,7 @@ static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2) * @sequence: sequence number of acquired buffer * @active: current active buffer * @lock: used in videobuf2 callback + * @v4l_lock: serialize its video4linux ioctls * @tcount: Number of top frames * @bcount: Number of bottom frames * @overflow: Number of FIFO overflows @@ -145,6 +146,7 @@ struct sta2x11_vip { unsigned int sequence; struct vip_buffer *active; /* current active buffer */ spinlock_t lock; /* Used in videobuf2 callback */ + struct mutex v4l_lock; /* Interrupt counters */ int tcount, bcount; @@ -385,6 +387,8 @@ static const struct vb2_ops vip_video_qops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; @@ -870,6 +874,7 @@ static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vip->vb_vidq.dev = &vip->pdev->dev; + vip->vb_vidq.lock = &vip->v4l_lock; err = vb2_queue_init(&vip->vb_vidq); if (err) return err; @@ -1034,6 +1039,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->std = V4L2_STD_PAL; vip->format = formats_50[0]; vip->config = config; + mutex_init(&vip->v4l_lock); ret = sta2x11_vip_init_controls(vip); if (ret) @@ -1080,6 +1086,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vip->video_dev = video_dev_template; vip->video_dev.v4l2_dev = &vip->v4l2_dev; vip->video_dev.queue = &vip->vb_vidq; + 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); diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index 0ea8dd44026c..3a06c000f97b 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1190,6 +1190,14 @@ int tw686x_video_init(struct tw686x_dev *dev) return err; } + /* Initialize vc->dev and vc->ch for the error path */ + for (ch = 0; ch < max_channels(dev); ch++) { + struct tw686x_video_channel *vc = &dev->video_channels[ch]; + + vc->dev = dev; + vc->ch = ch; + } + for (ch = 0; ch < max_channels(dev); ch++) { struct tw686x_video_channel *vc = &dev->video_channels[ch]; struct video_device *vdev; @@ -1198,9 +1206,6 @@ int tw686x_video_init(struct tw686x_dev *dev) spin_lock_init(&vc->qlock); INIT_LIST_HEAD(&vc->vidq_queued); - vc->dev = dev; - vc->ch = ch; - /* default settings */ err = tw686x_set_standard(vc, V4L2_STD_NTSC); if (err) diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 2728376b04b5..b25c8d3c1c31 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -90,7 +90,7 @@ config VIDEO_PXA27x This is a v4l2 driver for the PXA27x Quick Capture Interface config VIDEO_QCOM_CAMSS - tristate "Qualcomm 8x16 V4L2 Camera Subsystem driver" + tristate "Qualcomm V4L2 Camera Subsystem driver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST select VIDEOBUF2_DMA_SG @@ -384,8 +384,8 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_FDP1 tristate "Renesas Fine Display Processor" depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_SHMOBILE || COMPILE_TEST - depends on (!ARCH_RENESAS && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP + depends on ARCH_RENESAS || COMPILE_TEST + depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -514,6 +514,9 @@ config VIDEO_VIM2M ---help--- This is a virtual test device for the memory-to-memory driver framework. + +source "drivers/media/platform/vicodec/Kconfig" + endif #V4L_TEST_DRIVERS menuconfig DVB_PLATFORM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 04bc1502a30e..08640ba87fc2 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIMC) += vimc/ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o +obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/ @@ -88,7 +89,7 @@ obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/ obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/ -obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ +obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index e5be21a31640..e8db4df1e7c4 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1106,23 +1106,20 @@ static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) struct device_node *ep = NULL; struct device_node *remote; - while (1) { - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + return -EINVAL; - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - of_node_put(ep); - return -EINVAL; - } + remote = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (!remote) + return -EINVAL; - /* Remote node to connect */ - isi->entity.node = remote; - isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - isi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; - } + /* Remote node to connect */ + isi->entity.node = remote; + isi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + isi->entity.asd.match.fwnode = of_fwnode_handle(remote); + return 0; } static int isi_graph_init(struct atmel_isi *isi) diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig index 3bf0f2454384..cf6124da3c54 100644 --- a/drivers/media/platform/cadence/Kconfig +++ b/drivers/media/platform/cadence/Kconfig @@ -11,6 +11,7 @@ if VIDEO_CADENCE config VIDEO_CADENCE_CSI2RX tristate "Cadence MIPI-CSI2 RX Controller" + depends on VIDEO_V4L2 depends on MEDIA_CONTROLLER depends on VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE @@ -22,6 +23,7 @@ config VIDEO_CADENCE_CSI2RX config VIDEO_CADENCE_CSI2TX tristate "Cadence MIPI-CSI2 TX Controller" + depends on VIDEO_V4L2 depends on MEDIA_CONTROLLER depends on VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index a0f02916006b..43e43c7b3e98 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -13,6 +13,7 @@ #include <linux/of_graph.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index dfa1d88d955b..40d0de690ff4 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index 69f8242209c2..d2861749d640 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -23,6 +23,11 @@ struct cec_gpio { int hpd_irq; bool hpd_is_high; ktime_t hpd_ts; + + struct gpio_desc *v5_gpio; + int v5_irq; + bool v5_is_high; + ktime_t v5_ts; }; static bool cec_gpio_read(struct cec_adapter *adap) @@ -65,6 +70,26 @@ static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv) return IRQ_HANDLED; } +static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + bool is_high = gpiod_get_value(cec->v5_gpio); + + if (is_high == cec->v5_is_high) + return IRQ_HANDLED; + cec->v5_ts = ktime_get(); + cec->v5_is_high = is_high; + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv) +{ + struct cec_gpio *cec = priv; + + cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts); + return IRQ_HANDLED; +} + static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv) { struct cec_gpio *cec = priv; @@ -119,6 +144,9 @@ static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) if (cec->hpd_gpio) seq_printf(file, "hpd: %s\n", cec->hpd_is_high ? "high" : "low"); + if (cec->v5_gpio) + seq_printf(file, "5V: %s\n", + cec->v5_is_high ? "high" : "low"); } static int cec_gpio_read_hpd(struct cec_adapter *adap) @@ -130,6 +158,15 @@ static int cec_gpio_read_hpd(struct cec_adapter *adap) return gpiod_get_value(cec->hpd_gpio); } +static int cec_gpio_read_5v(struct cec_adapter *adap) +{ + struct cec_gpio *cec = cec_get_drvdata(adap); + + if (!cec->v5_gpio) + return -ENOTTY; + return gpiod_get_value(cec->v5_gpio); +} + static void cec_gpio_free(struct cec_adapter *adap) { cec_gpio_disable_irq(adap); @@ -144,6 +181,7 @@ static const struct cec_pin_ops cec_gpio_pin_ops = { .status = cec_gpio_status, .free = cec_gpio_free, .read_hpd = cec_gpio_read_hpd, + .read_5v = cec_gpio_read_5v, }; static int cec_gpio_probe(struct platform_device *pdev) @@ -167,6 +205,10 @@ static int cec_gpio_probe(struct platform_device *pdev) if (IS_ERR(cec->hpd_gpio)) return PTR_ERR(cec->hpd_gpio); + cec->v5_gpio = devm_gpiod_get_optional(dev, "v5", GPIOD_IN); + if (IS_ERR(cec->v5_gpio)) + return PTR_ERR(cec->v5_gpio); + cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops, cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN); @@ -185,6 +227,18 @@ static int cec_gpio_probe(struct platform_device *pdev) return ret; } + if (cec->v5_gpio) { + cec->v5_irq = gpiod_to_irq(cec->v5_gpio); + ret = devm_request_threaded_irq(dev, cec->v5_irq, + cec_5v_gpio_irq_handler, + cec_5v_gpio_irq_handler_thread, + IRQF_ONESHOT | + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "v5-gpio", cec); + if (ret) + return ret; + } + ret = cec_register_adapter(cec->adap, &pdev->dev); if (ret) { cec_delete_adapter(cec->adap); diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 68ed2a564ad1..d26c2d85a009 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -261,11 +261,12 @@ void coda_fill_bitstream(struct coda_ctx *ctx, struct list_head *buffer_list) while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) { /* - * Only queue a single JPEG into the bitstream buffer, except - * to increase payload over 512 bytes or if in hold state. + * Only queue two JPEGs into the bitstream buffer to keep + * latency low. We need at least one complete buffer and the + * header of another buffer (for prescan) in the bitstream. */ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG && - (coda_get_bitstream_payload(ctx) >= 512) && !ctx->hold) + ctx->num_metas > 1) break; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); @@ -389,32 +390,29 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) { struct coda_dev *dev = ctx->dev; - int width, height; - int ysize; + unsigned int ysize, ycbcr_size; int ret; int i; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 || ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) { - width = round_up(q_data->width, 16); - height = round_up(q_data->height, 16); - } else { - width = round_up(q_data->width, 8); - height = q_data->height; - } - ysize = width * height; + ctx->codec->dst_fourcc == V4L2_PIX_FMT_MPEG4) + ysize = round_up(q_data->rect.width, 16) * + round_up(q_data->rect.height, 16); + else + ysize = round_up(q_data->rect.width, 8) * q_data->rect.height; + + if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) + ycbcr_size = round_up(ysize, 4096) + ysize / 2; + else + ycbcr_size = ysize + ysize / 2; /* Allocate frame buffers */ for (i = 0; i < ctx->num_internal_frames; i++) { - size_t size; + size_t size = ycbcr_size; char *name; - if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) - size = round_up(ysize, 4096) + ysize / 2; - else - size = ysize + ysize / 2; /* Add space for mvcol buffers */ if (dev->devtype->product != CODA_DX6 && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || @@ -499,8 +497,8 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->width, 16) * - DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512; + size = (DIV_ROUND_UP(q_data->rect.width, 16) * + DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); if (ret < 0) @@ -538,6 +536,8 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, { struct vb2_buffer *vb = &buf->vb2_buf; struct coda_dev *dev = ctx->dev; + struct coda_q_data *q_data_src; + struct v4l2_rect *r; size_t bufsize; int ret; int i; @@ -551,6 +551,23 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf, if (dev->devtype->product == CODA_960) bufsize /= 1024; coda_write(dev, bufsize, CODA_CMD_ENC_HEADER_BB_SIZE); + if (dev->devtype->product == CODA_960 && + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264 && + header_code == CODA_HEADER_H264_SPS) { + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + r = &q_data_src->rect; + + if (r->width % 16 || r->height % 16) { + u32 crop_right = round_up(r->width, 16) - r->width; + u32 crop_bottom = round_up(r->height, 16) - r->height; + + coda_write(dev, crop_right, + CODA9_CMD_ENC_HEADER_FRAME_CROP_H); + coda_write(dev, crop_bottom, + CODA9_CMD_ENC_HEADER_FRAME_CROP_V); + header_code |= CODA9_HEADER_FRAME_CROP; + } + } coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); if (ret < 0) { @@ -632,7 +649,7 @@ static void coda_setup_iram(struct coda_ctx *ctx) struct coda_q_data *q_data_src; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - mb_width = DIV_ROUND_UP(q_data_src->width, 16); + mb_width = DIV_ROUND_UP(q_data_src->rect.width, 16); w128 = mb_width * 128; w64 = mb_width * 64; @@ -721,6 +738,7 @@ static u32 coda_supported_firmwares[] = { CODA_FIRMWARE_VERNUM(CODA_HX4, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50), CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 5), + CODA_FIRMWARE_VERNUM(CODA_960, 2, 1, 9), CODA_FIRMWARE_VERNUM(CODA_960, 2, 3, 10), CODA_FIRMWARE_VERNUM(CODA_960, 3, 1, 1), }; @@ -928,25 +946,25 @@ static int coda_start_encoding(struct coda_ctx *ctx) value = 0; switch (dev->devtype->product) { case CODA_DX6: - value = (q_data_src->width & CODADX6_PICWIDTH_MASK) + value = (q_data_src->rect.width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) + value |= (q_data_src->rect.height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; case CODA_HX4: case CODA_7541: if (dst_fourcc == V4L2_PIX_FMT_H264) { - value = (round_up(q_data_src->width, 16) & + value = (round_up(q_data_src->rect.width, 16) & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (round_up(q_data_src->height, 16) & + value |= (round_up(q_data_src->rect.height, 16) & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; } /* fallthrough */ case CODA_960: - value = (q_data_src->width & CODA7_PICWIDTH_MASK) + value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; - value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) + value |= (q_data_src->rect.height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; } coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); @@ -1198,6 +1216,27 @@ static int coda_start_encoding(struct coda_ctx *ctx) goto out; /* + * If visible width or height are not aligned to macroblock + * size, the crop_right and crop_bottom SPS fields must be set + * to the difference between visible and coded size. This is + * only supported by CODA960 firmware. All others do not allow + * writing frame cropping parameters, so we have to manually + * fix up the SPS RBSP (Sequence Parameter Set Raw Byte + * Sequence Payload) ourselves. + */ + if (ctx->dev->devtype->product != CODA_960 && + ((q_data_src->rect.width % 16) || + (q_data_src->rect.height % 16))) { + ret = coda_h264_sps_fixup(ctx, q_data_src->rect.width, + q_data_src->rect.height, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0], + sizeof(ctx->vpu_header[0])); + if (ret < 0) + goto out; + } + + /* * Get PPS in the first frame and copy it to an * intermediate buffer. */ @@ -1362,7 +1401,8 @@ static int coda_prepare_encode(struct coda_ctx *ctx) if (dev->devtype->product == CODA_960) { coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX); - coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE); + coda_write(dev, q_data_src->bytesperline, + CODA9_CMD_ENC_PIC_SRC_STRIDE); coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC); reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y; @@ -1582,10 +1622,8 @@ static int coda_decoder_reqbufs(struct coda_ctx *ctx, static bool coda_reorder_enable(struct coda_ctx *ctx) { - const char * const *profile_names; - const char * const *level_names; struct coda_dev *dev = ctx->dev; - int profile, level; + int profile; if (dev->devtype->product != CODA_HX4 && dev->devtype->product != CODA_7541 && @@ -1599,24 +1637,9 @@ static bool coda_reorder_enable(struct coda_ctx *ctx) return true; profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) { - v4l2_warn(&dev->v4l2_dev, "Invalid H264 Profile: %d\n", - ctx->params.h264_profile_idc); - return false; - } - - level = coda_h264_level(ctx->params.h264_level_idc); - if (level < 0) { - v4l2_warn(&dev->v4l2_dev, "Invalid H264 Level: %d\n", - ctx->params.h264_level_idc); - return false; - } - - profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); - level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); - - v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "H264 Profile/Level: %s L%s\n", - profile_names[profile], level_names[level]); + if (profile < 0) + v4l2_warn(&dev->v4l2_dev, "Unknown H264 Profile: %u\n", + ctx->params.h264_profile_idc); /* Baseline profile does not support reordering */ return profile > V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; @@ -1694,6 +1717,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) coda_write(dev, 512, CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE); } } + if (src_fourcc == V4L2_PIX_FMT_JPEG) + coda_write(dev, 0, CODA_CMD_DEC_SEQ_JPG_THUMB_EN); if (dev->devtype->product != CODA_960) coda_write(dev, 0, CODA_CMD_DEC_SEQ_SRC_SIZE); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index c7631e117dd3..726b3b93a486 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -569,8 +569,6 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, f->fmt.pix.height * 2; break; case V4L2_PIX_FMT_JPEG: - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - /* fallthrough */ case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_MPEG4: case V4L2_PIX_FMT_MPEG2: @@ -935,6 +933,40 @@ static int coda_g_selection(struct file *file, void *fh, return 0; } +static int coda_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct coda_ctx *ctx = fh_to_ctx(fh); + struct coda_q_data *q_data; + + if (ctx->inst_type == CODA_INST_ENCODER && + s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + s->target == V4L2_SEL_TGT_CROP) { + q_data = get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + s->r.left = 0; + s->r.top = 0; + s->r.width = clamp(s->r.width, 2U, q_data->width); + s->r.height = clamp(s->r.height, 2U, q_data->height); + + if (s->flags & V4L2_SEL_FLAG_LE) { + s->r.width = round_up(s->r.width, 2); + s->r.height = round_up(s->r.height, 2); + } else { + s->r.width = round_down(s->r.width, 2); + s->r.height = round_down(s->r.height, 2); + } + + q_data->rect = s->r; + + return 0; + } + + return coda_g_selection(file, fh, s); +} + static int coda_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec) { @@ -1148,6 +1180,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, .vidioc_g_selection = coda_g_selection, + .vidioc_s_selection = coda_s_selection, .vidioc_try_encoder_cmd = coda_try_encoder_cmd, .vidioc_encoder_cmd = coda_encoder_cmd, @@ -1297,28 +1330,10 @@ static void coda_job_abort(void *priv) "Aborting task\n"); } -static void coda_lock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - - mutex_lock(&pcdev->dev_mutex); -} - -static void coda_unlock(void *m2m_priv) -{ - struct coda_ctx *ctx = m2m_priv; - struct coda_dev *pcdev = ctx->dev; - - mutex_unlock(&pcdev->dev_mutex); -} - static const struct v4l2_m2m_ops coda_m2m_ops = { .device_run = coda_device_run, .job_ready = coda_job_ready, .job_abort = coda_job_abort, - .lock = coda_lock, - .unlock = coda_unlock, }; static void set_default_params(struct coda_ctx *ctx) @@ -1413,6 +1428,72 @@ static int coda_buf_prepare(struct vb2_buffer *vb) return 0; } +static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) +{ + if (!ctrl) + return; + + v4l2_ctrl_lock(ctrl); + + /* + * Extend the control range if the parsed stream contains a known but + * unsupported value or level. + */ + if (value > ctrl->maximum) { + __v4l2_ctrl_modify_range(ctrl, ctrl->minimum, value, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } else if (value < ctrl->minimum) { + __v4l2_ctrl_modify_range(ctrl, value, ctrl->maximum, + ctrl->menu_skip_mask & ~(1 << value), + ctrl->default_value); + } + + __v4l2_ctrl_s_ctrl(ctrl, value); + + v4l2_ctrl_unlock(ctrl); +} + +static void coda_update_h264_profile_ctrl(struct coda_ctx *ctx) +{ + const char * const *profile_names; + int profile; + + profile = coda_h264_profile(ctx->params.h264_profile_idc); + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Profile: %u\n", + ctx->params.h264_profile_idc); + return; + } + + coda_update_menu_ctrl(ctx->h264_profile_ctrl, profile); + + profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Parsed H264 Profile: %s\n", + profile_names[profile]); +} + +static void coda_update_h264_level_ctrl(struct coda_ctx *ctx) +{ + const char * const *level_names; + int level; + + level = coda_h264_level(ctx->params.h264_level_idc); + if (level < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Level: %u\n", + ctx->params.h264_level_idc); + return; + } + + coda_update_menu_ctrl(ctx->h264_level_ctrl, level); + + level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); + + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Parsed H264 Level: %s\n", + level_names[level]); +} + static void coda_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -1441,8 +1522,11 @@ static void coda_buf_queue(struct vb2_buffer *vb) * whether to enable reordering during sequence * initialization. */ - if (!ctx->params.h264_profile_idc) + if (!ctx->params.h264_profile_idc) { coda_sps_parse_profile(ctx, vb); + coda_update_h264_profile_ctrl(ctx); + coda_update_h264_level_ctrl(ctx); + } } mutex_lock(&ctx->bitstream_mutex); @@ -1538,12 +1622,12 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) goto out; q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if ((q_data_src->width != q_data_dst->width && - round_up(q_data_src->width, 16) != q_data_dst->width) || - (q_data_src->height != q_data_dst->height && - round_up(q_data_src->height, 16) != q_data_dst->height)) { + if ((q_data_src->rect.width != q_data_dst->width && + round_up(q_data_src->rect.width, 16) != q_data_dst->width) || + (q_data_src->rect.height != q_data_dst->height && + round_up(q_data_src->rect.height, 16) != q_data_dst->height)) { v4l2_err(v4l2_dev, "can't convert %dx%d to %dx%d\n", - q_data_src->width, q_data_src->height, + q_data_src->rect.width, q_data_src->rect.height, q_data_dst->width, q_data_dst->height); ret = -EINVAL; goto err; @@ -1652,6 +1736,7 @@ static void coda_stop_streaming(struct vb2_queue *q) ctx->bitstream.vaddr, ctx->bitstream.size); ctx->runcounter = 0; ctx->aborting = 0; + ctx->hold = false; } if (!ctx->streamon_out && !ctx->streamon_cap) @@ -1880,6 +1965,47 @@ static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx) V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0); } +static void coda_decode_ctrls(struct coda_ctx *ctx) +{ + u64 mask; + u8 max; + + ctx->h264_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + if (ctx->h264_profile_ctrl) + ctx->h264_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + if (ctx->dev->devtype->product == CODA_HX4 || + ctx->dev->devtype->product == CODA_7541) { + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + mask = ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0)); + } else if (ctx->dev->devtype->product == CODA_960) { + max = V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + mask = ~((1 << V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + (1 << V4L2_MPEG_VIDEO_H264_LEVEL_4_1)); + } else { + return; + } + ctx->h264_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, mask, + max); + if (ctx->h264_level_ctrl) + ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; +} + static int coda_ctrls_setup(struct coda_ctx *ctx) { v4l2_ctrl_handler_init(&ctx->ctrls, 2); @@ -1893,6 +2019,9 @@ static int coda_ctrls_setup(struct coda_ctx *ctx) coda_jpeg_encode_ctrls(ctx); else coda_encode_ctrls(ctx); + } else { + if (ctx->cvd->src_formats[0] == V4L2_PIX_FMT_H264) + coda_decode_ctrls(ctx); } if (ctx->ctrls.error) { @@ -2092,9 +2221,9 @@ static int coda_open(struct file *file) INIT_LIST_HEAD(&ctx->buffer_meta_list); spin_lock_init(&ctx->buffer_meta_lock); - coda_lock(ctx); + mutex_lock(&dev->dev_mutex); list_add(&ctx->list, &dev->instances); - coda_unlock(ctx); + mutex_unlock(&dev->dev_mutex); v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", ctx->idx, ctx); @@ -2142,9 +2271,9 @@ static int coda_release(struct file *file) flush_work(&ctx->seq_end_work); } - coda_lock(ctx); + mutex_lock(&dev->dev_mutex); list_del(&ctx->list); - coda_unlock(ctx); + mutex_unlock(&dev->dev_mutex); if (ctx->dev->devtype->product == CODA_DX6) coda_free_aux_buf(dev, &ctx->workbuf); diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c index 0e27412e01f5..635356a839cf 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/coda/coda-h264.c @@ -108,6 +108,325 @@ int coda_h264_level(int level_idc) case 32: return V4L2_MPEG_VIDEO_H264_LEVEL_3_2; case 40: return V4L2_MPEG_VIDEO_H264_LEVEL_4_0; case 41: return V4L2_MPEG_VIDEO_H264_LEVEL_4_1; + case 42: return V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + case 50: return V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + case 51: return V4L2_MPEG_VIDEO_H264_LEVEL_5_1; default: return -EINVAL; } } + +struct rbsp { + char *buf; + int size; + int pos; +}; + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + return (rbsp->buf[ofs] >> shift) & 1; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, int bit) +{ + int shift = 7 - (rbsp->pos % 8); + int ofs = rbsp->pos++ / 8; + + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->buf[ofs] &= ~(1 << shift); + rbsp->buf[ofs] |= bit << shift; + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int num, int *val) +{ + int i, ret; + int tmp = 0; + + if (num > 32) + return -EINVAL; + + for (i = 0; i < num; i++) { + ret = rbsp_read_bit(rbsp); + if (ret < 0) + return ret; + tmp |= ret << (num - i - 1); + } + + if (val) + *val = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int num, int value) +{ + int ret; + + while (num--) { + ret = rbsp_write_bit(rbsp, (value >> num) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *val) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (val) + *val = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int value) +{ + int i; + int ret; + int tmp = value + 1; + int leading_zero_bits = fls(tmp) - 1; + + for (i = 0; i < leading_zero_bits; i++) { + ret = rbsp_write_bit(rbsp, 0); + if (ret) + return ret; + } + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, tmp); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *val) +{ + unsigned int tmp; + int ret; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (val) { + if (tmp & 1) + *val = (tmp + 1) / 2; + else + *val = -(tmp / 2); + } + + return 0; +} + +/** + * coda_h264_sps_fixup - fixes frame cropping values in h.264 SPS + * @ctx: encoder context + * @width: visible width + * @height: visible height + * @buf: buffer containing h.264 SPS RBSP, starting with NAL header + * @size: modified RBSP size return value + * @max_size: available size in buf + * + * Rewrites the frame cropping values in an h.264 SPS RBSP correctly for the + * given visible width and height. + */ +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size) +{ + int profile_idc; + unsigned int pic_order_cnt_type; + int pic_width_in_mbs_minus1, pic_height_in_map_units_minus1; + int frame_mbs_only_flag, frame_cropping_flag; + int vui_parameters_present_flag; + unsigned int crop_right, crop_bottom; + struct rbsp sps; + int pos; + int ret; + + if (*size < 8 || *size >= max_size) + return -EINVAL; + + sps.buf = buf + 5; /* Skip NAL header */ + sps.size = *size - 5; + + profile_idc = sps.buf[0]; + /* Skip constraint_set[0-5]_flag, reserved_zero_2bits */ + /* Skip level_idc */ + sps.pos = 24; + + /* seq_parameter_set_id */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || + profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || + profile_idc == 86 || profile_idc == 118 || profile_idc == 128 || + profile_idc == 138 || profile_idc == 139 || profile_idc == 134 || + profile_idc == 135) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling profile_idc %d not implemented\n", + __func__, profile_idc); + return -EINVAL; + } + + /* log2_max_frame_num_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, &pic_order_cnt_type); + if (ret) + return ret; + + if (pic_order_cnt_type == 0) { + /* log2_max_pic_order_cnt_lsb_minus4 */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + } else if (pic_order_cnt_type == 1) { + unsigned int i, num_ref_frames_in_pic_order_cnt_cycle; + + /* delta_pic_order_always_zero_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + /* offset_for_non_ref_pic */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + /* offset_for_top_to_bottom_field */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + + ret = rbsp_read_uev(&sps, + &num_ref_frames_in_pic_order_cnt_cycle); + if (ret) + return ret; + for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + /* offset_for_ref_frame */ + ret = rbsp_read_sev(&sps, NULL); + if (ret) + return ret; + } + } + + /* max_num_ref_frames */ + ret = rbsp_read_uev(&sps, NULL); + if (ret) + return ret; + + /* gaps_in_frame_num_value_allowed_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + ret = rbsp_read_uev(&sps, &pic_width_in_mbs_minus1); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &pic_height_in_map_units_minus1); + if (ret) + return ret; + frame_mbs_only_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (!frame_mbs_only_flag) { + /* mb_adaptive_frame_field_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + } + /* direct_8x8_inference_flag */ + ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + + /* Mark position of the frame cropping flag */ + pos = sps.pos; + frame_cropping_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (frame_cropping_flag) { + unsigned int crop_left, crop_top; + + ret = rbsp_read_uev(&sps, &crop_left); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_right); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_top); + if (ret) + return ret; + ret = rbsp_read_uev(&sps, &crop_bottom); + if (ret) + return ret; + } + vui_parameters_present_flag = ret = rbsp_read_bit(&sps); + if (ret < 0) + return ret; + if (vui_parameters_present_flag) { + dev_err(ctx->fh.vdev->dev_parent, + "%s: Handling vui_parameters not implemented\n", + __func__); + return -EINVAL; + } + + crop_right = round_up(width, 16) - width; + crop_bottom = round_up(height, 16) - height; + crop_right /= 2; + if (frame_mbs_only_flag) + crop_bottom /= 2; + else + crop_bottom /= 4; + + + sps.size = max_size - 5; + sps.pos = pos; + frame_cropping_flag = 1; + ret = rbsp_write_bit(&sps, frame_cropping_flag); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_left */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_right); + if (ret) + return ret; + ret = rbsp_write_uev(&sps, 0); /* crop_top */ + if (ret) + return ret; + ret = rbsp_write_uev(&sps, crop_bottom); + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 0); /* vui_parameters_present_flag */ + if (ret) + return ret; + ret = rbsp_write_bit(&sps, 1); + if (ret) + return ret; + + *size = 5 + DIV_ROUND_UP(sps.pos, 8); + + return 0; +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index c70cfab2433f..19ac0b9dc6eb 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -214,6 +214,8 @@ struct coda_ctx { enum v4l2_quantization quantization; struct coda_params params; struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *h264_profile_ctrl; + struct v4l2_ctrl *h264_level_ctrl; struct v4l2_fh fh; int gopcounter; int runcounter; @@ -303,6 +305,8 @@ int coda_h264_padding(int size, char *p); int coda_h264_profile(int profile_idc); int coda_h264_level(int level_idc); int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); +int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, + int *size, int max_size); bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index 3b650b8aabe9..5e7b00a97671 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -157,6 +157,7 @@ #define CODA_CMD_DEC_SEQ_START_BYTE 0x190 #define CODA_CMD_DEC_SEQ_PS_BB_START 0x194 #define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198 +#define CODA_CMD_DEC_SEQ_JPG_THUMB_EN 0x19c #define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c #define CODA_MP4_CLASS_MPEG4 0 #define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 7f610320426d..c551a25d90d9 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -18,6 +18,7 @@ * */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/platform_device.h> diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index ba157827192c..ddcad7b3e76c 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/ctype.h> diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 7be636237acf..0f324055cc9f 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1114,6 +1114,14 @@ vpif_init_free_channel_objects: return err; } +static void free_vpif_objs(void) +{ + int i; + + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + static int vpif_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) @@ -1255,11 +1263,6 @@ static __init int vpif_probe(struct platform_device *pdev) return -EINVAL; } - if (!pdev->dev.platform_data) { - dev_warn(&pdev->dev, "Missing platform data. Giving up.\n"); - return -EINVAL; - } - vpif_dev = &pdev->dev; err = initialize_vpif(); @@ -1271,7 +1274,7 @@ static __init int vpif_probe(struct platform_device *pdev) err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); if (err) { v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); - return err; + goto vpif_free; } while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { @@ -1314,7 +1317,10 @@ static __init int vpif_probe(struct platform_device *pdev) if (vpif_obj.sd[i]) vpif_obj.sd[i]->grp_id = 1 << i; } - vpif_probe_complete(); + err = vpif_probe_complete(); + if (err) { + goto probe_subdev_out; + } } else { vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; @@ -1334,6 +1340,8 @@ probe_subdev_out: kfree(vpif_obj.sd); vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); +vpif_free: + free_vpif_objs(); return err; } @@ -1355,8 +1363,8 @@ static int vpif_remove(struct platform_device *device) ch = vpif_obj.dev[i]; /* Unregister video device */ video_unregister_device(&ch->video_dev); - kfree(vpif_obj.dev[i]); } + free_vpif_objs(); return 0; } diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e9ff27949a91..c9d2f6c5311a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -713,7 +713,7 @@ static __poll_t gsc_m2m_poll(struct file *file, __poll_t ret; if (mutex_lock_interruptible(&gsc->lock)) - return -ERESTARTSYS; + return EPOLLERR; ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); mutex_unlock(&gsc->lock); diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index 55ba696b8cf4..a920164f53f1 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -384,12 +384,17 @@ static void __isp_video_try_fmt(struct fimc_isp *isp, struct v4l2_pix_format_mplane *pixm, const struct fimc_fmt **fmt) { - *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); + const struct fimc_fmt *__fmt; + + __fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); + + if (fmt) + *fmt = __fmt; pixm->colorspace = V4L2_COLORSPACE_SRGB; pixm->field = V4L2_FIELD_NONE; - pixm->num_planes = (*fmt)->memplanes; - pixm->pixelformat = (*fmt)->fourcc; + pixm->num_planes = __fmt->memplanes; + pixm->pixelformat = __fmt->fourcc; /* * TODO: double check with the docmentation these width/height * constraints are correct. diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 78b48a1fa26c..deb499f76412 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1201,8 +1201,7 @@ static const struct media_device_ops fimc_md_ops = { static ssize_t fimc_md_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *pdev = to_platform_device(dev); - struct fimc_md *fmd = platform_get_drvdata(pdev); + struct fimc_md *fmd = dev_get_drvdata(dev); if (fmd->user_subdev_api) return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE); @@ -1214,8 +1213,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct platform_device *pdev = to_platform_device(dev); - struct fimc_md *fmd = platform_get_drvdata(pdev); + struct fimc_md *fmd = dev_get_drvdata(dev); bool subdev_api; int i; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index cba46a656338..b4e28a299e26 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -891,8 +891,7 @@ e_clkput: static int s5pcsis_pm_suspend(struct device *dev, bool runtime) { - struct platform_device *pdev = to_platform_device(dev); - struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csis_state *state = sd_to_csis_state(sd); int ret = 0; @@ -921,8 +920,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) static int s5pcsis_pm_resume(struct device *dev, bool runtime) { - struct platform_device *pdev = to_platform_device(dev); - struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csis_state *state = sd_to_csis_state(sd); int ret = 0; diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index e41510ce69a4..0273302aa741 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1414,7 +1414,7 @@ static int viu_of_probe(struct platform_device *op) sizeof(struct viu_reg), DRV_NAME)) { dev_err(&op->dev, "Error while requesting mem region\n"); ret = -EBUSY; - goto err; + goto err_irq; } /* remap registers */ @@ -1422,7 +1422,7 @@ static int viu_of_probe(struct platform_device *op) if (!viu_regs) { dev_err(&op->dev, "Can't map register set\n"); ret = -ENOMEM; - goto err; + goto err_irq; } /* Prepare our private structure */ @@ -1430,7 +1430,7 @@ static int viu_of_probe(struct platform_device *op) if (!viu_dev) { dev_err(&op->dev, "Can't allocate private structure\n"); ret = -ENOMEM; - goto err; + goto err_irq; } viu_dev->vr = viu_regs; @@ -1446,16 +1446,21 @@ static int viu_of_probe(struct platform_device *op) ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); if (ret < 0) { dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); - goto err; + goto err_irq; } ad = i2c_get_adapter(0); + if (!ad) { + ret = -EFAULT; + dev_err(&op->dev, "couldn't get i2c adapter\n"); + goto err_v4l2; + } v4l2_ctrl_handler_init(&viu_dev->hdl, 5); if (viu_dev->hdl.error) { ret = viu_dev->hdl.error; dev_err(&op->dev, "couldn't register control\n"); - goto err_vdev; + goto err_i2c; } /* This control handler will inherit the control(s) from the sub-device(s). */ @@ -1471,7 +1476,7 @@ static int viu_of_probe(struct platform_device *op) vdev = video_device_alloc(); if (vdev == NULL) { ret = -ENOMEM; - goto err_vdev; + goto err_hdl; } *vdev = viu_template; @@ -1492,7 +1497,7 @@ static int viu_of_probe(struct platform_device *op) ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { video_device_release(viu_dev->vdev); - goto err_vdev; + goto err_unlock; } /* enable VIU clock */ @@ -1500,12 +1505,12 @@ static int viu_of_probe(struct platform_device *op) if (IS_ERR(clk)) { dev_err(&op->dev, "failed to lookup the clock!\n"); ret = PTR_ERR(clk); - goto err_clk; + goto err_vdev; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&op->dev, "failed to enable the clock!\n"); - goto err_clk; + goto err_vdev; } viu_dev->clk = clk; @@ -1516,7 +1521,7 @@ static int viu_of_probe(struct platform_device *op) if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { dev_err(&op->dev, "Request VIU IRQ failed.\n"); ret = -ENODEV; - goto err_irq; + goto err_clk; } mutex_unlock(&viu_dev->lock); @@ -1524,16 +1529,19 @@ static int viu_of_probe(struct platform_device *op) dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); return ret; -err_irq: - clk_disable_unprepare(viu_dev->clk); err_clk: - video_unregister_device(viu_dev->vdev); + clk_disable_unprepare(viu_dev->clk); err_vdev: - v4l2_ctrl_handler_free(&viu_dev->hdl); + video_unregister_device(viu_dev->vdev); +err_unlock: mutex_unlock(&viu_dev->lock); +err_hdl: + v4l2_ctrl_handler_free(&viu_dev->hdl); +err_i2c: i2c_put_adapter(ad); +err_v4l2: v4l2_device_unregister(&viu_dev->v4l2_dev); -err: +err_irq: irq_dispose_mapping(viu_irq); return ret; } diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 1e4195144f39..5f84d2aa47ab 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -181,20 +181,6 @@ static void deinterlace_job_abort(void *priv) v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); } -static void deinterlace_lock(void *priv) -{ - struct deinterlace_ctx *ctx = priv; - struct deinterlace_dev *pcdev = ctx->dev; - mutex_lock(&pcdev->dev_mutex); -} - -static void deinterlace_unlock(void *priv) -{ - struct deinterlace_ctx *ctx = priv; - struct deinterlace_dev *pcdev = ctx->dev; - mutex_unlock(&pcdev->dev_mutex); -} - static void dma_callback(void *data) { struct deinterlace_ctx *curr_ctx = data; @@ -856,6 +842,8 @@ static const struct vb2_ops deinterlace_qops = { .queue_setup = deinterlace_queue_setup, .buf_prepare = deinterlace_buf_prepare, .buf_queue = deinterlace_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -872,6 +860,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = ctx->dev->v4l2_dev.dev; + src_vq->lock = &ctx->dev->dev_mutex; q_data[V4L2_M2M_SRC].fmt = &formats[0]; q_data[V4L2_M2M_SRC].width = 640; q_data[V4L2_M2M_SRC].height = 480; @@ -890,6 +879,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = ctx->dev->v4l2_dev.dev; + dst_vq->lock = &ctx->dev->dev_mutex; q_data[V4L2_M2M_DST].fmt = &formats[0]; q_data[V4L2_M2M_DST].width = 640; q_data[V4L2_M2M_DST].height = 480; @@ -956,9 +946,9 @@ static __poll_t deinterlace_poll(struct file *file, struct deinterlace_ctx *ctx = file->private_data; __poll_t ret; - deinterlace_lock(ctx); + mutex_lock(&ctx->dev->dev_mutex); ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - deinterlace_unlock(ctx); + mutex_unlock(&ctx->dev->dev_mutex); return ret; } @@ -992,8 +982,6 @@ static const struct v4l2_m2m_ops m2m_ops = { .device_run = deinterlace_device_run, .job_ready = deinterlace_job_ready, .job_abort = deinterlace_job_abort, - .lock = deinterlace_lock, - .unlock = deinterlace_unlock, }; static int deinterlace_probe(struct platform_device *pdev) @@ -1040,7 +1028,6 @@ static int deinterlace_probe(struct platform_device *pdev) } video_set_drvdata(vfd, pcdev); - snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name); v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME " Device registered as /dev/video%d\n", vfd->num); diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c index 8040a6285c3f..cd4be38ab5ac 100644 --- a/drivers/media/platform/meson/ao-cec.c +++ b/drivers/media/platform/meson/ao-cec.c @@ -524,7 +524,7 @@ static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts, return ret; if (reg == TX_BUSY) { - dev_err(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", + dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n", __func__); meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret); } diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 328e8f650d9b..4f24da8afecc 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -861,14 +861,9 @@ static int mtk_jpeg_job_ready(void *priv) return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0; } -static void mtk_jpeg_job_abort(void *priv) -{ -} - static const struct v4l2_m2m_ops mtk_jpeg_m2m_ops = { .device_run = mtk_jpeg_device_run, .job_ready = mtk_jpeg_job_ready, - .job_abort = mtk_jpeg_job_abort, }; static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq, diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index 583d47724ee8..ceffc31cc6eb 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -394,20 +394,6 @@ static bool mtk_mdp_ctx_state_is_set(struct mtk_mdp_ctx *ctx, u32 mask) return ret; } -static void mtk_mdp_ctx_lock(struct vb2_queue *vq) -{ - struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_lock(&ctx->mdp_dev->lock); -} - -static void mtk_mdp_ctx_unlock(struct vb2_queue *vq) -{ - struct mtk_mdp_ctx *ctx = vb2_get_drv_priv(vq); - - mutex_unlock(&ctx->mdp_dev->lock); -} - static void mtk_mdp_set_frame_size(struct mtk_mdp_frame *frame, int width, int height) { @@ -455,10 +441,6 @@ static void mtk_mdp_m2m_stop_streaming(struct vb2_queue *q) pm_runtime_put(&ctx->mdp_dev->pdev->dev); } -static void mtk_mdp_m2m_job_abort(void *priv) -{ -} - /* The color format (num_planes) must be already configured. */ static void mtk_mdp_prepare_addr(struct mtk_mdp_ctx *ctx, struct vb2_buffer *vb, @@ -625,10 +607,10 @@ static const struct vb2_ops mtk_mdp_m2m_qops = { .queue_setup = mtk_mdp_m2m_queue_setup, .buf_prepare = mtk_mdp_m2m_buf_prepare, .buf_queue = mtk_mdp_m2m_buf_queue, - .wait_prepare = mtk_mdp_ctx_unlock, - .wait_finish = mtk_mdp_ctx_lock, .stop_streaming = mtk_mdp_m2m_stop_streaming, .start_streaming = mtk_mdp_m2m_start_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int mtk_mdp_m2m_querycap(struct file *file, void *fh, @@ -996,6 +978,7 @@ static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq, src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = &ctx->mdp_dev->pdev->dev; + src_vq->lock = &ctx->mdp_dev->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -1010,6 +993,7 @@ static int mtk_mdp_m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = &ctx->mdp_dev->pdev->dev; + dst_vq->lock = &ctx->mdp_dev->lock; return vb2_queue_init(dst_vq); } @@ -1227,7 +1211,6 @@ static const struct v4l2_file_operations mtk_mdp_m2m_fops = { static const struct v4l2_m2m_ops mtk_mdp_m2m_ops = { .device_run = mtk_mdp_m2m_device_run, - .job_abort = mtk_mdp_m2m_job_abort, }; int mtk_mdp_register_m2m_device(struct mtk_mdp_dev *mdp) diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 86f0a7134365..0c8a8b4c4e1c 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1400,6 +1400,11 @@ int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 0, 32, 1, 1); ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, + &mtk_vcodec_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + V4L2_MPEG_VIDEO_VP9_PROFILE_0, + 0, V4L2_MPEG_VIDEO_VP9_PROFILE_0); if (ctx->ctrl_hdl.error) { mtk_v4l2_err("Adding control failed %d", @@ -1411,28 +1416,10 @@ int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) return 0; } -static void m2mops_vdec_lock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - mutex_lock(&ctx->dev->dev_mutex); -} - -static void m2mops_vdec_unlock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - mutex_unlock(&ctx->dev->dev_mutex); -} - const struct v4l2_m2m_ops mtk_vdec_m2m_ops = { .device_run = m2mops_vdec_device_run, .job_ready = m2mops_vdec_job_ready, .job_abort = m2mops_vdec_job_abort, - .lock = m2mops_vdec_lock, - .unlock = m2mops_vdec_unlock, }; static const struct vb2_ops mtk_vdec_vb2_ops = { diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 1b1a28abbf1f..6ad408514a99 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -1181,26 +1181,10 @@ static void m2mops_venc_job_abort(void *priv) ctx->state = MTK_STATE_ABORT; } -static void m2mops_venc_lock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mutex_lock(&ctx->dev->dev_mutex); -} - -static void m2mops_venc_unlock(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mutex_unlock(&ctx->dev->dev_mutex); -} - const struct v4l2_m2m_ops mtk_venc_m2m_ops = { .device_run = m2mops_venc_device_run, .job_ready = m2mops_venc_job_ready, .job_abort = m2mops_venc_job_abort, - .lock = m2mops_venc_lock, - .unlock = m2mops_venc_unlock, }; void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index 1ff6a93262b7..f8d35e3ac1dc 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -960,4 +960,4 @@ static struct platform_driver mtk_vpu_driver = { module_platform_driver(mtk_vpu_driver); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver"); +MODULE_DESCRIPTION("Mediatek Video Processor Unit driver"); diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 5a8eff60e95f..64195c4ddeaf 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -250,20 +250,6 @@ static void emmaprp_job_abort(void *priv) v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); } -static void emmaprp_lock(void *priv) -{ - struct emmaprp_ctx *ctx = priv; - struct emmaprp_dev *pcdev = ctx->dev; - mutex_lock(&pcdev->dev_mutex); -} - -static void emmaprp_unlock(void *priv) -{ - struct emmaprp_ctx *ctx = priv; - struct emmaprp_dev *pcdev = ctx->dev; - mutex_unlock(&pcdev->dev_mutex); -} - static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) { dprintk(pcdev, @@ -747,6 +733,8 @@ static const struct vb2_ops emmaprp_qops = { .queue_setup = emmaprp_queue_setup, .buf_prepare = emmaprp_buf_prepare, .buf_queue = emmaprp_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -763,6 +751,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->dev = ctx->dev->v4l2_dev.dev; + src_vq->lock = &ctx->dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -776,6 +765,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->dev = ctx->dev->v4l2_dev.dev; + dst_vq->lock = &ctx->dev->dev_mutex; return vb2_queue_init(dst_vq); } @@ -885,8 +875,6 @@ static const struct video_device emmaprp_videodev = { static const struct v4l2_m2m_ops m2m_ops = { .device_run = emmaprp_device_run, .job_abort = emmaprp_job_abort, - .lock = emmaprp_lock, - .unlock = emmaprp_unlock, }; static int emmaprp_probe(struct platform_device *pdev) @@ -934,7 +922,6 @@ static int emmaprp_probe(struct platform_device *pdev) vfd->v4l2_dev = &pcdev->v4l2_dev; video_set_drvdata(vfd, pcdev); - snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); pcdev->vfd = vfd; v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME " Device registered as /dev/video%d\n", vfd->num); diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index d827b6c285a6..4b5e55d41ad4 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -8,6 +8,7 @@ config VIDEO_OMAP2_VOUT depends on MMU depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n) depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST + depends on VIDEO_V4L2 select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index f22cf351e3ee..842e2235047d 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -300,7 +300,7 @@ static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) static int isp_xclk_init(struct isp_device *isp) { struct device_node *np = isp->dev->of_node; - struct clk_init_data init; + struct clk_init_data init = {}; unsigned int i; for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) @@ -971,7 +971,7 @@ static void isp_resume_module_pipeline(struct media_entity *me) * Returns 0 if suspend left in idle state all the submodules properly, * or returns 1 if a general Reset is required to suspend the submodules. */ -static int isp_suspend_modules(struct isp_device *isp) +static int __maybe_unused isp_suspend_modules(struct isp_device *isp) { unsigned long timeout; @@ -1005,7 +1005,7 @@ static int isp_suspend_modules(struct isp_device *isp) * isp_resume_modules - Resume ISP submodules. * @isp: OMAP3 ISP device */ -static void isp_resume_modules(struct isp_device *isp) +static void __maybe_unused isp_resume_modules(struct isp_device *isp) { omap3isp_stat_resume(&isp->isp_aewb); omap3isp_stat_resume(&isp->isp_af); diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h deleted file mode 100644 index 53d5b66a9dfb..000000000000 --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * camss-vfe.h - * - * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module - * - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef QC_MSM_CAMSS_VFE_H -#define QC_MSM_CAMSS_VFE_H - -#include <linux/clk.h> -#include <linux/spinlock_types.h> -#include <media/media-entity.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> - -#include "camss-video.h" - -#define MSM_VFE_PAD_SINK 0 -#define MSM_VFE_PAD_SRC 1 -#define MSM_VFE_PADS_NUM 2 - -#define MSM_VFE_LINE_NUM 4 -#define MSM_VFE_IMAGE_MASTERS_NUM 7 -#define MSM_VFE_COMPOSITE_IRQ_NUM 4 - -#define MSM_VFE_VFE0_UB_SIZE 1023 -#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) -#define MSM_VFE_VFE1_UB_SIZE 1535 -#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) - -enum vfe_output_state { - VFE_OUTPUT_OFF, - VFE_OUTPUT_RESERVED, - VFE_OUTPUT_SINGLE, - VFE_OUTPUT_CONTINUOUS, - VFE_OUTPUT_IDLE, - VFE_OUTPUT_STOPPING -}; - -enum vfe_line_id { - VFE_LINE_NONE = -1, - VFE_LINE_RDI0 = 0, - VFE_LINE_RDI1 = 1, - VFE_LINE_RDI2 = 2, - VFE_LINE_PIX = 3 -}; - -struct vfe_output { - u8 wm_num; - u8 wm_idx[3]; - - int active_buf; - struct camss_buffer *buf[2]; - struct camss_buffer *last_buffer; - struct list_head pending_bufs; - - unsigned int drop_update_idx; - - enum vfe_output_state state; - unsigned int sequence; - int wait_sof; - int wait_reg_update; - struct completion sof; - struct completion reg_update; -}; - -struct vfe_line { - enum vfe_line_id id; - struct v4l2_subdev subdev; - struct media_pad pads[MSM_VFE_PADS_NUM]; - struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM]; - struct v4l2_rect compose; - struct v4l2_rect crop; - struct camss_video video_out; - struct vfe_output output; -}; - -struct vfe_device { - u8 id; - void __iomem *base; - u32 irq; - char irq_name[30]; - struct camss_clock *clock; - int nclocks; - struct completion reset_complete; - struct completion halt_complete; - struct mutex power_lock; - int power_count; - struct mutex stream_lock; - int stream_count; - spinlock_t output_lock; - enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; - struct vfe_line line[MSM_VFE_LINE_NUM]; - u32 reg_update; - u8 was_streaming; -}; - -struct resources; - -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res); - -int msm_vfe_register_entities(struct vfe_device *vfe, - struct v4l2_device *v4l2_dev); - -void msm_vfe_unregister_entities(struct vfe_device *vfe); - -void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); -void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); - -void msm_vfe_stop_streaming(struct vfe_device *vfe); - -#endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/Makefile b/drivers/media/platform/qcom/camss/Makefile index 3c4024fbb768..f5e6e255f2a1 100644 --- a/drivers/media/platform/qcom/camss-8x16/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -3,8 +3,12 @@ qcom-camss-objs += \ camss.o \ camss-csid.o \ + camss-csiphy-2ph-1-0.o \ + camss-csiphy-3ph-1-0.o \ camss-csiphy.o \ camss-ispif.o \ + camss-vfe-4-1.o \ + camss-vfe-4-7.o \ camss-vfe.o \ camss-video.o \ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 226f36ef7419..729b31891466 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-csid.c * * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/clk.h> #include <linux/completion.h> @@ -21,9 +13,11 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <media/media-entity.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-subdev.h> #include "camss-csid.h" @@ -34,21 +28,35 @@ #define CAMSS_CSID_HW_VERSION 0x0 #define CAMSS_CSID_CORE_CTRL_0 0x004 #define CAMSS_CSID_CORE_CTRL_1 0x008 -#define CAMSS_CSID_RST_CMD 0x00c -#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n)) -#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n)) -#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060 -#define CAMSS_CSID_IRQ_MASK 0x064 -#define CAMSS_CSID_IRQ_STATUS 0x068 -#define CAMSS_CSID_TG_CTRL 0x0a0 +#define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010) +#define CAMSS_CSID_CID_LUT_VC_n(v, n) \ + (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG(v, n) \ + (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0) +#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1) +#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4 +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (0 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (1 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9) +#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9) +#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10) +#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10) +#define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064) +#define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068) +#define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c) +#define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8) #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 -#define CAMSS_CSID_TG_VC_CFG 0x0a4 +#define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac) #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f -#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) -#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \ + (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \ + (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \ + (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n)) #define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 #define DATA_TYPE_YUV422_8BIT 0x1e @@ -56,15 +64,17 @@ #define DATA_TYPE_RAW_8BIT 0x2a #define DATA_TYPE_RAW_10BIT 0x2b #define DATA_TYPE_RAW_12BIT 0x2c +#define DATA_TYPE_RAW_14BIT 0x2d #define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0 #define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1 #define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2 #define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3 +#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8 #define CSID_RESET_TIMEOUT_MS 500 -struct csid_fmts { +struct csid_format { u32 code; u8 data_type; u8 decode_format; @@ -72,7 +82,7 @@ struct csid_fmts { u8 spp; /* bus samples per pixel */ }; -static const struct csid_fmts csid_input_fmts[] = { +static const struct csid_format csid_formats_8x16[] = { { MEDIA_BUS_FMT_UYVY8_2X8, DATA_TYPE_YUV422_8BIT, @@ -184,20 +194,241 @@ static const struct csid_fmts csid_input_fmts[] = { DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, - } + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, }; -static const struct csid_fmts *csid_get_fmt_entry(u32 code) +static const struct csid_format csid_formats_8x96[] = { + { + MEDIA_BUS_FMT_UYVY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_2X8, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static u32 csid_find_code(u32 *code, unsigned int n_code, + unsigned int index, u32 req_code) +{ + int i; + + if (!req_code && (index >= n_code)) + return 0; + + for (i = 0; i < n_code; i++) + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + + return code[0]; +} + +static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, + unsigned int index, u32 src_req_code) +{ + if (csid->camss->version == CAMSS_8x16) { + if (index > 0) + return 0; + + return sink_code; + } else if (csid->camss->version == CAMSS_8x96) { + switch (sink_code) { + case MEDIA_BUS_FMT_SBGGR10_1X10: + { + u32 src_code[] = { + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + }; + + return csid_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_Y10_1X10: + { + u32 src_code[] = { + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, + }; + + return csid_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + default: + if (index > 0) + return 0; + + return sink_code; + } + } else { + return 0; + } +} + +static const struct csid_format *csid_get_fmt_entry( + const struct csid_format *formats, + unsigned int nformat, + u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (code == csid_input_fmts[i].code) - return &csid_input_fmts[i]; + for (i = 0; i < nformat; i++) + if (code == formats[i].code) + return &formats[i]; WARN(1, "Unknown format\n"); - return &csid_input_fmts[0]; + return &formats[0]; } /* @@ -210,10 +441,11 @@ static const struct csid_fmts *csid_get_fmt_entry(u32 code) static irqreturn_t csid_isr(int irq, void *dev) { struct csid_device *csid = dev; + enum camss_version ver = csid->camss->version; u32 value; - value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); - writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); + value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver)); + writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver)); if ((value >> 11) & 0x1) complete(&csid->reset_complete); @@ -227,7 +459,7 @@ static irqreturn_t csid_isr(int irq, void *dev) */ static int csid_set_clock_rates(struct csid_device *csid) { - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; u32 pixel_clock; int i, j; int ret; @@ -240,11 +472,16 @@ static int csid_set_clock_rates(struct csid_device *csid) struct camss_clock *clock = &csid->clock[i]; if (!strcmp(clock->name, "csi0") || - !strcmp(clock->name, "csi1")) { - u8 bpp = csid_get_fmt_entry( - csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp; + !strcmp(clock->name, "csi1") || + !strcmp(clock->name, "csi2") || + !strcmp(clock->name, "csi3")) { + const struct csid_format *f = csid_get_fmt_entry( + csid->formats, + csid->nformats, + csid->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csid->phy.lane_cnt; - u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); + u64 min_rate = pixel_clock * f->bpp / + (2 * num_lanes * 4); long rate; camss_add_clock_margin(&min_rate); @@ -294,13 +531,13 @@ static int csid_reset(struct csid_device *csid) reinit_completion(&csid->reset_complete); - writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); + writel_relaxed(0x7fff, csid->base + + CAMSS_CSID_RST_CMD(csid->camss->version)); time = wait_for_completion_timeout(&csid->reset_complete, msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device_index(csid, csid->id), - "CSID reset timeout\n"); + dev_err(csid->camss->dev, "CSID reset timeout\n"); return -EIO; } @@ -317,25 +554,33 @@ static int csid_reset(struct csid_device *csid) static int csid_set_power(struct v4l2_subdev *sd, int on) { struct csid_device *csid = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; int ret; if (on) { u32 hw_version; - ret = regulator_enable(csid->vdda); + ret = pm_runtime_get_sync(dev); if (ret < 0) return ret; + ret = regulator_enable(csid->vdda); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + ret = csid_set_clock_rates(csid); if (ret < 0) { regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } ret = camss_enable_clocks(csid->nclocks, csid->clock, dev); if (ret < 0) { regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } @@ -346,6 +591,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); return ret; } @@ -355,6 +601,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); ret = regulator_disable(csid->vdda); + pm_runtime_put_sync(dev); } return ret; @@ -373,6 +620,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) { struct csid_device *csid = v4l2_get_subdevdata(sd); struct csid_testgen_config *tg = &csid->testgen; + enum camss_version ver = csid->camss->version; u32 val; if (enable) { @@ -383,7 +631,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) ret = v4l2_ctrl_handler_setup(&csid->ctrls); if (ret < 0) { - dev_err(to_device_index(csid, csid->id), + dev_err(csid->camss->dev, "could not sync v4l2 controls: %d\n", ret); return ret; } @@ -392,40 +640,47 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) return -ENOLINK; - dt = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SRC].code)-> - data_type; - if (tg->enabled) { /* Config Test Generator */ struct v4l2_mbus_framefmt *f = &csid->fmt[MSM_CSID_PAD_SRC]; - u8 bpp = csid_get_fmt_entry(f->code)->bpp; - u8 spp = csid_get_fmt_entry(f->code)->spp; - u32 num_bytes_per_line = f->width * bpp * spp / 8; + const struct csid_format *format = csid_get_fmt_entry( + csid->formats, csid->nformats, f->code); + u32 num_bytes_per_line = + f->width * format->bpp * format->spp / 8; u32 num_lines = f->height; /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ /* 1:0 VC */ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); - writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_VC_CFG(ver)); /* 28:16 bytes per lines, 12:0 num of lines */ val = ((num_bytes_per_line & 0x1fff) << 16) | (num_lines & 0x1fff); writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_0(0)); + CAMSS_CSID_TG_DT_n_CGG_0(ver, 0)); + + dt = format->data_type; /* 5:0 data type */ val = dt; writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_1(0)); + CAMSS_CSID_TG_DT_n_CGG_1(ver, 0)); /* 2:0 output test pattern */ val = tg->payload_mode; writel_relaxed(val, csid->base + - CAMSS_CSID_TG_DT_n_CGG_2(0)); + CAMSS_CSID_TG_DT_n_CGG_2(ver, 0)); + + df = format->decode_format; } else { + struct v4l2_mbus_framefmt *f = + &csid->fmt[MSM_CSID_PAD_SINK]; + const struct csid_format *format = csid_get_fmt_entry( + csid->formats, csid->nformats, f->code); struct csid_phy_config *phy = &csid->phy; val = phy->lane_cnt - 1; @@ -439,30 +694,54 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); + + dt = format->data_type; + df = format->decode_format; } /* Config LUT */ dt_shift = (cid % 4) * 8; - df = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SINK].code)-> - decode_format; - val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + val = readl_relaxed(csid->base + + CAMSS_CSID_CID_LUT_VC_n(ver, vc)); val &= ~(0xff << dt_shift); val |= dt << dt_shift; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + writel_relaxed(val, csid->base + + CAMSS_CSID_CID_LUT_VC_n(ver, vc)); + + val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; + val |= CAMSS_CSID_CID_n_CFG_RDI_EN; + val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; + + if (csid->camss->version == CAMSS_8x96) { + u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; + u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; + + if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 && + src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) || + (sink_code == MEDIA_BUS_FMT_Y10_1X10 && + src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) { + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; + val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; + } + } - val = (df << 4) | 0x3; - writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); + writel_relaxed(val, csid->base + + CAMSS_CSID_CID_n_CFG(ver, cid)); if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_ENABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_CTRL(ver)); } } else { if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_DISABLE; - writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + writel_relaxed(val, csid->base + + CAMSS_CSID_TG_CTRL(ver)); } } @@ -510,12 +789,12 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (fmt->code == csid_input_fmts[i].code) + for (i = 0; i < csid->nformats; i++) + if (fmt->code == csid->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) + if (i >= csid->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -528,23 +807,23 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SRC: if (csid->testgen_mode->cur.val == 0) { - /* Test generator is disabled, keep pad formats */ - /* in sync - set and return a format same as sink pad */ - struct v4l2_mbus_framefmt format; + /* Test generator is disabled, */ + /* keep pad formats in sync */ + u32 code = fmt->code; - format = *__csid_get_format(csid, cfg, - MSM_CSID_PAD_SINK, which); - *fmt = format; + *fmt = *__csid_get_format(csid, cfg, + MSM_CSID_PAD_SINK, which); + fmt->code = csid_src_pad_code(csid, fmt->code, 0, code); } else { - /* Test generator is enabled, set format on source*/ + /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ - for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++) - if (csid_input_fmts[i].code == fmt->code) + for (i = 0; i < csid->nformats; i++) + if (csid->formats[i].code == fmt->code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csid_input_fmts)) + if (i >= csid->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -570,27 +849,29 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct csid_device *csid = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) + if (code->index >= csid->nformats) return -EINVAL; - code->code = csid_input_fmts[code->index].code; + code->code = csid->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { - if (code->index > 0) - return -EINVAL; + struct v4l2_mbus_framefmt *sink_fmt; - format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, - code->which); + sink_fmt = __csid_get_format(csid, cfg, + MSM_CSID_PAD_SINK, + code->which); - code->code = format->code; + code->code = csid_src_pad_code(csid, sink_fmt->code, + code->index, 0); + if (!code->code) + return -EINVAL; } else { - if (code->index >= ARRAY_SIZE(csid_input_fmts)) + if (code->index >= csid->nformats) return -EINVAL; - code->code = csid_input_fmts[code->index].code; + code->code = csid->formats[code->index].code; } } @@ -798,17 +1079,30 @@ static const struct v4l2_ctrl_ops csid_ctrl_ops = { * * Return 0 on success or a negative error code otherwise */ -int msm_csid_subdev_init(struct csid_device *csid, +int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct resources *res, u8 id) { - struct device *dev = to_device_index(csid, id); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; int i, j; int ret; + csid->camss = camss; csid->id = id; + if (camss->version == CAMSS_8x16) { + csid->formats = csid_formats_8x16; + csid->nformats = + ARRAY_SIZE(csid_formats_8x16); + } else if (camss->version == CAMSS_8x96) { + csid->formats = csid_formats_8x96; + csid->nformats = + ARRAY_SIZE(csid_formats_8x96); + } else { + return -EINVAL; + } + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -980,6 +1274,8 @@ static int csid_link_setup(struct media_entity *entity, static const struct v4l2_subdev_core_ops csid_core_ops = { .s_power = csid_set_power, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops csid_video_ops = { @@ -1020,12 +1316,13 @@ int msm_csid_register_entity(struct csid_device *csid, { struct v4l2_subdev *sd = &csid->subdev; struct media_pad *pads = csid->pads; - struct device *dev = to_device_index(csid, csid->id); + struct device *dev = csid->camss->dev; int ret; v4l2_subdev_init(sd, &csid_v4l2_ops); sd->internal_ops = &csid_v4l2_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", MSM_CSID_NAME, csid->id); v4l2_set_subdevdata(sd, csid); diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 8682d3081bc3..1824b3745e10 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -1,19 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-csid.h * * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module * * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #ifndef QC_MSM_CAMSS_CSID_H #define QC_MSM_CAMSS_CSID_H @@ -50,6 +42,7 @@ struct csid_phy_config { }; struct csid_device { + struct camss *camss; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSID_PADS_NUM]; @@ -65,11 +58,13 @@ struct csid_device { struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *testgen_mode; + const struct csid_format *formats; + unsigned int nformats; }; struct resources; -int msm_csid_subdev_init(struct csid_device *csid, +int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct resources *res, u8 id); int msm_csid_register_entity(struct csid_device *csid, diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c new file mode 100644 index 000000000000..c832539397d7 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csiphy-2ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 2phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#include "camss-csiphy.h" + +#include <linux/delay.h> +#include <linux/interrupt.h> + +#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) +#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) +#define CAMSS_CSI_PHY_GLBL_RESET 0x140 +#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 +#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 +#define CAMSS_CSI_PHY_HW_VERSION 0x188 +#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) +#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) +#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec +#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 + +static void csiphy_hw_version_read(struct csiphy_device *csiphy, + struct device *dev) +{ + u8 hw_version = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_HW_VERSION); + + dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); +} + +/* + * csiphy_reset - Perform software reset on CSIPHY module + * @csiphy: CSIPHY device + */ +static void csiphy_reset(struct csiphy_device *csiphy) +{ + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + usleep_range(5000, 8000); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); +} + +/* + * csiphy_settle_cnt_calc - Calculate settle count value + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter pixel clock + * frequency. + * + * Return settle count value or 0 if the CSI2 pixel clock + * frequency is not available + */ +static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, + u32 timer_clk_rate) +{ + u32 mipi_clock; /* Hz */ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_prepare_zero_min; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + mipi_clock = pixel_clock * bpp / (2 * num_lanes); + ui = div_u64(1000000000000LL, mipi_clock); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_prepare_zero_min = 145000 + 10 * ui; + t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 1; + + return settle_cnt; +} + +static void csiphy_lanes_enable(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 settle_cnt; + u8 val, l = 0; + int i = 0; + + settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data, + csiphy->timer_clk_rate); + + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); + writel_relaxed(0x1, csiphy->base + + CAMSS_CSI_PHY_T_WAKEUP_CFG0); + + val = 0x1; + val |= lane_mask << 1; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); + + val = cfg->combo_mode << 4; + writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); + + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = c->clk.pos; + else + l = c->data[i].pos; + + writel_relaxed(0x10, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(l)); + writel_relaxed(settle_cnt, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG3(l)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_MASKn(l)); + writel_relaxed(0x3f, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(l)); + } +} + +static void csiphy_lanes_disable(struct csiphy_device *csiphy, + struct csiphy_config *cfg) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 l = 0; + int i = 0; + + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = c->clk.pos; + else + l = c->data[i].pos; + + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_LNn_CFG2(l)); + } + + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); +} + +/* + * csiphy_isr - CSIPHY module interrupt handler + * @irq: Interrupt line + * @dev: CSIPHY device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csiphy_isr(int irq, void *dev) +{ + struct csiphy_device *csiphy = dev; + u8 i; + + for (i = 0; i < 8; i++) { + u8 val = readl_relaxed(csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); + writel_relaxed(val, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); + writel_relaxed(0x0, csiphy->base + + CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); + } + + return IRQ_HANDLED; +} + +const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = { + .hw_version_read = csiphy_hw_version_read, + .reset = csiphy_reset, + .lanes_enable = csiphy_lanes_enable, + .lanes_disable = csiphy_lanes_disable, + .isr = csiphy_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c new file mode 100644 index 000000000000..bcd0dfd33618 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csiphy-3ph-1-0.c + * + * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0 + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#include "camss-csiphy.h" + +#include <linux/delay.h> +#include <linux/interrupt.h> + +#define CSIPHY_3PH_LNn_CFG1(n) (0x000 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG (BIT(7) | BIT(6)) +#define CSIPHY_3PH_LNn_CFG2(n) (0x004 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT BIT(3) +#define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4 +#define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02 +#define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50 +#define CSIPHY_3PH_LNn_TEST_IMP(n) (0x01c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP 0xa +#define CSIPHY_3PH_LNn_MISC1(n) (0x028 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_MISC1_IS_CLKLANE BIT(2) +#define CSIPHY_3PH_LNn_CFG6(n) (0x02c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT BIT(0) +#define CSIPHY_3PH_LNn_CFG7(n) (0x030 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG7_SWI_T_INIT 0x2 +#define CSIPHY_3PH_LNn_CFG8(n) (0x034 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP BIT(0) +#define CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE BIT(1) +#define CSIPHY_3PH_LNn_CFG9(n) (0x038 + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP 0x1 +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n)) +#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8 + +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n)) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0) +#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) +#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n)) + +static void csiphy_hw_version_read(struct csiphy_device *csiphy, + struct device *dev) +{ + u32 hw_version; + + writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, + csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); + + hw_version = readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(12)); + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(13)) << 8; + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(14)) << 16; + hw_version |= readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24; + + dev_err(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version); +} + +/* + * csiphy_reset - Perform software reset on CSIPHY module + * @csiphy: CSIPHY device + */ +static void csiphy_reset(struct csiphy_device *csiphy) +{ + writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); + usleep_range(5000, 8000); + writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); +} + +static irqreturn_t csiphy_isr(int irq, void *dev) +{ + struct csiphy_device *csiphy = dev; + int i; + + for (i = 0; i < 11; i++) { + int c = i + 22; + u8 val = readl_relaxed(csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(i)); + + writel_relaxed(val, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(c)); + } + + writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10)); + writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10)); + + for (i = 22; i < 33; i++) + writel_relaxed(0x0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i)); + + return IRQ_HANDLED; +} + +/* + * csiphy_settle_cnt_calc - Calculate settle count value + * + * Helper function to calculate settle count value. This is + * based on the CSI2 T_hs_settle parameter which in turn + * is calculated based on the CSI2 transmitter pixel clock + * frequency. + * + * Return settle count value or 0 if the CSI2 pixel clock + * frequency is not available + */ +static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, + u32 timer_clk_rate) +{ + u32 mipi_clock; /* Hz */ + u32 ui; /* ps */ + u32 timer_period; /* ps */ + u32 t_hs_prepare_max; /* ps */ + u32 t_hs_settle; /* ps */ + u8 settle_cnt; + + mipi_clock = pixel_clock * bpp / (2 * num_lanes); + ui = div_u64(1000000000000LL, mipi_clock); + ui /= 2; + t_hs_prepare_max = 85000 + 6 * ui; + t_hs_settle = t_hs_prepare_max; + + timer_period = div_u64(1000000000000LL, timer_clk_rate); + settle_cnt = t_hs_settle / timer_period - 6; + + return settle_cnt; +} + +static void csiphy_lanes_enable(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 settle_cnt; + u8 val, l = 0; + int i; + + settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data, + csiphy->timer_clk_rate); + + val = BIT(c->clk.pos); + for (i = 0; i < c->num_data; i++) + val |= BIT(c->data[i].pos * 2); + + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5)); + + val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); + + for (i = 0; i <= c->num_data; i++) { + if (i == c->num_data) + l = 7; + else + l = c->data[i].pos * 2; + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + val |= 0x17; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG2(l)); + + val = settle_cnt; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG3(l)); + + val = CSIPHY_3PH_LNn_CFG5_T_HS_DTERM | + CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG5(l)); + + val = CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG6(l)); + + val = CSIPHY_3PH_LNn_CFG7_SWI_T_INIT; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG7(l)); + + val = CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP | + CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG8(l)); + + val = CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG9(l)); + + val = CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_TEST_IMP(l)); + + val = CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL; + writel_relaxed(val, csiphy->base + + CSIPHY_3PH_LNn_CSI_LANE_CTRL15(l)); + } + + val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l)); + + val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG4(l)); + + val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_MISC1(l)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(12)); + + val = 0xfb; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(13)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(14)); + + val = 0x7f; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(15)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(16)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(17)); + + val = 0xef; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(18)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(19)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(20)); + + val = 0xff; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(21)); +} + +static void csiphy_lanes_disable(struct csiphy_device *csiphy, + struct csiphy_config *cfg) +{ + writel_relaxed(0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5)); + + writel_relaxed(0, csiphy->base + + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6)); +} + +const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = { + .hw_version_read = csiphy_hw_version_read, + .reset = csiphy_reset, + .lanes_enable = csiphy_lanes_enable, + .lanes_disable = csiphy_lanes_disable, + .isr = csiphy_isr, +}; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 7e61caba6a2d..4559f3b1b38c 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-csiphy.c * * Qualcomm MSM Camera Subsystem - CSIPHY Module * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2016-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2016-2018 Linaro Ltd. */ #include <linux/clk.h> #include <linux/delay.h> @@ -21,6 +13,7 @@ #include <linux/kernel.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> @@ -30,131 +23,75 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n)) -#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n)) -#define CAMSS_CSI_PHY_GLBL_RESET 0x140 -#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144 -#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164 -#define CAMSS_CSI_PHY_HW_VERSION 0x188 -#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n)) -#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n)) -#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec -#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4 - -static const struct { +struct csiphy_format { u32 code; u8 bpp; -} csiphy_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } +}; + +static const struct csiphy_format csiphy_formats_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, +}; + +static const struct csiphy_format csiphy_formats_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; /* * csiphy_get_bpp - map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array * @code: media bus format code * * Return number of bits per pixel */ -static u8 csiphy_get_bpp(u32 code) +static u8 csiphy_get_bpp(const struct csiphy_format *formats, + unsigned int nformats, u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (code == csiphy_formats[i].code) - return csiphy_formats[i].bpp; + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].bpp; WARN(1, "Unknown format\n"); - return csiphy_formats[0].bpp; -} - -/* - * csiphy_isr - CSIPHY module interrupt handler - * @irq: Interrupt line - * @dev: CSIPHY device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t csiphy_isr(int irq, void *dev) -{ - struct csiphy_device *csiphy = dev; - u8 i; - - for (i = 0; i < 8; i++) { - u8 val = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_STATUSn(i)); - writel_relaxed(val, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD); - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - return IRQ_HANDLED; + return formats[0].bpp; } /* @@ -163,7 +100,7 @@ static irqreturn_t csiphy_isr(int irq, void *dev) */ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) { - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; u32 pixel_clock; int i, j; int ret; @@ -176,8 +113,10 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) struct camss_clock *clock = &csiphy->clock[i]; if (!strcmp(clock->name, "csiphy0_timer") || - !strcmp(clock->name, "csiphy1_timer")) { - u8 bpp = csiphy_get_bpp( + !strcmp(clock->name, "csiphy1_timer") || + !strcmp(clock->name, "csiphy2_timer")) { + u8 bpp = csiphy_get_bpp(csiphy->formats, + csiphy->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4); @@ -221,17 +160,6 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) } /* - * csiphy_reset - Perform software reset on CSIPHY module - * @csiphy: CSIPHY device - */ -static void csiphy_reset(struct csiphy_device *csiphy) -{ - writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - usleep_range(5000, 8000); - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); -} - -/* * csiphy_set_power - Power on/off CSIPHY module * @sd: CSIPHY V4L2 subdevice * @on: Requested power state @@ -241,31 +169,38 @@ static void csiphy_reset(struct csiphy_device *csiphy) static int csiphy_set_power(struct v4l2_subdev *sd, int on) { struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; if (on) { - u8 hw_version; int ret; - ret = csiphy_set_clock_rates(csiphy); + ret = pm_runtime_get_sync(dev); if (ret < 0) return ret; + ret = csiphy_set_clock_rates(csiphy); + if (ret < 0) { + pm_runtime_put_sync(dev); + return ret; + } + ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } enable_irq(csiphy->irq); - csiphy_reset(csiphy); + csiphy->ops->reset(csiphy); - hw_version = readl_relaxed(csiphy->base + - CAMSS_CSI_PHY_HW_VERSION); - dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version); + csiphy->ops->hw_version_read(csiphy, dev); } else { disable_irq(csiphy->irq); camss_disable_clocks(csiphy->nclocks, csiphy->clock); + + pm_runtime_put_sync(dev); } return 0; @@ -291,58 +226,6 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) } /* - * csiphy_settle_cnt_calc - Calculate settle count value - * @csiphy: CSIPHY device - * - * Helper function to calculate settle count value. This is - * based on the CSI2 T_hs_settle parameter which in turn - * is calculated based on the CSI2 transmitter pixel clock - * frequency. - * - * Return settle count value or 0 if the CSI2 pixel clock - * frequency is not available - */ -static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) -{ - u8 bpp = csiphy_get_bpp( - csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); - u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; - u32 pixel_clock; /* Hz */ - u32 mipi_clock; /* Hz */ - u32 ui; /* ps */ - u32 timer_period; /* ps */ - u32 t_hs_prepare_max; /* ps */ - u32 t_hs_prepare_zero_min; /* ps */ - u32 t_hs_settle; /* ps */ - u8 settle_cnt; - int ret; - - ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); - if (ret) { - dev_err(to_device_index(csiphy, csiphy->id), - "Cannot get CSI2 transmitter's pixel clock\n"); - return 0; - } - if (!pixel_clock) { - dev_err(to_device_index(csiphy, csiphy->id), - "Got pixel clock == 0, cannot continue\n"); - return 0; - } - - mipi_clock = pixel_clock * bpp / (2 * num_lanes); - ui = div_u64(1000000000000LL, mipi_clock); - ui /= 2; - t_hs_prepare_max = 85000 + 6 * ui; - t_hs_prepare_zero_min = 145000 + 10 * ui; - t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2; - - timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate); - settle_cnt = t_hs_settle / timer_period; - - return settle_cnt; -} - -/* * csiphy_stream_on - Enable streaming on CSIPHY module * @csiphy: CSIPHY device * @@ -354,14 +237,24 @@ static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy) static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; + u32 pixel_clock; u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); - u8 settle_cnt; + u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 val; - int i = 0; + int ret; - settle_cnt = csiphy_settle_cnt_calc(csiphy); - if (!settle_cnt) + ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); + if (ret) { + dev_err(csiphy->camss->dev, + "Cannot get CSI2 transmitter's pixel clock\n"); return -EINVAL; + } + if (!pixel_clock) { + dev_err(csiphy->camss->dev, + "Got pixel clock == 0, cannot continue\n"); + return -EINVAL; + } val = readl_relaxed(csiphy->base_clk_mux); if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { @@ -372,34 +265,9 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) val |= cfg->csid_id; } writel_relaxed(val, csiphy->base_clk_mux); + wmb(); - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); - writel_relaxed(0x1, csiphy->base + - CAMSS_CSI_PHY_T_WAKEUP_CFG0); - - val = 0x1; - val |= lane_mask << 1; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); - - val = cfg->combo_mode << 4; - writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET); - - while (lane_mask) { - if (lane_mask & 0x1) { - writel_relaxed(0x10, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - writel_relaxed(settle_cnt, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG3(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_MASKn(i)); - writel_relaxed(0x3f, csiphy->base + - CAMSS_CSI_PHY_INTERRUPT_CLEARn(i)); - } - - lane_mask >>= 1; - i++; - } + csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask); return 0; } @@ -412,19 +280,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg); - int i = 0; - - while (lane_mask) { - if (lane_mask & 0x1) - writel_relaxed(0x0, csiphy->base + - CAMSS_CSI_PHY_LNn_CFG2(i)); - - lane_mask >>= 1; - i++; - } - - writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG); + csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); } @@ -489,12 +345,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy, case MSM_CSIPHY_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++) - if (fmt->code == csiphy_formats[i].code) + for (i = 0; i < csiphy->nformats; i++) + if (fmt->code == csiphy->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(csiphy_formats)) + if (i >= csiphy->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -530,10 +386,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= ARRAY_SIZE(csiphy_formats)) + if (code->index >= csiphy->nformats) return -EINVAL; - code->code = csiphy_formats[code->index].code; + code->code = csiphy->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -677,18 +533,32 @@ static int csiphy_init_formats(struct v4l2_subdev *sd, * * Return 0 on success or a negative error code otherwise */ -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, const struct resources *res, u8 id) { - struct device *dev = to_device_index(csiphy, id); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; int i, j; int ret; + csiphy->camss = camss; csiphy->id = id; csiphy->cfg.combo_mode = 0; + if (camss->version == CAMSS_8x16) { + csiphy->ops = &csiphy_ops_2ph_1_0; + csiphy->formats = csiphy_formats_8x16; + csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); + } else if (camss->version == CAMSS_8x96) { + csiphy->ops = &csiphy_ops_3ph_1_0; + csiphy->formats = csiphy_formats_8x96; + csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); + } else { + return -EINVAL; + } + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -717,7 +587,8 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy, csiphy->irq = r->start; snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy_isr, + + ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); @@ -846,7 +717,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, { struct v4l2_subdev *sd = &csiphy->subdev; struct media_pad *pads = csiphy->pads; - struct device *dev = to_device_index(csiphy, csiphy->id); + struct device *dev = csiphy->camss->dev; int ret; v4l2_subdev_init(sd, &csiphy_v4l2_ops); diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index ba8781122065..376f865ad383 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -1,24 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-csiphy.h * * Qualcomm MSM Camera Subsystem - CSIPHY Module * * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2016-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2016-2018 Linaro Ltd. */ #ifndef QC_MSM_CAMSS_CSIPHY_H #define QC_MSM_CAMSS_CSIPHY_H #include <linux/clk.h> +#include <linux/interrupt.h> #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-mediabus.h> @@ -49,7 +42,22 @@ struct csiphy_config { struct csiphy_csi2_cfg *csi2; }; +struct csiphy_device; + +struct csiphy_hw_ops { + void (*hw_version_read)(struct csiphy_device *csiphy, + struct device *dev); + void (*reset)(struct csiphy_device *csiphy); + void (*lanes_enable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u32 pixel_clock, u8 bpp, u8 lane_mask); + void (*lanes_disable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg); + irqreturn_t (*isr)(int irq, void *dev); +}; + struct csiphy_device { + struct camss *camss; u8 id; struct v4l2_subdev subdev; struct media_pad pads[MSM_CSIPHY_PADS_NUM]; @@ -62,11 +70,15 @@ struct csiphy_device { u32 timer_clk_rate; struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; + const struct csiphy_hw_ops *ops; + const struct csiphy_format *formats; + unsigned int nformats; }; struct resources; -int msm_csiphy_subdev_init(struct csiphy_device *csiphy, +int msm_csiphy_subdev_init(struct camss *camss, + struct csiphy_device *csiphy, const struct resources *res, u8 id); int msm_csiphy_register_entity(struct csiphy_device *csiphy, @@ -74,4 +86,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); +extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; +extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0; + #endif /* QC_MSM_CAMSS_CSIPHY_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index 9d1af9353c1d..7f269021d08c 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-ispif.c * * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/clk.h> #include <linux/completion.h> @@ -22,6 +14,7 @@ #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> @@ -31,12 +24,6 @@ #define MSM_ISPIF_NAME "msm_ispif" -#define ispif_line_array(ptr_line) \ - ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)])) - -#define to_ispif(ptr_line) \ - container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line) - #define ISPIF_RST_CMD_0 0x008 #define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0) #define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1) @@ -89,6 +76,13 @@ (0x254 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \ (0x264 + 0x200 * (m) + 0x4 * (n)) +/* PACK_CFG registers are 8x96 only */ +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(m, n) \ + (0x270 + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(m, n) \ + (0x27c + 0x200 * (m) + 0x4 * (n)) +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(c) \ + (1 << ((cid % 8) * 4)) #define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ (0x2c0 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ @@ -109,7 +103,7 @@ enum ispif_intf_cmd { CMD_ALL_NO_CHANGE = 0xffffffff, }; -static const u32 ispif_formats[] = { +static const u32 ispif_formats_8x16[] = { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, @@ -126,16 +120,107 @@ static const u32 ispif_formats[] = { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_Y10_1X10, +}; + +static const u32 ispif_formats_8x96[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR14_1X14, + MEDIA_BUS_FMT_SGBRG14_1X14, + MEDIA_BUS_FMT_SGRBG14_1X14, + MEDIA_BUS_FMT_SRGGB14_1X14, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, }; /* - * ispif_isr - ISPIF module interrupt handler + * ispif_isr_8x96 - ISPIF module interrupt handler for 8x96 * @irq: Interrupt line * @dev: ISPIF device * * Return IRQ_HANDLED on success */ -static irqreturn_t ispif_isr(int irq, void *dev) +static irqreturn_t ispif_isr_8x96(int irq, void *dev) +{ + struct ispif_device *ispif = dev; + u32 value0, value1, value2, value3, value4, value5; + + value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); + value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0)); + value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0)); + value3 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(1)); + value4 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(1)); + value5 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(1)); + + writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0)); + writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0)); + writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0)); + writel_relaxed(value3, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(1)); + writel_relaxed(value4, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(1)); + writel_relaxed(value5, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(1)); + + writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD); + + if ((value0 >> 27) & 0x1) + complete(&ispif->reset_complete); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); + + if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n"); + + if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n"); + + if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n"); + + if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 pix0 overflow\n"); + + if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi0 overflow\n"); + + if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 pix1 overflow\n"); + + if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi1 overflow\n"); + + if (unlikely(value5 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW)) + dev_err_ratelimited(to_device(ispif), "VFE1 rdi2 overflow\n"); + + return IRQ_HANDLED; +} + +/* + * ispif_isr_8x16 - ISPIF module interrupt handler for 8x16 + * @irq: Interrupt line + * @dev: ISPIF device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t ispif_isr_8x16(int irq, void *dev) { struct ispif_device *ispif = dev; u32 value0, value1, value2; @@ -183,6 +268,14 @@ static int ispif_reset(struct ispif_device *ispif) u32 val; int ret; + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0); + if (ret < 0) + return ret; + + ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1); + if (ret < 0) + return ret; + ret = camss_enable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset, to_device(ispif)); @@ -215,12 +308,15 @@ static int ispif_reset(struct ispif_device *ispif) msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); if (!time) { dev_err(to_device(ispif), "ISPIF reset timeout\n"); - return -EIO; + ret = -EIO; } camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); - return 0; + camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0); + camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1); + + return ret; } /* @@ -233,7 +329,7 @@ static int ispif_reset(struct ispif_device *ispif) static int ispif_set_power(struct v4l2_subdev *sd, int on) { struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); + struct ispif_device *ispif = line->ispif; struct device *dev = to_device(ispif); int ret = 0; @@ -246,12 +342,19 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } - ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) goto exit; + ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev); + if (ret < 0) { + pm_runtime_put_sync(dev); + goto exit; + } + ret = ispif_reset(ispif); if (ret < 0) { + pm_runtime_put_sync(dev); camss_disable_clocks(ispif->nclocks, ispif->clock); goto exit; } @@ -266,6 +369,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) goto exit; } else if (ispif->power_count == 1) { camss_disable_clocks(ispif->nclocks, ispif->clock); + pm_runtime_put_sync(dev); } ispif->power_count--; @@ -578,6 +682,55 @@ static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf, } /* + * ispif_config_pack - Config packing for PRDI mode + * @ispif: ISPIF device + * @code: media bus format code + * @intf: VFE interface + * @cid: desired CID to handle + * @vfe: VFE HW module id + * @enable: enable or disable + */ +static void ispif_config_pack(struct ispif_device *ispif, u32 code, + enum ispif_intf intf, u8 cid, u8 vfe, u8 enable) +{ + u32 addr, val; + + if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE && + code != MEDIA_BUS_FMT_Y10_2X8_PADHI_LE) + return; + + switch (intf) { + case RDI0: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 0); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 0); + break; + case RDI1: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 1); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 1); + break; + case RDI2: + if (cid < 8) + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 2); + else + addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 2); + break; + default: + return; + } + + if (enable) + val = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(cid); + else + val = 0; + + writel_relaxed(val, ispif->base + addr); +} + +/* * ispif_set_intf_cmd - Set command to enable/disable interface * @ispif: ISPIF device * @cmd: interface command @@ -619,7 +772,7 @@ static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd, static int ispif_set_stream(struct v4l2_subdev *sd, int enable) { struct ispif_line *line = v4l2_get_subdevdata(sd); - struct ispif_device *ispif = to_ispif(line); + struct ispif_device *ispif = line->ispif; enum ispif_intf intf = line->interface; u8 csid = line->csid_id; u8 vfe = line->vfe_id; @@ -645,6 +798,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) ispif_select_csid(ispif, intf, csid, vfe, 1); ispif_select_cid(ispif, intf, cid, vfe, 1); ispif_config_irq(ispif, intf, vfe, 1); + if (to_camss(ispif)->version == CAMSS_8x96) + ispif_config_pack(ispif, + line->fmt[MSM_ISPIF_PAD_SINK].code, + intf, cid, vfe, 1); ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY, intf, vfe, vc); } else { @@ -658,6 +815,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) return ret; mutex_lock(&ispif->config_lock); + if (to_camss(ispif)->version == CAMSS_8x96) + ispif_config_pack(ispif, + line->fmt[MSM_ISPIF_PAD_SINK].code, + intf, cid, vfe, 0); ispif_config_irq(ispif, intf, vfe, 0); ispif_select_cid(ispif, intf, cid, vfe, 0); ispif_select_csid(ispif, intf, csid, vfe, 0); @@ -710,12 +871,12 @@ static void ispif_try_format(struct ispif_line *line, case MSM_ISPIF_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(ispif_formats); i++) - if (fmt->code == ispif_formats[i]) + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i]) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(ispif_formats)) + if (i >= line->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -753,10 +914,10 @@ static int ispif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_ISPIF_PAD_SINK) { - if (code->index >= ARRAY_SIZE(ispif_formats)) + if (code->index >= line->nformats) return -EINVAL; - code->code = ispif_formats[code->index]; + code->code = line->formats[code->index]; } else { if (code->index > 0) return -EINVAL; @@ -907,6 +1068,36 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, int i; int ret; + /* Number of ISPIF lines - same as number of CSID hardware modules */ + if (to_camss(ispif)->version == CAMSS_8x16) + ispif->line_num = 2; + else if (to_camss(ispif)->version == CAMSS_8x96) + ispif->line_num = 4; + else + return -EINVAL; + + ispif->line = kcalloc(ispif->line_num, sizeof(*ispif->line), + GFP_KERNEL); + if (!ispif->line) + return -ENOMEM; + + for (i = 0; i < ispif->line_num; i++) { + ispif->line[i].ispif = ispif; + ispif->line[i].id = i; + + if (to_camss(ispif)->version == CAMSS_8x16) { + ispif->line[i].formats = ispif_formats_8x16; + ispif->line[i].nformats = + ARRAY_SIZE(ispif_formats_8x16); + } else if (to_camss(ispif)->version == CAMSS_8x96) { + ispif->line[i].formats = ispif_formats_8x96; + ispif->line[i].nformats = + ARRAY_SIZE(ispif_formats_8x96); + } else { + return -EINVAL; + } + } + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -935,8 +1126,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, ispif->irq = r->start; snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s", dev_name(dev), MSM_ISPIF_NAME); - ret = devm_request_irq(dev, ispif->irq, ispif_isr, + if (to_camss(ispif)->version == CAMSS_8x16) + ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16, + IRQF_TRIGGER_RISING, ispif->irq_name, ispif); + else if (to_camss(ispif)->version == CAMSS_8x96) + ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96, IRQF_TRIGGER_RISING, ispif->irq_name, ispif); + else + ret = -EINVAL; if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); return ret; @@ -987,9 +1184,6 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, clock->nfreqs = 0; } - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) - ispif->line[i].id = i; - mutex_init(&ispif->power_lock); ispif->power_count = 0; @@ -1108,7 +1302,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, int ret; int i; - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + for (i = 0; i < ispif->line_num; i++) { struct v4l2_subdev *sd = &ispif->line[i].subdev; struct media_pad *pads = ispif->line[i].pads; @@ -1169,7 +1363,7 @@ void msm_ispif_unregister_entities(struct ispif_device *ispif) mutex_destroy(&ispif->power_lock); mutex_destroy(&ispif->config_lock); - for (i = 0; i < ARRAY_SIZE(ispif->line); i++) { + for (i = 0; i < ispif->line_num; i++) { struct v4l2_subdev *sd = &ispif->line[i].subdev; v4l2_device_unregister_subdev(sd); diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index f668306020c3..1a5ba2425a42 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -1,19 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-ispif.h * * Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module * * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #ifndef QC_MSM_CAMSS_ISPIF_H #define QC_MSM_CAMSS_ISPIF_H @@ -23,14 +15,11 @@ #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -/* Number of ISPIF lines - same as number of CSID hardware modules */ -#define MSM_ISPIF_LINE_NUM 2 - #define MSM_ISPIF_PAD_SINK 0 #define MSM_ISPIF_PAD_SRC 1 #define MSM_ISPIF_PADS_NUM 2 -#define MSM_ISPIF_VFE_NUM 1 +#define MSM_ISPIF_VFE_NUM 2 enum ispif_intf { PIX0, @@ -46,6 +35,7 @@ struct ispif_intf_cmd_reg { }; struct ispif_line { + struct ispif_device *ispif; u8 id; u8 csid_id; u8 vfe_id; @@ -53,6 +43,8 @@ struct ispif_line { struct v4l2_subdev subdev; struct media_pad pads[MSM_ISPIF_PADS_NUM]; struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM]; + const u32 *formats; + unsigned int nformats; }; struct ispif_device { @@ -69,7 +61,8 @@ struct ispif_device { struct mutex power_lock; struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM]; struct mutex config_lock; - struct ispif_line line[MSM_ISPIF_LINE_NUM]; + unsigned int line_num; + struct ispif_line *line; }; struct resources_ispif; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c new file mode 100644 index 000000000000..da3a9fed9f2d --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -0,0 +1,1018 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-4-1.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.1 + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + */ + +#include <linux/interrupt.h> +#include <linux/iopoll.h> + +#include "camss-vfe.h" + +#define VFE_0_HW_VERSION 0x000 + +#define VFE_0_GLOBAL_RESET_CMD 0x00c +#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0) +#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1) +#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2) +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3) +#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4) +#define VFE_0_GLOBAL_RESET_CMD_TIMER BIT(5) +#define VFE_0_GLOBAL_RESET_CMD_PM BIT(6) +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(7) +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(8) + +#define VFE_0_MODULE_CFG 0x018 +#define VFE_0_MODULE_CFG_DEMUX BIT(2) +#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE BIT(3) +#define VFE_0_MODULE_CFG_SCALE_ENC BIT(23) +#define VFE_0_MODULE_CFG_CROP_ENC BIT(27) + +#define VFE_0_CORE_CFG 0x01c +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 + +#define VFE_0_IRQ_CMD 0x024 +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_0_IRQ_MASK_0 0x028 +#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1) +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_MASK_1 0x02c +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0) +#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7) +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9) +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_CLEAR_0 0x030 +#define VFE_0_IRQ_CLEAR_1 0x034 + +#define VFE_0_IRQ_STATUS_0 0x038 +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_STATUS_1 0x03c +#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7) +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 +#define VFE_0_VIOLATION_STATUS 0x48 + +#define VFE_0_BUS_CMD 0x4c +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x) + +#define VFE_0_BUS_CFG 0x050 + +#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(1) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2) + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ + (0x088 + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ + (0x08c + 0x24 * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff + +#define VFE_0_BUS_PING_PONG_STATUS 0x268 + +#define VFE_0_BUS_BDG_CMD 0x2c0 +#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 + +#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 +#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 +#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 +#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc +#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 +#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 +#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc +#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 +#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 + +#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) +#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2) +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 +#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) BIT(16 + (r)) + +#define VFE_0_CAMIF_CMD 0x2f4 +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_NO_CHANGE 3 +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2) +#define VFE_0_CAMIF_CFG 0x2f8 +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6) +#define VFE_0_CAMIF_FRAME_CFG 0x300 +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 +#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 +#define VFE_0_CAMIF_STATUS 0x31c +#define VFE_0_CAMIF_STATUS_HALT BIT(31) + +#define VFE_0_REG_UPDATE 0x378 +#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n)) +#define VFE_0_REG_UPDATE_line_n(n) \ + ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) + +#define VFE_0_DEMUX_CFG 0x424 +#define VFE_0_DEMUX_CFG_PERIOD 0x3 +#define VFE_0_DEMUX_GAIN_0 0x428 +#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) +#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) +#define VFE_0_DEMUX_GAIN_1 0x42c +#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) +#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) +#define VFE_0_DEMUX_EVEN_CFG 0x438 +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 +#define VFE_0_DEMUX_ODD_CFG 0x43c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 + +#define VFE_0_SCALE_ENC_Y_CFG 0x75c +#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 +#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 +#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c +#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 +#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c +#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 +#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 + +#define VFE_0_CROP_ENC_Y_WIDTH 0x854 +#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 +#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c +#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 + +#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 +#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) +#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 +#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) + +#define VFE_0_CGC_OVERRIDE_1 0x974 +#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) BIT(x) + +#define CAMIF_TIMEOUT_SLEEP_US 1000 +#define CAMIF_TIMEOUT_ALL_US 1000000 + +#define MSM_VFE_VFE0_UB_SIZE 1023 +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) + +static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); + + dev_dbg(dev, "VFE HW Version = 0x%08x\n", hw_version); +} + +static u16 vfe_get_ub_size(u8 vfe_id) +{ + if (vfe_id == 0) + return MSM_VFE_VFE0_UB_SIZE_RDI; + + return 0; +} + +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits & ~clr_bits, vfe->base + reg); +} + +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits | set_bits, vfe->base + reg); +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | + VFE_0_GLOBAL_RESET_CMD_BUS_MISR | + VFE_0_GLOBAL_RESET_CMD_PM | + VFE_0_GLOBAL_RESET_CMD_TIMER | + VFE_0_GLOBAL_RESET_CMD_REGISTER | + VFE_0_GLOBAL_RESET_CMD_BUS_BDG | + VFE_0_GLOBAL_RESET_CMD_BUS | + VFE_0_GLOBAL_RESET_CMD_CAMIF | + VFE_0_GLOBAL_RESET_CMD_CORE; + + writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); +} + +static void vfe_halt_request(struct vfe_device *vfe) +{ + writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, + vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_halt_clear(struct vfe_device *vfe) +{ + writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); +} + +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +static int vfe_word_per_line(u32 format, u32 pixel_per_line) +{ + int val = 0; + + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CALC_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CALC_WORD(pixel_per_line, 2, 8); + break; + } + + return val; +} + +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, + u16 *width, u16 *height, u16 *bytesperline) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + if (plane == 1) + *height /= 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + break; + } +} + +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable) +{ + u32 reg; + + if (enable) { + u16 width = 0, height = 0, bytesperline = 0, wpl; + + vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); + + wpl = vfe_word_per_line(pix->pixelformat, width); + + reg = height - 1; + reg |= ((wpl + 1) / 2 - 1) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + + wpl = vfe_word_per_line(pix->pixelformat, bytesperline); + + reg = 0x3; + reg |= (height - 1) << 4; + reg |= wpl << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } else { + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } +} + +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); + + reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); + + reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) + & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; + + writel_relaxed(reg, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); +} + +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, + u32 pattern) +{ + writel_relaxed(pattern, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); +} + +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, + u16 offset, u16 depth) +{ + u32 reg; + + reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | + depth; + writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); +} + +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) +{ + wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + wmb(); +} + +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); +} + +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); +} + +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); + + return (reg >> wm) & 0x1; +} + +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) +{ + if (enable) + writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); + else + writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); +} + +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & + VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) +{ + writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, + vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); +} + +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, + u8 enable) +{ + struct vfe_line *line = container_of(output, struct vfe_line, output); + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (i == 0) { + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + } else if (i == 1) { + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + } else { + /* On current devices output->wm_num is always <= 2 */ + break; + } + + if (output->wm_idx[i] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), + reg); + } +} + +static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line, + u8 enable) +{ + /* empty */ +} +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) +{ + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), + VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); + + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), + cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); + wmb(); + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + wmb(); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); +} + +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | + VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | + VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } +} + +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable) +{ + struct vfe_output *output = &vfe->line[line_id].output; + unsigned int i; + u32 irq_en0; + u32 irq_en1; + u32 comp_mask = 0; + + irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; + irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; + irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); + irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; + for (i = 0; i < output->wm_num; i++) { + irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( + output->wm_idx[i]); + comp_mask |= (1 << output->wm_idx[i]) << comp * 8; + } + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; + u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | + VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; + + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); +} + +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val, even_cfg, odd_cfg; + + writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); + + val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); + + val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; + break; + } + + writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); + writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); +} + +static inline u8 vfe_calc_interp_reso(u16 input, u16 output) +{ + if (input / output >= 16) + return 0; + + if (input / output >= 8) + return 1; + + if (input / output >= 4) + return 2; + + return 3; +} + +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 input, output; + u8 interp_reso; + u32 phase_mult; + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width; + output = line->compose.width / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height; + output = line->compose.height; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) + output = line->compose.height / 2; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (13 + interp_reso)) / output; + reg = (interp_reso << 20) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); +} + +static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 first, last; + + first = line->crop.left; + last = line->crop.left + line->crop.width - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); + + first = line->crop.left / 2; + last = line->crop.left / 2 + line->crop.width / 2 - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { + first = line->crop.top / 2; + last = line->crop.top / 2 + line->crop.height / 2 - 1; + } + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); +} + +static void vfe_set_clamp_cfg(struct vfe_device *vfe) +{ + u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | + VFE_0_CLAMP_ENC_MAX_CFG_CH1 | + VFE_0_CLAMP_ENC_MAX_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); + + val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | + VFE_0_CLAMP_ENC_MIN_CFG_CH1 | + VFE_0_CLAMP_ENC_MIN_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe_set_ds(struct vfe_device *vfe) +{ + /* empty */ +} + +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) +{ + u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); + + if (enable) + vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); + else + vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); + + wmb(); +} + +static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val; + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; + break; + } + + writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2; + val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].height - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); + + val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); + + val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); +} + +static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable) +{ + u32 cmd; + + cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE; + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); + wmb(); + + if (enable) + cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY; + else + cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY; + + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); +} + +static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) +{ + u32 val = VFE_0_MODULE_CFG_DEMUX | + VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | + VFE_0_MODULE_CFG_SCALE_ENC | + VFE_0_MODULE_CFG_CROP_ENC; + + if (enable) + writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); + else + writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); +} + +static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, + val, + (val & VFE_0_CAMIF_STATUS_HALT), + CAMIF_TIMEOUT_SLEEP_US, + CAMIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(dev, "%s: camif stop timeout\n", __func__); + + return ret; +} + +static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) +{ + *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); + *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); + + writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0); + writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1); + + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); +} + +static void vfe_violation_read(struct vfe_device *vfe) +{ + u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); + + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); +} + +/* + * vfe_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 value0, value1; + int i, j; + + vfe->ops->isr_read(vfe, &value0, &value1); + + trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", + value0, value1); + + if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) + vfe->isr_ops.reset_ack(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) + vfe->ops->violation_read(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) + vfe->isr_ops.halt_ack(vfe); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) + if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) + vfe->isr_ops.reg_update(vfe, i); + + if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) + vfe->isr_ops.sof(vfe, VFE_LINE_PIX); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) + vfe->isr_ops.sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { + vfe->isr_ops.comp_done(vfe, i); + for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) + if (vfe->wm_output_map[j] == VFE_LINE_PIX) + value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); + } + + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) + vfe->isr_ops.wm_done(vfe, i); + + return IRQ_HANDLED; +} + +const struct vfe_hw_ops vfe_ops_4_1 = { + .hw_version_read = vfe_hw_version_read, + .get_ub_size = vfe_get_ub_size, + .global_reset = vfe_global_reset, + .halt_request = vfe_halt_request, + .halt_clear = vfe_halt_clear, + .wm_enable = vfe_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, + .bus_reload_wm = vfe_bus_reload_wm, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .wm_set_subsample = vfe_wm_set_subsample, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .set_xbar_cfg = vfe_set_xbar_cfg, + .set_realign_cfg = vfe_set_realign_cfg, + .set_rdi_cid = vfe_set_rdi_cid, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_common = vfe_enable_irq_common, + .set_demux_cfg = vfe_set_demux_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_qos = vfe_set_qos, + .set_ds = vfe_set_ds, + .set_cgc_override = vfe_set_cgc_override, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_module_cfg = vfe_set_module_cfg, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .isr_read = vfe_isr_read, + .violation_read = vfe_violation_read, + .isr = vfe_isr, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c new file mode 100644 index 000000000000..4c584bffd179 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-4-7.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.7 + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + */ + +#include <linux/interrupt.h> +#include <linux/iopoll.h> + +#include "camss-vfe.h" + +#define VFE_0_HW_VERSION 0x000 + +#define VFE_0_GLOBAL_RESET_CMD 0x018 +#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0) +#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1) +#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2) +#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3) +#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4) +#define VFE_0_GLOBAL_RESET_CMD_PM BIT(5) +#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(6) +#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(7) +#define VFE_0_GLOBAL_RESET_CMD_DSP BIT(8) +#define VFE_0_GLOBAL_RESET_CMD_IDLE_CGC BIT(9) + +#define VFE_0_MODULE_LENS_EN 0x040 +#define VFE_0_MODULE_LENS_EN_DEMUX BIT(2) +#define VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE BIT(3) + +#define VFE_0_MODULE_ZOOM_EN 0x04c +#define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1) +#define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2) +#define VFE_0_MODULE_ZOOM_EN_REALIGN_BUF BIT(9) + +#define VFE_0_CORE_CFG 0x050 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 +#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 +#define VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN BIT(4) + +#define VFE_0_IRQ_CMD 0x058 +#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_0_IRQ_MASK_0 0x05c +#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1) +#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_MASK_1 0x060 +#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0) +#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7) +#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9) +#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_CLEAR_0 0x064 +#define VFE_0_IRQ_CLEAR_1 0x068 + +#define VFE_0_IRQ_STATUS_0 0x06c +#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0) +#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5) +#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ + ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) +#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31) +#define VFE_0_IRQ_STATUS_1 0x070 +#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7) +#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8) +#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29) + +#define VFE_0_IRQ_COMPOSITE_MASK_0 0x074 +#define VFE_0_VIOLATION_STATUS 0x07c + +#define VFE_0_BUS_CMD 0x80 +#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x) + +#define VFE_0_BUS_CFG 0x084 + +#define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2)) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2) +#define VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN BIT(3) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTRA (0x1 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER (0x2 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0 +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 0xc +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 0xd +#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 0xe + +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x0a0 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x0a4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x0ac + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x0b4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT 1 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x0b8 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x0bc + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x0c0 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ + (0x0c4 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ + (0x0c8 + 0x2c * (n)) +#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff + +#define VFE_0_BUS_PING_PONG_STATUS 0x338 + +#define VFE_0_BUS_BDG_CMD 0x400 +#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 + +#define VFE_0_BUS_BDG_QOS_CFG_0 0x404 +#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa9aaa9 +#define VFE_0_BUS_BDG_QOS_CFG_1 0x408 +#define VFE_0_BUS_BDG_QOS_CFG_2 0x40c +#define VFE_0_BUS_BDG_QOS_CFG_3 0x410 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x414 +#define VFE_0_BUS_BDG_QOS_CFG_5 0x418 +#define VFE_0_BUS_BDG_QOS_CFG_6 0x41c +#define VFE_0_BUS_BDG_QOS_CFG_7 0x420 +#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa9 + +#define VFE_0_BUS_BDG_DS_CFG_0 0x424 +#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc0011 +#define VFE_0_BUS_BDG_DS_CFG_1 0x428 +#define VFE_0_BUS_BDG_DS_CFG_2 0x42c +#define VFE_0_BUS_BDG_DS_CFG_3 0x430 +#define VFE_0_BUS_BDG_DS_CFG_4 0x434 +#define VFE_0_BUS_BDG_DS_CFG_5 0x438 +#define VFE_0_BUS_BDG_DS_CFG_6 0x43c +#define VFE_0_BUS_BDG_DS_CFG_7 0x440 +#define VFE_0_BUS_BDG_DS_CFG_8 0x444 +#define VFE_0_BUS_BDG_DS_CFG_9 0x448 +#define VFE_0_BUS_BDG_DS_CFG_10 0x44c +#define VFE_0_BUS_BDG_DS_CFG_11 0x450 +#define VFE_0_BUS_BDG_DS_CFG_12 0x454 +#define VFE_0_BUS_BDG_DS_CFG_13 0x458 +#define VFE_0_BUS_BDG_DS_CFG_14 0x45c +#define VFE_0_BUS_BDG_DS_CFG_15 0x460 +#define VFE_0_BUS_BDG_DS_CFG_16 0x464 +#define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x40000103 + +#define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x))) +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 +#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 +#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) +#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2) +#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 + +#define VFE_0_CAMIF_CMD 0x478 +#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 +#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 +#define VFE_0_CAMIF_CMD_NO_CHANGE 3 +#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2) +#define VFE_0_CAMIF_CFG 0x47c +#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6) +#define VFE_0_CAMIF_FRAME_CFG 0x484 +#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x488 +#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x48c +#define VFE_0_CAMIF_SUBSAMPLE_CFG 0x490 +#define VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN 0x498 +#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x49c +#define VFE_0_CAMIF_STATUS 0x4a4 +#define VFE_0_CAMIF_STATUS_HALT BIT(31) + +#define VFE_0_REG_UPDATE 0x4ac +#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n)) +#define VFE_0_REG_UPDATE_line_n(n) \ + ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) + +#define VFE_0_DEMUX_CFG 0x560 +#define VFE_0_DEMUX_CFG_PERIOD 0x3 +#define VFE_0_DEMUX_GAIN_0 0x564 +#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) +#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) +#define VFE_0_DEMUX_GAIN_1 0x568 +#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) +#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) +#define VFE_0_DEMUX_EVEN_CFG 0x574 +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 +#define VFE_0_DEMUX_ODD_CFG 0x578 +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac +#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c +#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca +#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 + +#define VFE_0_SCALE_ENC_Y_CFG 0x91c +#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x920 +#define VFE_0_SCALE_ENC_Y_H_PHASE 0x924 +#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x934 +#define VFE_0_SCALE_ENC_Y_V_PHASE 0x938 +#define VFE_0_SCALE_ENC_CBCR_CFG 0x948 +#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x94c +#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x950 +#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x960 +#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x964 + +#define VFE_0_CROP_ENC_Y_WIDTH 0x974 +#define VFE_0_CROP_ENC_Y_HEIGHT 0x978 +#define VFE_0_CROP_ENC_CBCR_WIDTH 0x97c +#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x980 + +#define VFE_0_CLAMP_ENC_MAX_CFG 0x984 +#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) +#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) +#define VFE_0_CLAMP_ENC_MIN_CFG 0x988 +#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) +#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) + +#define VFE_0_REALIGN_BUF_CFG 0xaac +#define VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL BIT(2) +#define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3) +#define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4) + +#define CAMIF_TIMEOUT_SLEEP_US 1000 +#define CAMIF_TIMEOUT_ALL_US 1000000 + +#define MSM_VFE_VFE0_UB_SIZE 2047 +#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3) +#define MSM_VFE_VFE1_UB_SIZE 1535 +#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3) + +static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); + + dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version); +} + +static u16 vfe_get_ub_size(u8 vfe_id) +{ + if (vfe_id == 0) + return MSM_VFE_VFE0_UB_SIZE_RDI; + else if (vfe_id == 1) + return MSM_VFE_VFE1_UB_SIZE_RDI; + + return 0; +} + +static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits & ~clr_bits, vfe->base + reg); +} + +static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) +{ + u32 bits = readl_relaxed(vfe->base + reg); + + writel_relaxed(bits | set_bits, vfe->base + reg); +} + +static void vfe_global_reset(struct vfe_device *vfe) +{ + u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_IDLE_CGC | + VFE_0_GLOBAL_RESET_CMD_DSP | + VFE_0_GLOBAL_RESET_CMD_TESTGEN | + VFE_0_GLOBAL_RESET_CMD_BUS_MISR | + VFE_0_GLOBAL_RESET_CMD_PM | + VFE_0_GLOBAL_RESET_CMD_REGISTER | + VFE_0_GLOBAL_RESET_CMD_BUS_BDG | + VFE_0_GLOBAL_RESET_CMD_BUS | + VFE_0_GLOBAL_RESET_CMD_CAMIF | + VFE_0_GLOBAL_RESET_CMD_CORE; + + writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0); + wmb(); + writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); +} + +static void vfe_halt_request(struct vfe_device *vfe) +{ + writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, + vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_halt_clear(struct vfe_device *vfe) +{ + writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); +} + +static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT); + else + vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm), + 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT); +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +static int vfe_word_per_line_by_pixel(u32 format, u32 pixel_per_line) +{ + int val = 0; + + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CALC_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CALC_WORD(pixel_per_line, 2, 8); + break; + } + + return val; +} + +static int vfe_word_per_line_by_bytes(u32 bytes_per_line) +{ + return CALC_WORD(bytes_per_line, 1, 8); +} + +static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, + u16 *width, u16 *height, u16 *bytesperline) +{ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + if (plane == 1) + *height /= 2; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[0].bytesperline; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + *width = pix->width; + *height = pix->height; + *bytesperline = pix->plane_fmt[plane].bytesperline; + break; + + } +} + +static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable) +{ + u32 reg; + + if (enable) { + u16 width = 0, height = 0, bytesperline = 0, wpl; + + vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); + + wpl = vfe_word_per_line_by_pixel(pix->pixelformat, width); + + reg = height - 1; + reg |= ((wpl + 3) / 4 - 1) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + + wpl = vfe_word_per_line_by_bytes(bytesperline); + + reg = 0x3; + reg |= (height - 1) << 2; + reg |= ((wpl + 1) / 2) << 16; + + writel_relaxed(reg, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } else { + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); + writel_relaxed(0, vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); + } +} + +static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); + + reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); + + reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) + & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; + + writel_relaxed(reg, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); +} + +static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, + u32 pattern) +{ + writel_relaxed(pattern, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); +} + +static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, + u16 offset, u16 depth) +{ + u32 reg; + + reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | + depth; + writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); +} + +static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) +{ + wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + wmb(); +} + +static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); +} + +static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) +{ + writel_relaxed(addr, + vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); +} + +static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) +{ + u32 reg; + + reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); + + return (reg >> wm) & 0x1; +} + +static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) +{ + if (enable) + writel_relaxed(0x101, vfe->base + VFE_0_BUS_CFG); + else + writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); +} + +static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & + VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) +{ + writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, + vfe->base + + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); +} + +static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id) +{ + u32 reg; + + reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); + + switch (id) { + case VFE_LINE_RDI0: + default: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI1: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + case VFE_LINE_RDI2: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + break; + } + + if (wm % 2 == 1) + reg <<= 16; + + vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); +} + +static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, + u8 enable) +{ + struct vfe_line *line = container_of(output, struct vfe_line, output); + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + + switch (p) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << + VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; + + if (output->wm_idx[0] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + + reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + + if (output->wm_idx[1] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]), + reg); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_UYVY: + reg = VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN; + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; + + if (p == V4L2_PIX_FMT_YUYV || p == V4L2_PIX_FMT_YVYU) + reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + + if (output->wm_idx[0] % 2 == 1) + reg <<= 16; + + if (enable) + vfe_reg_set(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + else + vfe_reg_clr(vfe, + VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]), + reg); + break; + default: + break; + } +} + +static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line, + u8 enable) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 val = VFE_0_MODULE_ZOOM_EN_REALIGN_BUF; + + if (p != V4L2_PIX_FMT_YUYV && p != V4L2_PIX_FMT_YVYU && + p != V4L2_PIX_FMT_VYUY && p != V4L2_PIX_FMT_UYVY) + return; + + if (enable) { + vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val); + } else { + vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val); + return; + } + + val = VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE; + + if (p == V4L2_PIX_FMT_UYVY || p == V4L2_PIX_FMT_YUYV) + val |= VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL; + else + val |= VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL; + + writel_relaxed(val, vfe->base + VFE_0_REALIGN_BUF_CFG); +} + +static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) +{ + vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), + VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); + + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), + cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); + wmb(); + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + wmb(); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); +} + +static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | + VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | + VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + } +} + +static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable) +{ + struct vfe_output *output = &vfe->line[line_id].output; + unsigned int i; + u32 irq_en0; + u32 irq_en1; + u32 comp_mask = 0; + + irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; + irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; + irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); + irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); + irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; + for (i = 0; i < output->wm_num; i++) { + irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( + output->wm_idx[i]); + comp_mask |= (1 << output->wm_idx[i]) << comp * 8; + } + + if (enable) { + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } else { + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); + vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); + } +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; + u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | + VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; + + vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); + vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); +} + +static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val, even_cfg, odd_cfg; + + writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); + + val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); + + val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; + writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; + odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; + break; + } + + writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); + writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); +} + +static inline u8 vfe_calc_interp_reso(u16 input, u16 output) +{ + if (input / output >= 16) + return 0; + + if (input / output >= 8) + return 1; + + if (input / output >= 4) + return 2; + + return 3; +} + +static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 input, output; + u8 interp_reso; + u32 phase_mult; + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width - 1; + output = line->compose.width - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height - 1; + output = line->compose.height - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); + + writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); + + input = line->fmt[MSM_VFE_PAD_SINK].width - 1; + output = line->compose.width / 2 - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); + + input = line->fmt[MSM_VFE_PAD_SINK].height - 1; + output = line->compose.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) + output = line->compose.height / 2 - 1; + reg = (output << 16) | input; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); + + interp_reso = vfe_calc_interp_reso(input, output); + phase_mult = input * (1 << (14 + interp_reso)) / output; + reg = (interp_reso << 28) | phase_mult; + writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); +} + +static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; + u32 reg; + u16 first, last; + + first = line->crop.left; + last = line->crop.left + line->crop.width - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); + + first = line->crop.left / 2; + last = line->crop.left / 2 + line->crop.width / 2 - 1; + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); + + first = line->crop.top; + last = line->crop.top + line->crop.height - 1; + if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { + first = line->crop.top / 2; + last = line->crop.top / 2 + line->crop.height / 2 - 1; + } + reg = (first << 16) | last; + writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); +} + +static void vfe_set_clamp_cfg(struct vfe_device *vfe) +{ + u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | + VFE_0_CLAMP_ENC_MAX_CFG_CH1 | + VFE_0_CLAMP_ENC_MAX_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); + + val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | + VFE_0_CLAMP_ENC_MIN_CFG_CH1 | + VFE_0_CLAMP_ENC_MIN_CFG_CH2; + + writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); + writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); +} + +static void vfe_set_ds(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG; + u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG; + + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14); + writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15); + writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16); +} + +static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) +{ + /* empty */ +} + +static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) +{ + u32 val; + + switch (line->fmt[MSM_VFE_PAD_SINK].code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + default: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; + break; + } + + val |= VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN; + writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + val |= (line->fmt[MSM_VFE_PAD_SINK].height - 1) << 16; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); + + val = line->fmt[MSM_VFE_PAD_SINK].height - 1; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN); + + val = 0xffffffff; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); + + val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; + vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); + + val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; + writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); +} + +static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable) +{ + u32 cmd; + + cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE; + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); + wmb(); + + if (enable) + cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY; + else + cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY; + + writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); +} + +static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) +{ + u32 val_lens = VFE_0_MODULE_LENS_EN_DEMUX | + VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE; + u32 val_zoom = VFE_0_MODULE_ZOOM_EN_SCALE_ENC | + VFE_0_MODULE_ZOOM_EN_CROP_ENC; + + if (enable) { + vfe_reg_set(vfe, VFE_0_MODULE_LENS_EN, val_lens); + vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom); + } else { + vfe_reg_clr(vfe, VFE_0_MODULE_LENS_EN, val_lens); + vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom); + } +} + +static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, + val, + (val & VFE_0_CAMIF_STATUS_HALT), + CAMIF_TIMEOUT_SLEEP_US, + CAMIF_TIMEOUT_ALL_US); + if (ret < 0) + dev_err(dev, "%s: camif stop timeout\n", __func__); + + return ret; +} + +static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) +{ + *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); + *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); + + writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0); + writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1); + + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); +} + +static void vfe_violation_read(struct vfe_device *vfe) +{ + u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); + + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); +} + +/* + * vfe_isr - ISPIF module interrupt handler + * @irq: Interrupt line + * @dev: VFE device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t vfe_isr(int irq, void *dev) +{ + struct vfe_device *vfe = dev; + u32 value0, value1; + int i, j; + + vfe->ops->isr_read(vfe, &value0, &value1); + + trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", + value0, value1); + + if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) + vfe->isr_ops.reset_ack(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) + vfe->ops->violation_read(vfe); + + if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) + vfe->isr_ops.halt_ack(vfe); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) + if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) + vfe->isr_ops.reg_update(vfe, i); + + if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) + vfe->isr_ops.sof(vfe, VFE_LINE_PIX); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) + vfe->isr_ops.sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { + vfe->isr_ops.comp_done(vfe, i); + for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) + if (vfe->wm_output_map[j] == VFE_LINE_PIX) + value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); + } + + for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) + if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) + vfe->isr_ops.wm_done(vfe, i); + + return IRQ_HANDLED; +} + +const struct vfe_hw_ops vfe_ops_4_7 = { + .hw_version_read = vfe_hw_version_read, + .get_ub_size = vfe_get_ub_size, + .global_reset = vfe_global_reset, + .halt_request = vfe_halt_request, + .halt_clear = vfe_halt_clear, + .wm_enable = vfe_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, + .bus_reload_wm = vfe_bus_reload_wm, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .wm_set_subsample = vfe_wm_set_subsample, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .set_xbar_cfg = vfe_set_xbar_cfg, + .set_realign_cfg = vfe_set_realign_cfg, + .set_rdi_cid = vfe_set_rdi_cid, + .reg_update = vfe_reg_update, + .reg_update_clear = vfe_reg_update_clear, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_common = vfe_enable_irq_common, + .set_demux_cfg = vfe_set_demux_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_qos = vfe_set_qos, + .set_ds = vfe_set_ds, + .set_cgc_override = vfe_set_cgc_override, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_module_cfg = vfe_set_module_cfg, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .isr_read = vfe_isr_read, + .violation_read = vfe_violation_read, + .isr = vfe_isr, +}; diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index a6329a8a7c4a..ed6a557de65d 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -1,28 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-vfe.c * * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/clk.h> #include <linux/completion.h> #include <linux/interrupt.h> #include <linux/iommu.h> -#include <linux/iopoll.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/spinlock_types.h> #include <linux/spinlock.h> #include <media/media-entity.h> @@ -38,194 +30,7 @@ ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) #define to_vfe(ptr_line) \ - container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line) - -#define VFE_0_HW_VERSION 0x000 - -#define VFE_0_GLOBAL_RESET_CMD 0x00c -#define VFE_0_GLOBAL_RESET_CMD_CORE (1 << 0) -#define VFE_0_GLOBAL_RESET_CMD_CAMIF (1 << 1) -#define VFE_0_GLOBAL_RESET_CMD_BUS (1 << 2) -#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG (1 << 3) -#define VFE_0_GLOBAL_RESET_CMD_REGISTER (1 << 4) -#define VFE_0_GLOBAL_RESET_CMD_TIMER (1 << 5) -#define VFE_0_GLOBAL_RESET_CMD_PM (1 << 6) -#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR (1 << 7) -#define VFE_0_GLOBAL_RESET_CMD_TESTGEN (1 << 8) - -#define VFE_0_MODULE_CFG 0x018 -#define VFE_0_MODULE_CFG_DEMUX (1 << 2) -#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE (1 << 3) -#define VFE_0_MODULE_CFG_SCALE_ENC (1 << 23) -#define VFE_0_MODULE_CFG_CROP_ENC (1 << 27) - -#define VFE_0_CORE_CFG 0x01c -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6 -#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7 - -#define VFE_0_IRQ_CMD 0x024 -#define VFE_0_IRQ_CMD_GLOBAL_CLEAR (1 << 0) - -#define VFE_0_IRQ_MASK_0 0x028 -#define VFE_0_IRQ_MASK_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_MASK_0_CAMIF_EOF (1 << 1) -#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_MASK_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_MASK_1 0x02c -#define VFE_0_IRQ_MASK_1_CAMIF_ERROR (1 << 0) -#define VFE_0_IRQ_MASK_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) (1 << ((n) + 9)) -#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_CLEAR_0 0x030 -#define VFE_0_IRQ_CLEAR_1 0x034 - -#define VFE_0_IRQ_STATUS_0 0x038 -#define VFE_0_IRQ_STATUS_0_CAMIF_SOF (1 << 0) -#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5)) -#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \ - ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n)) -#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8)) -#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25)) -#define VFE_0_IRQ_STATUS_0_RESET_ACK (1 << 31) -#define VFE_0_IRQ_STATUS_1 0x03c -#define VFE_0_IRQ_STATUS_1_VIOLATION (1 << 7) -#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK (1 << 8) -#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) (1 << ((n) + 29)) - -#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40 -#define VFE_0_VIOLATION_STATUS 0x48 - -#define VFE_0_BUS_CMD 0x4c -#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) (1 << (x)) - -#define VFE_0_BUS_CFG 0x050 - -#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2)) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN (1 << 1) -#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4) -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6 -#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7 - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1F << 2) - -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16 -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \ - (0x088 + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \ - (0x08c + 0x24 * (n)) -#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff - -#define VFE_0_BUS_PING_PONG_STATUS 0x268 - -#define VFE_0_BUS_BDG_CMD 0x2c0 -#define VFE_0_BUS_BDG_CMD_HALT_REQ 1 - -#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4 -#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5 -#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8 -#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc -#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0 -#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4 -#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8 -#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc -#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0 -#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5 - -#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x))) -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28 -#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28) -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4 -#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4) -#define VFE_0_RDI_CFG_x_RDI_EN_BIT (1 << 2) -#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3 -#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) (1 << (16 + (r))) - -#define VFE_0_CAMIF_CMD 0x2f4 -#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0 -#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1 -#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2) -#define VFE_0_CAMIF_CFG 0x2f8 -#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6) -#define VFE_0_CAMIF_FRAME_CFG 0x300 -#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304 -#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308 -#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c -#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314 -#define VFE_0_CAMIF_STATUS 0x31c -#define VFE_0_CAMIF_STATUS_HALT (1 << 31) - -#define VFE_0_REG_UPDATE 0x378 -#define VFE_0_REG_UPDATE_RDIn(n) (1 << (1 + (n))) -#define VFE_0_REG_UPDATE_line_n(n) \ - ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n)) - -#define VFE_0_DEMUX_CFG 0x424 -#define VFE_0_DEMUX_CFG_PERIOD 0x3 -#define VFE_0_DEMUX_GAIN_0 0x428 -#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0) -#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16) -#define VFE_0_DEMUX_GAIN_1 0x42c -#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0) -#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16) -#define VFE_0_DEMUX_EVEN_CFG 0x438 -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9 -#define VFE_0_DEMUX_ODD_CFG 0x43c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac -#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c -#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca -#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9 - -#define VFE_0_SCALE_ENC_Y_CFG 0x75c -#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760 -#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764 -#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c -#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770 -#define VFE_0_SCALE_ENC_CBCR_CFG 0x778 -#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c -#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780 -#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790 -#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794 - -#define VFE_0_CROP_ENC_Y_WIDTH 0x854 -#define VFE_0_CROP_ENC_Y_HEIGHT 0x858 -#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c -#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860 - -#define VFE_0_CLAMP_ENC_MAX_CFG 0x874 -#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8) -#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16) -#define VFE_0_CLAMP_ENC_MIN_CFG 0x878 -#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8) -#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16) - -#define VFE_0_CGC_OVERRIDE_1 0x974 -#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) (1 << (x)) + container_of(vfe_line_array(ptr_line), struct vfe_device, line) /* VFE reset timeout */ #define VFE_RESET_TIMEOUT_MS 50 @@ -238,633 +43,230 @@ #define VFE_NEXT_SOF_MS 500 -#define CAMIF_TIMEOUT_SLEEP_US 1000 -#define CAMIF_TIMEOUT_ALL_US 1000000 - #define SCALER_RATIO_MAX 16 -static const struct { +struct vfe_format { u32 code; u8 bpp; -} vfe_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_VYUY8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YUYV8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_YVYU8_2X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - 8, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - 10, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - 12, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - 12, - } +}; + +static const struct vfe_format formats_rdi_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, +}; + +static const struct vfe_format formats_pix_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, +}; + +static const struct vfe_format formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +}; + +static const struct vfe_format formats_pix_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, }; /* * vfe_get_bpp - map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array * @code: media bus format code * * Return number of bits per pixel */ -static u8 vfe_get_bpp(u32 code) +static u8 vfe_get_bpp(const struct vfe_format *formats, + unsigned int nformats, u32 code) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (code == vfe_formats[i].code) - return vfe_formats[i].bpp; + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].bpp; WARN(1, "Unknown format\n"); - return vfe_formats[0].bpp; -} - -static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits & ~clr_bits, vfe->base + reg); -} - -static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits) -{ - u32 bits = readl_relaxed(vfe->base + reg); - - writel_relaxed(bits | set_bits, vfe->base + reg); -} - -static void vfe_global_reset(struct vfe_device *vfe) -{ - u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN | - VFE_0_GLOBAL_RESET_CMD_BUS_MISR | - VFE_0_GLOBAL_RESET_CMD_PM | - VFE_0_GLOBAL_RESET_CMD_TIMER | - VFE_0_GLOBAL_RESET_CMD_REGISTER | - VFE_0_GLOBAL_RESET_CMD_BUS_BDG | - VFE_0_GLOBAL_RESET_CMD_BUS | - VFE_0_GLOBAL_RESET_CMD_CAMIF | - VFE_0_GLOBAL_RESET_CMD_CORE; - - writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); -} - -static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT); + return formats[0].bpp; } -static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) -{ - if (enable) - vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); - else - vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm), - 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT); -} - -#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) - -static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line) +static u32 vfe_find_code(u32 *code, unsigned int n_code, + unsigned int index, u32 req_code) { - int val = 0; - - switch (format) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - val = CALC_WORD(pixel_per_line, 1, 8); - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - val = CALC_WORD(pixel_per_line, 2, 8); - break; - } - - return val; -} - -static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane, - u16 *width, u16 *height, u16 *bytesperline) -{ - switch (pix->pixelformat) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - if (plane == 1) - *height /= 2; - break; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - *width = pix->width; - *height = pix->height; - *bytesperline = pix->plane_fmt[0].bytesperline; - break; - } -} - -static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm, - struct v4l2_pix_format_mplane *pix, - u8 plane, u32 enable) -{ - u32 reg; - - if (enable) { - u16 width = 0, height = 0, bytesperline = 0, wpl; - - vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline); - - wpl = vfe_word_per_line(pix->pixelformat, width); - - reg = height - 1; - reg |= ((wpl + 1) / 2 - 1) << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - - wpl = vfe_word_per_line(pix->pixelformat, bytesperline); - - reg = 0x3; - reg |= (height - 1) << 4; - reg |= wpl << 16; - - writel_relaxed(reg, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } else { - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm)); - writel_relaxed(0, vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm)); - } -} - -static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); - - reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK); - - reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT) - & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK; - - writel_relaxed(reg, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm)); -} - -static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm, - u32 pattern) -{ - writel_relaxed(pattern, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm)); -} - -static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset, - u16 depth) -{ - u32 reg; - - reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) | - depth; - writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm)); -} - -static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) -{ - wmb(); - writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); - wmb(); -} - -static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm)); -} - -static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr) -{ - writel_relaxed(addr, - vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm)); -} - -static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm) -{ - u32 reg; - - reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS); - - return (reg >> wm) & 0x1; -} - -static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable) -{ - if (enable) - writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG); - else - writel_relaxed(0, vfe->base + VFE_0_BUS_CFG); -} - -static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) & - VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} - -static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm) -{ - writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF, - vfe->base + - VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm)); -} - -static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm, - enum vfe_line_id id) -{ - u32 reg; - - reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id); - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg); - - reg = VFE_0_RDI_CFG_x_RDI_EN_BIT; - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg); - - switch (id) { - case VFE_LINE_RDI0: - default: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI1: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - case VFE_LINE_RDI2: - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - break; - } - - if (wm % 2 == 1) - reg <<= 16; - - vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg); -} + int i; -static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output, - u8 enable) -{ - struct vfe_line *line = container_of(output, struct vfe_line, output); - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - unsigned int i; + if (!req_code && (index >= n_code)) + return 0; - for (i = 0; i < output->wm_num; i++) { - if (i == 0) { - reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA << - VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT; - } else if (i == 1) { - reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16) - reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA; + for (i = 0; i < n_code; i++) + if (req_code) { + if (req_code == code[i]) + return req_code; } else { - /* On current devices output->wm_num is always <= 2 */ - break; + if (i == index) + return code[i]; } - if (output->wm_idx[i] % 2 == 1) - reg <<= 16; - - if (enable) - vfe_reg_set(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - else - vfe_reg_clr(vfe, - VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]), - reg); - } -} - -static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) -{ - vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), - VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK); - - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), - cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT); + return code[0]; } -static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, + unsigned int index, u32 src_req_code) { - vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id); - wmb(); - writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); - wmb(); -} - -static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm, - enum vfe_line_id line_id, u8 enable) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) | - VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) | - VFE_0_IRQ_MASK_1_RDIn_SOF(line_id); - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - } -} - -static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp, - enum vfe_line_id line_id, u8 enable) -{ - struct vfe_output *output = &vfe->line[line_id].output; - unsigned int i; - u32 irq_en0; - u32 irq_en1; - u32 comp_mask = 0; - - irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF; - irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF; - irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp); - irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id); - irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR; - for (i = 0; i < output->wm_num; i++) { - irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW( - output->wm_idx[i]); - comp_mask |= (1 << output->wm_idx[i]) << comp * 8; - } - - if (enable) { - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } else { - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1); - vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask); - } -} - -static void vfe_enable_irq_common(struct vfe_device *vfe) -{ - u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK; - u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION | - VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK; - - vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0); - vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1); -} - -static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val, even_cfg, odd_cfg; - - writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG); - - val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0); - - val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2; - writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1); + struct vfe_device *vfe = to_vfe(line); - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY; - odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY; - break; - } + if (vfe->camss->version == CAMSS_8x16) + switch (sink_code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YUYV8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_YVYU8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_YVYU8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_UYVY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_UYVY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_VYUY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_VYUY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + default: + if (index > 0) + return 0; - writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG); - writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG); -} + return sink_code; + } + else if (vfe->camss->version == CAMSS_8x96) + switch (sink_code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_YVYU8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YVYU8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_UYVY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_UYVY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + case MEDIA_BUS_FMT_VYUY8_2X8: + { + u32 src_code[] = { + MEDIA_BUS_FMT_VYUY8_2X8, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_VYUY8_1_5X8, + }; + + return vfe_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); + } + default: + if (index > 0) + return 0; -static inline u8 vfe_calc_interp_reso(u16 input, u16 output) -{ - if (input / output >= 16) + return sink_code; + } + else return 0; - - if (input / output >= 8) - return 1; - - if (input / output >= 4) - return 2; - - return 3; -} - -static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 input, output; - u8 interp_reso; - u32 phase_mult; - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE); - - writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG); - - input = line->fmt[MSM_VFE_PAD_SINK].width; - output = line->compose.width / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE); - - input = line->fmt[MSM_VFE_PAD_SINK].height; - output = line->compose.height; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) - output = line->compose.height / 2; - reg = (output << 16) | input; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE); - - interp_reso = vfe_calc_interp_reso(input, output); - phase_mult = input * (1 << (13 + interp_reso)) / output; - reg = (interp_reso << 20) | phase_mult; - writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE); -} - -static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat; - u32 reg; - u16 first, last; - - first = line->crop.left; - last = line->crop.left + line->crop.width - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT); - - first = line->crop.left / 2; - last = line->crop.left / 2 + line->crop.width / 2 - 1; - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH); - - first = line->crop.top; - last = line->crop.top + line->crop.height - 1; - if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) { - first = line->crop.top / 2; - last = line->crop.top / 2 + line->crop.height / 2 - 1; - } - reg = (first << 16) | last; - writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT); -} - -static void vfe_set_clamp_cfg(struct vfe_device *vfe) -{ - u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 | - VFE_0_CLAMP_ENC_MAX_CFG_CH1 | - VFE_0_CLAMP_ENC_MAX_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG); - - val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 | - VFE_0_CLAMP_ENC_MIN_CFG_CH1 | - VFE_0_CLAMP_ENC_MIN_CFG_CH2; - - writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); } /* @@ -879,12 +281,12 @@ static int vfe_reset(struct vfe_device *vfe) reinit_completion(&vfe->reset_complete); - vfe_global_reset(vfe); + vfe->ops->global_reset(vfe); time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device(vfe), "VFE reset timeout\n"); + dev_err(vfe->camss->dev, "VFE reset timeout\n"); return -EIO; } @@ -903,13 +305,12 @@ static int vfe_halt(struct vfe_device *vfe) reinit_completion(&vfe->halt_complete); - writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ, - vfe->base + VFE_0_BUS_BDG_CMD); + vfe->ops->halt_request(vfe); time = wait_for_completion_timeout(&vfe->halt_complete, msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); if (!time) { - dev_err(to_device(vfe), "VFE halt timeout\n"); + dev_err(vfe->camss->dev, "VFE halt timeout\n"); return -EIO; } @@ -927,10 +328,6 @@ static void vfe_init_outputs(struct vfe_device *vfe) output->buf[0] = NULL; output->buf[1] = NULL; INIT_LIST_HEAD(&output->pending_bufs); - - output->wm_num = 1; - if (vfe->line[i].id == VFE_LINE_PIX) - output->wm_num = 2; } } @@ -942,115 +339,6 @@ static void vfe_reset_output_maps(struct vfe_device *vfe) vfe->wm_output_map[i] = VFE_LINE_NONE; } -static void vfe_set_qos(struct vfe_device *vfe) -{ - u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; - u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG; - - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); - writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); - writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); -} - -static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable) -{ - u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm); - - if (enable) - vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val); - else - vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val); - - wmb(); -} - -static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable) -{ - u32 val = VFE_0_MODULE_CFG_DEMUX | - VFE_0_MODULE_CFG_CHROMA_UPSAMPLE | - VFE_0_MODULE_CFG_SCALE_ENC | - VFE_0_MODULE_CFG_CROP_ENC; - - if (enable) - writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG); - else - writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG); -} - -static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line) -{ - u32 val; - - switch (line->fmt[MSM_VFE_PAD_SINK].code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY; - break; - } - - writel_relaxed(val, vfe->base + VFE_0_CORE_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2; - val |= line->fmt[MSM_VFE_PAD_SINK].height << 16; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG); - - val = line->fmt[MSM_VFE_PAD_SINK].height - 1; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0); - - val = 0xffffffff; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN); - - val = VFE_0_RDI_CFG_x_MIPI_EN_BITS; - vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val); - - val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN; - writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG); -} - -static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd) -{ - writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS, - vfe->base + VFE_0_CAMIF_CMD); - - writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); -} - -static int vfe_camif_wait_for_stop(struct vfe_device *vfe) -{ - u32 val; - int ret; - - ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS, - val, - (val & VFE_0_CAMIF_STATUS_HALT), - CAMIF_TIMEOUT_SLEEP_US, - CAMIF_TIMEOUT_ALL_US); - if (ret < 0) - dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__); - - return ret; -} - static void vfe_output_init_addrs(struct vfe_device *vfe, struct vfe_output *output, u8 sync) { @@ -1071,10 +359,10 @@ static void vfe_output_init_addrs(struct vfe_device *vfe, else pong_addr = ping_addr; - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1090,9 +378,9 @@ static void vfe_output_update_ping_addr(struct vfe_device *vfe, else addr = 0; - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1108,9 +396,9 @@ static void vfe_output_update_pong_addr(struct vfe_device *vfe, else addr = 0; - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr); if (sync) - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); } } @@ -1154,12 +442,13 @@ static void vfe_output_frame_drop(struct vfe_device *vfe, drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; for (i = 0; i < output->wm_num; i++) { - vfe_wm_set_framedrop_period(vfe, output->wm_idx[i], - drop_period); - vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i], - drop_pattern); + vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i], + drop_period); + vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i], + drop_pattern); } - vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id); + vfe->ops->reg_update(vfe, + container_of(output, struct vfe_line, output)->id); } static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) @@ -1214,7 +503,7 @@ static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, break; case VFE_OUTPUT_SINGLE: default: - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Next buf in wrong state! %d\n", output->state); break; @@ -1234,7 +523,7 @@ static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, vfe_output_frame_drop(vfe, output, 0); break; default: - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Last buff in wrong state! %d\n", output->state); break; @@ -1263,7 +552,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, output->state = VFE_OUTPUT_CONTINUOUS; } else { vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Inactive buffer is busy\n"); } break; @@ -1278,7 +567,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, output->state = VFE_OUTPUT_SINGLE; } else { vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Output idle with buffer set!\n"); } break; @@ -1294,6 +583,7 @@ static int vfe_get_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; + struct v4l2_format *f = &line->video_out.active_fmt; unsigned long flags; int i; int wm_idx; @@ -1302,17 +592,29 @@ static int vfe_get_output(struct vfe_line *line) output = &line->output; if (output->state != VFE_OUTPUT_OFF) { - dev_err(to_device(vfe), "Output is running\n"); + dev_err(vfe->camss->dev, "Output is running\n"); goto error; } output->state = VFE_OUTPUT_RESERVED; output->active_buf = 0; + switch (f->fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + output->wm_num = 2; + break; + default: + output->wm_num = 1; + break; + } + for (i = 0; i < output->wm_num; i++) { wm_idx = vfe_reserve_wm(vfe, line->id); if (wm_idx < 0) { - dev_err(to_device(vfe), "Can not reserve wm\n"); + dev_err(vfe->camss->dev, "Can not reserve wm\n"); goto error_get_wm; } output->wm_idx[i] = wm_idx; @@ -1356,27 +658,21 @@ static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; + const struct vfe_hw_ops *ops = vfe->ops; unsigned long flags; unsigned int i; u16 ub_size; - switch (vfe->id) { - case 0: - ub_size = MSM_VFE_VFE0_UB_SIZE_RDI; - break; - case 1: - ub_size = MSM_VFE_VFE1_UB_SIZE_RDI; - break; - default: + ub_size = ops->get_ub_size(vfe->id); + if (!ub_size) return -EINVAL; - } spin_lock_irqsave(&vfe->output_lock, flags); - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id); + ops->reg_update_clear(vfe, line->id); if (output->state != VFE_OUTPUT_RESERVED) { - dev_err(to_device(vfe), "Output is not in reserved state %d\n", + dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); spin_unlock_irqrestore(&vfe->output_lock, flags); return -EINVAL; @@ -1418,42 +714,43 @@ static int vfe_enable_output(struct vfe_line *line) vfe_output_init_addrs(vfe, output, 0); if (line->id != VFE_LINE_PIX) { - vfe_set_cgc_override(vfe, output->wm_idx[0], 1); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); - vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); - vfe_wm_set_subsample(vfe, output->wm_idx[0]); - vfe_set_rdi_cid(vfe, line->id, 0); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[0], - (ub_size + 1) * output->wm_idx[0], ub_size); - vfe_wm_frame_based(vfe, output->wm_idx[0], 1); - vfe_wm_enable(vfe, output->wm_idx[0], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[0]); + ops->set_cgc_override(vfe, output->wm_idx[0], 1); + ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); + ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); + ops->wm_set_subsample(vfe, output->wm_idx[0]); + ops->set_rdi_cid(vfe, line->id, 0); + ops->wm_set_ub_cfg(vfe, output->wm_idx[0], + (ub_size + 1) * output->wm_idx[0], ub_size); + ops->wm_frame_based(vfe, output->wm_idx[0], 1); + ops->wm_enable(vfe, output->wm_idx[0], 1); + ops->bus_reload_wm(vfe, output->wm_idx[0]); } else { ub_size /= output->wm_num; for (i = 0; i < output->wm_num; i++) { - vfe_set_cgc_override(vfe, output->wm_idx[i], 1); - vfe_wm_set_subsample(vfe, output->wm_idx[i]); - vfe_wm_set_ub_cfg(vfe, output->wm_idx[i], - (ub_size + 1) * output->wm_idx[i], - ub_size); - vfe_wm_line_based(vfe, output->wm_idx[i], + ops->set_cgc_override(vfe, output->wm_idx[i], 1); + ops->wm_set_subsample(vfe, output->wm_idx[i]); + ops->wm_set_ub_cfg(vfe, output->wm_idx[i], + (ub_size + 1) * output->wm_idx[i], + ub_size); + ops->wm_line_based(vfe, output->wm_idx[i], &line->video_out.active_fmt.fmt.pix_mp, i, 1); - vfe_wm_enable(vfe, output->wm_idx[i], 1); - vfe_bus_reload_wm(vfe, output->wm_idx[i]); + ops->wm_enable(vfe, output->wm_idx[i], 1); + ops->bus_reload_wm(vfe, output->wm_idx[i]); } - vfe_enable_irq_pix_line(vfe, 0, line->id, 1); - vfe_set_module_cfg(vfe, 1); - vfe_set_camif_cfg(vfe, line); - vfe_set_xbar_cfg(vfe, output, 1); - vfe_set_demux_cfg(vfe, line); - vfe_set_scale_cfg(vfe, line); - vfe_set_crop_cfg(vfe, line); - vfe_set_clamp_cfg(vfe); - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY); + ops->enable_irq_pix_line(vfe, 0, line->id, 1); + ops->set_module_cfg(vfe, 1); + ops->set_camif_cfg(vfe, line); + ops->set_realign_cfg(vfe, line, 1); + ops->set_xbar_cfg(vfe, output, 1); + ops->set_demux_cfg(vfe, line); + ops->set_scale_cfg(vfe, line); + ops->set_crop_cfg(vfe, line); + ops->set_clamp_cfg(vfe); + ops->set_camif_cmd(vfe, 1); } - vfe_reg_update(vfe, line->id); + ops->reg_update(vfe, line->id); spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -1464,6 +761,7 @@ static int vfe_disable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; + const struct vfe_hw_ops *ops = vfe->ops; unsigned long flags; unsigned long time; unsigned int i; @@ -1476,43 +774,45 @@ static int vfe_disable_output(struct vfe_line *line) time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS)); if (!time) - dev_err(to_device(vfe), "VFE sof timeout\n"); + dev_err(vfe->camss->dev, "VFE sof timeout\n"); spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) - vfe_wm_enable(vfe, output->wm_idx[i], 0); + ops->wm_enable(vfe, output->wm_idx[i], 0); - vfe_reg_update(vfe, line->id); + ops->reg_update(vfe, line->id); output->wait_reg_update = 1; spin_unlock_irqrestore(&vfe->output_lock, flags); time = wait_for_completion_timeout(&output->reg_update, msecs_to_jiffies(VFE_NEXT_SOF_MS)); if (!time) - dev_err(to_device(vfe), "VFE reg update timeout\n"); + dev_err(vfe->camss->dev, "VFE reg update timeout\n"); spin_lock_irqsave(&vfe->output_lock, flags); if (line->id != VFE_LINE_PIX) { - vfe_wm_frame_based(vfe, output->wm_idx[0], 0); - vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id); - vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); - vfe_set_cgc_override(vfe, output->wm_idx[0], 0); + ops->wm_frame_based(vfe, output->wm_idx[0], 0); + ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], + line->id); + ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); + ops->set_cgc_override(vfe, output->wm_idx[0], 0); spin_unlock_irqrestore(&vfe->output_lock, flags); } else { for (i = 0; i < output->wm_num; i++) { - vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); - vfe_set_cgc_override(vfe, output->wm_idx[i], 0); + ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); + ops->set_cgc_override(vfe, output->wm_idx[i], 0); } - vfe_enable_irq_pix_line(vfe, 0, line->id, 0); - vfe_set_module_cfg(vfe, 0); - vfe_set_xbar_cfg(vfe, output, 0); + ops->enable_irq_pix_line(vfe, 0, line->id, 0); + ops->set_module_cfg(vfe, 0); + ops->set_realign_cfg(vfe, line, 0); + ops->set_xbar_cfg(vfe, output, 0); - vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY); + ops->set_camif_cmd(vfe, 0); spin_unlock_irqrestore(&vfe->output_lock, flags); - vfe_camif_wait_for_stop(vfe); + ops->camif_wait_for_stop(vfe, vfe->camss->dev); } return 0; @@ -1532,11 +832,13 @@ static int vfe_enable(struct vfe_line *line) mutex_lock(&vfe->stream_lock); if (!vfe->stream_count) { - vfe_enable_irq_common(vfe); + vfe->ops->enable_irq_common(vfe); + + vfe->ops->bus_enable_wr_if(vfe, 1); - vfe_bus_enable_wr_if(vfe, 1); + vfe->ops->set_qos(vfe); - vfe_set_qos(vfe); + vfe->ops->set_ds(vfe); } vfe->stream_count++; @@ -1563,7 +865,7 @@ error_get_output: mutex_lock(&vfe->stream_lock); if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); + vfe->ops->bus_enable_wr_if(vfe, 0); vfe->stream_count--; @@ -1589,7 +891,7 @@ static int vfe_disable(struct vfe_line *line) mutex_lock(&vfe->stream_lock); if (vfe->stream_count == 1) - vfe_bus_enable_wr_if(vfe, 0); + vfe->ops->bus_enable_wr_if(vfe, 0); vfe->stream_count--; @@ -1628,7 +930,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id); + vfe->ops->reg_update_clear(vfe, line_id); output = &vfe->line[line_id].output; @@ -1698,19 +1000,19 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) u64 ts = ktime_get_ns(); unsigned int i; - active_index = vfe_wm_get_ping_pong_status(vfe, wm); + active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm); spin_lock_irqsave(&vfe->output_lock, flags); if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Received wm done for unmapped index\n"); goto out_unlock; } output = &vfe->line[vfe->wm_output_map[wm]].output; if (output->active_buf == active_index) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Active buffer mismatch!\n"); goto out_unlock; } @@ -1718,7 +1020,7 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) ready_buf = output->buf[!active_index]; if (!ready_buf) { - dev_err_ratelimited(to_device(vfe), + dev_err_ratelimited(vfe->camss->dev, "Missing ready buf %d %d!\n", !active_index, output->state); goto out_unlock; @@ -1740,12 +1042,12 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) if (active_index) for (i = 0; i < output->wm_num; i++) - vfe_wm_set_ping_addr(vfe, output->wm_idx[i], - new_addr[i]); + vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], + new_addr[i]); else for (i = 0; i < output->wm_num; i++) - vfe_wm_set_pong_addr(vfe, output->wm_idx[i], - new_addr[i]); + vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], + new_addr[i]); spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -1776,67 +1078,15 @@ static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) } } -/* - * vfe_isr - ISPIF module interrupt handler - * @irq: Interrupt line - * @dev: VFE device - * - * Return IRQ_HANDLED on success - */ -static irqreturn_t vfe_isr(int irq, void *dev) +static inline void vfe_isr_reset_ack(struct vfe_device *vfe) { - struct vfe_device *vfe = dev; - u32 value0, value1; - u32 violation; - int i, j; - - value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0); - value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1); - - writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0); - writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1); - - wmb(); - writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); - - if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK) - complete(&vfe->reset_complete); - - if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) { - violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); - dev_err_ratelimited(to_device(vfe), - "VFE: violation = 0x%08x\n", violation); - } - - if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) { - complete(&vfe->halt_complete); - writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); - } - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) - if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) - vfe_isr_reg_update(vfe, i); - - if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF) - vfe_isr_sof(vfe, VFE_LINE_PIX); - - for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) - if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i)) - vfe_isr_sof(vfe, i); - - for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) { - vfe_isr_comp_done(vfe, i); - for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++) - if (vfe->wm_output_map[j] == VFE_LINE_PIX) - value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j); - } - - for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) - if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i)) - vfe_isr_wm_done(vfe, i); + complete(&vfe->reset_complete); +} - return IRQ_HANDLED; +static inline void vfe_isr_halt_ack(struct vfe_device *vfe) +{ + complete(&vfe->halt_complete); + vfe->ops->halt_clear(vfe); } /* @@ -1847,7 +1097,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) */ static int vfe_set_clock_rates(struct vfe_device *vfe) { - struct device *dev = to_device(vfe); + struct device *dev = vfe->camss->dev; u32 pixel_clock[MSM_VFE_LINE_NUM]; int i, j; int ret; @@ -1862,7 +1112,8 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) for (i = 0; i < vfe->nclocks; i++) { struct camss_clock *clock = &vfe->clock[i]; - if (!strcmp(clock->name, "camss_vfe_vfe")) { + if (!strcmp(clock->name, "vfe0") || + !strcmp(clock->name, "vfe1")) { u64 min_rate = 0; long rate; @@ -1873,8 +1124,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) if (j == VFE_LINE_PIX) { tmp = pixel_clock[j]; } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); + struct vfe_line *l = &vfe->line[j]; + + bpp = vfe_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -1940,7 +1194,8 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) for (i = 0; i < vfe->nclocks; i++) { struct camss_clock *clock = &vfe->clock[i]; - if (!strcmp(clock->name, "camss_vfe_vfe")) { + if (!strcmp(clock->name, "vfe0") || + !strcmp(clock->name, "vfe1")) { u64 min_rate = 0; unsigned long rate; @@ -1951,8 +1206,11 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) if (j == VFE_LINE_PIX) { tmp = pixel_clock[j]; } else { - bpp = vfe_get_bpp(vfe->line[j]. - fmt[MSM_VFE_PAD_SINK].code); + struct vfe_line *l = &vfe->line[j]; + + bpp = vfe_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -1984,12 +1242,20 @@ static int vfe_get(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { + ret = camss_pm_domain_on(vfe->camss, vfe->id); + if (ret < 0) + goto error_pm_domain; + + ret = pm_runtime_get_sync(vfe->camss->dev); + if (ret < 0) + goto error_pm_runtime_get; + ret = vfe_set_clock_rates(vfe); if (ret < 0) goto error_clocks; ret = camss_enable_clocks(vfe->nclocks, vfe->clock, - to_device(vfe)); + vfe->camss->dev); if (ret < 0) goto error_clocks; @@ -2015,6 +1281,12 @@ error_reset: camss_disable_clocks(vfe->nclocks, vfe->clock); error_clocks: + pm_runtime_put_sync(vfe->camss->dev); + +error_pm_runtime_get: + camss_pm_domain_off(vfe->camss, vfe->id); + +error_pm_domain: mutex_unlock(&vfe->power_lock); return ret; @@ -2029,7 +1301,7 @@ static void vfe_put(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { - dev_err(to_device(vfe), "vfe power off on power_count == 0\n"); + dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n"); goto exit; } else if (vfe->power_count == 1) { if (vfe->was_streaming) { @@ -2037,6 +1309,8 @@ static void vfe_put(struct vfe_device *vfe) vfe_halt(vfe); } camss_disable_clocks(vfe->nclocks, vfe->clock); + pm_runtime_put_sync(vfe->camss->dev); + camss_pm_domain_off(vfe->camss, vfe->id); } vfe->power_count--; @@ -2046,26 +1320,6 @@ exit: } /* - * vfe_video_pad_to_line - Get pointer to VFE line by media pad - * @pad: Media pad - * - * Return pointer to vfe line structure - */ -static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) -{ - struct media_pad *vfe_pad; - struct v4l2_subdev *subdev; - - vfe_pad = media_entity_remote_pad(pad); - if (vfe_pad == NULL) - return NULL; - - subdev = media_entity_to_v4l2_subdev(vfe_pad->entity); - - return container_of(subdev, struct vfe_line, subdev); -} - -/* * vfe_queue_buffer - Add empty buffer * @vid: Video device structure * @buf: Buffer to be enqueued @@ -2078,16 +1332,11 @@ static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad) static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf) { - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; + struct vfe_line *line = container_of(vid, struct vfe_line, video_out); + struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; unsigned long flags; - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not queue buffer\n"); - return -1; - } output = &line->output; spin_lock_irqsave(&vfe->output_lock, flags); @@ -2112,16 +1361,11 @@ static int vfe_queue_buffer(struct camss_video *vid, static int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state) { - struct vfe_device *vfe = &vid->camss->vfe; - struct vfe_line *line; + struct vfe_line *line = container_of(vid, struct vfe_line, video_out); + struct vfe_device *vfe = to_vfe(line); struct vfe_output *output; unsigned long flags; - line = vfe_video_pad_to_line(&vid->pad); - if (!line) { - dev_err(to_device(vfe), "Can not flush buffers\n"); - return -1; - } output = &line->output; spin_lock_irqsave(&vfe->output_lock, flags); @@ -2158,15 +1402,11 @@ static int vfe_set_power(struct v4l2_subdev *sd, int on) int ret; if (on) { - u32 hw_version; - ret = vfe_get(vfe); if (ret < 0) return ret; - hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION); - dev_dbg(to_device(vfe), - "VFE HW Version = 0x%08x\n", hw_version); + vfe->ops->hw_version_read(vfe, vfe->camss->dev); } else { vfe_put(vfe); } @@ -2192,12 +1432,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) if (enable) { ret = vfe_enable(line); if (ret < 0) - dev_err(to_device(vfe), + dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { ret = vfe_disable(line); if (ret < 0) - dev_err(to_device(vfe), + dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); } @@ -2286,12 +1526,12 @@ static void vfe_try_format(struct vfe_line *line, case MSM_VFE_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < ARRAY_SIZE(vfe_formats); i++) - if (fmt->code == vfe_formats[i].code) + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= ARRAY_SIZE(vfe_formats)) + if (i >= line->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -2304,11 +1544,11 @@ static void vfe_try_format(struct vfe_line *line, case MSM_VFE_PAD_SRC: /* Set and return a format same as sink pad */ - code = fmt->code; - *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - which); + *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); + + fmt->code = vfe_src_pad_code(line, fmt->code, 0, code); if (line->id == VFE_LINE_PIX) { struct v4l2_rect *rect; @@ -2317,34 +1557,6 @@ static void vfe_try_format(struct vfe_line *line, fmt->width = rect->width; fmt->height = rect->height; - - switch (fmt->code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - if (code == MEDIA_BUS_FMT_YUYV8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; - break; - case MEDIA_BUS_FMT_YVYU8_2X8: - if (code == MEDIA_BUS_FMT_YVYU8_1_5X8) - fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_YVYU8_2X8; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - default: - if (code == MEDIA_BUS_FMT_UYVY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - break; - case MEDIA_BUS_FMT_VYUY8_2X8: - if (code == MEDIA_BUS_FMT_VYUY8_1_5X8) - fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8; - else - fmt->code = MEDIA_BUS_FMT_VYUY8_2X8; - break; - } } break; @@ -2448,21 +1660,22 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct vfe_line *line = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; if (code->pad == MSM_VFE_PAD_SINK) { - if (code->index >= ARRAY_SIZE(vfe_formats)) + if (code->index >= line->nformats) return -EINVAL; - code->code = vfe_formats[code->index].code; + code->code = line->formats[code->index].code; } else { - if (code->index > 0) - return -EINVAL; + struct v4l2_mbus_framefmt *sink_fmt; - format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, - code->which); + sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, + code->which); - code->code = format->code; + code->code = vfe_src_pad_code(line, sink_fmt->code, + code->index, 0); + if (!code->code) + return -EINVAL; } return 0; @@ -2751,15 +1964,29 @@ static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) * * Return 0 on success or a negative error code otherwise */ -int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) +int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, + const struct resources *res, u8 id) { - struct device *dev = to_device(vfe); + struct device *dev = camss->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *r; - struct camss *camss = to_camss(vfe); int i, j; int ret; + vfe->isr_ops.reset_ack = vfe_isr_reset_ack; + vfe->isr_ops.halt_ack = vfe_isr_halt_ack; + vfe->isr_ops.reg_update = vfe_isr_reg_update; + vfe->isr_ops.sof = vfe_isr_sof; + vfe->isr_ops.comp_done = vfe_isr_comp_done; + vfe->isr_ops.wm_done = vfe_isr_wm_done; + + if (camss->version == CAMSS_8x16) + vfe->ops = &vfe_ops_4_1; + else if (camss->version == CAMSS_8x96) + vfe->ops = &vfe_ops_4_7; + else + return -EINVAL; + /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); @@ -2781,7 +2008,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) vfe->irq = r->start; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", dev_name(dev), MSM_VFE_NAME, vfe->id); - ret = devm_request_irq(dev, vfe->irq, vfe_isr, + ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); @@ -2836,16 +2063,38 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res) spin_lock_init(&vfe->output_lock); - vfe->id = 0; + vfe->camss = camss; + vfe->id = id; vfe->reg_update = 0; for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { - vfe->line[i].video_out.type = - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - vfe->line[i].video_out.camss = camss; - vfe->line[i].id = i; - init_completion(&vfe->line[i].output.sof); - init_completion(&vfe->line[i].output.reg_update); + struct vfe_line *l = &vfe->line[i]; + + l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + l->video_out.camss = camss; + l->id = i; + init_completion(&l->output.sof); + init_completion(&l->output.reg_update); + + if (camss->version == CAMSS_8x16) { + if (i == VFE_LINE_PIX) { + l->formats = formats_pix_8x16; + l->nformats = ARRAY_SIZE(formats_pix_8x16); + } else { + l->formats = formats_rdi_8x16; + l->nformats = ARRAY_SIZE(formats_rdi_8x16); + } + } else if (camss->version == CAMSS_8x96) { + if (i == VFE_LINE_PIX) { + l->formats = formats_pix_8x96; + l->nformats = ARRAY_SIZE(formats_pix_8x96); + } else { + l->formats = formats_rdi_8x96; + l->nformats = ARRAY_SIZE(formats_rdi_8x96); + } + } else { + return -EINVAL; + } } init_completion(&vfe->reset_complete); @@ -2968,7 +2217,7 @@ void msm_vfe_stop_streaming(struct vfe_device *vfe) int msm_vfe_register_entities(struct vfe_device *vfe, struct v4l2_device *v4l2_dev) { - struct device *dev = to_device(vfe); + struct device *dev = vfe->camss->dev; struct v4l2_subdev *sd; struct media_pad *pads; struct camss_video *video_out; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h new file mode 100644 index 000000000000..0d10071ae881 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-vfe.h + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2018 Linaro Ltd. + */ +#ifndef QC_MSM_CAMSS_VFE_H +#define QC_MSM_CAMSS_VFE_H + +#include <linux/clk.h> +#include <linux/spinlock_types.h> +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> + +#include "camss-video.h" + +#define MSM_VFE_PAD_SINK 0 +#define MSM_VFE_PAD_SRC 1 +#define MSM_VFE_PADS_NUM 2 + +#define MSM_VFE_LINE_NUM 4 +#define MSM_VFE_IMAGE_MASTERS_NUM 7 +#define MSM_VFE_COMPOSITE_IRQ_NUM 4 + +enum vfe_output_state { + VFE_OUTPUT_OFF, + VFE_OUTPUT_RESERVED, + VFE_OUTPUT_SINGLE, + VFE_OUTPUT_CONTINUOUS, + VFE_OUTPUT_IDLE, + VFE_OUTPUT_STOPPING +}; + +enum vfe_line_id { + VFE_LINE_NONE = -1, + VFE_LINE_RDI0 = 0, + VFE_LINE_RDI1 = 1, + VFE_LINE_RDI2 = 2, + VFE_LINE_PIX = 3 +}; + +struct vfe_output { + u8 wm_num; + u8 wm_idx[3]; + + int active_buf; + struct camss_buffer *buf[2]; + struct camss_buffer *last_buffer; + struct list_head pending_bufs; + + unsigned int drop_update_idx; + + enum vfe_output_state state; + unsigned int sequence; + int wait_sof; + int wait_reg_update; + struct completion sof; + struct completion reg_update; +}; + +struct vfe_line { + enum vfe_line_id id; + struct v4l2_subdev subdev; + struct media_pad pads[MSM_VFE_PADS_NUM]; + struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM]; + struct v4l2_rect compose; + struct v4l2_rect crop; + struct camss_video video_out; + struct vfe_output output; + const struct vfe_format *formats; + unsigned int nformats; +}; + +struct vfe_device; + +struct vfe_hw_ops { + void (*hw_version_read)(struct vfe_device *vfe, struct device *dev); + u16 (*get_ub_size)(u8 vfe_id); + void (*global_reset)(struct vfe_device *vfe); + void (*halt_request)(struct vfe_device *vfe); + void (*halt_clear)(struct vfe_device *vfe); + void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*wm_line_based)(struct vfe_device *vfe, u32 wm, + struct v4l2_pix_format_mplane *pix, + u8 plane, u32 enable); + void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per); + void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm, + u32 pattern); + void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset, + u16 depth); + void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm); + void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr); + void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr); + int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm); + void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable); + void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id); + void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm); + void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id id); + void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output, + u8 enable); + void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id, + u8 cid); + void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line, + u8 enable); + void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*reg_update_clear)(struct vfe_device *vfe, + enum vfe_line_id line_id); + void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm, + enum vfe_line_id line_id, u8 enable); + void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp, + enum vfe_line_id line_id, u8 enable); + void (*enable_irq_common)(struct vfe_device *vfe); + void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_clamp_cfg)(struct vfe_device *vfe); + void (*set_qos)(struct vfe_device *vfe); + void (*set_ds)(struct vfe_device *vfe); + void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable); + void (*set_module_cfg)(struct vfe_device *vfe, u8 enable); + int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev); + void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1); + void (*violation_read)(struct vfe_device *vfe); + irqreturn_t (*isr)(int irq, void *dev); +}; + +struct vfe_isr_ops { + void (*reset_ack)(struct vfe_device *vfe); + void (*halt_ack)(struct vfe_device *vfe); + void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*sof)(struct vfe_device *vfe, enum vfe_line_id line_id); + void (*comp_done)(struct vfe_device *vfe, u8 comp); + void (*wm_done)(struct vfe_device *vfe, u8 wm); +}; + +struct vfe_device { + struct camss *camss; + u8 id; + void __iomem *base; + u32 irq; + char irq_name[30]; + struct camss_clock *clock; + int nclocks; + struct completion reset_complete; + struct completion halt_complete; + struct mutex power_lock; + int power_count; + struct mutex stream_lock; + int stream_count; + spinlock_t output_lock; + enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; + struct vfe_line line[MSM_VFE_LINE_NUM]; + u32 reg_update; + u8 was_streaming; + const struct vfe_hw_ops *ops; + struct vfe_isr_ops isr_ops; +}; + +struct resources; + +int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, + const struct resources *res, u8 id); + +int msm_vfe_register_entities(struct vfe_device *vfe, + struct v4l2_device *v4l2_dev); + +void msm_vfe_unregister_entities(struct vfe_device *vfe); + +void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); +void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); + +void msm_vfe_stop_streaming(struct vfe_device *vfe); + +extern const struct vfe_hw_ops vfe_ops_4_1; +extern const struct vfe_hw_ops vfe_ops_4_7; + +#endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index ffaa2849e0c1..c9bb0d023db4 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss-video.c * * Qualcomm MSM Camera Subsystem - V4L2 device node * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/slab.h> #include <media/media-entity.h> @@ -49,7 +41,7 @@ struct camss_format_info { unsigned int bpp[3]; }; -static const struct camss_format_info formats_rdi[] = { +static const struct camss_format_info formats_rdi_8x16[] = { { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, { { 1, 1 } }, { { 1, 1 } }, { 16 } }, { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, @@ -82,9 +74,60 @@ static const struct camss_format_info formats_rdi[] = { { { 1, 1 } }, { { 1, 1 } }, { 12 } }, { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, }; -static const struct camss_format_info formats_pix[] = { +static const struct camss_format_info formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, + { { 1, 1 } }, { { 1, 1 } }, { 8 } }, + { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 14 } }, + { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, +}; + +static const struct camss_format_info formats_pix_8x16[] = { { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, { { 1, 1 } }, { { 2, 3 } }, { 8 } }, { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, @@ -119,6 +162,49 @@ static const struct camss_format_info formats_pix[] = { { { 1, 1 } }, { { 1, 2 } }, { 8 } }, }; +static const struct camss_format_info formats_pix_8x96[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1, + { { 1, 1 } }, { { 1, 2 } }, { 8 } }, + { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, + { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1, + { { 1, 1 } }, { { 1, 1 } }, { 16 } }, +}; + /* ----------------------------------------------------------------------------- * Helper functions */ @@ -798,11 +884,24 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, mutex_init(&video->lock); - video->formats = formats_rdi; - video->nformats = ARRAY_SIZE(formats_rdi); - if (is_pix) { - video->formats = formats_pix; - video->nformats = ARRAY_SIZE(formats_pix); + if (video->camss->version == CAMSS_8x16) { + if (is_pix) { + video->formats = formats_pix_8x16; + video->nformats = ARRAY_SIZE(formats_pix_8x16); + } else { + video->formats = formats_rdi_8x16; + video->nformats = ARRAY_SIZE(formats_rdi_8x16); + } + } else if (video->camss->version == CAMSS_8x96) { + if (is_pix) { + video->formats = formats_pix_8x96; + video->nformats = ARRAY_SIZE(formats_pix_8x96); + } else { + video->formats = formats_rdi_8x96; + video->nformats = ARRAY_SIZE(formats_rdi_8x96); + } + } else { + goto error_video_register; } ret = msm_video_init_format(video); diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index 38bd1f2eec54..aa35e8cc6fd5 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -1,19 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss-video.h * * Qualcomm MSM Camera Subsystem - V4L2 device node * * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #ifndef QC_MSM_CAMSS_VIDEO_H #define QC_MSM_CAMSS_VIDEO_H diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss/camss.c index 23fda6207a23..dcc0c30ef1b1 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1,19 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* * camss.c * * Qualcomm MSM Camera Subsystem - Core * * Copyright (c) 2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #include <linux/clk.h> #include <linux/media-bus-format.h> @@ -22,6 +14,8 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -36,12 +30,11 @@ #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 -static const struct resources csiphy_res[] = { +static const struct resources csiphy_res_8x16[] = { /* CSIPHY0 */ { .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy0_timer" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, .clock_rate = { { 0 }, { 0 }, { 0 }, @@ -53,8 +46,7 @@ static const struct resources csiphy_res[] = { /* CSIPHY1 */ { .regulator = { NULL }, - .clock = { "camss_top_ahb", "ispif_ahb", - "camss_ahb", "csiphy1_timer" }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, .clock_rate = { { 0 }, { 0 }, { 0 }, @@ -64,12 +56,11 @@ static const struct resources csiphy_res[] = { } }; -static const struct resources csid_res[] = { +static const struct resources csid_res_8x16[] = { /* CSID0 */ { .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi0_ahb", "camss_ahb", + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, .clock_rate = { { 0 }, { 0 }, @@ -86,8 +77,7 @@ static const struct resources csid_res[] = { /* CSID1 */ { .regulator = { "vdda" }, - .clock = { "camss_top_ahb", "ispif_ahb", - "csi1_ahb", "camss_ahb", + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, .clock_rate = { { 0 }, { 0 }, @@ -102,35 +92,195 @@ static const struct resources csid_res[] = { }, }; -static const struct resources_ispif ispif_res = { +static const struct resources_ispif ispif_res_8x16 = { /* ISPIF */ - .clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb", + .clock = { "top_ahb", "ahb", "ispif_ahb", "csi0", "csi0_pix", "csi0_rdi", "csi1", "csi1_pix", "csi1_rdi" }, - .clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" }, + .clock_for_reset = { "vfe0", "csi_vfe0" }, .reg = { "ispif", "csi_clk_mux" }, .interrupt = "ispif" }; -static const struct resources vfe_res = { +static const struct resources vfe_res_8x16[] = { + /* VFE0 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "vfe0", "csi_vfe0", + "vfe_ahb", "vfe_axi", "ahb" }, + .clock_rate = { { 0 }, + { 50000000, 80000000, 100000000, 160000000, + 177780000, 200000000, 266670000, 320000000, + 400000000, 465000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + } +}; + +static const struct resources csiphy_res_8x96[] = { + /* CSIPHY0 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy0", "csiphy0_clk_mux" }, + .interrupt = { "csiphy0" } + }, + + /* CSIPHY1 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy1", "csiphy1_clk_mux" }, + .interrupt = { "csiphy1" } + }, + + /* CSIPHY2 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 } }, + .reg = { "csiphy2", "csiphy2_clk_mux" }, + .interrupt = { "csiphy2" } + } +}; + +static const struct resources csid_res_8x96[] = { + /* CSID0 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb", + "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + + /* CSID1 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb", + "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, + + /* CSID2 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb", + "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" } + }, + + /* CSID3 */ + { + .regulator = { "vdda" }, + .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb", + "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 100000000, 200000000, 266666667 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "csid3" }, + .interrupt = { "csid3" } + } +}; + +static const struct resources_ispif ispif_res_8x96 = { + /* ISPIF */ + .clock = { "top_ahb", "ahb", "ispif_ahb", + "csi0", "csi0_pix", "csi0_rdi", + "csi1", "csi1_pix", "csi1_rdi", + "csi2", "csi2_pix", "csi2_rdi", + "csi3", "csi3_pix", "csi3_rdi" }, + .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, + .reg = { "ispif", "csi_clk_mux" }, + .interrupt = "ispif" +}; + +static const struct resources vfe_res_8x96[] = { /* VFE0 */ - .regulator = { NULL }, - .clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe", - "iface", "bus", "camss_ahb" }, - .clock_rate = { { 0 }, - { 50000000, 80000000, 100000000, 160000000, - 177780000, 200000000, 266670000, 320000000, - 400000000, 465000000 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - { 0 } }, - .reg = { "vfe0" }, - .interrupt = { "vfe0" } + { + .regulator = { NULL }, + .clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb", + "vfe0_ahb", "vfe_axi", "vfe0_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 75000000, 100000000, 300000000, + 320000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + }, + + /* VFE1 */ + { + .regulator = { NULL }, + .clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb", + "vfe1_ahb", "vfe_axi", "vfe1_stream"}, + .clock_rate = { { 0 }, + { 0 }, + { 75000000, 100000000, 300000000, + 320000000, 480000000, 600000000 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" } + } }; /* @@ -245,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) return 0; } +int camss_pm_domain_on(struct camss *camss, int id) +{ + if (camss->version == CAMSS_8x96) { + camss->genpd_link[id] = device_link_add(camss->dev, + camss->genpd[id], DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); + + if (!camss->genpd_link[id]) + return -EINVAL; + } + + return 0; +} + +void camss_pm_domain_off(struct camss *camss, int id) +{ + if (camss->version == CAMSS_8x96) + device_link_del(camss->genpd_link[id]); +} + /* * camss_of_parse_endpoint_node - Parse port endpoint node * @dev: Device @@ -304,6 +474,7 @@ static int camss_of_parse_ports(struct device *dev, if (of_device_is_available(node)) notifier->num_subdevs++; + of_node_put(node); size = sizeof(*notifier->subdevs) * notifier->num_subdevs; notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL); if (!notifier->subdevs) { @@ -334,16 +505,16 @@ static int camss_of_parse_ports(struct device *dev, } remote = of_graph_get_remote_port_parent(node); - of_node_put(node); - if (!remote) { dev_err(dev, "Cannot get remote parent\n"); + of_node_put(node); return -EINVAL; } csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; csd->asd.match.fwnode = of_fwnode_handle(remote); } + of_node_put(node); return notifier->num_subdevs; } @@ -356,11 +527,29 @@ static int camss_of_parse_ports(struct device *dev, */ static int camss_init_subdevices(struct camss *camss) { + const struct resources *csiphy_res; + const struct resources *csid_res; + const struct resources_ispif *ispif_res; + const struct resources *vfe_res; unsigned int i; int ret; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - ret = msm_csiphy_subdev_init(&camss->csiphy[i], + if (camss->version == CAMSS_8x16) { + csiphy_res = csiphy_res_8x16; + csid_res = csid_res_8x16; + ispif_res = &ispif_res_8x16; + vfe_res = vfe_res_8x16; + } else if (camss->version == CAMSS_8x96) { + csiphy_res = csiphy_res_8x96; + csid_res = csid_res_8x96; + ispif_res = &ispif_res_8x96; + vfe_res = vfe_res_8x96; + } else { + return -EINVAL; + } + + for (i = 0; i < camss->csiphy_num; i++) { + ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i], &csiphy_res[i], i); if (ret < 0) { dev_err(camss->dev, @@ -370,8 +559,8 @@ static int camss_init_subdevices(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - ret = msm_csid_subdev_init(&camss->csid[i], + for (i = 0; i < camss->csid_num; i++) { + ret = msm_csid_subdev_init(camss, &camss->csid[i], &csid_res[i], i); if (ret < 0) { dev_err(camss->dev, @@ -381,17 +570,21 @@ static int camss_init_subdevices(struct camss *camss) } } - ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res); + ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); if (ret < 0) { dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", ret); return ret; } - ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res); - if (ret < 0) { - dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret); - return ret; + for (i = 0; i < camss->vfe_num; i++) { + ret = msm_vfe_subdev_init(camss, &camss->vfe[i], + &vfe_res[i], i); + if (ret < 0) { + dev_err(camss->dev, + "Fail to init vfe%d sub-device: %d\n", i, ret); + return ret; + } } return 0; @@ -405,10 +598,10 @@ static int camss_init_subdevices(struct camss *camss) */ static int camss_register_entities(struct camss *camss) { - int i, j; + int i, j, k; int ret; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { + for (i = 0; i < camss->csiphy_num; i++) { ret = msm_csiphy_register_entity(&camss->csiphy[i], &camss->v4l2_dev); if (ret < 0) { @@ -419,7 +612,7 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { + for (i = 0; i < camss->csid_num; i++) { ret = msm_csid_register_entity(&camss->csid[i], &camss->v4l2_dev); if (ret < 0) { @@ -437,15 +630,19 @@ static int camss_register_entities(struct camss *camss) goto err_reg_ispif; } - ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register vfe entities: %d\n", - ret); - goto err_reg_vfe; + for (i = 0; i < camss->vfe_num; i++) { + ret = msm_vfe_register_entities(&camss->vfe[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register vfe%d entities: %d\n", + i, ret); + goto err_reg_vfe; + } } - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) { - for (j = 0; j < ARRAY_SIZE(camss->csid); j++) { + for (i = 0; i < camss->csiphy_num; i++) { + for (j = 0; j < camss->csid_num; j++) { ret = media_create_pad_link( &camss->csiphy[i].subdev.entity, MSM_CSIPHY_PAD_SRC, @@ -463,8 +660,8 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) { - for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) { + for (i = 0; i < camss->csid_num; i++) { + for (j = 0; j < camss->ispif.line_num; j++) { ret = media_create_pad_link( &camss->csid[i].subdev.entity, MSM_CSID_PAD_SRC, @@ -482,39 +679,42 @@ static int camss_register_entities(struct camss *camss) } } - for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) { - for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) { - ret = media_create_pad_link( - &camss->ispif.line[i].subdev.entity, - MSM_ISPIF_PAD_SRC, - &camss->vfe.line[j].subdev.entity, - MSM_VFE_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->ispif.line[i].subdev.entity.name, - camss->vfe.line[j].subdev.entity.name, - ret); - goto err_link; + for (i = 0; i < camss->ispif.line_num; i++) + for (k = 0; k < camss->vfe_num; k++) + for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { + ret = media_create_pad_link( + &camss->ispif.line[i].subdev.entity, + MSM_ISPIF_PAD_SRC, + &camss->vfe[k].line[j].subdev.entity, + MSM_VFE_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + camss->ispif.line[i].subdev.entity.name, + camss->vfe[k].line[j].subdev.entity.name, + ret); + goto err_link; + } } - } - } return 0; err_link: - msm_vfe_unregister_entities(&camss->vfe); + i = camss->vfe_num; err_reg_vfe: + for (i--; i >= 0; i--) + msm_vfe_unregister_entities(&camss->vfe[i]); + msm_ispif_unregister_entities(&camss->ispif); err_reg_ispif: - i = ARRAY_SIZE(camss->csid); + i = camss->csid_num; err_reg_csid: for (i--; i >= 0; i--) msm_csid_unregister_entity(&camss->csid[i]); - i = ARRAY_SIZE(camss->csiphy); + i = camss->csiphy_num; err_reg_csiphy: for (i--; i >= 0; i--) msm_csiphy_unregister_entity(&camss->csiphy[i]); @@ -532,14 +732,16 @@ static void camss_unregister_entities(struct camss *camss) { unsigned int i; - for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) + for (i = 0; i < camss->csiphy_num; i++) msm_csiphy_unregister_entity(&camss->csiphy[i]); - for (i = 0; i < ARRAY_SIZE(camss->csid); i++) + for (i = 0; i < camss->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); msm_ispif_unregister_entities(&camss->ispif); - msm_vfe_unregister_entities(&camss->vfe); + + for (i = 0; i < camss->vfe_num; i++) + msm_vfe_unregister_entities(&camss->vfe[i]); } static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async, @@ -631,6 +833,35 @@ static int camss_probe(struct platform_device *pdev) camss->dev = dev; platform_set_drvdata(pdev, camss); + if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) { + camss->version = CAMSS_8x16; + camss->csiphy_num = 2; + camss->csid_num = 2; + camss->vfe_num = 1; + } else if (of_device_is_compatible(dev->of_node, + "qcom,msm8996-camss")) { + camss->version = CAMSS_8x96; + camss->csiphy_num = 3; + camss->csid_num = 4; + camss->vfe_num = 2; + } else { + return -EINVAL; + } + + camss->csiphy = kcalloc(camss->csiphy_num, sizeof(*camss->csiphy), + GFP_KERNEL); + if (!camss->csiphy) + return -ENOMEM; + + camss->csid = kcalloc(camss->csid_num, sizeof(*camss->csid), + GFP_KERNEL); + if (!camss->csid) + return -ENOMEM; + + camss->vfe = kcalloc(camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); + if (!camss->vfe) + return -ENOMEM; + ret = camss_of_parse_ports(dev, &camss->notifier); if (ret < 0) return ret; @@ -687,6 +918,23 @@ static int camss_probe(struct platform_device *pdev) } } + if (camss->version == CAMSS_8x96) { + camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id( + camss->dev, PM_DOMAIN_VFE0); + if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0])) + return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]); + + camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id( + camss->dev, PM_DOMAIN_VFE1); + if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) { + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], + true); + return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]); + } + } + + pm_runtime_enable(dev); + return 0; err_register_subdevs: @@ -703,6 +951,13 @@ void camss_delete(struct camss *camss) media_device_unregister(&camss->media_dev); media_device_cleanup(&camss->media_dev); + pm_runtime_disable(camss->dev); + + if (camss->version == CAMSS_8x96) { + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); + dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); + } + kfree(camss); } @@ -714,9 +969,12 @@ void camss_delete(struct camss *camss) */ static int camss_remove(struct platform_device *pdev) { + unsigned int i; + struct camss *camss = platform_get_drvdata(pdev); - msm_vfe_stop_streaming(&camss->vfe); + for (i = 0; i < camss->vfe_num; i++) + msm_vfe_stop_streaming(&camss->vfe[i]); v4l2_async_notifier_unregister(&camss->notifier); camss_unregister_entities(camss); @@ -729,17 +987,35 @@ static int camss_remove(struct platform_device *pdev) static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss" }, + { .compatible = "qcom,msm8996-camss" }, { } }; MODULE_DEVICE_TABLE(of, camss_dt_match); +static int camss_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int camss_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops camss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL) +}; + static struct platform_driver qcom_camss_driver = { .probe = camss_probe, .remove = camss_remove, .driver = { .name = "qcom-camss", .of_match_table = camss_dt_match, + .pm = &camss_pm_ops, }, }; diff --git a/drivers/media/platform/qcom/camss-8x16/camss.h b/drivers/media/platform/qcom/camss/camss.h index 4ad223443e4b..418996d8dad8 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -1,23 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * camss.h * * Qualcomm MSM Camera Subsystem - Core * * Copyright (c) 2015, The Linux Foundation. All rights reserved. - * Copyright (C) 2015-2017 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Copyright (C) 2015-2018 Linaro Ltd. */ #ifndef QC_MSM_CAMSS_H #define QC_MSM_CAMSS_H +#include <linux/device.h> #include <linux/types.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> @@ -31,9 +24,6 @@ #include "camss-ispif.h" #include "camss-vfe.h" -#define CAMSS_CSID_NUM 2 -#define CAMSS_CSIPHY_NUM 2 - #define to_camss(ptr_module) \ container_of(ptr_module, struct camss, ptr_module) @@ -50,7 +40,7 @@ #define to_device_index(ptr_module, index) \ (to_camss_index(ptr_module, index)->dev) -#define CAMSS_RES_MAX 15 +#define CAMSS_RES_MAX 17 struct resources { char *regulator[CAMSS_RES_MAX]; @@ -67,16 +57,33 @@ struct resources_ispif { char *interrupt; }; +enum pm_domain { + PM_DOMAIN_VFE0, + PM_DOMAIN_VFE1, + PM_DOMAIN_COUNT +}; + +enum camss_version { + CAMSS_8x16, + CAMSS_8x96, +}; + struct camss { + enum camss_version version; struct v4l2_device v4l2_dev; struct v4l2_async_notifier notifier; struct media_device media_dev; struct device *dev; - struct csiphy_device csiphy[CAMSS_CSIPHY_NUM]; - struct csid_device csid[CAMSS_CSID_NUM]; + int csiphy_num; + struct csiphy_device *csiphy; + int csid_num; + struct csid_device *csid; struct ispif_device ispif; - struct vfe_device vfe; + int vfe_num; + struct vfe_device *vfe; atomic_t ref_count; + struct device *genpd[PM_DOMAIN_COUNT]; + struct device_link *genpd_link[PM_DOMAIN_COUNT]; }; struct camss_camera_interface { @@ -101,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); void camss_disable_clocks(int nclocks, struct camss_clock *clock); int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock); +int camss_pm_domain_on(struct camss *camss, int id); +void camss_pm_domain_off(struct camss *camss, int id); void camss_delete(struct camss *camss); #endif /* QC_MSM_CAMSS_H */ diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index bfd4edf7c83f..b44b11b03e12 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -2,7 +2,8 @@ # Makefile for Qualcomm Venus driver venus-core-objs += core.o helpers.o firmware.o \ - hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o + hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ + hfi_parser.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 41eef376eb2d..bb6add9d340e 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -152,6 +152,83 @@ static void venus_clks_disable(struct venus_core *core) clk_disable_unprepare(core->clks[i]); } +static u32 to_v4l2_codec_type(u32 codec) +{ + switch (codec) { + case HFI_VIDEO_CODEC_H264: + return V4L2_PIX_FMT_H264; + case HFI_VIDEO_CODEC_H263: + return V4L2_PIX_FMT_H263; + case HFI_VIDEO_CODEC_MPEG1: + return V4L2_PIX_FMT_MPEG1; + case HFI_VIDEO_CODEC_MPEG2: + return V4L2_PIX_FMT_MPEG2; + case HFI_VIDEO_CODEC_MPEG4: + return V4L2_PIX_FMT_MPEG4; + case HFI_VIDEO_CODEC_VC1: + return V4L2_PIX_FMT_VC1_ANNEX_G; + case HFI_VIDEO_CODEC_VP8: + return V4L2_PIX_FMT_VP8; + case HFI_VIDEO_CODEC_VP9: + return V4L2_PIX_FMT_VP9; + case HFI_VIDEO_CODEC_DIVX: + case HFI_VIDEO_CODEC_DIVX_311: + return V4L2_PIX_FMT_XVID; + default: + return 0; + } +} + +static int venus_enumerate_codecs(struct venus_core *core, u32 type) +{ + const struct hfi_inst_ops dummy_ops = {}; + struct venus_inst *inst; + u32 codec, codecs; + unsigned int i; + int ret; + + if (core->res->hfi_version != HFI_VERSION_1XX) + return 0; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + mutex_init(&inst->lock); + inst->core = core; + inst->session_type = type; + if (type == VIDC_SESSION_TYPE_DEC) + codecs = core->dec_codecs; + else + codecs = core->enc_codecs; + + ret = hfi_session_create(inst, &dummy_ops); + if (ret) + goto err; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + codec = (1 << i) & codecs; + if (!codec) + continue; + + ret = hfi_session_init(inst, to_v4l2_codec_type(codec)); + if (ret) + goto done; + + ret = hfi_session_deinit(inst); + if (ret) + goto done; + } + +done: + hfi_session_destroy(inst); +err: + mutex_destroy(&inst->lock); + kfree(inst); + + return ret; +} + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -219,6 +296,14 @@ static int venus_probe(struct platform_device *pdev) if (ret) goto err_venus_shutdown; + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC); + if (ret) + goto err_venus_shutdown; + + ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC); + if (ret) + goto err_venus_shutdown; + ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) goto err_core_deinit; @@ -365,9 +450,31 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mdt", }; +static const struct freq_tbl sdm845_freq_table[] = { + { 1944000, 380000000 }, /* 4k UHD @ 60 */ + { 972000, 320000000 }, /* 4k UHD @ 30 */ + { 489600, 200000000 }, /* 1080p @ 60 */ + { 244800, 100000000 }, /* 1080p @ 30 */ +}; + +static const struct venus_resources sdm845_res = { + .freq_tbl = sdm845_freq_table, + .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), + .clks = {"core", "iface", "bus" }, + .clks_num = 3, + .max_load = 2563200, + .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 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, }, { } }; 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 0360d295f4c8..2f02365f4818 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -57,6 +57,30 @@ struct venus_format { u32 type; }; +#define MAX_PLANES 4 +#define MAX_FMT_ENTRIES 32 +#define MAX_CAP_ENTRIES 32 +#define MAX_ALLOC_MODE_ENTRIES 16 +#define MAX_CODEC_NUM 32 + +struct raw_formats { + u32 buftype; + u32 fmt; +}; + +struct venus_caps { + u32 codec; + u32 domain; + bool cap_bufs_mode_dynamic; + unsigned int num_caps; + struct hfi_capability caps[MAX_CAP_ENTRIES]; + unsigned int num_pl; + struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; + unsigned int num_fmts; + struct raw_formats fmts[MAX_FMT_ENTRIES]; + bool valid; /* used only for Venus v1xx */ +}; + /** * struct venus_core - holds core parameters valid for all instances * @@ -65,6 +89,8 @@ struct venus_format { * @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 * @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 @@ -94,6 +120,8 @@ struct venus_core { 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 video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -109,8 +137,8 @@ struct venus_core { unsigned int error; bool sys_error; const struct hfi_core_ops *core_ops; - u32 enc_codecs; - u32 dec_codecs; + unsigned long enc_codecs; + unsigned long dec_codecs; unsigned int max_sessions_supported; #define ENC_ROTATION_CAPABILITY 0x1 #define ENC_SCALING_CAPABILITY 0x2 @@ -120,6 +148,8 @@ struct venus_core { void *priv; const struct hfi_ops *ops; struct delayed_work work; + struct venus_caps caps[MAX_CODEC_NUM]; + unsigned int codecs_count; }; struct vdec_controls { @@ -160,10 +190,12 @@ struct venc_controls { u32 mpeg4; u32 h264; u32 vpx; + u32 hevc; } profile; struct { u32 mpeg4; u32 h264; + u32 hevc; } level; }; @@ -185,6 +217,7 @@ struct venus_buffer { * @list: used for attach an instance to the core * @lock: instance lock * @core: a reference to the core struct + * @dpbbufs: a list of decoded picture buffers * @internalbufs: a list of internal bufferes * @registeredbufs: a list of registered capture bufferes * @delayed_process a list of delayed buffers @@ -209,9 +242,15 @@ struct venus_buffer { * @num_output_bufs: holds number of output buffers * @input_buf_size holds input buffer size * @output_buf_size: holds output buffer size + * @output2_buf_size: holds secondary decoder output buffer size + * @dpb_buftype: decoded picture buffer type + * @dpb_fmt: decoded picture buffer raw format + * @opb_buftype: output picture buffer type + * @opb_fmt: output picture buffer raw format * @reconfig: a flag raised by decoder when the stream resolution changed * @reconfig_width: holds the new width * @reconfig_height: holds the new height + * @hfi_codec: current codec for this instance in HFI space * @sequence_cap: a sequence counter for capture queue * @sequence_out: a sequence counter for output queue * @m2m_dev: a reference to m2m device structure @@ -224,27 +263,12 @@ struct venus_buffer { * @priv: a private for HFI operations callbacks * @session_type: the type of the session (decoder or encoder) * @hprop: a union used as a holder by get property - * @cap_width: width capability - * @cap_height: height capability - * @cap_mbs_per_frame: macroblocks per frame capability - * @cap_mbs_per_sec: macroblocks per second capability - * @cap_framerate: framerate capability - * @cap_scale_x: horizontal scaling capability - * @cap_scale_y: vertical scaling capability - * @cap_bitrate: bitrate capability - * @cap_hier_p: hier capability - * @cap_ltr_count: LTR count capability - * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability - * @cap_bufs_mode_static: buffers allocation mode capability - * @cap_bufs_mode_dynamic: buffers allocation mode capability - * @pl_count: count of supported profiles/levels - * @pl: supported profiles/levels - * @bufreq: holds buffer requirements */ struct venus_inst { struct list_head list; struct mutex lock; struct venus_core *core; + struct list_head dpbbufs; struct list_head internalbufs; struct list_head registeredbufs; struct list_head delayed_process; @@ -273,9 +297,15 @@ struct venus_inst { unsigned int num_output_bufs; unsigned int input_buf_size; unsigned int output_buf_size; + unsigned int output2_buf_size; + u32 dpb_buftype; + u32 dpb_fmt; + u32 opb_buftype; + u32 opb_fmt; bool reconfig; u32 reconfig_width; u32 reconfig_height; + u32 hfi_codec; u32 sequence_cap; u32 sequence_out; struct v4l2_m2m_dev *m2m_dev; @@ -287,24 +317,12 @@ struct venus_inst { const struct hfi_inst_ops *ops; u32 session_type; union hfi_get_property hprop; - struct hfi_capability cap_width; - struct hfi_capability cap_height; - struct hfi_capability cap_mbs_per_frame; - struct hfi_capability cap_mbs_per_sec; - struct hfi_capability cap_framerate; - struct hfi_capability cap_scale_x; - struct hfi_capability cap_scale_y; - struct hfi_capability cap_bitrate; - struct hfi_capability cap_hier_p; - struct hfi_capability cap_ltr_count; - struct hfi_capability cap_secure_output2_threshold; - bool cap_bufs_mode_static; - bool cap_bufs_mode_dynamic; - unsigned int pl_count; - struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT]; - struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX]; }; +#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX) +#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX) +#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX) + #define ctrl_to_inst(ctrl) \ container_of((ctrl)->handler, struct venus_inst, ctrl_handler) @@ -318,4 +336,18 @@ static inline void *to_hfi_priv(struct venus_core *core) return core->priv; } +static inline struct venus_caps * +venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain) +{ + unsigned int c; + + for (c = 0; c < core->codecs_count; c++) { + if (core->caps[c].codec == codec && + core->caps[c].domain == domain) + return &core->caps[c]; + } + + return NULL; +} + #endif diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 0ce9559a2924..cd3b96e6f24b 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -13,6 +13,7 @@ * */ #include <linux/clk.h> +#include <linux/iopoll.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/pm_runtime.h> @@ -24,6 +25,7 @@ #include "core.h" #include "helpers.h" #include "hfi_helper.h" +#include "hfi_venus_io.h" struct intbuf { struct list_head list; @@ -69,6 +71,9 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) case V4L2_PIX_FMT_XVID: codec = HFI_VIDEO_CODEC_DIVX; break; + case V4L2_PIX_FMT_HEVC: + codec = HFI_VIDEO_CODEC_HEVC; + break; default: return false; } @@ -83,6 +88,106 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt) } EXPORT_SYMBOL_GPL(venus_helper_check_codec); +static int venus_helper_queue_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf; + int ret = 0; + + list_for_each_entry(buf, &inst->dpbbufs, list) { + struct hfi_frame_data fdata; + + memset(&fdata, 0, sizeof(fdata)); + fdata.alloc_len = buf->size; + fdata.device_addr = buf->da; + fdata.buffer_type = buf->type; + + ret = hfi_session_process_buf(inst, &fdata); + if (ret) + goto fail; + } + +fail: + return ret; +} + +int venus_helper_free_dpb_bufs(struct venus_inst *inst) +{ + struct intbuf *buf, *n; + + list_for_each_entry_safe(buf, n, &inst->dpbbufs, list) { + list_del_init(&buf->list); + dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da, + buf->attrs); + kfree(buf); + } + + INIT_LIST_HEAD(&inst->dpbbufs); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_free_dpb_bufs); + +int venus_helper_alloc_dpb_bufs(struct venus_inst *inst) +{ + struct venus_core *core = inst->core; + struct device *dev = core->dev; + enum hfi_version ver = core->res->hfi_version; + struct hfi_buffer_requirements bufreq; + u32 buftype = inst->dpb_buftype; + unsigned int dpb_size = 0; + struct intbuf *buf; + unsigned int i; + u32 count; + int ret; + + /* no need to allocate dpb buffers */ + if (!inst->dpb_fmt) + return 0; + + if (inst->dpb_buftype == HFI_BUFFER_OUTPUT) + dpb_size = inst->output_buf_size; + else if (inst->dpb_buftype == HFI_BUFFER_OUTPUT2) + dpb_size = inst->output2_buf_size; + + if (!dpb_size) + return 0; + + ret = venus_helper_get_bufreq(inst, buftype, &bufreq); + if (ret) + return ret; + + count = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + + for (i = 0; i < count; i++) { + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto fail; + } + + buf->type = buftype; + buf->size = dpb_size; + buf->attrs = DMA_ATTR_WRITE_COMBINE | + DMA_ATTR_NO_KERNEL_MAPPING; + buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL, + buf->attrs); + if (!buf->va) { + kfree(buf); + ret = -ENOMEM; + goto fail; + } + + list_add_tail(&buf->list, &inst->dpbbufs); + } + + return 0; + +fail: + venus_helper_free_dpb_bufs(inst); + return ret; +} +EXPORT_SYMBOL_GPL(venus_helper_alloc_dpb_bufs); + static int intbufs_set_buffer(struct venus_inst *inst, u32 type) { struct venus_core *core = inst->core; @@ -166,21 +271,38 @@ static int intbufs_unset_buffers(struct venus_inst *inst) return ret; } -static const unsigned int intbuf_types[] = { - HFI_BUFFER_INTERNAL_SCRATCH, - HFI_BUFFER_INTERNAL_SCRATCH_1, - HFI_BUFFER_INTERNAL_SCRATCH_2, +static const unsigned int intbuf_types_1xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_1XX), + HFI_BUFFER_INTERNAL_PERSIST, + HFI_BUFFER_INTERNAL_PERSIST_1, +}; + +static const unsigned int intbuf_types_4xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_4XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_4XX), HFI_BUFFER_INTERNAL_PERSIST, HFI_BUFFER_INTERNAL_PERSIST_1, }; static int intbufs_alloc(struct venus_inst *inst) { - unsigned int i; + const unsigned int *intbuf; + size_t arr_sz, i; int ret; - for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) { - ret = intbufs_set_buffer(inst, intbuf_types[i]); + if (IS_V4(inst->core)) { + arr_sz = ARRAY_SIZE(intbuf_types_4xx); + intbuf = intbuf_types_4xx; + } else { + arr_sz = ARRAY_SIZE(intbuf_types_1xx); + intbuf = intbuf_types_1xx; + } + + for (i = 0; i < arr_sz; i++) { + ret = intbufs_set_buffer(inst, intbuf[i]); if (ret) goto error; } @@ -257,20 +379,23 @@ static int load_scale_clocks(struct venus_core *core) set_freq: - if (core->res->hfi_version == HFI_VERSION_3XX) { - ret = clk_set_rate(clk, freq); - ret |= clk_set_rate(core->core0_clk, freq); - ret |= clk_set_rate(core->core1_clk, freq); - } else { - ret = clk_set_rate(clk, freq); - } + ret = clk_set_rate(clk, freq); + if (ret) + goto err; - if (ret) { - dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); - return ret; - } + ret = clk_set_rate(core->core0_clk, freq); + if (ret) + goto err; + + ret = clk_set_rate(core->core1_clk, freq); + if (ret) + goto err; return 0; + +err: + dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret); + return ret; } static void fill_buffer_desc(const struct venus_buffer *buf, @@ -325,7 +450,10 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len) fdata.flags |= HFI_BUFFERFLAG_EOS; } else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - fdata.buffer_type = HFI_BUFFER_OUTPUT; + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + fdata.buffer_type = HFI_BUFFER_OUTPUT; + else + fdata.buffer_type = inst->opb_buftype; fdata.filled_len = 0; fdata.offset = 0; } @@ -337,18 +465,16 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) return 0; } -static inline int is_reg_unreg_needed(struct venus_inst *inst) +static bool is_dynamic_bufmode(struct venus_inst *inst) { - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->core->res->hfi_version == HFI_VERSION_3XX) - return 0; + struct venus_core *core = inst->core; + struct venus_caps *caps; - if (inst->session_type == VIDC_SESSION_TYPE_DEC && - inst->cap_bufs_mode_dynamic && - inst->core->res->hfi_version == HFI_VERSION_1XX) + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) return 0; - return 1; + return caps->cap_bufs_mode_dynamic; } static int session_unregister_bufs(struct venus_inst *inst) @@ -357,7 +483,7 @@ static int session_unregister_bufs(struct venus_inst *inst) struct hfi_buffer_desc bd; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) { @@ -377,7 +503,7 @@ static int session_register_bufs(struct venus_inst *inst) struct venus_buffer *buf; int ret = 0; - if (!is_reg_unreg_needed(inst)) + if (is_dynamic_bufmode(inst)) return 0; list_for_each_entry(buf, &inst->registeredbufs, reg_list) { @@ -392,6 +518,20 @@ static int session_register_bufs(struct venus_inst *inst) return ret; } +static u32 to_hfi_raw_fmt(u32 v4l2_fmt) +{ + switch (v4l2_fmt) { + case V4L2_PIX_FMT_NV12: + return HFI_COLOR_FORMAT_NV12; + case V4L2_PIX_FMT_NV21: + return HFI_COLOR_FORMAT_NV21; + default: + break; + } + + return 0; +} + int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req) { @@ -423,6 +563,104 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, } EXPORT_SYMBOL_GPL(venus_helper_get_bufreq); +static u32 get_framesize_raw_nv12(u32 width, u32 height) +{ + u32 y_stride, uv_stride, y_plane; + u32 y_sclines, uv_sclines, uv_plane; + u32 size; + + y_stride = ALIGN(width, 128); + uv_stride = ALIGN(width, 128); + y_sclines = ALIGN(height, 32); + uv_sclines = ALIGN(((height + 1) >> 1), 16); + + y_plane = y_stride * y_sclines; + uv_plane = uv_stride * uv_sclines + SZ_4K; + size = y_plane + uv_plane + SZ_8K; + + return ALIGN(size, SZ_4K); +} + +static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height) +{ + u32 y_meta_stride, y_meta_plane; + u32 y_stride, y_plane; + u32 uv_meta_stride, uv_meta_plane; + u32 uv_stride, uv_plane; + u32 extradata = SZ_16K; + + y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64); + y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(height, 8), 16); + y_meta_plane = ALIGN(y_meta_plane, SZ_4K); + + y_stride = ALIGN(width, 128); + y_plane = ALIGN(y_stride * ALIGN(height, 32), SZ_4K); + + uv_meta_stride = ALIGN(DIV_ROUND_UP(width / 2, 16), 64); + uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(height / 2, 8), 16); + uv_meta_plane = ALIGN(uv_meta_plane, SZ_4K); + + uv_stride = ALIGN(width, 128); + uv_plane = ALIGN(uv_stride * ALIGN(height / 2, 32), SZ_4K); + + return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane + + max(extradata, y_stride * 48), SZ_4K); +} + +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height) +{ + switch (hfi_fmt) { + case HFI_COLOR_FORMAT_NV12: + case HFI_COLOR_FORMAT_NV21: + return get_framesize_raw_nv12(width, height); + case HFI_COLOR_FORMAT_NV12_UBWC: + return get_framesize_raw_nv12_ubwc(width, height); + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz_raw); + +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height) +{ + u32 hfi_fmt, sz; + bool compressed; + + switch (v4l2_fmt) { + case V4L2_PIX_FMT_MPEG: + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_H264_NO_SC: + case V4L2_PIX_FMT_H264_MVC: + case V4L2_PIX_FMT_H263: + case V4L2_PIX_FMT_MPEG1: + case V4L2_PIX_FMT_MPEG2: + case V4L2_PIX_FMT_MPEG4: + case V4L2_PIX_FMT_XVID: + case V4L2_PIX_FMT_VC1_ANNEX_G: + case V4L2_PIX_FMT_VC1_ANNEX_L: + case V4L2_PIX_FMT_VP8: + case V4L2_PIX_FMT_VP9: + case V4L2_PIX_FMT_HEVC: + compressed = true; + break; + default: + compressed = false; + break; + } + + if (compressed) { + sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; + return ALIGN(sz, SZ_4K); + } + + hfi_fmt = to_hfi_raw_fmt(v4l2_fmt); + if (!hfi_fmt) + return 0; + + return venus_helper_get_framesz_raw(hfi_fmt, width, height); +} +EXPORT_SYMBOL_GPL(venus_helper_get_framesz); + int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height) { @@ -438,12 +676,13 @@ int venus_helper_set_input_resolution(struct venus_inst *inst, EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + u32 buftype) { u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE; struct hfi_framesize fs; - fs.buffer_type = HFI_BUFFER_OUTPUT; + fs.buffer_type = buftype; fs.width = width; fs.height = height; @@ -451,8 +690,37 @@ int venus_helper_set_output_resolution(struct venus_inst *inst, } EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode) +{ + const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE; + struct hfi_video_work_mode wm; + + if (!IS_V4(inst->core)) + return 0; + + wm.video_work_mode = mode; + + return hfi_session_set_property(inst, ptype, &wm); +} +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; + + 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_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, - unsigned int output_bufs) + unsigned int output_bufs, + unsigned int output2_bufs) { u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL; struct hfi_buffer_count_actual buf_count; @@ -468,41 +736,122 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, buf_count.type = HFI_BUFFER_OUTPUT; buf_count.count_actual = output_bufs; - return hfi_session_set_property(inst, ptype, &buf_count); + ret = hfi_session_set_property(inst, ptype, &buf_count); + if (ret) + return ret; + + if (output2_bufs) { + buf_count.type = HFI_BUFFER_OUTPUT2; + buf_count.count_actual = output2_bufs; + + ret = hfi_session_set_property(inst, ptype, &buf_count); + } + + return ret; } EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs); -int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype) { + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; struct hfi_uncompressed_format_select fmt; - u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT; - int ret; + + fmt.buffer_type = buftype; + fmt.format = hfi_format; + + return hfi_session_set_property(inst, ptype, &fmt); +} +EXPORT_SYMBOL_GPL(venus_helper_set_raw_format); + +int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt) +{ + u32 hfi_format, buftype; if (inst->session_type == VIDC_SESSION_TYPE_DEC) - fmt.buffer_type = HFI_BUFFER_OUTPUT; + buftype = HFI_BUFFER_OUTPUT; else if (inst->session_type == VIDC_SESSION_TYPE_ENC) - fmt.buffer_type = HFI_BUFFER_INPUT; + buftype = HFI_BUFFER_INPUT; else return -EINVAL; - switch (pixfmt) { - case V4L2_PIX_FMT_NV12: - fmt.format = HFI_COLOR_FORMAT_NV12; - break; - case V4L2_PIX_FMT_NV21: - fmt.format = HFI_COLOR_FORMAT_NV21; - break; - default: + hfi_format = to_hfi_raw_fmt(pixfmt); + if (!hfi_format) return -EINVAL; - } - ret = hfi_session_set_property(inst, ptype, &fmt); + return venus_helper_set_raw_format(inst, hfi_format, buftype); +} +EXPORT_SYMBOL_GPL(venus_helper_set_color_format); + +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en) +{ + struct hfi_multi_stream multi = {0}; + u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM; + int ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT; + multi.enable = out_en; + + ret = hfi_session_set_property(inst, ptype, &multi); + if (ret) + return ret; + + multi.buffer_type = HFI_BUFFER_OUTPUT2; + multi.enable = out2_en; + + return hfi_session_set_property(inst, ptype, &multi); +} +EXPORT_SYMBOL_GPL(venus_helper_set_multistream); + +int venus_helper_set_dyn_bufmode(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; + struct hfi_buffer_alloc_mode mode; + int ret; + + if (!is_dynamic_bufmode(inst)) + return 0; + + mode.type = HFI_BUFFER_OUTPUT; + mode.mode = HFI_BUFFER_MODE_DYNAMIC; + + ret = hfi_session_set_property(inst, ptype, &mode); if (ret) return ret; + mode.type = HFI_BUFFER_OUTPUT2; + + return hfi_session_set_property(inst, ptype, &mode); +} +EXPORT_SYMBOL_GPL(venus_helper_set_dyn_bufmode); + +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype) +{ + const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; + struct hfi_buffer_size_actual bufsz; + + bufsz.type = buftype; + bufsz.size = bufsize; + + return hfi_session_set_property(inst, ptype, &bufsz); +} +EXPORT_SYMBOL_GPL(venus_helper_set_bufsize); + +unsigned int venus_helper_get_opb_size(struct venus_inst *inst) +{ + /* the encoder has only one output */ + if (inst->session_type == VIDC_SESSION_TYPE_ENC) + return inst->output_buf_size; + + if (inst->opb_buftype == HFI_BUFFER_OUTPUT) + return inst->output_buf_size; + else if (inst->opb_buftype == HFI_BUFFER_OUTPUT2) + return inst->output2_buf_size; + return 0; } -EXPORT_SYMBOL_GPL(venus_helper_set_color_format); +EXPORT_SYMBOL_GPL(venus_helper_get_opb_size); static void delayed_process_buf_func(struct work_struct *work) { @@ -602,9 +951,10 @@ EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init); int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + unsigned int out_buf_size = venus_helper_get_opb_size(inst); if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - vb2_plane_size(vb, 0) < inst->output_buf_size) + vb2_plane_size(vb, 0) < out_buf_size) return -EINVAL; if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && vb2_plane_size(vb, 0) < inst->input_buf_size) @@ -674,6 +1024,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q) if (ret) hfi_session_abort(inst); + venus_helper_free_dpb_bufs(inst); + load_scale_clocks(core); INIT_LIST_HEAD(&inst->registeredbufs); } @@ -712,8 +1064,14 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst) if (ret) goto err_unload_res; + ret = venus_helper_queue_dpb_bufs(inst); + if (ret) + goto err_session_stop; + return 0; +err_session_stop: + hfi_session_stop(inst); err_unload_res: hfi_session_unload_res(inst); err_unreg_bufs: @@ -766,3 +1124,113 @@ void venus_helper_init_instance(struct venus_inst *inst) } } EXPORT_SYMBOL_GPL(venus_helper_init_instance); + +static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt) +{ + unsigned int i; + + for (i = 0; i < caps->num_fmts; i++) { + if (caps->fmts[i].buftype == buftype && + caps->fmts[i].fmt == fmt) + return true; + } + + return false; +} + +int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt, + u32 *out_fmt, u32 *out2_fmt, bool ubwc) +{ + struct venus_core *core = inst->core; + struct venus_caps *caps; + u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt); + bool found, found_ubwc; + + *out_fmt = *out2_fmt = 0; + + if (!fmt) + return -EINVAL; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return -EINVAL; + + if (ubwc) { + ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE; + found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, + ubwc_fmt); + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + + if (found_ubwc && found) { + *out_fmt = ubwc_fmt; + *out2_fmt = fmt; + return 0; + } + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt); + if (found) { + *out_fmt = fmt; + *out2_fmt = 0; + return 0; + } + + found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt); + if (found) { + *out_fmt = 0; + *out2_fmt = fmt; + return 0; + } + + 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 971392be5df5..2475f284f396 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -33,14 +33,33 @@ void venus_helper_m2m_device_run(void *priv); void venus_helper_m2m_job_abort(void *priv); int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, struct hfi_buffer_requirements *req); +u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height); +u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height); int venus_helper_set_input_resolution(struct venus_inst *inst, unsigned int width, unsigned int height); int venus_helper_set_output_resolution(struct venus_inst *inst, - unsigned int width, unsigned int height); + unsigned int width, unsigned int height, + u32 buftype); +int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +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 output_bufs, + unsigned int output2_bufs); +int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format, + u32 buftype); int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt); +int venus_helper_set_dyn_bufmode(struct venus_inst *inst); +int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype); +int venus_helper_set_multistream(struct venus_inst *inst, bool out_en, + bool out2_en); +unsigned int venus_helper_get_opb_size(struct venus_inst *inst); void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf); void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx); void venus_helper_init_instance(struct venus_inst *inst); +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); #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index bca894a00c07..24207829982f 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -49,6 +49,8 @@ static u32 to_codec_type(u32 pixfmt) return HFI_VIDEO_CODEC_VP9; case V4L2_PIX_FMT_XVID: return HFI_VIDEO_CODEC_DIVX; + case V4L2_PIX_FMT_HEVC: + return HFI_VIDEO_CODEC_HEVC; default: return 0; } @@ -203,13 +205,12 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt) { struct venus_core *core = inst->core; const struct hfi_ops *ops = core->ops; - u32 codec; int ret; - codec = to_codec_type(pixfmt); + inst->hfi_codec = to_codec_type(pixfmt); reinit_completion(&inst->done); - ret = ops->session_init(inst, inst->session_type, codec); + ret = ops->session_init(inst, inst->session_type, inst->hfi_codec); if (ret) return ret; @@ -312,7 +313,7 @@ int hfi_session_continue(struct venus_inst *inst) { struct venus_core *core = inst->core; - if (core->res->hfi_version != HFI_VERSION_3XX) + if (core->res->hfi_version == HFI_VERSION_1XX) return 0; return core->ops->session_continue(inst); @@ -473,7 +474,8 @@ int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd) if (fd->buffer_type == HFI_BUFFER_INPUT) return ops->session_etb(inst, fd); - else if (fd->buffer_type == HFI_BUFFER_OUTPUT) + else if (fd->buffer_type == HFI_BUFFER_OUTPUT || + fd->buffer_type == HFI_BUFFER_OUTPUT2) return ops->session_ftb(inst, fd); return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index 5466b7d60dd0..6038d8e0ab22 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -74,6 +74,16 @@ struct hfi_event_data { u32 tag; u32 profile; u32 level; + /* the following properties start appear from v4 onwards */ + u32 bit_depth; + u32 pic_struct; + u32 colour_space; + u32 entropy_mode; + u32 buf_count; + struct { + u32 left, top; + u32 width, height; + } input_crop; }; /* define core states */ diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 1cfeb7743041..e8389d8d8c48 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1166,6 +1166,63 @@ pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt, return ret; } +static int +pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + prop_data = &pkt->data[1]; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + pkt->data[0] = ptype; + + /* + * Any session set property which is different in 3XX packetization + * should be added as a new case below. All unchanged session set + * properties will be handled in the default case. + */ + switch (ptype) { + case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: { + struct hfi_buffer_count_actual *in = pdata; + struct hfi_buffer_count_actual_4xx *count = prop_data; + + count->count_actual = in->count_actual; + count->type = in->type; + count->count_min_host = in->count_actual; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count); + break; + } + case HFI_PROPERTY_PARAM_WORK_MODE: { + struct hfi_video_work_mode *in = pdata, *wm = prop_data; + + wm->video_work_mode = in->video_work_mode; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*wm); + break; + } + case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: { + struct hfi_videocores_usage_type *in = pdata, *cu = prop_data; + + cu->video_core_enable_mask = in->video_core_enable_mask; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); + break; + } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: + /* not implemented on Venus 4xx */ + break; + default: + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + } + + return 0; +} + int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, void *cookie, u32 ptype) { @@ -1181,7 +1238,10 @@ int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, if (hfi_ver == HFI_VERSION_1XX) return pkt_session_set_property_1x(pkt, cookie, ptype, pdata); - return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + if (hfi_ver == HFI_VERSION_3XX) + return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); + + return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); } void pkt_set_version(enum hfi_version version) diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 55d8eb21403a..15804ad7e65d 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -121,6 +121,7 @@ #define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002 #define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e +#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000f #define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010 #define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003 @@ -376,13 +377,18 @@ #define HFI_BUFFER_OUTPUT2 0x3 #define HFI_BUFFER_INTERNAL_PERSIST 0x4 #define HFI_BUFFER_INTERNAL_PERSIST_1 0x5 -#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001 -#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002 -#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003 -#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004 -#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005 -#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006 - +#define HFI_BUFFER_INTERNAL_SCRATCH(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001) +#define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005) +#define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006) +#define HFI_BUFFER_EXTRADATA_INPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002) +#define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xa : 0x1000003) +#define HFI_BUFFER_EXTRADATA_OUTPUT2(ver) \ + (((ver) == HFI_VERSION_4XX) ? 0xb : 0x1000004) #define HFI_BUFFER_TYPE_MAX 11 #define HFI_BUFFER_MODE_STATIC 0x1000001 @@ -424,12 +430,14 @@ #define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e #define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f #define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010 +#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015 /* * HFI_PROPERTY_CONFIG_COMMON_START * HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000 */ #define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001 +#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002 /* * HFI_PROPERTY_PARAM_VDEC_COMMON_START @@ -438,6 +446,9 @@ #define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001 #define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002 #define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003 +#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007 +#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009 +#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a /* * HFI_PROPERTY_CONFIG_VDEC_COMMON_START @@ -518,6 +529,7 @@ enum hfi_version { HFI_VERSION_1XX, HFI_VERSION_3XX, + HFI_VERSION_4XX }; struct hfi_buffer_info { @@ -767,12 +779,56 @@ struct hfi_framesize { u32 height; }; +#define VIDC_CORE_ID_DEFAULT 0 +#define VIDC_CORE_ID_1 1 +#define VIDC_CORE_ID_2 2 +#define VIDC_CORE_ID_3 3 + +struct hfi_videocores_usage_type { + u32 video_core_enable_mask; +}; + +#define VIDC_WORK_MODE_1 1 +#define VIDC_WORK_MODE_2 2 + +struct hfi_video_work_mode { + u32 video_work_mode; +}; + struct hfi_h264_vui_timing_info { u32 enable; u32 fixed_framerate; u32 time_scale; }; +struct hfi_bit_depth { + u32 buffer_type; + u32 bit_depth; +}; + +struct hfi_picture_type { + u32 is_sync_frame; + u32 picture_type; +}; + +struct hfi_pic_struct { + u32 progressive_only; +}; + +struct hfi_colour_space { + u32 colour_space; +}; + +struct hfi_extradata_input_crop { + u32 size; + u32 version; + u32 port_index; + u32 left; + u32 top; + u32 width; + u32 height; +}; + #define HFI_COLOR_FORMAT_MONOCHROME 0x01 #define HFI_COLOR_FORMAT_NV12 0x02 #define HFI_COLOR_FORMAT_NV21 0x03 @@ -802,10 +858,23 @@ struct hfi_uncompressed_format_select { u32 format; }; +struct hfi_uncompressed_plane_constraints { + u32 stride_multiples; + u32 max_stride; + u32 min_plane_buffer_height_multiple; + u32 buffer_alignment; +}; + +struct hfi_uncompressed_plane_info { + u32 format; + u32 num_planes; + struct hfi_uncompressed_plane_constraints plane_constraints[1]; +}; + struct hfi_uncompressed_format_supported { u32 buffer_type; u32 format_entries; - u32 format_info[1]; + struct hfi_uncompressed_plane_info plane_info[1]; }; struct hfi_uncompressed_plane_actual { @@ -819,19 +888,6 @@ struct hfi_uncompressed_plane_actual_info { struct hfi_uncompressed_plane_actual plane_format[1]; }; -struct hfi_uncompressed_plane_constraints { - u32 stride_multiples; - u32 max_stride; - u32 min_plane_buffer_height_multiple; - u32 buffer_alignment; -}; - -struct hfi_uncompressed_plane_info { - u32 format; - u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_format[1]; -}; - struct hfi_uncompressed_plane_actual_constraints_info { u32 buffer_type; u32 num_planes; @@ -961,6 +1017,12 @@ struct hfi_buffer_count_actual { u32 count_actual; }; +struct hfi_buffer_count_actual_4xx { + u32 type; + u32 count_actual; + u32 count_min_host; +}; + struct hfi_buffer_size_actual { u32 type; u32 size; @@ -971,6 +1033,14 @@ struct hfi_buffer_display_hold_count_actual { u32 hold_count; }; +/* HFI 4XX reorder the fields, use these macros */ +#define HFI_BUFREQ_HOLD_COUNT(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? 0 : (bufreq)->hold_count) +#define HFI_BUFREQ_COUNT_MIN(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->hold_count : (bufreq)->count_min) +#define HFI_BUFREQ_COUNT_MIN_HOST(bufreq, ver) \ + ((ver) == HFI_VERSION_4XX ? (bufreq)->count_min : 0) + struct hfi_buffer_requirements { u32 type; u32 size; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 90c93d9603dc..0ecdaa15c296 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -21,14 +21,21 @@ #include "hfi.h" #include "hfi_helper.h" #include "hfi_msgs.h" +#include "hfi_parser.h" static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_msg_event_notify_pkt *pkt) { + enum hfi_version ver = core->res->hfi_version; struct hfi_event_data event = {0}; int num_properties_changed; struct hfi_framesize *frame_sz; struct hfi_profile_level *profile_level; + struct hfi_bit_depth *pixel_depth; + struct hfi_pic_struct *pic_struct; + struct hfi_colour_space *colour_info; + struct hfi_buffer_requirements *bufreq; + struct hfi_extradata_input_crop *crop; u8 *data_ptr; u32 ptype; @@ -60,14 +67,52 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, frame_sz = (struct hfi_framesize *)data_ptr; event.width = frame_sz->width; event.height = frame_sz->height; - data_ptr += sizeof(frame_sz); + data_ptr += sizeof(*frame_sz); break; case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: data_ptr += sizeof(u32); profile_level = (struct hfi_profile_level *)data_ptr; event.profile = profile_level->profile; event.level = profile_level->level; - data_ptr += sizeof(profile_level); + data_ptr += sizeof(*profile_level); + break; + case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH: + data_ptr += sizeof(u32); + pixel_depth = (struct hfi_bit_depth *)data_ptr; + event.bit_depth = pixel_depth->bit_depth; + data_ptr += sizeof(*pixel_depth); + break; + case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT: + data_ptr += sizeof(u32); + pic_struct = (struct hfi_pic_struct *)data_ptr; + event.pic_struct = pic_struct->progressive_only; + data_ptr += sizeof(*pic_struct); + break; + case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE: + data_ptr += sizeof(u32); + colour_info = (struct hfi_colour_space *)data_ptr; + event.colour_space = colour_info->colour_space; + data_ptr += sizeof(*colour_info); + break; + case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: + data_ptr += sizeof(u32); + event.entropy_mode = *(u32 *)data_ptr; + data_ptr += sizeof(u32); + break; + case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: + data_ptr += sizeof(u32); + bufreq = (struct hfi_buffer_requirements *)data_ptr; + event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver); + data_ptr += sizeof(*bufreq); + break; + case HFI_INDEX_EXTRADATA_INPUT_CROP: + data_ptr += sizeof(u32); + crop = (struct hfi_extradata_input_crop *)data_ptr; + event.input_crop.left = crop->left; + event.input_crop.top = crop->top; + event.input_crop.width = crop->width; + event.input_crop.height = crop->height; + data_ptr += sizeof(*crop); break; default: break; @@ -173,81 +218,28 @@ static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_sys_init_done_pkt *pkt = packet; - u32 rem_bytes, read_bytes = 0, num_properties; - u32 error, ptype; - u8 *data; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) - goto err_no_prop; - - num_properties = pkt->num_properties; + goto done; - if (!num_properties) { + if (!pkt->num_properties) { error = HFI_ERR_SYS_INVALID_PARAMETER; - goto err_no_prop; + goto done; } rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32); - - if (!rem_bytes) { + if (rem_bytes <= 0) { /* missing property data */ error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - goto err_no_prop; + goto done; } - data = (u8 *)&pkt->data[0]; - - if (core->res->hfi_version == HFI_VERSION_3XX) - goto err_no_prop; - - while (num_properties && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - data += sizeof(u32); + error = hfi_parser(core, inst, pkt->data, rem_bytes); - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: { - struct hfi_codec_supported *prop; - - prop = (struct hfi_codec_supported *)data; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - read_bytes += sizeof(*prop) + sizeof(u32); - core->dec_codecs = prop->dec_codecs; - core->enc_codecs = prop->enc_codecs; - break; - } - case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: { - struct hfi_max_sessions_supported *prop; - - if (rem_bytes < sizeof(*prop)) { - error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES; - break; - } - - prop = (struct hfi_max_sessions_supported *)data; - read_bytes += sizeof(*prop) + sizeof(u32); - core->max_sessions_supported = prop->max_sessions; - break; - } - default: - error = HFI_ERR_SYS_INVALID_PARAMETER; - break; - } - - if (error) - break; - - rem_bytes -= read_bytes; - data += read_bytes; - num_properties--; - } - -err_no_prop: +done: core->error = error; complete(&core->done); } @@ -325,51 +317,6 @@ static void hfi_sys_pc_prepare_done(struct venus_core *core, dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type); } -static void -hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst) -{ - if (!in || !inst) - return; - - switch (in->capability_type) { - case HFI_CAPABILITY_FRAME_WIDTH: - inst->cap_width = *in; - break; - case HFI_CAPABILITY_FRAME_HEIGHT: - inst->cap_height = *in; - break; - case HFI_CAPABILITY_MBS_PER_FRAME: - inst->cap_mbs_per_frame = *in; - break; - case HFI_CAPABILITY_MBS_PER_SECOND: - inst->cap_mbs_per_sec = *in; - break; - case HFI_CAPABILITY_FRAMERATE: - inst->cap_framerate = *in; - break; - case HFI_CAPABILITY_SCALE_X: - inst->cap_scale_x = *in; - break; - case HFI_CAPABILITY_SCALE_Y: - inst->cap_scale_y = *in; - break; - case HFI_CAPABILITY_BITRATE: - inst->cap_bitrate = *in; - break; - case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS: - inst->cap_hier_p = *in; - break; - case HFI_CAPABILITY_ENC_LTR_COUNT: - inst->cap_ltr_count = *in; - break; - case HFI_CAPABILITY_CP_OUTPUT2_THRESH: - inst->cap_secure_output2_threshold = *in; - break; - default: - break; - } -} - static unsigned int session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt, struct hfi_profile_level *profile_level) @@ -459,248 +406,27 @@ done: complete(&inst->done); } -static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst, - struct hfi_msg_session_init_done_pkt *pkt) -{ - struct device *dev = core->dev; - u32 rem_bytes, num_props; - u32 ptype, next_offset = 0; - u32 err; - u8 *data; - - rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); - if (!rem_bytes) { - dev_err(dev, "%s: missing property info\n", __func__); - return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; - } - - err = pkt->error_type; - if (err) - return err; - - data = (u8 *)&pkt->data[0]; - num_props = pkt->num_properties; - - while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) { - ptype = *((u32 *)data); - next_offset = sizeof(u32); - - switch (ptype) { - case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: { - struct hfi_codec_mask_supported *masks = - (struct hfi_codec_mask_supported *) - (data + next_offset); - - next_offset += sizeof(*masks); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: { - struct hfi_capabilities *caps; - struct hfi_capability *cap; - u32 num_caps; - - if ((rem_bytes - next_offset) < sizeof(*cap)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - caps = (struct hfi_capabilities *)(data + next_offset); - - num_caps = caps->num_capabilities; - cap = &caps->data[0]; - next_offset += sizeof(u32); - - while (num_caps && - (rem_bytes - next_offset) >= sizeof(u32)) { - hfi_copy_cap_prop(cap, inst); - cap++; - next_offset += sizeof(*cap); - num_caps--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: { - struct hfi_uncompressed_format_supported *prop = - (struct hfi_uncompressed_format_supported *) - (data + next_offset); - u32 num_fmt_entries; - u8 *fmt; - struct hfi_uncompressed_plane_info *inf; - - if ((rem_bytes - next_offset) < sizeof(*prop)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - num_fmt_entries = prop->format_entries; - next_offset = sizeof(*prop) - sizeof(u32); - fmt = (u8 *)&prop->format_info[0]; - - dev_dbg(dev, "uncomm format support num entries:%u\n", - num_fmt_entries); - - while (num_fmt_entries) { - struct hfi_uncompressed_plane_constraints *cnts; - u32 bytes_to_skip; - - inf = (struct hfi_uncompressed_plane_info *)fmt; - - if ((rem_bytes - next_offset) < sizeof(*inf)) { - err = HFI_ERR_SESSION_INVALID_PARAMETER; - break; - } - - dev_dbg(dev, "plane info: fmt:%x, planes:%x\n", - inf->format, inf->num_planes); - - cnts = &inf->plane_format[0]; - dev_dbg(dev, "%u %u %u %u\n", - cnts->stride_multiples, - cnts->max_stride, - cnts->min_plane_buffer_height_multiple, - cnts->buffer_alignment); - - bytes_to_skip = sizeof(*inf) - sizeof(*cnts) + - inf->num_planes * sizeof(*cnts); - - fmt += bytes_to_skip; - next_offset += bytes_to_skip; - num_fmt_entries--; - } - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: { - struct hfi_properties_supported *prop = - (struct hfi_properties_supported *) - (data + next_offset); - - next_offset += sizeof(*prop) - sizeof(u32) - + prop->num_properties * sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: { - struct hfi_profile_level_supported *prop = - (struct hfi_profile_level_supported *) - (data + next_offset); - struct hfi_profile_level *pl; - unsigned int prop_count = 0; - unsigned int count = 0; - u8 *ptr; - - ptr = (u8 *)&prop->profile_level[0]; - prop_count = prop->profile_count; - - if (prop_count > HFI_MAX_PROFILE_COUNT) - prop_count = HFI_MAX_PROFILE_COUNT; - - while (prop_count) { - ptr++; - pl = (struct hfi_profile_level *)ptr; - - inst->pl[count].profile = pl->profile; - inst->pl[count].level = pl->level; - prop_count--; - count++; - ptr += sizeof(*pl) / sizeof(u32); - } - - inst->pl_count = count; - next_offset += sizeof(*prop) - sizeof(*pl) + - prop->profile_count * sizeof(*pl); - - num_props--; - break; - } - case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: { - next_offset += - sizeof(struct hfi_interlace_format_supported); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: { - struct hfi_nal_stream_format *nal = - (struct hfi_nal_stream_format *) - (data + next_offset); - dev_dbg(dev, "NAL format: %x\n", nal->format); - next_offset += sizeof(*nal); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: { - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: { - u32 *max_seq_sz = (u32 *)(data + next_offset); - - dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz); - next_offset += sizeof(u32); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: { - next_offset += sizeof(struct hfi_intra_refresh); - num_props--; - break; - } - case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: { - struct hfi_buffer_alloc_mode_supported *prop = - (struct hfi_buffer_alloc_mode_supported *) - (data + next_offset); - unsigned int i; - - for (i = 0; i < prop->num_entries; i++) { - if (prop->buffer_type == HFI_BUFFER_OUTPUT || - prop->buffer_type == HFI_BUFFER_OUTPUT2) { - switch (prop->data[i]) { - case HFI_BUFFER_MODE_STATIC: - inst->cap_bufs_mode_static = true; - break; - case HFI_BUFFER_MODE_DYNAMIC: - inst->cap_bufs_mode_dynamic = true; - break; - default: - break; - } - } - } - next_offset += sizeof(*prop) - - sizeof(u32) + prop->num_entries * sizeof(u32); - num_props--; - break; - } - default: - dev_dbg(dev, "%s: default case %#x\n", __func__, ptype); - break; - } - - rem_bytes -= next_offset; - data += next_offset; - } - - return err; -} - static void hfi_session_init_done(struct venus_core *core, struct venus_inst *inst, void *packet) { struct hfi_msg_session_init_done_pkt *pkt = packet; - unsigned int error; + int rem_bytes; + u32 error; error = pkt->error_type; if (error != HFI_ERR_NONE) goto done; - if (core->res->hfi_version != HFI_VERSION_1XX) + if (!IS_V1(core)) goto done; - error = init_done_read_prop(core, inst, pkt); + rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32); + if (rem_bytes <= 0) { + error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES; + goto done; + } + error = hfi_parser(core, inst, pkt->data, rem_bytes); done: inst->error = error; complete(&inst->done); @@ -779,7 +505,8 @@ static void hfi_session_ftb_done(struct venus_core *core, error = HFI_ERR_SESSION_INVALID_PARAMETER; } - if (buffer_type != HFI_BUFFER_OUTPUT) + if (buffer_type != HFI_BUFFER_OUTPUT && + buffer_type != HFI_BUFFER_OUTPUT2) goto done; if (hfi_flags & HFI_BUFFERFLAG_EOS) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c new file mode 100644 index 000000000000..2293d936e49c --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linaro Ltd. + * + * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org> + */ +#include <linux/bitops.h> +#include <linux/kernel.h> + +#include "core.h" +#include "hfi_helper.h" +#include "hfi_parser.h" + +typedef void (*func)(struct venus_caps *cap, const void *data, + unsigned int size); + +static void init_codecs(struct venus_core *core) +{ + struct venus_caps *caps = core->caps, *cap; + unsigned long bit; + + for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_DEC; + cap->valid = false; + } + + for_each_set_bit(bit, &core->enc_codecs, MAX_CODEC_NUM) { + cap = &caps[core->codecs_count++]; + cap->codec = BIT(bit); + cap->domain = VIDC_SESSION_TYPE_ENC; + cap->valid = false; + } +} + +static void for_each_codec(struct venus_caps *caps, unsigned int caps_num, + u32 codecs, u32 domain, func cb, void *data, + unsigned int size) +{ + struct venus_caps *cap; + unsigned int i; + + for (i = 0; i < caps_num; i++) { + cap = &caps[i]; + if (cap->valid && cap->domain == domain) + continue; + if (cap->codec & codecs && cap->domain == domain) + cb(cap, data, size); + } +} + +static void +fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num) +{ + const u32 *type = data; + + if (*type == HFI_BUFFER_MODE_DYNAMIC) + cap->cap_bufs_mode_dynamic = true; +} + +static void +parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_buffer_alloc_mode_supported *mode = data; + u32 num_entries = mode->num_entries; + u32 *type; + + if (num_entries > MAX_ALLOC_MODE_ENTRIES) + return; + + type = mode->data; + + while (num_entries--) { + if (mode->buffer_type == HFI_BUFFER_OUTPUT || + mode->buffer_type == HFI_BUFFER_OUTPUT2) + for_each_codec(core->caps, ARRAY_SIZE(core->caps), + codecs, domain, fill_buf_mode, type, 1); + + type++; + } +} + +static void fill_profile_level(struct venus_caps *cap, const void *data, + unsigned int num) +{ + const struct hfi_profile_level *pl = data; + + memcpy(&cap->pl[cap->num_pl], pl, num * sizeof(*pl)); + cap->num_pl += num; +} + +static void +parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_profile_level_supported *pl = data; + struct hfi_profile_level *proflevel = pl->profile_level; + struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {}; + + if (pl->profile_count > HFI_MAX_PROFILE_COUNT) + return; + + memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_profile_level, pl_arr, pl->profile_count); +} + +static void +fill_caps(struct venus_caps *cap, const void *data, unsigned int num) +{ + const struct hfi_capability *caps = data; + + memcpy(&cap->caps[cap->num_caps], caps, num * sizeof(*caps)); + cap->num_caps += num; +} + +static void +parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_capabilities *caps = data; + struct hfi_capability *cap = caps->data; + u32 num_caps = caps->num_capabilities; + struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {}; + + if (num_caps > MAX_CAP_ENTRIES) + return; + + memcpy(caps_arr, cap, num_caps * sizeof(*cap)); + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_caps, caps_arr, num_caps); +} + +static void fill_raw_fmts(struct venus_caps *cap, const void *fmts, + unsigned int num_fmts) +{ + const struct raw_formats *formats = fmts; + + memcpy(&cap->fmts[cap->num_fmts], formats, num_fmts * sizeof(*formats)); + cap->num_fmts += num_fmts; +} + +static void +parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) +{ + struct hfi_uncompressed_format_supported *fmt = data; + struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info; + struct hfi_uncompressed_plane_constraints *constr; + struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; + u32 entries = fmt->format_entries; + unsigned int i = 0; + u32 num_planes; + + while (entries) { + num_planes = pinfo->num_planes; + + rawfmts[i].fmt = pinfo->format; + rawfmts[i].buftype = fmt->buffer_type; + i++; + + if (pinfo->num_planes > MAX_PLANES) + break; + + pinfo = (void *)pinfo + sizeof(*constr) * num_planes + + 2 * sizeof(u32); + entries--; + } + + for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, + fill_raw_fmts, rawfmts, i); +} + +static void parse_codecs(struct venus_core *core, void *data) +{ + struct hfi_codec_supported *codecs = data; + + core->dec_codecs = codecs->dec_codecs; + core->enc_codecs = codecs->enc_codecs; + + if (IS_V1(core)) { + core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC; + core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK; + } +} + +static void parse_max_sessions(struct venus_core *core, const void *data) +{ + const struct hfi_max_sessions_supported *sessions = data; + + core->max_sessions_supported = sessions->max_sessions; +} + +static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data) +{ + struct hfi_codec_mask_supported *mask = data; + + *codecs = mask->codecs; + *domain = mask->video_domains; +} + +static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain) +{ + if (!inst || !IS_V1(inst->core)) + return; + + *codecs = inst->hfi_codec; + *domain = inst->session_type; +} + +static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain) +{ + struct venus_caps *caps, *cap; + unsigned int i; + u32 dom; + + if (!inst || !IS_V1(inst->core)) + return; + + caps = inst->core->caps; + dom = inst->session_type; + + for (i = 0; i < MAX_CODEC_NUM; i++) { + cap = &caps[i]; + if (cap->codec & codecs && cap->domain == dom) + cap->valid = true; + } +} + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, + u32 size) +{ + unsigned int words_count = size >> 2; + u32 *word = buf, *data, codecs = 0, domain = 0; + + if (size % 4) + return HFI_ERR_SYS_INSUFFICIENT_RESOURCES; + + parser_init(inst, &codecs, &domain); + + while (words_count) { + data = word + 1; + + switch (*word) { + case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: + parse_codecs(core, data); + init_codecs(core); + break; + case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: + parse_max_sessions(core, data); + break; + case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: + parse_codecs_mask(&codecs, &domain, data); + break; + case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: + parse_raw_formats(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: + parse_caps(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: + parse_profile_level(core, codecs, domain, data); + break; + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: + parse_alloc_mode(core, codecs, domain, data); + break; + default: + break; + } + + word++; + words_count--; + } + + parser_fini(inst, codecs, domain); + + return HFI_ERR_NONE; +} diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h new file mode 100644 index 000000000000..3e931c747e19 --- /dev/null +++ b/drivers/media/platform/qcom/venus/hfi_parser.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2018 Linaro Ltd. */ +#ifndef __VENUS_HFI_PARSER_H__ +#define __VENUS_HFI_PARSER_H__ + +#include "core.h" + +u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, + void *buf, u32 size); + +#define WHICH_CAP_MIN 0 +#define WHICH_CAP_MAX 1 +#define WHICH_CAP_STEP 2 + +static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which) +{ + struct venus_core *core = inst->core; + struct hfi_capability *cap = NULL; + struct venus_caps *caps; + unsigned int i; + + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); + if (!caps) + return 0; + + for (i = 0; i < caps->num_caps; i++) { + if (caps->caps[i].capability_type == type) { + cap = &caps->caps[i]; + break; + } + } + + if (!cap) + return 0; + + switch (which) { + case WHICH_CAP_MIN: + return cap->min; + case WHICH_CAP_MAX: + return cap->max; + case WHICH_CAP_STEP: + return cap->step_size; + default: + break; + } + + return 0; +} + +static inline u32 cap_min(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MIN); +} + +static inline u32 cap_max(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_MAX); +} + +static inline u32 cap_step(struct venus_inst *inst, u32 type) +{ + return get_cap(inst, type, WHICH_CAP_STEP); +} + +static inline u32 frame_width_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_width_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_WIDTH); +} + +static inline u32 frame_height_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frame_height_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAME_HEIGHT); +} + +static inline u32 frate_min(struct venus_inst *inst) +{ + return cap_min(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_max(struct venus_inst *inst) +{ + return cap_max(inst, HFI_CAPABILITY_FRAMERATE); +} + +static inline u32 frate_step(struct venus_inst *inst) +{ + return cap_step(inst, HFI_CAPABILITY_FRAMERATE); +} + +#endif diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 734ce11b0ed0..124085556b94 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -532,6 +532,24 @@ static int venus_halt_axi(struct venus_hfi_device *hdev) u32 val; int ret; + if (IS_V4(hdev->core)) { + val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT); + val |= WRAPPER_CPU_AXI_HALT_HALT; + venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val); + + ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS, + val, + val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) { + dev_err(dev, "AXI bus port halt timeout\n"); + return ret; + } + + return 0; + } + /* Halt AXI and AXI IMEM VBIF Access */ val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; @@ -861,6 +879,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) if (ret) dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret); + /* + * Idle indicator is disabled by default on some 4xx firmware versions, + * enable it explicitly in order to make suspend functional by checking + * WFI (wait-for-interrupt) bit. + */ + if (IS_V4(hdev->core)) + venus_sys_idle_indicator = true; + ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); if (ret) dev_warn(dev, "setting idle response ON failed (%d)\n", ret); @@ -1073,6 +1099,10 @@ static int venus_core_init(struct venus_core *core) if (ret) dev_warn(dev, "failed to send image version pkt to fw\n"); + ret = venus_sys_set_default_properties(hdev); + if (ret) + return ret; + return 0; } @@ -1117,10 +1147,6 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, struct hfi_session_init_pkt pkt; int ret; - ret = venus_sys_set_default_properties(hdev); - if (ret) - return ret; - ret = pkt_session_init(&pkt, inst, session_type, codec); if (ret) goto err; @@ -1426,13 +1452,40 @@ static int venus_suspend_1xx(struct venus_core *core) return 0; } +static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + return true; + + return false; +} + +static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev) +{ + u32 ctrl_status, cpu_status; + + cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); + ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + + if (cpu_status & WRAPPER_CPU_STATUS_WFI && + ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) + return true; + + return false; +} + static int venus_suspend_3xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; - u32 ctrl_status, wfi_status; + bool val; int ret; - int cnt = 100; if (!hdev->power_enabled || hdev->suspended) return 0; @@ -1446,29 +1499,30 @@ static int venus_suspend_3xx(struct venus_core *core) return -EINVAL; } - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - - ret = venus_prepare_power_collapse(hdev, false); - if (ret) { - dev_err(dev, "prepare for power collapse fail (%d)\n", - ret); - return ret; - } + /* + * Power collapse sequence for Venus 3xx and 4xx versions: + * 1. Check for ARM9 and video core to be idle by checking WFI bit + * (bit 0) in CPU status register and by checking Idle (bit 30) in + * Control status register for video core. + * 2. Send a command to prepare for power collapse. + * 3. Check for WFI and PC_READY bits. + */ + ret = readx_poll_timeout(venus_cpu_and_video_core_idle, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; - cnt = 100; - while (cnt--) { - wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); - if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY && - wfi_status & BIT(0)) - break; - usleep_range(1000, 1500); - } + ret = venus_prepare_power_collapse(hdev, false); + if (ret) { + dev_err(dev, "prepare for power collapse fail (%d)\n", ret); + return ret; } + ret = readx_poll_timeout(venus_cpu_idle_and_pc_ready, hdev, val, val, + 1500, 100 * 1500); + if (ret) + return ret; + mutex_lock(&hdev->lock); ret = venus_power_off(hdev); @@ -1487,7 +1541,7 @@ static int venus_suspend_3xx(struct venus_core *core) static int venus_suspend(struct venus_core *core) { - if (core->res->hfi_version == HFI_VERSION_3XX) + if (IS_V3(core) || IS_V4(core)) return venus_suspend_3xx(core); return venus_suspend_1xx(core); diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index 98cc350113ab..def0926a6dee 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -104,10 +104,20 @@ #define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) #define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_AXI_HALT_HALT BIT(16) #define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) +#define WRAPPER_CPU_AXI_HALT_STATUS_IDLE BIT(24) #define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010) #define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014) +#define WRAPPER_CPU_STATUS_WFI BIT(0) #define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) +/* Venus 4xx */ +#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90) +#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94) + +#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110) +#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114) + #endif diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 49bbd1861d3a..dfbbbf0f746f 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -14,6 +14,7 @@ */ #include <linux/clk.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -24,33 +25,11 @@ #include <media/videobuf2-dma-sg.h> #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "vdec.h" -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - - return ALIGN(size, SZ_4K); -} - -static u32 get_framesize_compressed(unsigned int width, unsigned int height) -{ - return ((width * height * 3 / 2) / 2) + 128; -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -99,6 +78,10 @@ static const struct venus_format vdec_formats[] = { .pixfmt = V4L2_PIX_FMT_XVID, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, }, }; @@ -159,7 +142,6 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -173,14 +155,12 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -190,18 +170,14 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -442,12 +418,12 @@ static int vdec_enum_framesizes(struct file *file, void *fh, fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -544,11 +520,41 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = { static int vdec_set_properties(struct venus_inst *inst) { struct vdec_controls *ctr = &inst->controls.dec; + struct hfi_enable en = { .enable = 1 }; + u32 ptype; + int ret; + + if (ctr->post_loop_deb_mode) { + ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + + return 0; +} + +#define is_ubwc_fmt(fmt) (!!((fmt) & HFI_COLOR_FORMAT_UBWC_BASE)) + +static int vdec_output_conf(struct venus_inst *inst) +{ struct venus_core *core = inst->core; struct hfi_enable en = { .enable = 1 }; + u32 width = inst->out_width; + u32 height = inst->out_height; + u32 out_fmt, out2_fmt; + bool ubwc = false; u32 ptype; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + 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); @@ -556,27 +562,84 @@ static int vdec_set_properties(struct venus_inst *inst) return ret; } - if (core->res->hfi_version == HFI_VERSION_3XX || - inst->cap_bufs_mode_dynamic) { - struct hfi_buffer_alloc_mode mode; + /* Force searching UBWC formats for bigger then HD resolutions */ + if (width > 1920 && height > ALIGN(1080, 32)) + ubwc = true; + + /* For Venus v4 UBWC format is mandatory */ + if (IS_V4(core)) + ubwc = true; + + ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt, + &out2_fmt, ubwc); + if (ret) + return ret; + + inst->output_buf_size = + venus_helper_get_framesz_raw(out_fmt, width, height); + inst->output2_buf_size = + venus_helper_get_framesz_raw(out2_fmt, width, height); + + if (is_ubwc_fmt(out_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT2; + inst->opb_fmt = out2_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT; + inst->dpb_fmt = out_fmt; + } else if (is_ubwc_fmt(out2_fmt)) { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = HFI_BUFFER_OUTPUT2; + inst->dpb_fmt = out2_fmt; + } else { + inst->opb_buftype = HFI_BUFFER_OUTPUT; + inst->opb_fmt = out_fmt; + inst->dpb_buftype = 0; + inst->dpb_fmt = 0; + } + + ret = venus_helper_set_raw_format(inst, inst->opb_fmt, + inst->opb_buftype); + if (ret) + return ret; - ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE; - mode.type = HFI_BUFFER_OUTPUT; - mode.mode = HFI_BUFFER_MODE_DYNAMIC; + if (inst->dpb_fmt) { + ret = venus_helper_set_multistream(inst, false, true); + if (ret) + return ret; - ret = hfi_session_set_property(inst, ptype, &mode); + ret = venus_helper_set_raw_format(inst, inst->dpb_fmt, + inst->dpb_buftype); if (ret) return ret; - } - if (ctr->post_loop_deb_mode) { - ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER; - en.enable = 1; - ret = hfi_session_set_property(inst, ptype, &en); + ret = venus_helper_set_output_resolution(inst, width, height, + HFI_BUFFER_OUTPUT2); if (ret) return ret; } + if (IS_V3(core) || IS_V4(core)) { + if (inst->output2_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output2_buf_size, + HFI_BUFFER_OUTPUT2); + if (ret) + return ret; + } + + if (inst->output_buf_size) { + ret = venus_helper_set_bufsize(inst, + inst->output_buf_size, + HFI_BUFFER_OUTPUT); + if (ret) + return ret; + } + } + + ret = venus_helper_set_dyn_bufmode(inst); + if (ret) + return ret; + return 0; } @@ -603,19 +666,32 @@ deinit: return ret; } -static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num) +static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num, + unsigned int *out_num) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; + *in_num = *out_num = 0; + ret = vdec_init_session(inst); if (ret) return ret; + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); + if (ret) + goto deinit; + + *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); + ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); + if (ret) + goto deinit; - *num = bufreq.count_actual; + *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver); +deinit: hfi_session_deinit(inst); return ret; @@ -626,10 +702,12 @@ static int vdec_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num; + unsigned int in_num, out_num; int ret = 0; if (*num_planes) { + unsigned int output_buf_size = venus_helper_get_opb_size(inst); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && *num_planes != inst->fmt_out->num_planes) return -EINVAL; @@ -643,41 +721,35 @@ static int vdec_queue_setup(struct vb2_queue *q, return -EINVAL; if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - sizes[0] < inst->output_buf_size) + sizes[0] < output_buf_size) return -EINVAL; return 0; } + ret = vdec_num_buffers(inst, &in_num, &out_num); + if (ret) + return ret; + switch (q->type) { case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: *num_planes = inst->fmt_out->num_planes; - sizes[0] = get_framesize_compressed(inst->out_width, + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->out_width, inst->out_height); inst->input_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, in_num); inst->num_input_bufs = *num_buffers; - - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - inst->num_output_bufs = num; + inst->num_output_bufs = out_num; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; - - ret = vdec_cap_num_buffers(inst, &num); - if (ret) - break; - - *num_buffers = max(*num_buffers, num); - - for (p = 0; p < *num_planes; p++) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); - - inst->num_output_bufs = *num_buffers; + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; + *num_buffers = max(*num_buffers, out_num); + inst->num_output_bufs = *num_buffers; break; default: ret = -EINVAL; @@ -689,6 +761,7 @@ static int vdec_queue_setup(struct vb2_queue *q, static int vdec_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -700,14 +773,14 @@ static int vdec_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); if (ret) return ret; - if (inst->num_input_bufs < bufreq.count_min) + if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; @@ -716,8 +789,6 @@ static int vdec_verify_conf(struct venus_inst *inst) static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) { struct venus_inst *inst = vb2_get_drv_priv(q); - struct venus_core *core = inst->core; - u32 ptype; int ret; mutex_lock(&inst->lock); @@ -746,24 +817,20 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) if (ret) goto deinit_sess; - if (core->res->hfi_version == HFI_VERSION_3XX) { - struct hfi_buffer_size_actual buf_sz; - - ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL; - buf_sz.type = HFI_BUFFER_OUTPUT; - buf_sz.size = inst->output_buf_size; - - ret = hfi_session_set_property(inst, ptype, &buf_sz); - if (ret) - goto deinit_sess; - } + ret = vdec_output_conf(inst); + if (ret) + goto deinit_sess; ret = vdec_verify_conf(inst); if (ret) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - VB2_MAX_FRAME); + VB2_MAX_FRAME, VB2_MAX_FRAME); + if (ret) + goto deinit_sess; + + ret = venus_helper_alloc_dpb_bufs(inst); if (ret) goto deinit_sess; @@ -818,9 +885,11 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type, vbuf->field = V4L2_FIELD_NONE; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + unsigned int opb_sz = venus_helper_get_opb_size(inst); + vb = &vbuf->vb2_buf; vb->planes[0].bytesused = - max_t(unsigned int, inst->output_buf_size, bytesused); + max_t(unsigned int, opb_sz, bytesused); vb->planes[0].data_offset = data_offset; vb->timestamp = timestamp_us * NSEC_PER_USEC; vbuf->sequence = inst->sequence_cap++; @@ -901,22 +970,7 @@ static void vdec_inst_init(struct venus_inst *inst) inst->fps = 30; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 30; - - inst->cap_width.min = 64; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 1; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 1; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 16; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static const struct v4l2_m2m_ops vdec_m2m_ops = { @@ -973,6 +1027,7 @@ static int vdec_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); @@ -1080,12 +1135,18 @@ static int vdec_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + 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); } + 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); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1130,15 +1191,21 @@ static int vdec_remove(struct platform_device *pdev) static __maybe_unused int vdec_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + 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); + clk_disable_unprepare(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); } static __maybe_unused int vdec_runtime_resume(struct device *dev) @@ -1146,13 +1213,29 @@ static __maybe_unused int vdec_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core0_clk); - writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + if (IS_V4(core)) + ret = clk_prepare_enable(core->core0_bus_clk); + + if (ret) + goto err_unprepare_core0; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false); + +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/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 032839bbc967..f4604b0cd57e 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -29,7 +29,7 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: @@ -54,7 +54,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ret = hfi_session_get_property(inst, ptype, &hprop); if (!ret) ctr->profile = hprop.profile_level.profile; @@ -130,8 +130,10 @@ int vdec_ctrl_init(struct venus_inst *inst) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6b2ce479584e..41249d1443fa 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -14,6 +14,7 @@ */ #include <linux/clk.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -24,38 +25,13 @@ #include <media/v4l2-ctrls.h> #include "hfi_venus_io.h" +#include "hfi_parser.h" #include "core.h" #include "helpers.h" #include "venc.h" #define NUM_B_FRAMES_MAX 4 -static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height) -{ - u32 y_stride, uv_stride, y_plane; - u32 y_sclines, uv_sclines, uv_plane; - u32 size; - - y_stride = ALIGN(width, 128); - uv_stride = ALIGN(width, 128); - y_sclines = ALIGN(height, 32); - uv_sclines = ALIGN(((height + 1) >> 1), 16); - - y_plane = y_stride * y_sclines; - uv_plane = uv_stride * uv_sclines + SZ_4K; - size = y_plane + uv_plane + SZ_8K; - size = ALIGN(size, SZ_4K); - - return size; -} - -static u32 get_framesize_compressed(u32 width, u32 height) -{ - u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2; - - return ALIGN(sz, SZ_4K); -} - /* * Three resons to keep MPLANE formats (despite that the number of planes * currently is one): @@ -84,6 +60,10 @@ static const struct venus_format venc_formats[] = { .pixfmt = V4L2_PIX_FMT_VP8, .num_planes = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + }, { + .pixfmt = V4L2_PIX_FMT_HEVC, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, }, }; @@ -223,7 +203,7 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: return HFI_H264_ENTROPY_CABAC; } - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: switch (value) { case 0: default: @@ -245,6 +225,46 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; } + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + default: + return HFI_HEVC_PROFILE_MAIN; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return HFI_HEVC_PROFILE_MAIN_STILL_PIC; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return HFI_HEVC_PROFILE_MAIN10; + } + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + switch (value) { + case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: + default: + return HFI_HEVC_LEVEL_1; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: + return HFI_HEVC_LEVEL_2; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: + return HFI_HEVC_LEVEL_21; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: + return HFI_HEVC_LEVEL_3; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: + return HFI_HEVC_LEVEL_31; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: + return HFI_HEVC_LEVEL_4; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: + return HFI_HEVC_LEVEL_41; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: + return HFI_HEVC_LEVEL_5; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: + return HFI_HEVC_LEVEL_51; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: + return HFI_HEVC_LEVEL_52; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: + return HFI_HEVC_LEVEL_6; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: + return HFI_HEVC_LEVEL_61; + case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: + return HFI_HEVC_LEVEL_62; + } } return 0; @@ -283,7 +303,6 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; const struct venus_format *fmt; - unsigned int p; memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); @@ -297,14 +316,12 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) else return NULL; fmt = find_format(inst, pixmp->pixelformat, f->type); - pixmp->width = 1280; - pixmp->height = 720; } - pixmp->width = clamp(pixmp->width, inst->cap_width.min, - inst->cap_width.max); - pixmp->height = clamp(pixmp->height, inst->cap_height.min, - inst->cap_height.max); + pixmp->width = clamp(pixmp->width, frame_width_min(inst), + frame_width_max(inst)); + pixmp->height = clamp(pixmp->height, frame_height_min(inst), + frame_height_max(inst)); if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) pixmp->height = ALIGN(pixmp->height, 32); @@ -317,19 +334,14 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f) pixmp->num_planes = fmt->num_planes; pixmp->flags = 0; - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - for (p = 0; p < pixmp->num_planes; p++) { - pfmt[p].sizeimage = - get_framesize_uncompressed(p, pixmp->width, - pixmp->height); + pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat, + pixmp->width, + pixmp->height); - pfmt[p].bytesperline = ALIGN(pixmp->width, 128); - } - } else { - pfmt[0].sizeimage = get_framesize_compressed(pixmp->width, - pixmp->height); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pfmt[0].bytesperline = ALIGN(pixmp->width, 128); + else pfmt[0].bytesperline = 0; - } return fmt; } @@ -553,12 +565,12 @@ static int venc_enum_framesizes(struct file *file, void *fh, if (fsize->index) return -EINVAL; - fsize->stepwise.min_width = inst->cap_width.min; - fsize->stepwise.max_width = inst->cap_width.max; - fsize->stepwise.step_width = inst->cap_width.step_size; - fsize->stepwise.min_height = inst->cap_height.min; - fsize->stepwise.max_height = inst->cap_height.max; - fsize->stepwise.step_height = inst->cap_height.step_size; + fsize->stepwise.min_width = frame_width_min(inst); + fsize->stepwise.max_width = frame_width_max(inst); + fsize->stepwise.step_width = frame_width_step(inst); + fsize->stepwise.min_height = frame_height_min(inst); + fsize->stepwise.max_height = frame_height_max(inst); + fsize->stepwise.step_height = frame_height_step(inst); return 0; } @@ -586,18 +598,18 @@ static int venc_enum_frameintervals(struct file *file, void *fh, if (!fival->width || !fival->height) return -EINVAL; - if (fival->width > inst->cap_width.max || - fival->width < inst->cap_width.min || - fival->height > inst->cap_height.max || - fival->height < inst->cap_height.min) + if (fival->width > frame_width_max(inst) || + fival->width < frame_width_min(inst) || + fival->height > frame_height_max(inst) || + fival->height < frame_height_min(inst)) return -EINVAL; fival->stepwise.min.numerator = 1; - fival->stepwise.min.denominator = inst->cap_framerate.max; + fival->stepwise.min.denominator = frate_max(inst); fival->stepwise.max.numerator = 1; - fival->stepwise.max.denominator = inst->cap_framerate.min; + fival->stepwise.max.denominator = frate_min(inst); fival->stepwise.step.numerator = 1; - fival->stepwise.step.denominator = inst->cap_framerate.max; + fival->stepwise.step.denominator = frate_max(inst); return 0; } @@ -642,6 +654,14 @@ static int venc_set_properties(struct venus_inst *inst) u32 ptype, rate_control, bitrate, profile = 0, level = 0; int ret; + ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + 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); @@ -756,7 +776,7 @@ static int venc_set_properties(struct venus_inst *inst) level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, ctr->level.h264); } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE, + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE, ctr->profile.vpx); level = 0; } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { @@ -767,6 +787,11 @@ static int venc_set_properties(struct venus_inst *inst) } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { profile = 0; level = 0; + } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + ctr->profile.hevc); + level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + ctr->level.hevc); } ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; @@ -794,7 +819,8 @@ static int venc_init_session(struct venus_inst *inst) goto deinit; ret = venus_helper_set_output_resolution(inst, inst->width, - inst->height); + inst->height, + HFI_BUFFER_OUTPUT); if (ret) goto deinit; @@ -835,7 +861,7 @@ static int venc_queue_setup(struct vb2_queue *q, unsigned int sizes[], struct device *alloc_devs[]) { struct venus_inst *inst = vb2_get_drv_priv(q); - unsigned int p, num, min = 4; + unsigned int num, min = 4; int ret = 0; if (*num_planes) { @@ -870,16 +896,18 @@ static int venc_queue_setup(struct vb2_queue *q, *num_buffers = max(*num_buffers, num); inst->num_input_bufs = *num_buffers; - for (p = 0; p < *num_planes; ++p) - sizes[p] = get_framesize_uncompressed(p, inst->width, - inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt, + inst->width, + inst->height); inst->input_buf_size = sizes[0]; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: *num_planes = inst->fmt_cap->num_planes; *num_buffers = max(*num_buffers, min); inst->num_output_bufs = *num_buffers; - sizes[0] = get_framesize_compressed(inst->width, inst->height); + sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt, + inst->width, + inst->height); inst->output_buf_size = sizes[0]; break; default: @@ -892,6 +920,7 @@ static int venc_queue_setup(struct vb2_queue *q, static int venc_verify_conf(struct venus_inst *inst) { + enum hfi_version ver = inst->core->res->hfi_version; struct hfi_buffer_requirements bufreq; int ret; @@ -903,7 +932,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_output_bufs < bufreq.count_actual || - inst->num_output_bufs < bufreq.count_min) + inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq); @@ -911,7 +940,7 @@ static int venc_verify_conf(struct venus_inst *inst) return ret; if (inst->num_input_bufs < bufreq.count_actual || - inst->num_input_bufs < bufreq.count_min) + inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver)) return -EINVAL; return 0; @@ -952,7 +981,7 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count) goto deinit_sess; ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs, - inst->num_output_bufs); + inst->num_output_bufs, 0); if (ret) goto deinit_sess; @@ -1090,22 +1119,7 @@ static void venc_inst_init(struct venus_inst *inst) inst->fps = 15; inst->timeperframe.numerator = 1; inst->timeperframe.denominator = 15; - - inst->cap_width.min = 96; - inst->cap_width.max = 1920; - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_width.max = 3840; - inst->cap_width.step_size = 2; - inst->cap_height.min = 64; - inst->cap_height.max = ALIGN(1080, 32); - if (inst->core->res->hfi_version == HFI_VERSION_3XX) - inst->cap_height.max = ALIGN(2160, 32); - inst->cap_height.step_size = 2; - inst->cap_framerate.min = 1; - inst->cap_framerate.max = 30; - inst->cap_framerate.step_size = 1; - inst->cap_mbs_per_frame.min = 24; - inst->cap_mbs_per_frame.max = 8160; + inst->hfi_codec = HFI_VIDEO_CODEC_H264; } static int venc_open(struct file *file) @@ -1118,6 +1132,7 @@ static int venc_open(struct file *file) if (!inst) return -ENOMEM; + INIT_LIST_HEAD(&inst->dpbbufs); INIT_LIST_HEAD(&inst->registeredbufs); INIT_LIST_HEAD(&inst->internalbufs); INIT_LIST_HEAD(&inst->list); @@ -1224,12 +1239,18 @@ static int venc_probe(struct platform_device *pdev) if (!core) return -EPROBE_DEFER; - if (core->res->hfi_version == HFI_VERSION_3XX) { + 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); } + 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); + } + platform_set_drvdata(pdev, core); vdev = video_device_alloc(); @@ -1274,15 +1295,21 @@ static int venc_remove(struct platform_device *pdev) static __maybe_unused int venc_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); + int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + 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); + clk_disable_unprepare(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); - return 0; + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); } static __maybe_unused int venc_runtime_resume(struct device *dev) @@ -1290,13 +1317,29 @@ static __maybe_unused int venc_runtime_resume(struct device *dev) struct venus_core *core = dev_get_drvdata(dev); int ret; - if (core->res->hfi_version == HFI_VERSION_1XX) + if (IS_V1(core)) return 0; - writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true); + if (ret) + return ret; + ret = clk_prepare_enable(core->core1_clk); - writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL); + if (ret) + goto err_power_disable; + + if (IS_V4(core)) + ret = clk_prepare_enable(core->core1_bus_clk); + + if (ret) + goto err_unprepare_core1; + + return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false); +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 21e938a28662..459101728d26 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -101,7 +101,7 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: ctr->profile.h264 = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile.vpx = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: @@ -248,6 +248,11 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT); @@ -257,9 +262,6 @@ int venc_ctrl_init(struct venus_inst *inst) BITRATE_STEP, BITRATE_DEFAULT_PEAK); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0); - - v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26); v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index 2988031d285d..b47af8eb145a 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * rcar-fcp.c -- R-Car Frame Compression Processor Driver * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index baf4eafff6e3..e3eb8fee2536 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 config VIDEO_RCAR_CSI2 tristate "R-Car MIPI CSI-2 Receiver" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile index 5ab803d3e7c1..00d809f5d2c1 100644 --- a/drivers/media/platform/rcar-vin/Makefile +++ b/drivers/media/platform/rcar-vin/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index d3072e166a1c..ce09799976ef 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/module.h> @@ -46,6 +42,8 @@ */ #define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) +#define v4l2_dev_to_vin(d) container_of(d, struct rvin_dev, v4l2_dev) + /* ----------------------------------------------------------------------------- * Media Controller link notification */ @@ -169,9 +167,37 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* Add the new link to the existing mask and check if it works. */ csi_id = rvin_group_entity_to_csi_id(group, link->source->entity); + + if (csi_id == -ENODEV) { + struct v4l2_subdev *sd; + unsigned int i; + + /* + * Make sure the source entity subdevice is registered as + * a parallel input of one of the enabled VINs if it is not + * one of the CSI-2 subdevices. + * + * No hardware configuration required for parallel inputs, + * we can return here. + */ + sd = media_entity_to_v4l2_subdev(link->source->entity); + for (i = 0; i < RCAR_VIN_NUM; i++) { + if (group->vin[i] && group->vin[i]->parallel && + group->vin[i]->parallel->subdev == sd) { + group->vin[i]->is_csi = false; + ret = 0; + goto out; + } + } + + vin_err(vin, "Subdevice %s not registered to any VIN\n", + link->source->entity->name); + ret = -ENODEV; + goto out; + } + channel = rvin_group_csi_pad_to_channel(link->source->index); mask_new = mask & rvin_group_get_mask(vin, csi_id, channel); - vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); if (!mask_new) { @@ -181,6 +207,11 @@ static int rvin_group_link_notify(struct media_link *link, u32 flags, /* New valid CHSEL found, set the new value. */ ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); + if (ret) + goto out; + + vin->is_csi = true; + out: mutex_unlock(&group->lock); @@ -359,8 +390,6 @@ out: * Async notifier */ -#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) - static int rvin_find_pad(struct v4l2_subdev *sd, int direction) { unsigned int pad; @@ -376,12 +405,12 @@ static int rvin_find_pad(struct v4l2_subdev *sd, int direction) } /* ----------------------------------------------------------------------------- - * Digital async notifier + * Parallel async notifier */ /* The vin lock should be held when calling the subdevice attach and detach */ -static int rvin_digital_subdevice_attach(struct rvin_dev *vin, - struct v4l2_subdev *subdev) +static int rvin_parallel_subdevice_attach(struct rvin_dev *vin, + struct v4l2_subdev *subdev) { struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -392,15 +421,20 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); if (ret < 0) return ret; - vin->digital->source_pad = ret; + vin->parallel->source_pad = ret; ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->digital->sink_pad = ret < 0 ? 0 : ret; + vin->parallel->sink_pad = ret < 0 ? 0 : ret; + + if (vin->info->use_mc) { + vin->parallel->subdev = subdev; + return 0; + } /* Find compatible subdevices mbus format */ vin->mbus_code = 0; code.index = 0; - code.pad = vin->digital->source_pad; + code.pad = vin->parallel->source_pad; while (!vin->mbus_code && !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { code.index++; @@ -450,23 +484,27 @@ static int rvin_digital_subdevice_attach(struct rvin_dev *vin, vin->vdev.ctrl_handler = &vin->ctrl_handler; - vin->digital->subdev = subdev; + vin->parallel->subdev = subdev; return 0; } -static void rvin_digital_subdevice_detach(struct rvin_dev *vin) +static void rvin_parallel_subdevice_detach(struct rvin_dev *vin) { rvin_v4l2_unregister(vin); - v4l2_ctrl_handler_free(&vin->ctrl_handler); + vin->parallel->subdev = NULL; - vin->vdev.ctrl_handler = NULL; - vin->digital->subdev = NULL; + if (!vin->info->use_mc) { + v4l2_ctrl_handler_free(&vin->ctrl_handler); + vin->vdev.ctrl_handler = NULL; + } } -static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) +static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); + struct media_entity *source; + struct media_entity *sink; int ret; ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); @@ -475,31 +513,50 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) return ret; } - return rvin_v4l2_register(vin); + if (!video_is_registered(&vin->vdev)) { + ret = rvin_v4l2_register(vin); + if (ret < 0) + return ret; + } + + if (!vin->info->use_mc) + return 0; + + /* If we're running with media-controller, link the subdevs. */ + source = &vin->parallel->subdev->entity; + sink = &vin->vdev.entity; + + ret = media_create_pad_link(source, vin->parallel->source_pad, + sink, vin->parallel->sink_pad, 0); + if (ret) + vin_err(vin, "Error adding link from %s to %s: %d\n", + source->name, sink->name, ret); + + return ret; } -static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); - vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name); mutex_lock(&vin->lock); - rvin_digital_subdevice_detach(vin); + rvin_parallel_subdevice_detach(vin); mutex_unlock(&vin->lock); } -static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); int ret; mutex_lock(&vin->lock); - ret = rvin_digital_subdevice_attach(vin, subdev); + ret = rvin_parallel_subdevice_attach(vin, subdev); mutex_unlock(&vin->lock); if (ret) return ret; @@ -507,70 +564,71 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, v4l2_set_subdev_hostdata(subdev, vin); vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", - subdev->name, vin->digital->source_pad, - vin->digital->sink_pad); + subdev->name, vin->parallel->source_pad, + vin->parallel->sink_pad); return 0; } -static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = { - .bound = rvin_digital_notify_bound, - .unbind = rvin_digital_notify_unbind, - .complete = rvin_digital_notify_complete, +static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = { + .bound = rvin_parallel_notify_bound, + .unbind = rvin_parallel_notify_unbind, + .complete = rvin_parallel_notify_complete, }; -static int rvin_digital_parse_v4l2(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) +static int rvin_parallel_parse_v4l2(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) { struct rvin_dev *vin = dev_get_drvdata(dev); - struct rvin_graph_entity *rvge = - container_of(asd, struct rvin_graph_entity, asd); + struct rvin_parallel_entity *rvpe = + container_of(asd, struct rvin_parallel_entity, asd); if (vep->base.port || vep->base.id) return -ENOTCONN; - vin->mbus_cfg.type = vep->bus_type; + vin->parallel = rvpe; + vin->parallel->mbus_type = vep->bus_type; - switch (vin->mbus_cfg.type) { + switch (vin->parallel->mbus_type) { case V4L2_MBUS_PARALLEL: vin_dbg(vin, "Found PARALLEL media bus\n"); - vin->mbus_cfg.flags = vep->bus.parallel.flags; + vin->parallel->mbus_flags = vep->bus.parallel.flags; break; case V4L2_MBUS_BT656: vin_dbg(vin, "Found BT656 media bus\n"); - vin->mbus_cfg.flags = 0; + vin->parallel->mbus_flags = 0; break; default: vin_err(vin, "Unknown media bus type\n"); return -EINVAL; } - vin->digital = rvge; - return 0; } -static int rvin_digital_graph_init(struct rvin_dev *vin) +static int rvin_parallel_init(struct rvin_dev *vin) { int ret; - ret = v4l2_async_notifier_parse_fwnode_endpoints( - vin->dev, &vin->notifier, - sizeof(struct rvin_graph_entity), rvin_digital_parse_v4l2); + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( + vin->dev, &vin->notifier, sizeof(struct rvin_parallel_entity), + 0, rvin_parallel_parse_v4l2); if (ret) return ret; - if (!vin->digital) - return -ENODEV; + /* If using mc, it's fine not to have any input registered. */ + if (!vin->parallel) + return vin->info->use_mc ? 0 : -ENODEV; - vin_dbg(vin, "Found digital subdevice %pOF\n", - to_of_node(vin->digital->asd.match.fwnode)); + vin_dbg(vin, "Found parallel subdevice %pOF\n", + to_of_node(vin->parallel->asd.match.fwnode)); - vin->notifier.ops = &rvin_digital_notify_ops; + vin->notifier.ops = &rvin_parallel_notify_ops; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } @@ -583,7 +641,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); const struct rvin_group_route *route; unsigned int i; int ret; @@ -596,7 +654,8 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) /* Register all video nodes for the group. */ for (i = 0; i < RCAR_VIN_NUM; i++) { - if (vin->group->vin[i]) { + if (vin->group->vin[i] && + !video_is_registered(&vin->group->vin[i]->vdev)) { ret = rvin_v4l2_register(vin->group->vin[i]); if (ret) return ret; @@ -649,7 +708,7 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; for (i = 0; i < RCAR_VIN_NUM; i++) @@ -673,7 +732,7 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev); unsigned int i; mutex_lock(&vin->group->lock); @@ -707,11 +766,9 @@ static int rvin_mc_parse_of_endpoint(struct device *dev, return -EINVAL; if (!of_device_is_available(to_of_node(asd->match.fwnode))) { - vin_dbg(vin, "OF device %pOF disabled, ignoring\n", to_of_node(asd->match.fwnode)); return -ENOTCONN; - } if (vin->group->csi[vep->base.id].fwnode) { @@ -736,12 +793,6 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_lock(&vin->group->lock); - /* If there already is a notifier something has gone wrong, bail out. */ - if (WARN_ON(vin->group->notifier)) { - mutex_unlock(&vin->group->lock); - return -EINVAL; - } - /* If not all VIN's are registered don't register the notifier. */ for (i = 0; i < RCAR_VIN_NUM; i++) if (vin->group->vin[i]) @@ -753,19 +804,16 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) } /* - * Have all VIN's look for subdevices. Some subdevices will overlap - * but the parser function can handle it, so each subdevice will - * only be registered once with the notifier. + * Have all VIN's look for CSI-2 subdevices. Some subdevices will + * overlap but the parser function can handle it, so each subdevice + * will only be registered once with the group notifier. */ - - vin->group->notifier = &vin->notifier; - for (i = 0; i < RCAR_VIN_NUM; i++) { if (!vin->group->vin[i]) continue; ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( - vin->group->vin[i]->dev, vin->group->notifier, + vin->group->vin[i]->dev, &vin->group->notifier, sizeof(struct v4l2_async_subdev), 1, rvin_mc_parse_of_endpoint); if (ret) { @@ -776,11 +824,15 @@ static int rvin_mc_parse_of_graph(struct rvin_dev *vin) mutex_unlock(&vin->group->lock); - vin->group->notifier->ops = &rvin_group_notify_ops; + if (!vin->group->notifier.num_subdevs) + return 0; - ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + vin->group->notifier.ops = &rvin_group_notify_ops; + ret = v4l2_async_notifier_register(&vin->v4l2_dev, + &vin->group->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); + v4l2_async_notifier_cleanup(&vin->group->notifier); return ret; } @@ -791,10 +843,6 @@ static int rvin_mc_init(struct rvin_dev *vin) { int ret; - /* All our sources are CSI-2 */ - vin->mbus_cfg.type = V4L2_MBUS_CSI2; - vin->mbus_cfg.flags = 0; - vin->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); if (ret) @@ -974,7 +1022,51 @@ static const struct rvin_info rcar_info_r8a7796 = { .routes = rcar_info_r8a7796_routes, }; -static const struct rvin_group_route _rcar_info_r8a77970_routes[] = { +static const struct rvin_group_route rcar_info_r8a77965_routes[] = { + { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77965 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77965_routes, +}; + +static const struct rvin_group_route rcar_info_r8a77970_routes[] = { { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, @@ -990,7 +1082,19 @@ static const struct rvin_info rcar_info_r8a77970 = { .use_mc = true, .max_width = 4096, .max_height = 4096, - .routes = _rcar_info_r8a77970_routes, + .routes = rcar_info_r8a77970_routes, +}; + +static const struct rvin_group_route rcar_info_r8a77995_routes[] = { + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77995 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a77995_routes, }; static const struct of_device_id rvin_of_id_table[] = { @@ -1031,9 +1135,17 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a7796, }, { + .compatible = "renesas,vin-r8a77965", + .data = &rcar_info_r8a77965, + }, + { .compatible = "renesas,vin-r8a77970", .data = &rcar_info_r8a77970, }, + { + .compatible = "renesas,vin-r8a77995", + .data = &rcar_info_r8a77995, + }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); @@ -1085,20 +1197,35 @@ static int rcar_vin_probe(struct platform_device *pdev) return ret; platform_set_drvdata(pdev, vin); - if (vin->info->use_mc) + + if (vin->info->use_mc) { ret = rvin_mc_init(vin); - else - ret = rvin_digital_graph_init(vin); - if (ret < 0) - goto error; + if (ret) + goto error_dma_unregister; + } + + ret = rvin_parallel_init(vin); + if (ret) + goto error_group_unregister; pm_suspend_ignore_children(&pdev->dev, true); pm_runtime_enable(&pdev->dev); return 0; -error: + +error_group_unregister: + if (vin->info->use_mc) { + mutex_lock(&vin->group->lock); + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } + mutex_unlock(&vin->group->lock); + rvin_group_put(vin); + } + +error_dma_unregister: rvin_dma_unregister(vin); - v4l2_async_notifier_cleanup(&vin->notifier); return ret; } @@ -1116,8 +1243,10 @@ static int rcar_vin_remove(struct platform_device *pdev) if (vin->info->use_mc) { mutex_lock(&vin->group->lock); - if (vin->group->notifier == &vin->notifier) - vin->group->notifier = NULL; + if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); + } mutex_unlock(&vin->group->lock); rvin_group_put(vin); } else { @@ -1142,4 +1271,4 @@ module_platform_driver(rcar_vin_driver); MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index daef72d410a3..dc5ae8025832 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -339,6 +339,7 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); + int (*confirm_start)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; bool clear_ulps; @@ -545,6 +546,13 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret) return ret; + /* Confirm start */ + if (priv->info->confirm_start) { + ret = priv->info->confirm_start(priv); + if (ret) + return ret; + } + /* Clear Ultra Low Power interrupt. */ if (priv->info->clear_ulps) rcsi2_write(priv, INTSTATE_REG, @@ -881,6 +889,11 @@ static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) { + return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); +} + +static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) +{ static const struct phtw_value step1[] = { { .data = 0xed, .code = 0x34 }, { .data = 0xed, .code = 0x44 }, @@ -890,12 +903,6 @@ static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) { /* sentinel */ }, }; - int ret; - - ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); - if (ret) - return ret; - return rcsi2_phtw_write_array(priv, step1); } @@ -949,6 +956,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { .init_phtw = rcsi2_init_phtw_v3m_e3, + .confirm_start = rcsi2_confirm_start_v3m_e3, }; static const struct of_device_id rcar_csi2_of_table[] = { diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index ac07f99e3516..92323310f735 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/delay.h> @@ -123,6 +119,7 @@ /* Video n Data Mode Register 2 bits */ #define VNDMR2_VPS (1 << 30) #define VNDMR2_HPS (1 << 29) +#define VNDMR2_CES (1 << 28) #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) @@ -659,8 +656,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV8_BT656; + else + vnmc |= VNMC_INF_YUV8_BT601; + input_is_yuv = true; break; case MEDIA_BUS_FMT_RGB888_1X24: @@ -668,8 +669,12 @@ static int rvin_setup(struct rvin_dev *vin) break; case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + if (!vin->is_csi && + vin->parallel->mbus_type == V4L2_MBUS_BT656) + vnmc |= VNMC_INF_YUV10_BT656; + else + vnmc |= VNMC_INF_YUV10_BT601; + input_is_yuv = true; break; default: @@ -682,13 +687,19 @@ static int rvin_setup(struct rvin_dev *vin) else dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); - /* Hsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_HPS; + if (!vin->is_csi) { + /* Hsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; - /* Vsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_VPS; + /* Data Enable Polarity Select */ + if (vin->parallel->mbus_flags & V4L2_MBUS_DATA_ENABLE_LOW) + dmr2 |= VNDMR2_CES; + } /* * Output format @@ -733,8 +744,8 @@ static int rvin_setup(struct rvin_dev *vin) vnmc |= VNMC_BPS; if (vin->info->model == RCAR_GEN3) { - /* Select between CSI-2 and Digital input */ - if (vin->mbus_cfg.type == V4L2_MBUS_CSI2) + /* Select between CSI-2 and parallel input */ + if (vin->is_csi) vnmc &= ~VNMC_DPINE; else vnmc |= VNMC_DPINE; @@ -856,7 +867,7 @@ static int rvin_capture_start(struct rvin_dev *vin) /* Continuous Frame Capture Mode */ rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - vin->state = RUNNING; + vin->state = STARTING; return 0; } @@ -910,6 +921,20 @@ static irqreturn_t rvin_irq(int irq, void *data) vnms = rvin_read(vin, VNMS_REG); slot = (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + /* + * To hand buffers back in a known order to userspace start + * to capture first from slot 0. + */ + if (vin->state == STARTING) { + if (slot != 0) { + vin_dbg(vin, "Starting sync slot: %d\n", slot); + goto done; + } + + vin_dbg(vin, "Capture start synced!\n"); + vin->state = RUNNING; + } + /* Capture frame */ if (vin->queue_buf[slot]) { vin->queue_buf[slot]->field = vin->format.field; @@ -1074,7 +1099,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on) /* No media controller used, simply pass operation to subdevice. */ if (!vin->info->use_mc) { - ret = v4l2_subdev_call(vin->digital->subdev, video, s_stream, + ret = v4l2_subdev_call(vin->parallel->subdev, video, s_stream, on); return ret == -ENOIOCTLCMD ? 0 : ret; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index e78fba84d590..5a54779cfc27 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/pm_runtime.h> @@ -144,7 +140,7 @@ static int rvin_reset_format(struct rvin_dev *vin) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; int ret; @@ -175,7 +171,7 @@ static int rvin_try_format(struct rvin_dev *vin, u32 which, struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, - .pad = vin->digital->source_pad, + .pad = vin->parallel->source_pad, }; enum v4l2_field field; u32 width, height; @@ -517,7 +513,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, if (timings->pad) return -EINVAL; - timings->pad = vin->digital->sink_pad; + timings->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -569,7 +565,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, if (cap->pad) return -EINVAL; - cap->pad = vin->digital->sink_pad; + cap->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -587,7 +583,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, get_edid, edid); @@ -605,7 +601,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) if (edid->pad) return -EINVAL; - edid->pad = vin->digital->sink_pad; + edid->pad = vin->parallel->sink_pad; ret = v4l2_subdev_call(sd, pad, set_edid, edid); diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index c2aef789b491..0b13b34d03e3 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Driver for Renesas R-Car VIN * @@ -7,11 +8,6 @@ * Copyright (C) 2008 Magnus Damm * * Based on the soc-camera rcar_vin driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef __RCAR_VIN__ @@ -53,11 +49,13 @@ enum rvin_csi_id { /** * STOPPED - No operation in progress + * STARTING - Capture starting up * RUNNING - Operation in progress have buffers * STOPPING - Stopping operation */ enum rvin_dma_state { STOPPED = 0, + STARTING, RUNNING, STOPPING, }; @@ -73,16 +71,22 @@ struct rvin_video_format { }; /** - * struct rvin_graph_entity - Video endpoint from async framework + * struct rvin_parallel_entity - Parallel video input endpoint descriptor * @asd: sub-device descriptor for async framework * @subdev: subdevice matched using async framework + * @mbus_type: media bus type + * @mbus_flags: media bus configuration flags * @source_pad: source pad of remote subdevice * @sink_pad: sink pad of remote subdevice + * */ -struct rvin_graph_entity { +struct rvin_parallel_entity { struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; + enum v4l2_mbus_type mbus_type; + unsigned int mbus_flags; + unsigned int source_pad; unsigned int sink_pad; }; @@ -146,7 +150,8 @@ struct rvin_info { * @v4l2_dev: V4L2 device * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @digital: entity in the DT for local digital subdevice + * + * @parallel: parallel input subdevice descriptor * * @group: Gen3 CSI group * @id: Gen3 group id for this VIN @@ -164,7 +169,8 @@ struct rvin_info { * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state * - * @mbus_cfg: media bus configuration from DT + * @is_csi: flag to mark the VIN as using a CSI-2 subdevice + * * @mbus_code: media bus format code * @format: active V4L2 pixel format * @@ -182,7 +188,8 @@ struct rvin_dev { struct v4l2_device v4l2_dev; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity *digital; + + struct rvin_parallel_entity *parallel; struct rvin_group *group; unsigned int id; @@ -199,7 +206,8 @@ struct rvin_dev { unsigned int sequence; enum rvin_dma_state state; - struct v4l2_mbus_config mbus_cfg; + bool is_csi; + u32 mbus_code; struct v4l2_pix_format format; @@ -209,7 +217,7 @@ struct rvin_dev { v4l2_std_id std; }; -#define vin_to_source(vin) ((vin)->digital->subdev) +#define vin_to_source(vin) ((vin)->parallel->subdev) /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) @@ -225,8 +233,7 @@ struct rvin_dev { * * @lock: protects the count, notifier, vin and csi members * @count: number of enabled VIN instances found in DT - * @notifier: pointer to the notifier of a VIN which handles the - * groups async sub-devices. + * @notifier: group notifier for CSI-2 async subdevices * @vin: VIN instances which are part of the group * @csi: array of pairs of fwnode and subdev pointers * to all CSI-2 subdevices. @@ -238,7 +245,7 @@ struct rvin_group { struct mutex lock; unsigned int count; - struct v4l2_async_notifier *notifier; + struct v4l2_async_notifier notifier; struct rvin_dev *vin[RCAR_VIN_NUM]; struct { diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index dc7e280c91b4..81413ab52475 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * R-Car Gen3 Digital Radio Interface (DRIF) driver * * Copyright (C) 2017 Renesas Electronics Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -1499,5 +1495,5 @@ module_platform_driver(rcar_drif_driver); MODULE_DESCRIPTION("Renesas R-Car Gen3 DRIF driver"); MODULE_ALIAS("platform:" RCAR_DRIF_DRV_NAME); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>"); diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index b13dec3081e5..2a15b7cca338 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Renesas R-Car Fine Display Processor * @@ -8,11 +9,6 @@ * * This code is developed and inspired from the vim2m, rcar_jpu, * m2m-deinterlace, and vsp1 drivers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the - * License, or (at your option) any later version */ #include <linux/clk.h> diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 8b44a849ab41..e5c882423f57 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Author: Mikhail Ulyanov * Copyright (C) 2014-2015 Cogent Embedded, Inc. <source@cogentembedded.com> @@ -11,10 +12,6 @@ * 1) Rotation * 2) Cropping * 3) V4L2_CID_JPEG_ACTIVE_MARKER - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <asm/unaligned.h> @@ -198,7 +195,6 @@ * @vfd_decoder: video device node for decoder mem2mem mode * @m2m_dev: v4l2 mem2mem device data * @curr: pointer to current context - * @irq_queue: interrupt handler waitqueue * @regs: JPEG IP registers mapping * @irq: JPEG IP irq * @clk: JPEG IP clock @@ -213,7 +209,6 @@ struct jpu { struct video_device vfd_decoder; struct v4l2_m2m_dev *m2m_dev; struct jpu_ctx *curr; - wait_queue_head_t irq_queue; void __iomem *regs; unsigned int irq; @@ -1494,24 +1489,8 @@ static void jpu_device_run(void *priv) spin_unlock_irqrestore(&ctx->jpu->lock, flags); } -static int jpu_job_ready(void *priv) -{ - return 1; -} - -static void jpu_job_abort(void *priv) -{ - struct jpu_ctx *ctx = priv; - - if (!wait_event_timeout(ctx->jpu->irq_queue, !ctx->jpu->curr, - msecs_to_jiffies(JPU_JOB_TIMEOUT))) - jpu_cleanup(ctx, true); -} - static const struct v4l2_m2m_ops jpu_m2m_ops = { .device_run = jpu_device_run, - .job_ready = jpu_job_ready, - .job_abort = jpu_job_abort, }; /* @@ -1592,9 +1571,6 @@ static irqreturn_t jpu_irq_handler(int irq, void *dev_id) v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx); - /* ...wakeup abort routine if needed */ - wake_up(&jpu->irq_queue); - return IRQ_HANDLED; handled: @@ -1628,7 +1604,6 @@ static int jpu_probe(struct platform_device *pdev) if (!jpu) return -ENOMEM; - init_waitqueue_head(&jpu->irq_queue); mutex_init(&jpu->mutex); spin_lock_init(&jpu->lock); jpu->dev = &pdev->dev; diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index fe4fe944592d..ad782901cd7a 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -254,6 +254,18 @@ static const struct ceu_fmt ceu_fmt_list[] = { .fourcc = V4L2_PIX_FMT_YUYV, .bpp = 16, }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .bpp = 16, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .bpp = 16, + }, }; static const struct ceu_fmt *get_ceu_fmt_from_fourcc(unsigned int fourcc) @@ -272,6 +284,9 @@ static bool ceu_fmt_mplane(struct v4l2_pix_format_mplane *pix) { switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: return false; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: @@ -380,7 +395,9 @@ static int ceu_hw_config(struct ceu_device *ceudev) switch (pix->pixelformat) { /* Data fetch sync mode */ case V4L2_PIX_FMT_YUYV: - /* TODO: handle YUYV permutations through DTARY bits. */ + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: camcr = CEU_CAMCR_JPEG; cdocr |= CEU_CDOCR_NO_DOWSAMPLE; cfzsr = (pix->height << 16) | pix->width; @@ -568,6 +585,9 @@ static void ceu_calc_plane_sizes(struct ceu_device *ceudev, switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: pix->num_planes = 1; bpl = pix->width * ceu_fmt->bpp / 8; szimage = pix->height * bpl; @@ -762,42 +782,59 @@ static const struct vb2_ops ceu_vb2_ops = { /* --- CEU image formats handling --- */ /* - * ceu_try_fmt() - test format on CEU and sensor + * __ceu_try_fmt() - test format on CEU and sensor * @ceudev: The CEU device. * @v4l2_fmt: format to test. + * @sd_mbus_code: the media bus code accepted by the subdevice; output param. * * Returns 0 for success, < 0 for errors. */ -static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +static int __ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt, + u32 *sd_mbus_code) { struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; struct v4l2_subdev_pad_config pad_cfg; const struct ceu_fmt *ceu_fmt; + u32 mbus_code_old; + u32 mbus_code; int ret; /* * Set format on sensor sub device: bus format used to produce memory - * format is selected at initialization time. + * format is selected depending on YUV component ordering or + * at initialization time. */ struct v4l2_subdev_format sd_format = { .which = V4L2_SUBDEV_FORMAT_TRY, - .format = { - .code = ceu_sd->mbus_fmt.mbus_code, - }, }; + mbus_code_old = ceu_sd->mbus_fmt.mbus_code; + switch (pix->pixelformat) { case V4L2_PIX_FMT_YUYV: + mbus_code = MEDIA_BUS_FMT_YUYV8_2X8; + break; + case V4L2_PIX_FMT_UYVY: + mbus_code = MEDIA_BUS_FMT_UYVY8_2X8; + break; + case V4L2_PIX_FMT_YVYU: + mbus_code = MEDIA_BUS_FMT_YVYU8_2X8; + break; + case V4L2_PIX_FMT_VYUY: + mbus_code = MEDIA_BUS_FMT_VYUY8_2X8; + break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: + mbus_code = ceu_sd->mbus_fmt.mbus_code; break; default: pix->pixelformat = V4L2_PIX_FMT_NV16; + mbus_code = ceu_sd->mbus_fmt.mbus_code; break; } @@ -808,9 +845,25 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) &pix->height, 4, CEU_MAX_HEIGHT, 4, 0); v4l2_fill_mbus_format_mplane(&sd_format.format, pix); + + /* + * Try with the mbus_code matching YUYV components ordering first, + * if that one fails, fallback to default selected at initialization + * time. + */ + sd_format.format.code = mbus_code; ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format); - if (ret) - return ret; + if (ret) { + if (ret == -EINVAL) { + /* fallback */ + sd_format.format.code = mbus_code_old; + ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, + &pad_cfg, &sd_format); + } + + if (ret) + return ret; + } /* Apply size returned by sensor as the CEU can't scale. */ v4l2_fill_pix_format_mplane(pix, &sd_format.format); @@ -818,16 +871,30 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) /* Calculate per-plane sizes based on image format. */ ceu_calc_plane_sizes(ceudev, ceu_fmt, pix); + /* Report to caller the configured mbus format. */ + *sd_mbus_code = sd_format.format.code; + return 0; } /* + * ceu_try_fmt() - Wrapper for __ceu_try_fmt; discard configured mbus_fmt + */ +static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) +{ + u32 mbus_code; + + return __ceu_try_fmt(ceudev, v4l2_fmt, &mbus_code); +} + +/* * ceu_set_fmt() - Apply the supplied format to both sensor and CEU */ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) { struct ceu_subdev *ceu_sd = ceudev->sd; struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; + u32 mbus_code; int ret; /* @@ -836,15 +903,13 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) */ struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .format = { - .code = ceu_sd->mbus_fmt.mbus_code, - }, }; - ret = ceu_try_fmt(ceudev, v4l2_fmt); + ret = __ceu_try_fmt(ceudev, v4l2_fmt, &mbus_code); if (ret) return ret; + format.format.code = mbus_code; v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp); ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format); if (ret) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index fa1ba98c96dc..356821c2dacf 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -64,43 +64,44 @@ static void rga_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } +static void rga_buf_return_buffers(struct vb2_queue *q, + enum vb2_buffer_state state) +{ + struct rga_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!vbuf) + break; + v4l2_m2m_buf_done(vbuf, state); + } +} + static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) { struct rga_ctx *ctx = vb2_get_drv_priv(q); struct rockchip_rga *rga = ctx->rga; - int ret, i; + int ret; ret = pm_runtime_get_sync(rga->dev); - - if (!ret) - return 0; - - for (i = 0; i < q->num_buffers; ++i) { - if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) { - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]), - VB2_BUF_STATE_QUEUED); - } + if (ret < 0) { + rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED); + return ret; } - return ret; + return 0; } static void rga_buf_stop_streaming(struct vb2_queue *q) { struct rga_ctx *ctx = vb2_get_drv_priv(q); struct rockchip_rga *rga = ctx->rga; - struct vb2_v4l2_buffer *vbuf; - - for (;;) { - if (V4L2_TYPE_IS_OUTPUT(q->type)) - vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - else - vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!vbuf) - break; - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - } + rga_buf_return_buffers(q, VB2_BUF_STATE_ERROR); pm_runtime_put(rga->dev); } diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index d508a8ba6f89..ab5a6f95044a 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -39,18 +39,6 @@ static int debug; module_param(debug, int, 0644); -static void job_abort(void *prv) -{ - struct rga_ctx *ctx = prv; - struct rockchip_rga *rga = ctx->rga; - - if (!rga->curr) /* No job currently running */ - return; - - wait_event_timeout(rga->irq_queue, - !rga->curr, msecs_to_jiffies(RGA_TIMEOUT)); -} - static void device_run(void *prv) { struct rga_ctx *ctx = prv; @@ -104,8 +92,6 @@ static irqreturn_t rga_isr(int irq, void *prv) v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx); - - wake_up(&rga->irq_queue); } return IRQ_HANDLED; @@ -113,7 +99,6 @@ static irqreturn_t rga_isr(int irq, void *prv) static struct v4l2_m2m_ops rga_m2m_ops = { .device_run = device_run, - .job_abort = job_abort, }; static int @@ -838,8 +823,6 @@ static int rga_probe(struct platform_device *pdev) spin_lock_init(&rga->ctrl_lock); mutex_init(&rga->mutex); - init_waitqueue_head(&rga->irq_queue); - ret = rga_parse_dt(rga); if (ret) dev_err(&pdev->dev, "Unable to parse OF data\n"); @@ -882,7 +865,6 @@ static int rga_probe(struct platform_device *pdev) vfd->v4l2_dev = &rga->v4l2_dev; video_set_drvdata(vfd, rga); - snprintf(vfd->name, sizeof(vfd->name), "%s", rga_videodev.name); rga->vfd = vfd; platform_set_drvdata(pdev, rga); @@ -943,7 +925,7 @@ static int rga_remove(struct platform_device *pdev) { struct rockchip_rga *rga = platform_get_drvdata(pdev); - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, &rga->cmdbuf_virt, + dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt, rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE); free_pages((unsigned long)rga->src_mmu_pages, 3); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 5d43e7ea88af..72d8a159fa7b 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -86,8 +86,6 @@ struct rockchip_rga { /* ctrl parm lock */ spinlock_t ctrl_lock; - wait_queue_head_t irq_queue; - struct rga_ctx *curr; dma_addr_t cmdbuf_phy; void *cmdbuf_virt; diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 9ab8e7ee2e1e..c02dce8b4c6c 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -117,6 +117,8 @@ static int sensor_set_power(struct camif_dev *camif, int on) if (camif->sensor.power_count == !on) err = v4l2_subdev_call(sensor->sd, core, s_power, on); + if (err == -ENOIOCTLCMD) + err = 0; if (!err) sensor->power_count += on ? 1 : -1; @@ -599,7 +601,7 @@ static __poll_t s3c_camif_poll(struct file *file, mutex_lock(&camif->lock); if (vp->owner && vp->owner != file->private_data) - ret = -EBUSY; + ret = EPOLLERR; else ret = vb2_poll(&vp->vb_queue, file, wait); diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 66aa8cf1d048..e901201b6fcc 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -142,6 +142,8 @@ static const struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, .buf_queue = g2d_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -481,19 +483,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c return 0; } -static void job_abort(void *prv) -{ - struct g2d_ctx *ctx = prv; - struct g2d_dev *dev = ctx->dev; - - if (dev->curr == NULL) /* No job currently running */ - return; - - wait_event_timeout(dev->irq_queue, - dev->curr == NULL, - msecs_to_jiffies(G2D_TIMEOUT)); -} - static void device_run(void *prv) { struct g2d_ctx *ctx = prv; @@ -563,7 +552,6 @@ static irqreturn_t g2d_isr(int irq, void *prv) v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); dev->curr = NULL; - wake_up(&dev->irq_queue); return IRQ_HANDLED; } @@ -613,7 +601,6 @@ static const struct video_device g2d_videodev = { static const struct v4l2_m2m_ops g2d_m2m_ops = { .device_run = device_run, - .job_abort = job_abort, }; static const struct of_device_id exynos_g2d_match[]; @@ -633,7 +620,6 @@ static int g2d_probe(struct platform_device *pdev) spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); - init_waitqueue_head(&dev->irq_queue); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -702,7 +688,6 @@ static int g2d_probe(struct platform_device *pdev) goto rel_vdev; } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name); dev->vfd = vfd; v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n", vfd->num); diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index dd812b557e87..9ffb458a1b93 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -31,7 +31,6 @@ struct g2d_dev { struct g2d_ctx *curr; struct g2d_variant *variant; int irq; - wait_queue_head_t irq_queue; }; struct g2d_frame { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 79b63da27f53..04fd2e0493c0 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -2467,26 +2467,19 @@ static int s5p_jpeg_job_ready(void *priv) return 1; } -static void s5p_jpeg_job_abort(void *priv) -{ -} - static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = { .device_run = s5p_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; static struct v4l2_m2m_ops exynos3250_jpeg_m2m_ops = { .device_run = exynos3250_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { .device_run = exynos4_jpeg_device_run, .job_ready = s5p_jpeg_job_ready, - .job_abort = s5p_jpeg_job_abort, }; /* diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index a80251ed3143..927a1235408d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -254,24 +254,24 @@ static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx) static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - struct s5p_mfc_buf *dst_buf, *src_buf; - size_t dec_y_addr; + struct s5p_mfc_buf *dst_buf, *src_buf; + u32 dec_y_addr; unsigned int frame_type; /* Make sure we actually have a new frame before continuing. */ frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) return; - dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev); + dec_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev); /* Copy timestamp / timecode from decoded src to dst and set appropriate flags. */ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) - == dec_y_addr) { - dst_buf->b->timecode = - src_buf->b->timecode; + u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); + + if (addr == dec_y_addr) { + dst_buf->b->timecode = src_buf->b->timecode; dst_buf->b->vb2_buf.timestamp = src_buf->b->vb2_buf.timestamp; dst_buf->b->flags &= @@ -307,10 +307,10 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf *dst_buf; - size_t dspl_y_addr; + u32 dspl_y_addr; unsigned int frame_type; - dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); + dspl_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); if (IS_MFCV6_PLUS(dev)) frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx); @@ -329,9 +329,10 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) /* The MFC returns address of the buffer, now we have to * check which videobuf does it correspond to */ list_for_each_entry(dst_buf, &ctx->dst_queue, list) { + u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); + /* Check if this is the buffer we're looking for */ - if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0) - == dspl_y_addr) { + if (addr == dspl_y_addr) { list_del(&dst_buf->list); ctx->dst_queue_cnt--; dst_buf->b->sequence = ctx->sequence; @@ -1449,8 +1450,7 @@ static int s5p_mfc_remove(struct platform_device *pdev) static int s5p_mfc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); + struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); int ret; if (m_dev->num_inst == 0) @@ -1484,8 +1484,7 @@ static int s5p_mfc_suspend(struct device *dev) static int s5p_mfc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev); + struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); if (m_dev->num_inst == 0) return 0; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 570f391f2cfd..3ad4f5073002 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -692,12 +692,12 @@ static struct mfc_control controls[] = { .default_value = 10, }, { - .id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE, - .type = V4L2_CTRL_TYPE_INTEGER, - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, + .id = V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + .type = V4L2_CTRL_TYPE_MENU, + .minimum = V4L2_MPEG_VIDEO_VP8_PROFILE_0, + .maximum = V4L2_MPEG_VIDEO_VP8_PROFILE_3, + .default_value = V4L2_MPEG_VIDEO_VP8_PROFILE_0, + .menu_skip_mask = 0, }, { .id = V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP, @@ -2057,7 +2057,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: p->codec.vp8.rc_p_frame_qp = ctrl->val; break; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: p->codec.vp8.profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: @@ -2711,4 +2711,3 @@ void s5p_mfc_enc_init(struct s5p_mfc_ctx *ctx) f.fmt.pix_mp.pixelformat = DEF_DST_FMT_ENC; ctx->dst_fmt = find_format(&f, MFC_FMT_ENC); } - diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 1a0cde017fdf..1d274c64de09 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * sh-mobile VEU mem2mem driver * * Copyright (C) 2012 Renesas Electronics Corporation * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> * Copyright (C) 2008 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the version 2 of the GNU General Public License as - * published by the Free Software Foundation */ #include <linux/err.h> diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 4dccf29e9d78..6135e13e24d4 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SuperH Video Output Unit (VOU) driver * * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/dma-mapping.h> diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 9897213f2618..0a2c0daaffef 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * V4L2 Driver for SuperH Mobile CEU interface * @@ -7,11 +8,6 @@ * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/init.h> diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index ce00e90d4e3c..6745a6e3f464 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic Platform Camera Driver * * Copyright (C) 2008 Magnus Damm * Based on mt9m001 driver, * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/init.h> diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 232d508c5b66..0b42acd4e3a6 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -339,22 +339,6 @@ static void register_decoders(struct delta_dev *delta) } } -static void delta_lock(void *priv) -{ - struct delta_ctx *ctx = priv; - struct delta_dev *delta = ctx->dev; - - mutex_lock(&delta->lock); -} - -static void delta_unlock(void *priv) -{ - struct delta_ctx *ctx = priv; - struct delta_dev *delta = ctx->dev; - - mutex_unlock(&delta->lock); -} - static int delta_open_decoder(struct delta_ctx *ctx, u32 streamformat, u32 pixelformat, const struct delta_dec **pdec) { @@ -1099,8 +1083,6 @@ static const struct v4l2_m2m_ops delta_m2m_ops = { .device_run = delta_device_run, .job_ready = delta_job_ready, .job_abort = delta_job_abort, - .lock = delta_lock, - .unlock = delta_unlock, }; /* diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 15080cb00fa7..5a807c7c5e79 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -6,6 +6,7 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <media/v4l2-event.h> diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 2e1933d872ee..721564176d8c 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -22,7 +22,9 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/videodev2.h> @@ -84,19 +86,14 @@ enum state { STOPPED = 0, + WAIT_FOR_BUFFER, RUNNING, - STOPPING, }; #define MIN_WIDTH 16U -#define MAX_WIDTH 2048U +#define MAX_WIDTH 2592U #define MIN_HEIGHT 16U -#define MAX_HEIGHT 2048U - -#define MIN_JPEG_WIDTH 16U -#define MAX_JPEG_WIDTH 2592U -#define MIN_JPEG_HEIGHT 16U -#define MAX_JPEG_HEIGHT 2592U +#define MAX_HEIGHT 2592U #define TIMEOUT_MS 1000 @@ -194,7 +191,7 @@ static inline void reg_clear(void __iomem *base, u32 reg, u32 mask) reg_write(base, reg, reg_read(base, reg) & ~mask); } -static int dcmi_start_capture(struct stm32_dcmi *dcmi); +static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf); static void dcmi_buffer_done(struct stm32_dcmi *dcmi, struct dcmi_buf *buf, @@ -206,6 +203,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi, if (!buf) return; + list_del_init(&buf->list); + vbuf = &buf->vb; vbuf->sequence = dcmi->sequence++; @@ -223,6 +222,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi, static int dcmi_restart_capture(struct stm32_dcmi *dcmi) { + struct dcmi_buf *buf; + spin_lock_irq(&dcmi->irqlock); if (dcmi->state != RUNNING) { @@ -232,34 +233,30 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi) /* Restart a new DMA transfer with next buffer */ if (list_empty(&dcmi->buffers)) { - dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture buffer\n", - __func__); - dcmi->errors_count++; - dcmi->active = NULL; - + dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer queueing\n"); + dcmi->state = WAIT_FOR_BUFFER; spin_unlock_irq(&dcmi->irqlock); - return -EINVAL; + return 0; } - - dcmi->active = list_entry(dcmi->buffers.next, - struct dcmi_buf, list); - list_del_init(&dcmi->active->list); + buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); + dcmi->active = buf; spin_unlock_irq(&dcmi->irqlock); - return dcmi_start_capture(dcmi); + return dcmi_start_capture(dcmi, buf); } static void dcmi_dma_callback(void *param) { struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param; - struct dma_chan *chan = dcmi->dma_chan; struct dma_tx_state state; enum dma_status status; struct dcmi_buf *buf = dcmi->active; + spin_lock_irq(&dcmi->irqlock); + /* Check DMA status */ - status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); switch (status) { case DMA_IN_PROGRESS: @@ -270,6 +267,9 @@ static void dcmi_dma_callback(void *param) break; case DMA_ERROR: dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__); + + /* Return buffer to V4L2 in error state */ + dcmi_buffer_done(dcmi, buf, 0, -EIO); break; case DMA_COMPLETE: dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__); @@ -277,15 +277,19 @@ static void dcmi_dma_callback(void *param) /* Return buffer to V4L2 */ dcmi_buffer_done(dcmi, buf, buf->size, 0); + spin_unlock_irq(&dcmi->irqlock); + /* Restart capture */ if (dcmi_restart_capture(dcmi)) dev_err(dcmi->dev, "%s: Cannot restart capture on DMA complete\n", __func__); - break; + return; default: dev_err(dcmi->dev, "%s: Received unknown status\n", __func__); break; } + + spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_dma(struct stm32_dcmi *dcmi, @@ -313,10 +317,11 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, /* Prepare a DMA transaction */ desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, buf->size, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); if (!desc) { - dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer size %zu\n", - __func__, buf->size); + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n", + __func__, &buf->paddr, buf->size); return -EINVAL; } @@ -336,10 +341,9 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, return 0; } -static int dcmi_start_capture(struct stm32_dcmi *dcmi) +static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf) { int ret; - struct dcmi_buf *buf = dcmi->active; if (!buf) return -EINVAL; @@ -382,7 +386,6 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) { struct dma_tx_state state; enum dma_status status; - struct dma_chan *chan = dcmi->dma_chan; struct dcmi_buf *buf = dcmi->active; if (!buf) @@ -390,8 +393,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) /* * Because of variable JPEG buffer size sent by sensor, - * DMA transfer never completes due to transfer size - * never reached. + * DMA transfer never completes due to transfer size never reached. * In order to ensure that all the JPEG data are transferred * in active buffer memory, DMA is drained. * Then DMA tx status gives the amount of data transferred @@ -400,10 +402,10 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi) */ /* Drain DMA */ - dmaengine_synchronize(chan); + dmaengine_synchronize(dcmi->dma_chan); /* Get DMA residue to get JPEG size */ - status = dmaengine_tx_status(chan, dcmi->dma_cookie, &state); + status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, &state); if (status != DMA_ERROR && state.residue < buf->size) { /* Return JPEG buffer to V4L2 with received JPEG buffer size */ dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0); @@ -430,18 +432,6 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg) spin_lock_irq(&dcmi->irqlock); - /* Stop capture is required */ - if (dcmi->state == STOPPING) { - reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); - - dcmi->state = STOPPED; - - complete(&dcmi->complete); - - spin_unlock_irq(&dcmi->irqlock); - return IRQ_HANDLED; - } - if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) { dcmi->errors_count++; if (dcmi->misr & IT_OVR) @@ -495,8 +485,6 @@ static int dcmi_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; - dcmi->active = NULL; - dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n", *nbuffers, size); @@ -554,21 +542,24 @@ static void dcmi_buf_queue(struct vb2_buffer *vb) spin_lock_irq(&dcmi->irqlock); - if (dcmi->state == RUNNING && !dcmi->active) { + /* Enqueue to video buffers list */ + list_add_tail(&buf->list, &dcmi->buffers); + + if (dcmi->state == WAIT_FOR_BUFFER) { + dcmi->state = RUNNING; dcmi->active = buf; dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n", buf->vb.vb2_buf.index); spin_unlock_irq(&dcmi->irqlock); - if (dcmi_start_capture(dcmi)) + if (dcmi_start_capture(dcmi, buf)) dev_err(dcmi->dev, "%s: Cannot restart capture on overflow or error\n", __func__); - } else { - /* Enqueue to video buffers list */ - list_add_tail(&buf->list, &dcmi->buffers); - spin_unlock_irq(&dcmi->irqlock); + return; } + + spin_unlock_irq(&dcmi->irqlock); } static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -578,9 +569,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) u32 val = 0; int ret; - ret = clk_enable(dcmi->mclk); + ret = pm_runtime_get_sync(dcmi->dev); if (ret) { - dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n", + dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync\n", __func__); goto err_release_buffers; } @@ -590,7 +581,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret && ret != -ENOIOCTLCMD) { dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", __func__); - goto err_disable_clock; + goto err_pm_put; } spin_lock_irq(&dcmi->irqlock); @@ -636,13 +627,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) /* Enable dcmi */ reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); - dcmi->state = RUNNING; - dcmi->sequence = 0; dcmi->errors_count = 0; dcmi->overrun_count = 0; dcmi->buffers_count = 0; - dcmi->active = NULL; /* * Start transfer if at least one buffer has been queued, @@ -650,17 +638,20 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) */ if (list_empty(&dcmi->buffers)) { dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer queueing\n"); + dcmi->state = WAIT_FOR_BUFFER; spin_unlock_irq(&dcmi->irqlock); return 0; } - dcmi->active = list_entry(dcmi->buffers.next, struct dcmi_buf, list); - list_del_init(&dcmi->active->list); + buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list); + dcmi->active = buf; + + dcmi->state = RUNNING; dev_dbg(dcmi->dev, "Start streaming, starting capture\n"); spin_unlock_irq(&dcmi->irqlock); - ret = dcmi_start_capture(dcmi); + ret = dcmi_start_capture(dcmi, buf); if (ret) { dev_err(dcmi->dev, "%s: Start streaming failed, cannot start capture\n", __func__); @@ -675,8 +666,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) err_subdev_streamoff: v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); -err_disable_clock: - clk_disable(dcmi->mclk); +err_pm_put: + pm_runtime_put(dcmi->dev); err_release_buffers: spin_lock_irq(&dcmi->irqlock); @@ -684,15 +675,11 @@ err_release_buffers: * Return all buffers to vb2 in QUEUED state. * This will give ownership back to userspace */ - if (dcmi->active) { - buf = dcmi->active; - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - dcmi->active = NULL; - } list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { list_del_init(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); } + dcmi->active = NULL; spin_unlock_irq(&dcmi->irqlock); return ret; @@ -702,8 +689,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) { struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq); struct dcmi_buf *buf, *node; - unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS); - long timeout; int ret; /* Disable stream on the sub device */ @@ -713,13 +698,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) __func__, ret); spin_lock_irq(&dcmi->irqlock); - dcmi->state = STOPPING; - spin_unlock_irq(&dcmi->irqlock); - - timeout = wait_for_completion_interruptible_timeout(&dcmi->complete, - time_ms); - - spin_lock_irq(&dcmi->irqlock); /* Disable interruptions */ reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); @@ -727,29 +705,21 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) /* Disable DCMI */ reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE); - if (!timeout) { - dev_err(dcmi->dev, "%s: Timeout during stop streaming\n", - __func__); - dcmi->state = STOPPED; - } - /* Return all queued buffers to vb2 in ERROR state */ - if (dcmi->active) { - buf = dcmi->active; - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - dcmi->active = NULL; - } list_for_each_entry_safe(buf, node, &dcmi->buffers, list) { list_del_init(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); } + dcmi->active = NULL; + dcmi->state = STOPPED; + spin_unlock_irq(&dcmi->irqlock); /* Stop all pending DMA operations */ dmaengine_terminate_all(dcmi->dma_chan); - clk_disable(dcmi->mclk); + pm_runtime_put(dcmi->dev); if (dcmi->errors_count) dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", @@ -843,14 +813,8 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f, } /* Limit to hardware capabilities */ - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { - pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH); - pix->height = - clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT); - } else { - pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); - pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); - } + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH); + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT); /* No crop if JPEG is requested */ do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG); @@ -1605,23 +1569,20 @@ static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) struct device_node *ep = NULL; struct device_node *remote; - while (1) { - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - return -EINVAL; + ep = of_graph_get_next_endpoint(node, ep); + if (!ep) + return -EINVAL; - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - of_node_put(ep); - return -EINVAL; - } + remote = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (!remote) + return -EINVAL; - /* Remote node to connect */ - dcmi->entity.node = remote; - dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); - return 0; - } + /* Remote node to connect */ + dcmi->entity.node = remote; + dcmi->entity.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + dcmi->entity.asd.match.fwnode = of_fwnode_handle(remote); + return 0; } static int dcmi_graph_init(struct stm32_dcmi *dcmi) @@ -1696,23 +1657,20 @@ static int dcmi_probe(struct platform_device *pdev) } ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &ep); + of_node_put(np); if (ret) { dev_err(&pdev->dev, "Could not parse the endpoint\n"); - of_node_put(np); return -ENODEV; } if (ep.bus_type == V4L2_MBUS_CSI2) { dev_err(&pdev->dev, "CSI bus not supported\n"); - of_node_put(np); return -ENODEV; } dcmi->bus.flags = ep.bus.parallel.flags; dcmi->bus.bus_width = ep.bus.parallel.bus_width; dcmi->bus.data_shift = ep.bus.parallel.data_shift; - of_node_put(np); - irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "Could not get irq\n"); @@ -1751,12 +1709,6 @@ static int dcmi_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - ret = clk_prepare(mclk); - if (ret) { - dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk); - goto err_dma_release; - } - spin_lock_init(&dcmi->irqlock); mutex_init(&dcmi->lock); init_completion(&dcmi->complete); @@ -1772,7 +1724,7 @@ static int dcmi_probe(struct platform_device *pdev) /* Initialize the top-level structure */ ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); if (ret) - goto err_clk_unprepare; + goto err_dma_release; dcmi->vdev = video_device_alloc(); if (!dcmi->vdev) { @@ -1832,14 +1784,15 @@ static int dcmi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probe done\n"); platform_set_drvdata(pdev, dcmi); + + pm_runtime_enable(&pdev->dev); + return 0; err_device_release: video_device_release(dcmi->vdev); err_device_unregister: v4l2_device_unregister(&dcmi->v4l2_dev); -err_clk_unprepare: - clk_unprepare(dcmi->mclk); err_dma_release: dma_release_channel(dcmi->dma_chan); @@ -1850,20 +1803,72 @@ static int dcmi_remove(struct platform_device *pdev) { struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + v4l2_async_notifier_unregister(&dcmi->notifier); v4l2_device_unregister(&dcmi->v4l2_dev); - clk_unprepare(dcmi->mclk); + dma_release_channel(dcmi->dma_chan); return 0; } +static __maybe_unused int dcmi_runtime_suspend(struct device *dev) +{ + struct stm32_dcmi *dcmi = dev_get_drvdata(dev); + + clk_disable_unprepare(dcmi->mclk); + + return 0; +} + +static __maybe_unused int dcmi_runtime_resume(struct device *dev) +{ + struct stm32_dcmi *dcmi = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dcmi->mclk); + if (ret) + dev_err(dev, "%s: Failed to prepare_enable clock\n", __func__); + + return ret; +} + +static __maybe_unused int dcmi_suspend(struct device *dev) +{ + /* disable clock */ + pm_runtime_force_suspend(dev); + + /* change pinctrl state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static __maybe_unused int dcmi_resume(struct device *dev) +{ + /* restore pinctl default state */ + pinctrl_pm_select_default_state(dev); + + /* clock enable */ + pm_runtime_force_resume(dev); + + return 0; +} + +static const struct dev_pm_ops dcmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) + SET_RUNTIME_PM_OPS(dcmi_runtime_suspend, + dcmi_runtime_resume, NULL) +}; + static struct platform_driver stm32_dcmi_driver = { .probe = dcmi_probe, .remove = dcmi_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), + .pm = &dcmi_pm_ops, }, }; diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index e395aa85c8ad..d70871d0ad2d 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -953,23 +953,6 @@ static void job_abort(void *priv) ctx->aborting = 1; } -/* - * Lock access to the device - */ -static void vpe_lock(void *priv) -{ - struct vpe_ctx *ctx = priv; - struct vpe_dev *dev = ctx->dev; - mutex_lock(&dev->dev_mutex); -} - -static void vpe_unlock(void *priv) -{ - struct vpe_ctx *ctx = priv; - struct vpe_dev *dev = ctx->dev; - mutex_unlock(&dev->dev_mutex); -} - static void vpe_dump_regs(struct vpe_dev *dev) { #define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) @@ -2434,8 +2417,6 @@ static const struct v4l2_m2m_ops m2m_ops = { .device_run = device_run, .job_ready = job_ready, .job_abort = job_abort, - .lock = vpe_lock, - .unlock = vpe_unlock, }; static int vpe_runtime_get(struct platform_device *pdev) @@ -2485,7 +2466,6 @@ static void vpe_fw_cb(struct platform_device *pdev) } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", vfd->num); } diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig new file mode 100644 index 000000000000..2503bcb1529f --- /dev/null +++ b/drivers/media/platform/vicodec/Kconfig @@ -0,0 +1,13 @@ +config VIDEO_VICODEC + tristate "Virtual Codec Driver" + depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + default n + help + Driver for a Virtual Codec + + This driver can be compared to the vim2m driver for emulating + a video device node that exposes an emulated hardware codec. + + When in doubt, say N. diff --git a/drivers/media/platform/vicodec/Makefile b/drivers/media/platform/vicodec/Makefile new file mode 100644 index 000000000000..197229428953 --- /dev/null +++ b/drivers/media/platform/vicodec/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +vicodec-objs := vicodec-core.o vicodec-codec.o + +obj-$(CONFIG_VIDEO_VICODEC) += vicodec.o diff --git a/drivers/media/platform/vicodec/vicodec-codec.c b/drivers/media/platform/vicodec/vicodec-codec.c new file mode 100644 index 000000000000..2d047646f614 --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-codec.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2016 Tom aan de Wiel + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * 8x8 Fast Walsh Hadamard Transform in sequency order based on the paper: + * + * A Recursive Algorithm for Sequency-Ordered Fast Walsh Transforms, + * R.D. Brown, 1977 + */ + +#include <linux/string.h> +#include "vicodec-codec.h" + +#define ALL_ZEROS 15 +#define DEADZONE_WIDTH 20 + +static const uint8_t zigzag[64] = { + 0, + 1, 8, + 2, 9, 16, + 3, 10, 17, 24, + 4, 11, 18, 25, 32, + 5, 12, 19, 26, 33, 40, + 6, 13, 20, 27, 34, 41, 48, + 7, 14, 21, 28, 35, 42, 49, 56, + 15, 22, 29, 36, 43, 50, 57, + 23, 30, 37, 44, 51, 58, + 31, 38, 45, 52, 59, + 39, 46, 53, 60, + 47, 54, 61, + 55, 62, + 63, +}; + + +static int rlc(const s16 *in, __be16 *output, int blocktype) +{ + s16 block[8 * 8]; + s16 *wp = block; + int i = 0; + int x, y; + int ret = 0; + + /* read in block from framebuffer */ + int lastzero_run = 0; + int to_encode; + + for (y = 0; y < 8; y++) { + for (x = 0; x < 8; x++) { + *wp = in[x + y * 8]; + wp++; + } + } + + /* keep track of amount of trailing zeros */ + for (i = 63; i >= 0 && !block[zigzag[i]]; i--) + lastzero_run++; + + *output++ = (blocktype == PBLOCK ? htons(PFRAME_BIT) : 0); + ret++; + + to_encode = 8 * 8 - (lastzero_run > 14 ? lastzero_run : 0); + + i = 0; + while (i < to_encode) { + int cnt = 0; + int tmp; + + /* count leading zeros */ + while ((tmp = block[zigzag[i]]) == 0 && cnt < 14) { + cnt++; + i++; + if (i == to_encode) { + cnt--; + break; + } + } + /* 4 bits for run, 12 for coefficient (quantization by 4) */ + *output++ = htons((cnt | tmp << 4)); + i++; + ret++; + } + if (lastzero_run > 14) { + *output = htons(ALL_ZEROS | 0); + ret++; + } + + return ret; +} + +/* + * This function will worst-case increase rlc_in by 65*2 bytes: + * one s16 value for the header and 8 * 8 coefficients of type s16. + */ +static s16 derlc(const __be16 **rlc_in, s16 *dwht_out) +{ + /* header */ + const __be16 *input = *rlc_in; + s16 ret = ntohs(*input++); + int dec_count = 0; + s16 block[8 * 8 + 16]; + s16 *wp = block; + int i; + + /* + * Now de-compress, it expands one byte to up to 15 bytes + * (or fills the remainder of the 64 bytes with zeroes if it + * is the last byte to expand). + * + * So block has to be 8 * 8 + 16 bytes, the '+ 16' is to + * allow for overflow if the incoming data was malformed. + */ + while (dec_count < 8 * 8) { + s16 in = ntohs(*input++); + int length = in & 0xf; + int coeff = in >> 4; + + /* fill remainder with zeros */ + if (length == 15) { + for (i = 0; i < 64 - dec_count; i++) + *wp++ = 0; + break; + } + + for (i = 0; i < length; i++) + *wp++ = 0; + *wp++ = coeff; + dec_count += length + 1; + } + + wp = block; + + for (i = 0; i < 64; i++) { + int pos = zigzag[i]; + int y = pos / 8; + int x = pos % 8; + + dwht_out[x + y * 8] = *wp++; + } + *rlc_in = input; + return ret; +} + +static const int quant_table[] = { + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 3, 6, + 2, 2, 2, 2, 2, 3, 6, 6, + 2, 2, 2, 2, 3, 6, 6, 6, + 2, 2, 2, 3, 6, 6, 6, 6, + 2, 2, 3, 6, 6, 6, 6, 8, +}; + +static const int quant_table_p[] = { + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 6, + 3, 3, 3, 3, 3, 3, 6, 6, + 3, 3, 3, 3, 3, 6, 6, 9, + 3, 3, 3, 3, 6, 6, 9, 9, + 3, 3, 3, 6, 6, 9, 9, 10, +}; + +static void quantize_intra(s16 *coeff, s16 *de_coeff) +{ + const int *quant = quant_table; + int i, j; + + for (j = 0; j < 8; j++) { + for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) { + *coeff >>= *quant; + if (*coeff >= -DEADZONE_WIDTH && + *coeff <= DEADZONE_WIDTH) + *coeff = *de_coeff = 0; + else + *de_coeff = *coeff << *quant; + } + } +} + +static void dequantize_intra(s16 *coeff) +{ + const int *quant = quant_table; + int i, j; + + for (j = 0; j < 8; j++) + for (i = 0; i < 8; i++, quant++, coeff++) + *coeff <<= *quant; +} + +static void quantize_inter(s16 *coeff, s16 *de_coeff) +{ + const int *quant = quant_table_p; + int i, j; + + for (j = 0; j < 8; j++) { + for (i = 0; i < 8; i++, quant++, coeff++, de_coeff++) { + *coeff >>= *quant; + if (*coeff >= -DEADZONE_WIDTH && + *coeff <= DEADZONE_WIDTH) + *coeff = *de_coeff = 0; + else + *de_coeff = *coeff << *quant; + } + } +} + +static void dequantize_inter(s16 *coeff) +{ + const int *quant = quant_table_p; + int i, j; + + for (j = 0; j < 8; j++) + for (i = 0; i < 8; i++, quant++, coeff++) + *coeff <<= *quant; +} + +static void fwht(const u8 *block, s16 *output_block, unsigned int stride, + unsigned int input_step, bool intra) +{ + /* we'll need more than 8 bits for the transformed coefficients */ + s32 workspace1[8], workspace2[8]; + const u8 *tmp = block; + s16 *out = output_block; + int add = intra ? 256 : 0; + unsigned int i; + + /* stage 1 */ + stride *= input_step; + + for (i = 0; i < 8; i++, tmp += stride, out += 8) { + if (input_step == 1) { + workspace1[0] = tmp[0] + tmp[1] - add; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3] - add; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5] - add; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7] - add; + workspace1[7] = tmp[6] - tmp[7]; + } else { + workspace1[0] = tmp[0] + tmp[2] - add; + workspace1[1] = tmp[0] - tmp[2]; + + workspace1[2] = tmp[4] + tmp[6] - add; + workspace1[3] = tmp[4] - tmp[6]; + + workspace1[4] = tmp[8] + tmp[10] - add; + workspace1[5] = tmp[8] - tmp[10]; + + workspace1[6] = tmp[12] + tmp[14] - add; + workspace1[7] = tmp[12] - tmp[14]; + } + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1 * 8]; + workspace1[1] = out[0] - out[1 * 8]; + + workspace1[2] = out[2 * 8] + out[3 * 8]; + workspace1[3] = out[2 * 8] - out[3 * 8]; + + workspace1[4] = out[4 * 8] + out[5 * 8]; + workspace1[5] = out[4 * 8] - out[5 * 8]; + + workspace1[6] = out[6 * 8] + out[7 * 8]; + workspace1[7] = out[6 * 8] - out[7 * 8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + /* stage 3 */ + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + } +} + +/* + * Not the nicest way of doing it, but P-blocks get twice the range of + * that of the I-blocks. Therefore we need a type bigger than 8 bits. + * Furthermore values can be negative... This is just a version that + * works with 16 signed data + */ +static void fwht16(const s16 *block, s16 *output_block, int stride, int intra) +{ + /* we'll need more than 8 bits for the transformed coefficients */ + s32 workspace1[8], workspace2[8]; + const s16 *tmp = block; + s16 *out = output_block; + int i; + + for (i = 0; i < 8; i++, tmp += stride, out += 8) { + /* stage 1 */ + workspace1[0] = tmp[0] + tmp[1]; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3]; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5]; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7]; + workspace1[7] = tmp[6] - tmp[7]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1*8]; + workspace1[1] = out[0] - out[1*8]; + + workspace1[2] = out[2*8] + out[3*8]; + workspace1[3] = out[2*8] - out[3*8]; + + workspace1[4] = out[4*8] + out[5*8]; + workspace1[5] = out[4*8] - out[5*8]; + + workspace1[6] = out[6*8] + out[7*8]; + workspace1[7] = out[6*8] - out[7*8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0*8] = workspace2[0] + workspace2[4]; + out[1*8] = workspace2[0] - workspace2[4]; + out[2*8] = workspace2[1] - workspace2[5]; + out[3*8] = workspace2[1] + workspace2[5]; + out[4*8] = workspace2[2] + workspace2[6]; + out[5*8] = workspace2[2] - workspace2[6]; + out[6*8] = workspace2[3] - workspace2[7]; + out[7*8] = workspace2[3] + workspace2[7]; + } +} + +static void ifwht(const s16 *block, s16 *output_block, int intra) +{ + /* + * we'll need more than 8 bits for the transformed coefficients + * use native unit of cpu + */ + int workspace1[8], workspace2[8]; + int inter = intra ? 0 : 1; + const s16 *tmp = block; + s16 *out = output_block; + int i; + + for (i = 0; i < 8; i++, tmp += 8, out += 8) { + /* stage 1 */ + workspace1[0] = tmp[0] + tmp[1]; + workspace1[1] = tmp[0] - tmp[1]; + + workspace1[2] = tmp[2] + tmp[3]; + workspace1[3] = tmp[2] - tmp[3]; + + workspace1[4] = tmp[4] + tmp[5]; + workspace1[5] = tmp[4] - tmp[5]; + + workspace1[6] = tmp[6] + tmp[7]; + workspace1[7] = tmp[6] - tmp[7]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + out[0] = workspace2[0] + workspace2[4]; + out[1] = workspace2[0] - workspace2[4]; + out[2] = workspace2[1] - workspace2[5]; + out[3] = workspace2[1] + workspace2[5]; + out[4] = workspace2[2] + workspace2[6]; + out[5] = workspace2[2] - workspace2[6]; + out[6] = workspace2[3] - workspace2[7]; + out[7] = workspace2[3] + workspace2[7]; + } + + out = output_block; + + for (i = 0; i < 8; i++, out++) { + /* stage 1 */ + workspace1[0] = out[0] + out[1 * 8]; + workspace1[1] = out[0] - out[1 * 8]; + + workspace1[2] = out[2 * 8] + out[3 * 8]; + workspace1[3] = out[2 * 8] - out[3 * 8]; + + workspace1[4] = out[4 * 8] + out[5 * 8]; + workspace1[5] = out[4 * 8] - out[5 * 8]; + + workspace1[6] = out[6 * 8] + out[7 * 8]; + workspace1[7] = out[6 * 8] - out[7 * 8]; + + /* stage 2 */ + workspace2[0] = workspace1[0] + workspace1[2]; + workspace2[1] = workspace1[0] - workspace1[2]; + workspace2[2] = workspace1[1] - workspace1[3]; + workspace2[3] = workspace1[1] + workspace1[3]; + + workspace2[4] = workspace1[4] + workspace1[6]; + workspace2[5] = workspace1[4] - workspace1[6]; + workspace2[6] = workspace1[5] - workspace1[7]; + workspace2[7] = workspace1[5] + workspace1[7]; + + /* stage 3 */ + if (inter) { + int d; + + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + + for (d = 0; d < 8; d++) + out[8 * d] >>= 6; + } else { + int d; + + out[0 * 8] = workspace2[0] + workspace2[4]; + out[1 * 8] = workspace2[0] - workspace2[4]; + out[2 * 8] = workspace2[1] - workspace2[5]; + out[3 * 8] = workspace2[1] + workspace2[5]; + out[4 * 8] = workspace2[2] + workspace2[6]; + out[5 * 8] = workspace2[2] - workspace2[6]; + out[6 * 8] = workspace2[3] - workspace2[7]; + out[7 * 8] = workspace2[3] + workspace2[7]; + + for (d = 0; d < 8; d++) { + out[8 * d] >>= 6; + out[8 * d] += 128; + } + } + } +} + +static void fill_encoder_block(const u8 *input, s16 *dst, + unsigned int stride, unsigned int input_step) +{ + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++, input += input_step) + *dst++ = *input; + input += (stride - 8) * input_step; + } +} + +static int var_intra(const s16 *input) +{ + int32_t mean = 0; + int32_t ret = 0; + const s16 *tmp = input; + int i; + + for (i = 0; i < 8 * 8; i++, tmp++) + mean += *tmp; + mean /= 64; + tmp = input; + for (i = 0; i < 8 * 8; i++, tmp++) + ret += (*tmp - mean) < 0 ? -(*tmp - mean) : (*tmp - mean); + return ret; +} + +static int var_inter(const s16 *old, const s16 *new) +{ + int32_t ret = 0; + int i; + + for (i = 0; i < 8 * 8; i++, old++, new++) + ret += (*old - *new) < 0 ? -(*old - *new) : (*old - *new); + return ret; +} + +static int decide_blocktype(const u8 *cur, const u8 *reference, + s16 *deltablock, unsigned int stride, + unsigned int input_step) +{ + s16 tmp[64]; + s16 old[64]; + s16 *work = tmp; + unsigned int k, l; + int vari; + int vard; + + fill_encoder_block(cur, tmp, stride, input_step); + fill_encoder_block(reference, old, 8, 1); + vari = var_intra(tmp); + + for (k = 0; k < 8; k++) { + for (l = 0; l < 8; l++) { + *deltablock = *work - *reference; + deltablock++; + work++; + reference++; + } + } + deltablock -= 64; + vard = var_inter(old, tmp); + return vari <= vard ? IBLOCK : PBLOCK; +} + +static void fill_decoder_block(u8 *dst, const s16 *input, int stride) +{ + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) + *dst++ = *input++; + dst += stride - 8; + } +} + +static void add_deltas(s16 *deltas, const u8 *ref, int stride) +{ + int k, l; + + for (k = 0; k < 8; k++) { + for (l = 0; l < 8; l++) { + *deltas += *ref++; + /* + * Due to quantizing, it might possible that the + * decoded coefficients are slightly out of range + */ + if (*deltas < 0) + *deltas = 0; + else if (*deltas > 255) + *deltas = 255; + deltas++; + } + ref += stride - 8; + } +} + +static u32 encode_plane(u8 *input, u8 *refp, __be16 **rlco, __be16 *rlco_max, + struct cframe *cf, u32 height, u32 width, + unsigned int input_step, + bool is_intra, bool next_is_intra) +{ + u8 *input_start = input; + __be16 *rlco_start = *rlco; + s16 deltablock[64]; + __be16 pframe_bit = htons(PFRAME_BIT); + u32 encoding = 0; + unsigned int last_size = 0; + unsigned int i, j; + + for (j = 0; j < height / 8; j++) { + for (i = 0; i < width / 8; i++) { + /* intra code, first frame is always intra coded. */ + int blocktype = IBLOCK; + unsigned int size; + + if (!is_intra) + blocktype = decide_blocktype(input, refp, + deltablock, width, input_step); + if (is_intra || blocktype == IBLOCK) { + fwht(input, cf->coeffs, width, input_step, 1); + quantize_intra(cf->coeffs, cf->de_coeffs); + blocktype = IBLOCK; + } else { + /* inter code */ + encoding |= FRAME_PCODED; + fwht16(deltablock, cf->coeffs, 8, 0); + quantize_inter(cf->coeffs, cf->de_coeffs); + } + if (!next_is_intra) { + ifwht(cf->de_coeffs, cf->de_fwht, blocktype); + + if (blocktype == PBLOCK) + add_deltas(cf->de_fwht, refp, 8); + fill_decoder_block(refp, cf->de_fwht, 8); + } + + input += 8 * input_step; + refp += 8 * 8; + + if (encoding & FRAME_UNENCODED) + continue; + + size = rlc(cf->coeffs, *rlco, blocktype); + if (last_size == size && + !memcmp(*rlco + 1, *rlco - size + 1, 2 * size - 2)) { + __be16 *last_rlco = *rlco - size; + s16 hdr = ntohs(*last_rlco); + + if (!((*last_rlco ^ **rlco) & pframe_bit) && + (hdr & DUPS_MASK) < DUPS_MASK) + *last_rlco = htons(hdr + 2); + else + *rlco += size; + } else { + *rlco += size; + } + if (*rlco >= rlco_max) + encoding |= FRAME_UNENCODED; + last_size = size; + } + input += width * 7 * input_step; + } + if (encoding & FRAME_UNENCODED) { + u8 *out = (u8 *)rlco_start; + + input = input_start; + /* + * The compressed stream should never contain the magic + * header, so when we copy the YUV data we replace 0xff + * by 0xfe. Since YUV is limited range such values + * shouldn't appear anyway. + */ + for (i = 0; i < height * width; i++, input += input_step) + *out++ = (*input == 0xff) ? 0xfe : *input; + *rlco = (__be16 *)out; + } + return encoding; +} + +u32 encode_frame(struct raw_frame *frm, struct raw_frame *ref_frm, + struct cframe *cf, bool is_intra, bool next_is_intra) +{ + unsigned int size = frm->height * frm->width; + __be16 *rlco = cf->rlc_data; + __be16 *rlco_max; + u32 encoding; + + rlco_max = rlco + size / 2 - 256; + encoding = encode_plane(frm->luma, ref_frm->luma, &rlco, rlco_max, cf, + frm->height, frm->width, + 1, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= LUMA_UNENCODED; + encoding &= ~FRAME_UNENCODED; + rlco_max = rlco + size / 8 - 256; + encoding |= encode_plane(frm->cb, ref_frm->cb, &rlco, rlco_max, cf, + frm->height / 2, frm->width / 2, + frm->chroma_step, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= CB_UNENCODED; + encoding &= ~FRAME_UNENCODED; + rlco_max = rlco + size / 8 - 256; + encoding |= encode_plane(frm->cr, ref_frm->cr, &rlco, rlco_max, cf, + frm->height / 2, frm->width / 2, + frm->chroma_step, is_intra, next_is_intra); + if (encoding & FRAME_UNENCODED) + encoding |= CR_UNENCODED; + encoding &= ~FRAME_UNENCODED; + cf->size = (rlco - cf->rlc_data) * sizeof(*rlco); + return encoding; +} + +static void decode_plane(struct cframe *cf, const __be16 **rlco, u8 *ref, + u32 height, u32 width, bool uncompressed) +{ + unsigned int copies = 0; + s16 copy[8 * 8]; + s16 stat; + unsigned int i, j; + + if (uncompressed) { + memcpy(ref, *rlco, width * height); + *rlco += width * height / 2; + return; + } + + /* + * When decoding each macroblock the rlco pointer will be increased + * by 65 * 2 bytes worst-case. + * To avoid overflow the buffer has to be 65/64th of the actual raw + * image size, just in case someone feeds it malicious data. + */ + for (j = 0; j < height / 8; j++) { + for (i = 0; i < width / 8; i++) { + u8 *refp = ref + j * 8 * width + i * 8; + + if (copies) { + memcpy(cf->de_fwht, copy, sizeof(copy)); + if (stat & PFRAME_BIT) + add_deltas(cf->de_fwht, refp, width); + fill_decoder_block(refp, cf->de_fwht, width); + copies--; + continue; + } + + stat = derlc(rlco, cf->coeffs); + + if (stat & PFRAME_BIT) + dequantize_inter(cf->coeffs); + else + dequantize_intra(cf->coeffs); + + ifwht(cf->coeffs, cf->de_fwht, + (stat & PFRAME_BIT) ? 0 : 1); + + copies = (stat & DUPS_MASK) >> 1; + if (copies) + memcpy(copy, cf->de_fwht, sizeof(copy)); + if (stat & PFRAME_BIT) + add_deltas(cf->de_fwht, refp, width); + fill_decoder_block(refp, cf->de_fwht, width); + } + } +} + +void decode_frame(struct cframe *cf, struct raw_frame *ref, u32 hdr_flags) +{ + const __be16 *rlco = cf->rlc_data; + + decode_plane(cf, &rlco, ref->luma, cf->height, cf->width, + hdr_flags & VICODEC_FL_LUMA_IS_UNCOMPRESSED); + decode_plane(cf, &rlco, ref->cb, cf->height / 2, cf->width / 2, + hdr_flags & VICODEC_FL_CB_IS_UNCOMPRESSED); + decode_plane(cf, &rlco, ref->cr, cf->height / 2, cf->width / 2, + hdr_flags & VICODEC_FL_CR_IS_UNCOMPRESSED); +} diff --git a/drivers/media/platform/vicodec/vicodec-codec.h b/drivers/media/platform/vicodec/vicodec-codec.h new file mode 100644 index 000000000000..cdfad1332a3e --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-codec.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2016 Tom aan de Wiel + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef VICODEC_RLC_H +#define VICODEC_RLC_H + +#include <linux/types.h> +#include <linux/bitops.h> +#include <asm/byteorder.h> + +/* + * The compressed format consists of a cframe_hdr struct followed by the + * compressed frame data. The header contains the size of that data. + * Each Y, Cb and Cr plane is compressed separately. If the compressed + * size of each plane becomes larger than the uncompressed size, then + * that plane is stored uncompressed and the corresponding bit is set + * in the flags field of the header. + * + * Each compressed plane consists of macroblocks and each macroblock + * is run-length-encoded. Each macroblock starts with a 16 bit value. + * Bit 15 indicates if this is a P-coded macroblock (1) or not (0). + * P-coded macroblocks contain a delta against the previous frame. + * + * Bits 1-12 contain a number. If non-zero, then this same macroblock + * repeats that number of times. This results in a high degree of + * compression for generated images like colorbars. + * + * Following this macroblock header the MB coefficients are run-length + * encoded: the top 12 bits contain the coefficient, the bottom 4 bits + * tell how many times this coefficient occurs. The value 0xf indicates + * that the remainder of the macroblock should be filled with zeroes. + * + * All 16 and 32 bit values are stored in big-endian (network) order. + * + * Each cframe_hdr starts with an 8 byte magic header that is + * guaranteed not to occur in the compressed frame data. This header + * can be used to sync to the next frame. + * + * This codec uses the Fast Walsh Hadamard Transform. Tom aan de Wiel + * developed this as part of a university project, specifically for use + * with this driver. His project report can be found here: + * + * https://hverkuil.home.xs4all.nl/fwht.pdf + */ + +/* + * Note: bit 0 of the header must always be 0. Otherwise it cannot + * be guaranteed that the magic 8 byte sequence (see below) can + * never occur in the rlc output. + */ +#define PFRAME_BIT (1 << 15) +#define DUPS_MASK 0x1ffe + +/* + * This is a sequence of 8 bytes with the low 4 bits set to 0xf. + * + * This sequence cannot occur in the encoded data + */ +#define VICODEC_MAGIC1 0x4f4f4f4f +#define VICODEC_MAGIC2 0xffffffff + +#define VICODEC_VERSION 1 + +#define VICODEC_MAX_WIDTH 3840 +#define VICODEC_MAX_HEIGHT 2160 +#define VICODEC_MIN_WIDTH 640 +#define VICODEC_MIN_HEIGHT 480 + +#define PBLOCK 0 +#define IBLOCK 1 + +/* Set if this is an interlaced format */ +#define VICODEC_FL_IS_INTERLACED BIT(0) +/* Set if this is a bottom-first (NTSC) interlaced format */ +#define VICODEC_FL_IS_BOTTOM_FIRST BIT(1) +/* Set if each 'frame' contains just one field */ +#define VICODEC_FL_IS_ALTERNATE BIT(2) +/* + * If VICODEC_FL_IS_ALTERNATE was set, then this is set if this + * 'frame' is the bottom field, else it is the top field. + */ +#define VICODEC_FL_IS_BOTTOM_FIELD BIT(3) +/* Set if this frame is uncompressed */ +#define VICODEC_FL_LUMA_IS_UNCOMPRESSED BIT(4) +#define VICODEC_FL_CB_IS_UNCOMPRESSED BIT(5) +#define VICODEC_FL_CR_IS_UNCOMPRESSED BIT(6) + +struct cframe_hdr { + u32 magic1; + u32 magic2; + __be32 version; + __be32 width, height; + __be32 flags; + __be32 colorspace; + __be32 xfer_func; + __be32 ycbcr_enc; + __be32 quantization; + __be32 size; +}; + +struct cframe { + unsigned int width, height; + __be16 *rlc_data; + s16 coeffs[8 * 8]; + s16 de_coeffs[8 * 8]; + s16 de_fwht[8 * 8]; + u32 size; +}; + +struct raw_frame { + unsigned int width, height; + unsigned int chroma_step; + u8 *luma, *cb, *cr; +}; + +#define FRAME_PCODED BIT(0) +#define FRAME_UNENCODED BIT(1) +#define LUMA_UNENCODED BIT(2) +#define CB_UNENCODED BIT(3) +#define CR_UNENCODED BIT(4) + +u32 encode_frame(struct raw_frame *frm, struct raw_frame *ref_frm, + struct cframe *cf, bool is_intra, bool next_is_intra); +void decode_frame(struct cframe *cf, struct raw_frame *ref, u32 hdr_flags); + +#endif diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c new file mode 100644 index 000000000000..408cd55d3580 --- /dev/null +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * A virtual codec example device. + * + * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This is a virtual codec device driver for testing the codec framework. + * It simulates a device that uses memory buffers for both source and + * destination and encodes or decodes the data. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <linux/platform_device.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-vmalloc.h> + +#include "vicodec-codec.h" + +MODULE_DESCRIPTION("Virtual codec device"); +MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); +MODULE_LICENSE("GPL v2"); + +static bool multiplanar; +module_param(multiplanar, bool, 0444); +MODULE_PARM_DESC(multiplanar, + " use multi-planar API instead of single-planar API"); + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, " activates debug info"); + +#define VICODEC_NAME "vicodec" +#define MAX_WIDTH 4096U +#define MIN_WIDTH 640U +#define MAX_HEIGHT 2160U +#define MIN_HEIGHT 480U + +#define dprintk(dev, fmt, arg...) \ + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) + + +static void vicodec_dev_release(struct device *dev) +{ +} + +static struct platform_device vicodec_pdev = { + .name = VICODEC_NAME, + .dev.release = vicodec_dev_release, +}; + +/* Per-queue, driver-specific private data */ +struct vicodec_q_data { + unsigned int width; + unsigned int height; + unsigned int flags; + unsigned int sizeimage; + unsigned int sequence; + u32 fourcc; +}; + +enum { + V4L2_M2M_SRC = 0, + V4L2_M2M_DST = 1, +}; + +struct vicodec_dev { + struct v4l2_device v4l2_dev; + struct video_device enc_vfd; + struct video_device dec_vfd; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif + + struct mutex enc_mutex; + struct mutex dec_mutex; + spinlock_t enc_lock; + spinlock_t dec_lock; + + struct v4l2_m2m_dev *enc_dev; + struct v4l2_m2m_dev *dec_dev; +}; + +struct vicodec_ctx { + struct v4l2_fh fh; + struct vicodec_dev *dev; + bool is_enc; + spinlock_t *lock; + + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *ctrl_gop_size; + unsigned int gop_size; + unsigned int gop_cnt; + + /* Abort requested by m2m */ + int aborting; + struct vb2_v4l2_buffer *last_src_buf; + struct vb2_v4l2_buffer *last_dst_buf; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_xfer_func xfer_func; + enum v4l2_quantization quantization; + + /* Source and destination queue data */ + struct vicodec_q_data q_data[2]; + struct raw_frame ref_frame; + u8 *compressed_frame; + u32 cur_buf_offset; + u32 comp_max_size; + u32 comp_size; + u32 comp_magic_cnt; + u32 comp_frame_size; + bool comp_has_frame; + bool comp_has_next_frame; +}; + +static const u32 pixfmts_yuv[] = { + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, +}; + +static inline struct vicodec_ctx *file2ctx(struct file *file) +{ + return container_of(file->private_data, struct vicodec_ctx, fh); +} + +static struct vicodec_q_data *get_q_data(struct vicodec_ctx *ctx, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return &ctx->q_data[V4L2_M2M_SRC]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return &ctx->q_data[V4L2_M2M_DST]; + default: + WARN_ON(1); + break; + } + return NULL; +} + +static void encode(struct vicodec_ctx *ctx, + struct vicodec_q_data *q_data, + u8 *p_in, u8 *p_out) +{ + unsigned int size = q_data->width * q_data->height; + struct cframe_hdr *p_hdr; + struct cframe cf; + struct raw_frame rf; + u32 encoding; + + rf.width = q_data->width; + rf.height = q_data->height; + rf.luma = p_in; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUV420: + rf.cb = rf.luma + size; + rf.cr = rf.cb + size / 4; + rf.chroma_step = 1; + break; + case V4L2_PIX_FMT_YVU420: + rf.cr = rf.luma + size; + rf.cb = rf.cr + size / 4; + rf.chroma_step = 1; + break; + case V4L2_PIX_FMT_NV12: + rf.cb = rf.luma + size; + rf.cr = rf.cb + 1; + rf.chroma_step = 2; + break; + case V4L2_PIX_FMT_NV21: + rf.cr = rf.luma + size; + rf.cb = rf.cr + 1; + rf.chroma_step = 2; + break; + } + + cf.width = q_data->width; + cf.height = q_data->height; + cf.rlc_data = (__be16 *)(p_out + sizeof(*p_hdr)); + + encoding = encode_frame(&rf, &ctx->ref_frame, &cf, !ctx->gop_cnt, + ctx->gop_cnt == ctx->gop_size - 1); + if (encoding != FRAME_PCODED) + ctx->gop_cnt = 0; + if (++ctx->gop_cnt == ctx->gop_size) + ctx->gop_cnt = 0; + + p_hdr = (struct cframe_hdr *)p_out; + p_hdr->magic1 = VICODEC_MAGIC1; + p_hdr->magic2 = VICODEC_MAGIC2; + p_hdr->version = htonl(VICODEC_VERSION); + p_hdr->width = htonl(cf.width); + p_hdr->height = htonl(cf.height); + p_hdr->flags = htonl(q_data->flags); + if (encoding & LUMA_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_LUMA_IS_UNCOMPRESSED); + if (encoding & CB_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_CB_IS_UNCOMPRESSED); + if (encoding & CR_UNENCODED) + p_hdr->flags |= htonl(VICODEC_FL_CR_IS_UNCOMPRESSED); + p_hdr->colorspace = htonl(ctx->colorspace); + p_hdr->xfer_func = htonl(ctx->xfer_func); + p_hdr->ycbcr_enc = htonl(ctx->ycbcr_enc); + p_hdr->quantization = htonl(ctx->quantization); + p_hdr->size = htonl(cf.size); + ctx->ref_frame.width = cf.width; + ctx->ref_frame.height = cf.height; +} + +static int decode(struct vicodec_ctx *ctx, + struct vicodec_q_data *q_data, + u8 *p_in, u8 *p_out) +{ + unsigned int size = q_data->width * q_data->height; + unsigned int i; + struct cframe_hdr *p_hdr; + struct cframe cf; + u8 *p; + + p_hdr = (struct cframe_hdr *)p_in; + cf.width = ntohl(p_hdr->width); + cf.height = ntohl(p_hdr->height); + q_data->flags = ntohl(p_hdr->flags); + ctx->colorspace = ntohl(p_hdr->colorspace); + ctx->xfer_func = ntohl(p_hdr->xfer_func); + ctx->ycbcr_enc = ntohl(p_hdr->ycbcr_enc); + ctx->quantization = ntohl(p_hdr->quantization); + cf.rlc_data = (__be16 *)(p_in + sizeof(*p_hdr)); + + if (p_hdr->magic1 != VICODEC_MAGIC1 || + p_hdr->magic2 != VICODEC_MAGIC2 || + ntohl(p_hdr->version) != VICODEC_VERSION || + cf.width < VICODEC_MIN_WIDTH || + cf.width > VICODEC_MAX_WIDTH || + cf.height < VICODEC_MIN_HEIGHT || + cf.height > VICODEC_MAX_HEIGHT || + (cf.width & 7) || (cf.height & 7)) + return -EINVAL; + + /* TODO: support resolution changes */ + if (cf.width != q_data->width || cf.height != q_data->height) + return -EINVAL; + + decode_frame(&cf, &ctx->ref_frame, q_data->flags); + memcpy(p_out, ctx->ref_frame.luma, size); + p_out += size; + + switch (q_data->fourcc) { + case V4L2_PIX_FMT_YUV420: + memcpy(p_out, ctx->ref_frame.cb, size / 4); + p_out += size / 4; + memcpy(p_out, ctx->ref_frame.cr, size / 4); + break; + case V4L2_PIX_FMT_YVU420: + memcpy(p_out, ctx->ref_frame.cr, size / 4); + p_out += size / 4; + memcpy(p_out, ctx->ref_frame.cb, size / 4); + break; + case V4L2_PIX_FMT_NV12: + for (i = 0, p = p_out; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cb[i]; + for (i = 0, p = p_out + 1; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cr[i]; + break; + case V4L2_PIX_FMT_NV21: + for (i = 0, p = p_out; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cr[i]; + for (i = 0, p = p_out + 1; i < size / 4; i++, p += 2) + *p = ctx->ref_frame.cb[i]; + break; + } + return 0; +} + +static int device_process(struct vicodec_ctx *ctx, + struct vb2_v4l2_buffer *in_vb, + struct vb2_v4l2_buffer *out_vb) +{ + struct vicodec_dev *dev = ctx->dev; + struct vicodec_q_data *q_out, *q_cap; + u8 *p_in, *p_out; + int ret; + + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + q_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ctx->is_enc) + p_in = vb2_plane_vaddr(&in_vb->vb2_buf, 0); + else + p_in = ctx->compressed_frame; + p_out = vb2_plane_vaddr(&out_vb->vb2_buf, 0); + if (!p_in || !p_out) { + v4l2_err(&dev->v4l2_dev, + "Acquiring kernel pointers to buffers failed\n"); + return -EFAULT; + } + + if (ctx->is_enc) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)p_out; + + encode(ctx, q_out, p_in, p_out); + vb2_set_plane_payload(&out_vb->vb2_buf, 0, + sizeof(*p_hdr) + ntohl(p_hdr->size)); + } else { + ret = decode(ctx, q_cap, p_in, p_out); + if (ret) + return ret; + vb2_set_plane_payload(&out_vb->vb2_buf, 0, + q_cap->width * q_cap->height * 3 / 2); + } + + out_vb->sequence = q_cap->sequence++; + out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp; + + if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE) + out_vb->timecode = in_vb->timecode; + out_vb->field = in_vb->field; + out_vb->flags &= ~V4L2_BUF_FLAG_LAST; + out_vb->flags |= in_vb->flags & + (V4L2_BUF_FLAG_TIMECODE | + V4L2_BUF_FLAG_KEYFRAME | + V4L2_BUF_FLAG_PFRAME | + V4L2_BUF_FLAG_BFRAME | + V4L2_BUF_FLAG_TSTAMP_SRC_MASK); + + return 0; +} + +/* + * mem2mem callbacks + */ + +/* device_run() - prepares and starts the device */ +static void device_run(void *priv) +{ + static const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + struct vicodec_ctx *ctx = priv; + struct vicodec_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct vicodec_q_data *q_out; + u32 state; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + + state = VB2_BUF_STATE_DONE; + if (device_process(ctx, src_buf, dst_buf)) + state = VB2_BUF_STATE_ERROR; + ctx->last_dst_buf = dst_buf; + + spin_lock(ctx->lock); + if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + if (ctx->is_enc) { + src_buf->sequence = q_out->sequence++; + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, state); + } else if (vb2_get_plane_payload(&src_buf->vb2_buf, 0) == ctx->cur_buf_offset) { + src_buf->sequence = q_out->sequence++; + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, state); + ctx->cur_buf_offset = 0; + ctx->comp_has_next_frame = false; + } + v4l2_m2m_buf_done(dst_buf, state); + ctx->comp_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = false; + spin_unlock(ctx->lock); + + if (ctx->is_enc) + v4l2_m2m_job_finish(dev->enc_dev, ctx->fh.m2m_ctx); + else + v4l2_m2m_job_finish(dev->dec_dev, ctx->fh.m2m_ctx); +} + +static void job_remove_out_buf(struct vicodec_ctx *ctx, u32 state) +{ + struct vb2_v4l2_buffer *src_buf; + struct vicodec_q_data *q_out; + + q_out = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + spin_lock(ctx->lock); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + src_buf->sequence = q_out->sequence++; + v4l2_m2m_buf_done(src_buf, state); + ctx->cur_buf_offset = 0; + spin_unlock(ctx->lock); +} + +static int job_ready(void *priv) +{ + static const u8 magic[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0xff, 0xff, 0xff, 0xff + }; + struct vicodec_ctx *ctx = priv; + struct vb2_v4l2_buffer *src_buf; + u8 *p_out; + u8 *p; + u32 sz; + u32 state; + + if (ctx->is_enc || ctx->comp_has_frame) + return 1; + +restart: + ctx->comp_has_next_frame = false; + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + if (!src_buf) + return 0; + p_out = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + sz = vb2_get_plane_payload(&src_buf->vb2_buf, 0); + p = p_out + ctx->cur_buf_offset; + + state = VB2_BUF_STATE_DONE; + + if (!ctx->comp_size) { + state = VB2_BUF_STATE_ERROR; + for (; p < p_out + sz; p++) { + u32 copy; + + p = memchr(p, magic[ctx->comp_magic_cnt], sz); + if (!p) { + ctx->comp_magic_cnt = 0; + break; + } + copy = sizeof(magic) - ctx->comp_magic_cnt; + if (p_out + sz - p < copy) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_magic_cnt, + p, copy); + ctx->comp_magic_cnt += copy; + if (!memcmp(ctx->compressed_frame, magic, ctx->comp_magic_cnt)) { + p += copy; + state = VB2_BUF_STATE_DONE; + break; + } + ctx->comp_magic_cnt = 0; + } + if (ctx->comp_magic_cnt < sizeof(magic)) { + job_remove_out_buf(ctx, state); + goto restart; + } + ctx->comp_size = sizeof(magic); + } + if (ctx->comp_size < sizeof(struct cframe_hdr)) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)ctx->compressed_frame; + u32 copy = sizeof(struct cframe_hdr) - ctx->comp_size; + + if (copy > p_out + sz - p) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_size, + p, copy); + p += copy; + ctx->comp_size += copy; + if (ctx->comp_size < sizeof(struct cframe_hdr)) { + job_remove_out_buf(ctx, state); + goto restart; + } + ctx->comp_frame_size = ntohl(p_hdr->size) + sizeof(*p_hdr); + if (ctx->comp_frame_size > ctx->comp_max_size) + ctx->comp_frame_size = ctx->comp_max_size; + } + if (ctx->comp_size < ctx->comp_frame_size) { + u32 copy = ctx->comp_frame_size - ctx->comp_size; + + if (copy > p_out + sz - p) + copy = p_out + sz - p; + memcpy(ctx->compressed_frame + ctx->comp_size, + p, copy); + p += copy; + ctx->comp_size += copy; + if (ctx->comp_size < ctx->comp_frame_size) { + job_remove_out_buf(ctx, state); + goto restart; + } + } + ctx->cur_buf_offset = p - p_out; + ctx->comp_has_frame = true; + ctx->comp_has_next_frame = false; + if (sz - ctx->cur_buf_offset >= sizeof(struct cframe_hdr)) { + struct cframe_hdr *p_hdr = (struct cframe_hdr *)p; + u32 frame_size = ntohl(p_hdr->size); + u32 remaining = sz - ctx->cur_buf_offset - sizeof(*p_hdr); + + if (!memcmp(p, magic, sizeof(magic))) + ctx->comp_has_next_frame = remaining >= frame_size; + } + return 1; +} + +static void job_abort(void *priv) +{ + struct vicodec_ctx *ctx = priv; + + /* Will cancel the transaction in the next interrupt handler */ + ctx->aborting = 1; +} + +/* + * video ioctls + */ + +static u32 find_fmt(u32 fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pixfmts_yuv); i++) + if (pixfmts_yuv[i] == fmt) + return fmt; + return pixfmts_yuv[0]; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strncpy(cap->driver, VICODEC_NAME, sizeof(cap->driver) - 1); + strncpy(cap->card, VICODEC_NAME, sizeof(cap->card) - 1); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", VICODEC_NAME); + cap->device_caps = V4L2_CAP_STREAMING | + (multiplanar ? + V4L2_CAP_VIDEO_M2M_MPLANE : + V4L2_CAP_VIDEO_M2M); + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +static int enum_fmt(struct v4l2_fmtdesc *f, bool is_enc, bool is_out) +{ + bool is_yuv = (is_enc && is_out) || (!is_enc && !is_out); + + if (V4L2_TYPE_IS_MULTIPLANAR(f->type) && !multiplanar) + return -EINVAL; + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && multiplanar) + return -EINVAL; + if (f->index >= (is_yuv ? ARRAY_SIZE(pixfmts_yuv) : 1)) + return -EINVAL; + + if (is_yuv) + f->pixelformat = pixfmts_yuv[f->index]; + else + f->pixelformat = V4L2_PIX_FMT_FWHT; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + + return enum_fmt(f, ctx->is_enc, false); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + + return enum_fmt(f, ctx->is_enc, true); +} + +static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct vb2_queue *vq; + struct vicodec_q_data *q_data; + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->width = q_data->width; + pix->height = q_data->height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = q_data->fourcc; + if (q_data->fourcc == V4L2_PIX_FMT_FWHT) + pix->bytesperline = 0; + else + pix->bytesperline = q_data->width; + pix->sizeimage = q_data->sizeimage; + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->width = q_data->width; + pix_mp->height = q_data->height; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = q_data->fourcc; + pix_mp->num_planes = 1; + if (q_data->fourcc == V4L2_PIX_FMT_FWHT) + pix_mp->plane_fmt[0].bytesperline = 0; + else + pix_mp->plane_fmt[0].bytesperline = q_data->width; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage; + pix_mp->colorspace = ctx->colorspace; + pix_mp->xfer_func = ctx->xfer_func; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(file2ctx(file), f); +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_g_fmt(file2ctx(file), f); +} + +static int vidioc_try_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH) & ~7; + pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + pix->bytesperline = pix->width; + pix->sizeimage = pix->width * pix->height * 3 / 2; + pix->field = V4L2_FIELD_NONE; + if (pix->pixelformat == V4L2_PIX_FMT_FWHT) { + pix->bytesperline = 0; + pix->sizeimage += sizeof(struct cframe_hdr); + } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + pix_mp->width = clamp(pix_mp->width, MIN_WIDTH, MAX_WIDTH) & ~7; + pix_mp->height = + clamp(pix_mp->height, MIN_HEIGHT, MAX_HEIGHT) & ~7; + pix_mp->plane_fmt[0].bytesperline = pix_mp->width; + pix_mp->plane_fmt[0].sizeimage = + pix_mp->width * pix_mp->height * 3 / 2; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->num_planes = 1; + if (pix_mp->pixelformat == V4L2_PIX_FMT_FWHT) { + pix_mp->plane_fmt[0].bytesperline = 0; + pix_mp->plane_fmt[0].sizeimage += + sizeof(struct cframe_hdr); + } + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(f->fmt.pix.pixelformat); + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix_mp->pixelformat); + pix_mp->colorspace = ctx->colorspace; + pix_mp->xfer_func = ctx->xfer_func; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); + memset(pix_mp->plane_fmt[0].reserved, 0, + sizeof(pix_mp->plane_fmt[0].reserved)); + break; + default: + return -EINVAL; + } + + return vidioc_try_fmt(ctx, f); +} + +static int vidioc_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (multiplanar) + return -EINVAL; + pix = &f->fmt.pix; + pix->pixelformat = !ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix->pixelformat); + if (!pix->colorspace) + pix->colorspace = V4L2_COLORSPACE_REC709; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (!multiplanar) + return -EINVAL; + pix_mp = &f->fmt.pix_mp; + pix_mp->pixelformat = !ctx->is_enc ? V4L2_PIX_FMT_FWHT : + find_fmt(pix_mp->pixelformat); + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_REC709; + break; + default: + return -EINVAL; + } + + return vidioc_try_fmt(ctx, f); +} + +static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) +{ + struct vicodec_q_data *q_data; + struct vb2_queue *vq; + bool fmt_changed = true; + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + q_data = get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) + fmt_changed = + q_data->fourcc != pix->pixelformat || + q_data->width != pix->width || + q_data->height != pix->height; + + if (vb2_is_busy(vq) && fmt_changed) + return -EBUSY; + + q_data->fourcc = pix->pixelformat; + q_data->width = pix->width; + q_data->height = pix->height; + q_data->sizeimage = pix->sizeimage; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + if (ctx->is_enc && V4L2_TYPE_IS_OUTPUT(f->type)) + fmt_changed = + q_data->fourcc != pix_mp->pixelformat || + q_data->width != pix_mp->width || + q_data->height != pix_mp->height; + + if (vb2_is_busy(vq) && fmt_changed) + return -EBUSY; + + q_data->fourcc = pix_mp->pixelformat; + q_data->width = pix_mp->width; + q_data->height = pix_mp->height; + q_data->sizeimage = pix_mp->plane_fmt[0].sizeimage; + break; + default: + return -EINVAL; + } + + dprintk(ctx->dev, + "Setting format for type %d, wxh: %dx%d, fourcc: %08x\n", + f->type, q_data->width, q_data->height, q_data->fourcc); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return vidioc_s_fmt(file2ctx(file), f); +} + +static int vidioc_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vicodec_ctx *ctx = file2ctx(file); + struct v4l2_pix_format_mplane *pix_mp; + struct v4l2_pix_format *pix; + int ret; + + ret = vidioc_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + ret = vidioc_s_fmt(file2ctx(file), f); + if (!ret) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + pix = &f->fmt.pix; + ctx->colorspace = pix->colorspace; + ctx->xfer_func = pix->xfer_func; + ctx->ycbcr_enc = pix->ycbcr_enc; + ctx->quantization = pix->quantization; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + pix_mp = &f->fmt.pix_mp; + ctx->colorspace = pix_mp->colorspace; + ctx->xfer_func = pix_mp->xfer_func; + ctx->ycbcr_enc = pix_mp->ycbcr_enc; + ctx->quantization = pix_mp->quantization; + break; + default: + break; + } + } + return ret; +} + +static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) +{ + static const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + spin_lock(ctx->lock); + ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + if (!ctx->last_src_buf && ctx->last_dst_buf) { + ctx->last_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&ctx->fh, &eos_event); + } + spin_unlock(ctx->lock); +} + +static int vicodec_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + if (ec->cmd != V4L2_ENC_CMD_STOP) + return -EINVAL; + + if (ec->flags & V4L2_ENC_CMD_STOP_AT_GOP_END) + return -EINVAL; + + return 0; +} + +static int vicodec_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + struct vicodec_ctx *ctx = file2ctx(file); + int ret; + + ret = vicodec_try_encoder_cmd(file, fh, ec); + if (ret < 0) + return ret; + + vicodec_mark_last_buf(ctx); + return 0; +} + +static int vicodec_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) + return -EINVAL; + + if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) + return -EINVAL; + + return 0; +} + +static int vicodec_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + struct vicodec_ctx *ctx = file2ctx(file); + int ret; + + ret = vicodec_try_decoder_cmd(file, fh, dc); + if (ret < 0) + return ret; + + vicodec_mark_last_buf(ctx); + return 0; +} + +static int vicodec_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_FWHT: + break; + default: + if (find_fmt(fsize->pixel_format) == fsize->pixel_format) + break; + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = MIN_WIDTH; + fsize->stepwise.max_width = MAX_WIDTH; + fsize->stepwise.step_width = 8; + fsize->stepwise.min_height = MIN_HEIGHT; + fsize->stepwise.max_height = MAX_HEIGHT; + fsize->stepwise.step_height = 8; + + return 0; +} + +static int vicodec_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, + + .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, + .vidioc_s_fmt_vid_out_mplane = vidioc_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_try_encoder_cmd = vicodec_try_encoder_cmd, + .vidioc_encoder_cmd = vicodec_encoder_cmd, + .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, + .vidioc_decoder_cmd = vicodec_decoder_cmd, + .vidioc_enum_framesizes = vicodec_enum_framesizes, + + .vidioc_subscribe_event = vicodec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + + +/* + * Queue operations + */ + +static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(vq); + struct vicodec_q_data *q_data = get_q_data(ctx, vq->type); + unsigned int size = q_data->sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + return 0; +} + +static int vicodec_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vicodec_q_data *q_data; + + dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); + + q_data = get_q_data(ctx, vb->vb2_queue->type); + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dprintk(ctx->dev, "%s field isn't supported\n", + __func__); + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + dprintk(ctx->dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)q_data->sizeimage); + return -EINVAL; + } + + return 0; +} + +static void vicodec_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct vicodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static void vicodec_return_bufs(struct vb2_queue *q, u32 state) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *vbuf; + + for (;;) { + if (V4L2_TYPE_IS_OUTPUT(q->type)) + vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + else + vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (vbuf == NULL) + return; + spin_lock(ctx->lock); + v4l2_m2m_buf_done(vbuf, state); + spin_unlock(ctx->lock); + } +} + +static int vicodec_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + struct vicodec_q_data *q_data = get_q_data(ctx, q->type); + unsigned int size = q_data->width * q_data->height; + + q_data->sequence = 0; + + if (!V4L2_TYPE_IS_OUTPUT(q->type)) + return 0; + + ctx->ref_frame.width = ctx->ref_frame.height = 0; + ctx->ref_frame.luma = kvmalloc(size * 3 / 2, GFP_KERNEL); + ctx->comp_max_size = size * 3 / 2 + sizeof(struct cframe_hdr); + ctx->compressed_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL); + if (!ctx->ref_frame.luma || !ctx->compressed_frame) { + kvfree(ctx->ref_frame.luma); + kvfree(ctx->compressed_frame); + vicodec_return_bufs(q, VB2_BUF_STATE_QUEUED); + return -ENOMEM; + } + ctx->ref_frame.cb = ctx->ref_frame.luma + size; + ctx->ref_frame.cr = ctx->ref_frame.cb + size / 4; + ctx->last_src_buf = NULL; + ctx->last_dst_buf = NULL; + v4l2_ctrl_grab(ctx->ctrl_gop_size, true); + ctx->gop_size = v4l2_ctrl_g_ctrl(ctx->ctrl_gop_size); + ctx->gop_cnt = 0; + ctx->cur_buf_offset = 0; + ctx->comp_size = 0; + ctx->comp_magic_cnt = 0; + ctx->comp_has_frame = false; + + return 0; +} + +static void vicodec_stop_streaming(struct vb2_queue *q) +{ + struct vicodec_ctx *ctx = vb2_get_drv_priv(q); + + vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); + + if (!V4L2_TYPE_IS_OUTPUT(q->type)) + return; + + kvfree(ctx->ref_frame.luma); + kvfree(ctx->compressed_frame); + v4l2_ctrl_grab(ctx->ctrl_gop_size, false); +} + +static const struct vb2_ops vicodec_qops = { + .queue_setup = vicodec_queue_setup, + .buf_prepare = vicodec_buf_prepare, + .buf_queue = vicodec_buf_queue, + .start_streaming = vicodec_start_streaming, + .stop_streaming = vicodec_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct vicodec_ctx *ctx = priv; + int ret; + + src_vq->type = (multiplanar ? + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : + V4L2_BUF_TYPE_VIDEO_OUTPUT); + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &vicodec_qops; + src_vq->mem_ops = &vb2_vmalloc_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = ctx->is_enc ? &ctx->dev->enc_mutex : + &ctx->dev->dec_mutex; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = (multiplanar ? + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : + V4L2_BUF_TYPE_VIDEO_CAPTURE); + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &vicodec_qops; + dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = src_vq->lock; + + return vb2_queue_init(dst_vq); +} + +/* + * File operations + */ +static int vicodec_open(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct vicodec_dev *dev = video_drvdata(file); + struct vicodec_ctx *ctx = NULL; + struct v4l2_ctrl_handler *hdl; + unsigned int size; + int rc = 0; + + if (mutex_lock_interruptible(vfd->lock)) + return -ERESTARTSYS; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + rc = -ENOMEM; + goto open_unlock; + } + + if (vfd == &dev->enc_vfd) + ctx->is_enc = true; + + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + ctx->dev = dev; + hdl = &ctx->hdl; + v4l2_ctrl_handler_init(hdl, 4); + ctx->ctrl_gop_size = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 16, 1, 10); + if (hdl->error) { + rc = hdl->error; + v4l2_ctrl_handler_free(hdl); + kfree(ctx); + goto open_unlock; + } + ctx->fh.ctrl_handler = hdl; + v4l2_ctrl_handler_setup(hdl); + + ctx->q_data[V4L2_M2M_SRC].fourcc = + ctx->is_enc ? V4L2_PIX_FMT_YUV420 : V4L2_PIX_FMT_FWHT; + ctx->q_data[V4L2_M2M_SRC].width = 1280; + ctx->q_data[V4L2_M2M_SRC].height = 720; + size = 1280 * 720 * 3 / 2; + ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; + ctx->q_data[V4L2_M2M_DST].fourcc = + ctx->is_enc ? V4L2_PIX_FMT_FWHT : V4L2_PIX_FMT_YUV420; + ctx->colorspace = V4L2_COLORSPACE_REC709; + + size += sizeof(struct cframe_hdr); + if (ctx->is_enc) { + ctx->q_data[V4L2_M2M_DST].sizeimage = size; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->enc_dev, ctx, + &queue_init); + ctx->lock = &dev->enc_lock; + } else { + ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->dec_dev, ctx, + &queue_init); + ctx->lock = &dev->dec_lock; + } + + if (IS_ERR(ctx->fh.m2m_ctx)) { + rc = PTR_ERR(ctx->fh.m2m_ctx); + + v4l2_ctrl_handler_free(hdl); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + goto open_unlock; + } + + v4l2_fh_add(&ctx->fh); + +open_unlock: + mutex_unlock(vfd->lock); + return rc; +} + +static int vicodec_release(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct vicodec_ctx *ctx = file2ctx(file); + + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->hdl); + mutex_lock(vfd->lock); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + mutex_unlock(vfd->lock); + kfree(ctx); + + return 0; +} + +static const struct v4l2_file_operations vicodec_fops = { + .owner = THIS_MODULE, + .open = vicodec_open, + .release = vicodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct video_device vicodec_videodev = { + .name = VICODEC_NAME, + .vfl_dir = VFL_DIR_M2M, + .fops = &vicodec_fops, + .ioctl_ops = &vicodec_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static const struct v4l2_m2m_ops m2m_ops = { + .device_run = device_run, + .job_abort = job_abort, + .job_ready = job_ready, +}; + +static int vicodec_probe(struct platform_device *pdev) +{ + struct vicodec_dev *dev; + struct video_device *vfd; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->enc_lock); + spin_lock_init(&dev->dec_lock); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &pdev->dev; + strlcpy(dev->mdev.model, "vicodec", sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->v4l2_dev.mdev = &dev->mdev; +#endif + + mutex_init(&dev->enc_mutex); + mutex_init(&dev->dec_mutex); + + platform_set_drvdata(pdev, dev); + + dev->enc_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->enc_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init vicodec device\n"); + ret = PTR_ERR(dev->enc_dev); + goto unreg_dev; + } + + dev->dec_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(dev->dec_dev)) { + v4l2_err(&dev->v4l2_dev, "Failed to init vicodec device\n"); + ret = PTR_ERR(dev->dec_dev); + goto err_enc_m2m; + } + + dev->enc_vfd = vicodec_videodev; + vfd = &dev->enc_vfd; + vfd->lock = &dev->enc_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + strlcpy(vfd->name, "vicodec-enc", sizeof(vfd->name)); + v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto err_dec_m2m; + } + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + + dev->dec_vfd = vicodec_videodev; + vfd = &dev->dec_vfd; + vfd->lock = &dev->dec_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; + strlcpy(vfd->name, "vicodec-dec", sizeof(vfd->name)); + v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); + v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); + video_set_drvdata(vfd, dev); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + goto unreg_enc; + } + v4l2_info(&dev->v4l2_dev, + "Device registered as /dev/video%d\n", vfd->num); + +#ifdef CONFIG_MEDIA_CONTROLLER + ret = v4l2_m2m_register_media_controller(dev->enc_dev, + &dev->enc_vfd, MEDIA_ENT_F_PROC_VIDEO_ENCODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m; + } + + ret = v4l2_m2m_register_media_controller(dev->dec_dev, + &dev->dec_vfd, MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m_enc_mc; + } + + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); + goto unreg_m2m_dec_mc; + } +#endif + return 0; + +#ifdef CONFIG_MEDIA_CONTROLLER +unreg_m2m_dec_mc: + v4l2_m2m_unregister_media_controller(dev->dec_dev); +unreg_m2m_enc_mc: + v4l2_m2m_unregister_media_controller(dev->enc_dev); +unreg_m2m: + video_unregister_device(&dev->dec_vfd); +#endif +unreg_enc: + video_unregister_device(&dev->enc_vfd); +err_dec_m2m: + v4l2_m2m_release(dev->dec_dev); +err_enc_m2m: + v4l2_m2m_release(dev->enc_dev); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); + + return ret; +} + +static int vicodec_remove(struct platform_device *pdev) +{ + struct vicodec_dev *dev = platform_get_drvdata(pdev); + + v4l2_info(&dev->v4l2_dev, "Removing " VICODEC_NAME); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->enc_dev); + v4l2_m2m_unregister_media_controller(dev->dec_dev); + media_device_cleanup(&dev->mdev); +#endif + + v4l2_m2m_release(dev->enc_dev); + v4l2_m2m_release(dev->dec_dev); + video_unregister_device(&dev->enc_vfd); + video_unregister_device(&dev->dec_vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +static struct platform_driver vicodec_pdrv = { + .probe = vicodec_probe, + .remove = vicodec_remove, + .driver = { + .name = VICODEC_NAME, + }, +}; + +static void __exit vicodec_exit(void) +{ + platform_driver_unregister(&vicodec_pdrv); + platform_device_unregister(&vicodec_pdev); +} + +static int __init vicodec_init(void) +{ + int ret; + + ret = platform_device_register(&vicodec_pdev); + if (ret) + return ret; + + ret = platform_driver_register(&vicodec_pdrv); + if (ret) + platform_device_unregister(&vicodec_pdev); + + return ret; +} + +module_init(vicodec_init); +module_exit(vicodec_exit); diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index 1fb887293337..c01e1592ad0a 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -34,6 +34,13 @@ struct video_mux { int active; }; +static const struct v4l2_mbus_framefmt video_mux_format_mbus_default = { + .width = 1, + .height = 1, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, +}; + static inline struct video_mux *v4l2_subdev_to_video_mux(struct v4l2_subdev *sd) { return container_of(sd, struct video_mux, subdev); @@ -180,6 +187,88 @@ static int video_mux_set_format(struct v4l2_subdev *sd, if (!source_mbusformat) return -EINVAL; + /* No size limitations except V4L2 compliance requirements */ + v4l_bound_align_image(&sdformat->format.width, 1, 65536, 0, + &sdformat->format.height, 1, 65536, 0, 0); + + /* All formats except LVDS and vendor specific formats are acceptable */ + switch (sdformat->format.code) { + case MEDIA_BUS_FMT_RGB444_1X12: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + case MEDIA_BUS_FMT_RGB565_1X16: + case MEDIA_BUS_FMT_BGR565_2X8_BE: + case MEDIA_BUS_FMT_BGR565_2X8_LE: + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_RBG888_1X24: + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + case MEDIA_BUS_FMT_BGR888_1X24: + case MEDIA_BUS_FMT_GBR888_1X24: + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_ARGB8888_1X32: + case MEDIA_BUS_FMT_RGB888_1X32_PADHI: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_Y8_1X8: + case MEDIA_BUS_FMT_UV8_1X8: + case MEDIA_BUS_FMT_UYVY8_1_5X8: + case MEDIA_BUS_FMT_VYUY8_1_5X8: + case MEDIA_BUS_FMT_YUYV8_1_5X8: + case MEDIA_BUS_FMT_YVYU8_1_5X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_Y10_1X10: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_VYUY10_2X10: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_YVYU10_2X10: + case MEDIA_BUS_FMT_Y12_1X12: + case MEDIA_BUS_FMT_UYVY12_2X12: + case MEDIA_BUS_FMT_VYUY12_2X12: + case MEDIA_BUS_FMT_YUYV12_2X12: + case MEDIA_BUS_FMT_YVYU12_2X12: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_YDYUYDYV8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_VUY8_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_AYUV8_1X32: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + case MEDIA_BUS_FMT_JPEG_1X8: + case MEDIA_BUS_FMT_AHSV8888_1X32: + break; + default: + sdformat->format.code = MEDIA_BUS_FMT_Y8_1X8; + break; + } + if (sdformat->format.field == V4L2_FIELD_ANY) + sdformat->format.field = V4L2_FIELD_NONE; + mutex_lock(&vmux->lock); /* Source pad mirrors active sink pad, no limitations on sink pads */ @@ -197,7 +286,27 @@ static int video_mux_set_format(struct v4l2_subdev *sd, return 0; } +static int video_mux_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + struct v4l2_mbus_framefmt *mbusformat; + unsigned int i; + + mutex_lock(&vmux->lock); + + for (i = 0; i < sd->entity.num_pads; i++) { + mbusformat = v4l2_subdev_get_try_format(sd, cfg, i); + *mbusformat = video_mux_format_mbus_default; + } + + mutex_unlock(&vmux->lock); + + return 0; +} + static const struct v4l2_subdev_pad_ops video_mux_pad_ops = { + .init_cfg = video_mux_init_cfg, .get_fmt = video_mux_get_format, .set_fmt = video_mux_set_format, }; @@ -214,8 +323,8 @@ static int video_mux_probe(struct platform_device *pdev) struct device_node *ep; struct video_mux *vmux; unsigned int num_pads = 0; + unsigned int i; int ret; - int i; vmux = devm_kzalloc(dev, sizeof(*vmux), GFP_KERNEL); if (!vmux) @@ -260,9 +369,11 @@ static int video_mux_probe(struct platform_device *pdev) sizeof(*vmux->format_mbus), GFP_KERNEL); - for (i = 0; i < num_pads - 1; i++) - vmux->pads[i].flags = MEDIA_PAD_FL_SINK; - vmux->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; + for (i = 0; i < num_pads; i++) { + vmux->pads[i].flags = (i < num_pads - 1) ? MEDIA_PAD_FL_SINK + : MEDIA_PAD_FL_SOURCE; + vmux->format_mbus[i] = video_mux_format_mbus_default; + } vmux->subdev.entity.function = MEDIA_ENT_F_VID_MUX; ret = media_entity_pads_init(&vmux->subdev.entity, num_pads, diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 065483e62db4..462099a141e4 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -140,6 +140,9 @@ static struct vim2m_fmt *find_format(struct v4l2_format *f) struct vim2m_dev { struct v4l2_device v4l2_dev; struct video_device vfd; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_device mdev; +#endif atomic_t num_inst; struct mutex dev_mutex; @@ -1016,11 +1019,10 @@ static int vim2m_probe(struct platform_device *pdev) ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto unreg_dev; + goto unreg_v4l2; } video_set_drvdata(vfd, dev); - snprintf(vfd->name, sizeof(vfd->name), "%s", vim2m_videodev.name); v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n", vfd->num); @@ -1031,15 +1033,39 @@ static int vim2m_probe(struct platform_device *pdev) if (IS_ERR(dev->m2m_dev)) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(dev->m2m_dev); - goto err_m2m; + goto unreg_dev; + } + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->mdev.dev = &pdev->dev; + strlcpy(dev->mdev.model, "vim2m", sizeof(dev->mdev.model)); + media_device_init(&dev->mdev); + dev->v4l2_dev.mdev = &dev->mdev; + + ret = v4l2_m2m_register_media_controller(dev->m2m_dev, + vfd, MEDIA_ENT_F_PROC_VIDEO_SCALER); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); + goto unreg_m2m; } + ret = media_device_register(&dev->mdev); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register mem2mem media device\n"); + goto unreg_m2m_mc; + } +#endif return 0; -err_m2m: +#ifdef CONFIG_MEDIA_CONTROLLER +unreg_m2m_mc: + v4l2_m2m_unregister_media_controller(dev->m2m_dev); +unreg_m2m: v4l2_m2m_release(dev->m2m_dev); - video_unregister_device(&dev->vfd); +#endif unreg_dev: + video_unregister_device(&dev->vfd); +unreg_v4l2: v4l2_device_unregister(&dev->v4l2_dev); return ret; @@ -1050,6 +1076,12 @@ static int vim2m_remove(struct platform_device *pdev) struct vim2m_dev *dev = platform_get_drvdata(pdev); v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME); + +#ifdef CONFIG_MEDIA_CONTROLLER + media_device_unregister(&dev->mdev); + v4l2_m2m_unregister_media_controller(dev->m2m_dev); + media_device_cleanup(&dev->mdev); +#endif v4l2_m2m_release(dev->m2m_dev); del_timer_sync(&dev->timer); video_unregister_device(&dev->vfd); diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index fe088a953860..9246f265de31 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -328,7 +328,6 @@ static int vimc_probe(struct platform_device *pdev) if (ret) { media_device_cleanup(&vimc->mdev); vimc_rm_subdevs(vimc); - kfree(vimc); return ret; } diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 6b0bfa091592..5429193fbb91 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -295,7 +295,7 @@ static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - dev->gain->val = dev->jiffies_vid_cap & 0xff; + dev->gain->val = (jiffies_to_msecs(jiffies) / 1000) & 0xff; break; } return 0; diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c index 3fdb280c36ca..f06003bb8e42 100644 --- a/drivers/media/platform/vivid/vivid-kthread-cap.c +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c @@ -477,7 +477,7 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) /* Updates stream time, only update at the start of a new frame. */ if (dev->field_cap != V4L2_FIELD_ALTERNATE || - (buf->vb.sequence & 1) == 0) + (dev->vid_cap_seq_count & 1) == 0) dev->ms_vid_cap = jiffies_to_msecs(jiffies - dev->jiffies_vid_cap); diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 33f632331474..56c62122a81a 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -53,6 +53,7 @@ struct vsp1_uif; #define VSP1_HAS_HGO (1 << 7) #define VSP1_HAS_HGT (1 << 8) #define VSP1_HAS_BRS (1 << 9) +#define VSP1_HAS_EXT_DL (1 << 10) struct vsp1_device_info { u32 version; @@ -68,6 +69,8 @@ struct vsp1_device_info { bool uapi; }; +#define vsp1_feature(vsp1, f) ((vsp1)->info->features & (f)) + struct vsp1_device { struct device *dev; const struct vsp1_device_info *info; diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index d9b9cdd8fbe2..26289adaf658 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -22,29 +22,79 @@ #define VSP1_DLH_INT_ENABLE (1 << 1) #define VSP1_DLH_AUTO_START (1 << 0) +#define VSP1_DLH_EXT_PRE_CMD_EXEC (1 << 9) +#define VSP1_DLH_EXT_POST_CMD_EXEC (1 << 8) + struct vsp1_dl_header_list { u32 num_bytes; u32 addr; -} __attribute__((__packed__)); +} __packed; struct vsp1_dl_header { u32 num_lists; struct vsp1_dl_header_list lists[8]; u32 next_header; u32 flags; -} __attribute__((__packed__)); +} __packed; + +/** + * struct vsp1_dl_ext_header - Extended display list header + * @padding: padding zero bytes for alignment + * @pre_ext_dl_num_cmd: number of pre-extended command bodies to parse + * @flags: enables or disables execution of the pre and post command + * @pre_ext_dl_plist: start address of pre-extended display list bodies + * @post_ext_dl_num_cmd: number of post-extended command bodies to parse + * @post_ext_dl_plist: start address of post-extended display list bodies + */ +struct vsp1_dl_ext_header { + u32 padding; + + /* + * The datasheet represents flags as stored before pre_ext_dl_num_cmd, + * expecting 32-bit accesses. The flags are appropriate to the whole + * header, not just the pre_ext command, and thus warrant being + * separated out. Due to byte ordering, and representing as 16 bit + * values here, the flags must be positioned after the + * pre_ext_dl_num_cmd. + */ + u16 pre_ext_dl_num_cmd; + u16 flags; + u32 pre_ext_dl_plist; + + u32 post_ext_dl_num_cmd; + u32 post_ext_dl_plist; +} __packed; + +struct vsp1_dl_header_extended { + struct vsp1_dl_header header; + struct vsp1_dl_ext_header ext; +} __packed; struct vsp1_dl_entry { u32 addr; u32 data; -} __attribute__((__packed__)); +} __packed; + +/** + * struct vsp1_pre_ext_dl_body - Pre Extended Display List Body + * @opcode: Extended display list command operation code + * @flags: Pre-extended command flags. These are specific to each command + * @address_set: Source address set pointer. Must have 16-byte alignment + * @reserved: Zero bits for alignment. + */ +struct vsp1_pre_ext_dl_body { + u32 opcode; + u32 flags; + u32 address_set; + u32 reserved; +} __packed; /** * struct vsp1_dl_body - Display list body * @list: entry in the display list list of bodies * @free: entry in the pool free body list + * @refcnt: reference tracking for the body * @pool: pool to which this body belongs - * @vsp1: the VSP1 device * @entries: array of entries * @dma: DMA address of the entries * @size: size of the DMA memory in bytes @@ -58,7 +108,6 @@ struct vsp1_dl_body { refcount_t refcnt; struct vsp1_dl_body_pool *pool; - struct vsp1_device *vsp1; struct vsp1_dl_entry *entries; dma_addr_t dma; @@ -93,13 +142,40 @@ struct vsp1_dl_body_pool { }; /** + * struct vsp1_cmd_pool - Display List commands pool + * @dma: DMA address of the entries + * @size: size of the full DMA memory pool in bytes + * @mem: CPU memory pointer for the pool + * @cmds: Array of command structures for the pool + * @free: Free pool entries + * @lock: Protects the free list + * @vsp1: the VSP1 device + */ +struct vsp1_dl_cmd_pool { + /* DMA allocation */ + dma_addr_t dma; + size_t size; + void *mem; + + struct vsp1_dl_ext_cmd *cmds; + struct list_head free; + + spinlock_t lock; + + struct vsp1_device *vsp1; +}; + +/** * struct vsp1_dl_list - Display list * @list: entry in the display list manager lists * @dlm: the display list manager - * @header: display list header, NULL for headerless lists + * @header: display list header + * @extension: extended display list header. NULL for normal lists * @dma: DMA address for the header * @body0: first display list body * @bodies: list of extra display list bodies + * @pre_cmd: pre command to be issued through extended dl header + * @post_cmd: post command to be issued through extended dl header * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain * @internal: whether the display list is used for internal purpose @@ -109,26 +185,24 @@ struct vsp1_dl_list { struct vsp1_dl_manager *dlm; struct vsp1_dl_header *header; + struct vsp1_dl_ext_header *extension; dma_addr_t dma; struct vsp1_dl_body *body0; struct list_head bodies; + struct vsp1_dl_ext_cmd *pre_cmd; + struct vsp1_dl_ext_cmd *post_cmd; + bool has_chain; struct list_head chain; bool internal; }; -enum vsp1_dl_mode { - VSP1_DL_MODE_HEADER, - VSP1_DL_MODE_HEADERLESS, -}; - /** * struct vsp1_dl_manager - Display List manager * @index: index of the related WPF - * @mode: display list operation mode (header or headerless) * @singleshot: execute the display list in single-shot mode * @vsp1: the VSP1 device * @lock: protects the free, active, queued, and pending lists @@ -137,10 +211,10 @@ enum vsp1_dl_mode { * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware * @pool: body pool for the display list bodies + * @cmdpool: commands pool for extended display list */ struct vsp1_dl_manager { unsigned int index; - enum vsp1_dl_mode mode; bool singleshot; struct vsp1_device *vsp1; @@ -151,6 +225,7 @@ struct vsp1_dl_manager { struct vsp1_dl_list *pending; struct vsp1_dl_body_pool *pool; + struct vsp1_dl_cmd_pool *cmdpool; }; /* ----------------------------------------------------------------------------- @@ -314,12 +389,164 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) } /* ----------------------------------------------------------------------------- + * Display List Extended Command Management + */ + +enum vsp1_extcmd_type { + VSP1_EXTCMD_AUTODISP, + VSP1_EXTCMD_AUTOFLD, +}; + +struct vsp1_extended_command_info { + u16 opcode; + size_t body_size; +}; + +static const struct vsp1_extended_command_info vsp1_extended_commands[] = { + [VSP1_EXTCMD_AUTODISP] = { 0x02, 96 }, + [VSP1_EXTCMD_AUTOFLD] = { 0x03, 160 }, +}; + +/** + * vsp1_dl_cmd_pool_create - Create a pool of commands from a single allocation + * @vsp1: The VSP1 device + * @type: The command pool type + * @num_cmds: The number of commands to allocate + * + * Allocate a pool of commands each with enough memory to contain the private + * data of each command. The allocation sizes are dependent upon the command + * type. + * + * Return a pointer to the pool on success or NULL if memory can't be allocated. + */ +static struct vsp1_dl_cmd_pool * +vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, + unsigned int num_cmds) +{ + struct vsp1_dl_cmd_pool *pool; + unsigned int i; + size_t cmd_size; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; + + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->free); + + pool->cmds = kcalloc(num_cmds, sizeof(*pool->cmds), GFP_KERNEL); + if (!pool->cmds) { + kfree(pool); + return NULL; + } + + cmd_size = sizeof(struct vsp1_pre_ext_dl_body) + + vsp1_extended_commands[type].body_size; + cmd_size = ALIGN(cmd_size, 16); + + pool->size = cmd_size * num_cmds; + pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, + GFP_KERNEL); + if (!pool->mem) { + kfree(pool->cmds); + kfree(pool); + return NULL; + } + + for (i = 0; i < num_cmds; ++i) { + struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i]; + size_t cmd_offset = i * cmd_size; + /* data_offset must be 16 byte aligned for DMA. */ + size_t data_offset = sizeof(struct vsp1_pre_ext_dl_body) + + cmd_offset; + + cmd->pool = pool; + cmd->opcode = vsp1_extended_commands[type].opcode; + + /* + * TODO: Auto-disp can utilise more than one extended body + * command per cmd. + */ + cmd->num_cmds = 1; + cmd->cmds = pool->mem + cmd_offset; + cmd->cmd_dma = pool->dma + cmd_offset; + + cmd->data = pool->mem + data_offset; + cmd->data_dma = pool->dma + data_offset; + + list_add_tail(&cmd->free, &pool->free); + } + + return pool; +} + +static +struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool) +{ + struct vsp1_dl_ext_cmd *cmd = NULL; + unsigned long flags; + + spin_lock_irqsave(&pool->lock, flags); + + if (!list_empty(&pool->free)) { + cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd, + free); + list_del(&cmd->free); + } + + spin_unlock_irqrestore(&pool->lock, flags); + + return cmd; +} + +static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd) +{ + unsigned long flags; + + if (!cmd) + return; + + /* Reset flags, these mark data usage. */ + cmd->flags = 0; + + spin_lock_irqsave(&cmd->pool->lock, flags); + list_add_tail(&cmd->free, &cmd->pool->free); + spin_unlock_irqrestore(&cmd->pool->lock, flags); +} + +static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool) +{ + if (!pool) + return; + + if (pool->mem) + dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, + pool->dma); + + kfree(pool->cmds); + kfree(pool); +} + +struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl) +{ + struct vsp1_dl_manager *dlm = dl->dlm; + + if (dl->pre_cmd) + return dl->pre_cmd; + + dl->pre_cmd = vsp1_dl_ext_cmd_get(dlm->cmdpool); + + return dl->pre_cmd; +} + +/* ---------------------------------------------------------------------------- * Display List Transaction Management */ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; + size_t header_offset; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) @@ -332,16 +559,14 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) dl->body0 = vsp1_dl_body_get(dlm->pool); if (!dl->body0) return NULL; - if (dlm->mode == VSP1_DL_MODE_HEADER) { - size_t header_offset = dl->body0->max_entries - * sizeof(*dl->body0->entries); - dl->header = ((void *)dl->body0->entries) + header_offset; - dl->dma = dl->body0->dma + header_offset; + header_offset = dl->body0->max_entries * sizeof(*dl->body0->entries); - memset(dl->header, 0, sizeof(*dl->header)); - dl->header->lists[0].addr = dl->body0->dma; - } + dl->header = ((void *)dl->body0->entries) + header_offset; + dl->dma = dl->body0->dma + header_offset; + + memset(dl->header, 0, sizeof(*dl->header)); + dl->header->lists[0].addr = dl->body0->dma; return dl; } @@ -398,7 +623,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) /* This function must be called with the display list manager lock held.*/ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) { - struct vsp1_dl_list *dl_child; + struct vsp1_dl_list *dl_next; if (!dl) return; @@ -408,14 +633,20 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) * hardware operation. */ if (dl->has_chain) { - list_for_each_entry(dl_child, &dl->chain, chain) - __vsp1_dl_list_put(dl_child); + list_for_each_entry(dl_next, &dl->chain, chain) + __vsp1_dl_list_put(dl_next); } dl->has_chain = false; vsp1_dl_list_bodies_put(dl); + vsp1_dl_ext_cmd_put(dl->pre_cmd); + vsp1_dl_ext_cmd_put(dl->post_cmd); + + dl->pre_cmd = NULL; + dl->post_cmd = NULL; + /* * body0 is reused as as an optimisation as presently every display list * has at least one body, thus we reinitialise the entries list. @@ -473,16 +704,9 @@ struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) * * The reference must be explicitly released by a call to vsp1_dl_body_put() * when the body isn't needed anymore. - * - * Additional bodies are only usable for display lists in header mode. - * Attempting to add a body to a header-less display list will return an error. */ int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { - /* Multi-body lists are only available in header mode. */ - if (dl->dlm->mode != VSP1_DL_MODE_HEADER) - return -EINVAL; - refcount_inc(&dlb->refcnt); list_add_tail(&dlb->list, &dl->bodies); @@ -503,22 +727,23 @@ int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) * Adding a display list to a chain passes ownership of the display list to * the head display list item. The chain is released when the head dl item is * put back with __vsp1_dl_list_put(). - * - * Chained display lists are only usable in header mode. Attempts to add a - * display list to a chain in header-less mode will return an error. */ int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl) { - /* Chained lists are only available in header mode. */ - if (head->dlm->mode != VSP1_DL_MODE_HEADER) - return -EINVAL; - head->has_chain = true; list_add_tail(&dl->chain, &head->chain); return 0; } +static void vsp1_dl_ext_cmd_fill_header(struct vsp1_dl_ext_cmd *cmd) +{ + cmd->cmds[0].opcode = cmd->opcode; + cmd->cmds[0].flags = cmd->flags; + cmd->cmds[0].address_set = cmd->data_dma; + cmd->cmds[0].reserved = 0; +} + static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) { struct vsp1_dl_manager *dlm = dl->dlm; @@ -571,6 +796,27 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) */ dl->header->flags = VSP1_DLH_INT_ENABLE; } + + if (!dl->extension) + return; + + dl->extension->flags = 0; + + if (dl->pre_cmd) { + dl->extension->pre_ext_dl_plist = dl->pre_cmd->cmd_dma; + dl->extension->pre_ext_dl_num_cmd = dl->pre_cmd->num_cmds; + dl->extension->flags |= VSP1_DLH_EXT_PRE_CMD_EXEC; + + vsp1_dl_ext_cmd_fill_header(dl->pre_cmd); + } + + if (dl->post_cmd) { + dl->extension->post_ext_dl_plist = dl->post_cmd->cmd_dma; + dl->extension->post_ext_dl_num_cmd = dl->post_cmd->num_cmds; + dl->extension->flags |= VSP1_DLH_EXT_POST_CMD_EXEC; + + vsp1_dl_ext_cmd_fill_header(dl->post_cmd); + } } static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) @@ -581,17 +827,10 @@ static bool vsp1_dl_list_hw_update_pending(struct vsp1_dl_manager *dlm) return false; /* - * Check whether the VSP1 has taken the update. In headerless mode the - * hardware indicates this by clearing the UPD bit in the DL_BODY_SIZE - * register, and in header mode by clearing the UPDHDR bit in the CMD - * register. + * Check whether the VSP1 has taken the update. The hardware indicates + * this by clearing the UPDHDR bit in the CMD register. */ - if (dlm->mode == VSP1_DL_MODE_HEADERLESS) - return !!(vsp1_read(vsp1, VI6_DL_BODY_SIZE) - & VI6_DL_BODY_SIZE_UPD); - else - return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) - & VI6_CMD_UPDHDR); + return !!(vsp1_read(vsp1, VI6_CMD(dlm->index)) & VI6_CMD_UPDHDR); } static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) @@ -599,26 +838,14 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_device *vsp1 = dlm->vsp1; - if (dlm->mode == VSP1_DL_MODE_HEADERLESS) { - /* - * In headerless mode, program the hardware directly with the - * display list body address and size and set the UPD bit. The - * bit will be cleared by the hardware when the display list - * processing starts. - */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma); - vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->body0->num_entries * sizeof(*dl->header->lists))); - } else { - /* - * In header mode, program the display list header address. If - * the hardware is idle (single-shot mode or first frame in - * continuous mode) it will then be started independently. If - * the hardware is operating, the VI6_DL_HDR_REF_ADDR register - * will be updated with the display list address. - */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); - } + /* + * Program the display list header address. If the hardware is idle + * (single-shot mode or first frame in continuous mode) it will then be + * started independently. If the hardware is operating, the + * VI6_DL_HDR_REF_ADDR register will be updated with the display list + * address. + */ + vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); } static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) @@ -673,18 +900,16 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) { struct vsp1_dl_manager *dlm = dl->dlm; - struct vsp1_dl_list *dl_child; + struct vsp1_dl_list *dl_next; unsigned long flags; - if (dlm->mode == VSP1_DL_MODE_HEADER) { - /* Fill the header for the head and chained display lists. */ - vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); + /* Fill the header for the head and chained display lists. */ + vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); - list_for_each_entry(dl_child, &dl->chain, chain) { - bool last = list_is_last(&dl_child->chain, &dl->chain); + list_for_each_entry(dl_next, &dl->chain, chain) { + bool last = list_is_last(&dl_next->chain, &dl->chain); - vsp1_dl_list_fill_header(dl_child, last); - } + vsp1_dl_list_fill_header(dl_next, last); } dl->internal = internal; @@ -713,7 +938,7 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) * has completed at frame end. If the flag is not returned display list * completion has been delayed by one frame because the display list commit * raced with the frame end interrupt. The function always returns with the flag - * set in header mode as display list processing is then not continuous and + * set in single-shot mode as display list processing is then not continuous and * races never occur. * * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list @@ -722,6 +947,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) */ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { + struct vsp1_device *vsp1 = dlm->vsp1; + u32 status = vsp1_read(vsp1, VI6_STATUS); unsigned int flags = 0; spin_lock(&dlm->lock); @@ -747,6 +974,14 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) goto done; /* + * Progressive streams report only TOP fields. If we have a BOTTOM + * field, we are interlaced, and expect the frame to complete on the + * next frame end interrupt. + */ + if (status & VI6_STATUS_FLD_STD(dlm->index)) + goto done; + + /* * The device starts processing the queued display list right after the * frame end interrupt. The display list thus becomes active. */ @@ -781,16 +1016,17 @@ done: /* Hardware Setup */ void vsp1_dlm_setup(struct vsp1_device *vsp1) { + unsigned int i; u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 | VI6_DL_CTRL_DLE; + u32 ext_dl = (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT) + | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT; - /* - * The DRM pipeline operates with display lists in Continuous Frame - * Mode, all other pipelines use manual start. - */ - if (vsp1->drm) - ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0; + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { + for (i = 0; i < vsp1->info->wpf_count; ++i) + vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl); + } vsp1_write(vsp1, VI6_DL_CTRL, ctrl); vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS); @@ -831,8 +1067,6 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, return NULL; dlm->index = index; - dlm->mode = index == 0 && !vsp1->info->uapi - ? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER; dlm->singleshot = vsp1->info->uapi; dlm->vsp1 = vsp1; @@ -841,14 +1075,16 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, /* * Initialize the display list body and allocate DMA memory for the body - * and the optional header. Both are allocated together to avoid memory + * and the header. Both are allocated together to avoid memory * fragmentation, with the header located right after the body in * memory. An extra body is allocated on top of the prealloc to account * for the cached body used by the vsp1_pipeline object. */ - header_size = dlm->mode == VSP1_DL_MODE_HEADER - ? ALIGN(sizeof(struct vsp1_dl_header), 8) - : 0; + header_size = vsp1_feature(vsp1, VSP1_HAS_EXT_DL) ? + sizeof(struct vsp1_dl_header_extended) : + sizeof(struct vsp1_dl_header); + + header_size = ALIGN(header_size, 8); dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, VSP1_DL_NUM_ENTRIES, header_size); @@ -859,12 +1095,28 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, struct vsp1_dl_list *dl; dl = vsp1_dl_list_alloc(dlm); - if (!dl) + if (!dl) { + vsp1_dlm_destroy(dlm); return NULL; + } + + /* The extended header immediately follows the header. */ + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) + dl->extension = (void *)dl->header + + sizeof(*dl->header); list_add_tail(&dl->list, &dlm->free); } + if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { + dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1, + VSP1_EXTCMD_AUTOFLD, prealloc); + if (!dlm->cmdpool) { + vsp1_dlm_destroy(dlm); + return NULL; + } + } + return dlm; } @@ -881,4 +1133,5 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) } vsp1_dl_body_pool_destroy(dlm->pool); + vsp1_dl_ext_cmd_pool_destroy(dlm->cmdpool); } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 7dba0469c92e..125750dc8b5c 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -20,6 +20,33 @@ struct vsp1_dl_manager; #define VSP1_DL_FRAME_END_COMPLETED BIT(0) #define VSP1_DL_FRAME_END_INTERNAL BIT(1) +/** + * struct vsp1_dl_ext_cmd - Extended Display command + * @pool: pool to which this command belongs + * @free: entry in the pool of free commands list + * @opcode: command type opcode + * @flags: flags used by the command + * @cmds: array of command bodies for this extended cmd + * @num_cmds: quantity of commands in @cmds array + * @cmd_dma: DMA address of the command body + * @data: memory allocation for command-specific data + * @data_dma: DMA address for command-specific data + */ +struct vsp1_dl_ext_cmd { + struct vsp1_dl_cmd_pool *pool; + struct list_head free; + + u8 opcode; + u32 flags; + + struct vsp1_pre_ext_dl_body *cmds; + unsigned int num_cmds; + dma_addr_t cmd_dma; + + void *data; + dma_addr_t data_dma; +}; + void vsp1_dlm_setup(struct vsp1_device *vsp1); struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, @@ -33,6 +60,7 @@ struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm); struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); void vsp1_dl_list_put(struct vsp1_dl_list *dl); struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl); +struct vsp1_dl_ext_cmd *vsp1_dl_get_pre_cmd(struct vsp1_dl_list *dl); void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal); struct vsp1_dl_body_pool * diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a99fc0ced7a7..b9c0f695d002 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -670,9 +670,11 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe->width = cfg->width; drm_pipe->height = cfg->height; + pipe->interlaced = cfg->interlaced; - dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n", - __func__, pipe_index, cfg->width, cfg->height); + dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u%s\n", + __func__, pipe_index, cfg->width, cfg->height, + pipe->interlaced ? "i" : ""); mutex_lock(&vsp1->drm->lock); @@ -803,7 +805,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, */ fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat); if (!fmtinfo) { - dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", + dev_dbg(vsp1->dev, "Unsupported pixel format %08x for RPF\n", cfg->pixelformat); return -EINVAL; } diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 5d82f6ee56ea..b6619c9c18bb 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -265,7 +265,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } /* Instantiate all the entities. */ - if (vsp1->info->features & VSP1_HAS_BRS) { + if (vsp1_feature(vsp1, VSP1_HAS_BRS)) { vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS); if (IS_ERR(vsp1->brs)) { ret = PTR_ERR(vsp1->brs); @@ -275,7 +275,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->brs->entity.list_dev, &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_BRU) { + if (vsp1_feature(vsp1, VSP1_HAS_BRU)) { vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU); if (IS_ERR(vsp1->bru)) { ret = PTR_ERR(vsp1->bru); @@ -285,7 +285,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_CLU) { + if (vsp1_feature(vsp1, VSP1_HAS_CLU)) { vsp1->clu = vsp1_clu_create(vsp1); if (IS_ERR(vsp1->clu)) { ret = PTR_ERR(vsp1->clu); @@ -311,7 +311,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->info->features & VSP1_HAS_HGO && vsp1->info->uapi) { + if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) { vsp1->hgo = vsp1_hgo_create(vsp1); if (IS_ERR(vsp1->hgo)) { ret = PTR_ERR(vsp1->hgo); @@ -322,7 +322,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) &vsp1->entities); } - if (vsp1->info->features & VSP1_HAS_HGT && vsp1->info->uapi) { + if (vsp1_feature(vsp1, VSP1_HAS_HGT) && vsp1->info->uapi) { vsp1->hgt = vsp1_hgt_create(vsp1); if (IS_ERR(vsp1->hgt)) { ret = PTR_ERR(vsp1->hgt); @@ -353,7 +353,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } } - if (vsp1->info->features & VSP1_HAS_LUT) { + if (vsp1_feature(vsp1, VSP1_HAS_LUT)) { vsp1->lut = vsp1_lut_create(vsp1); if (IS_ERR(vsp1->lut)) { ret = PTR_ERR(vsp1->lut); @@ -387,7 +387,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } } - if (vsp1->info->features & VSP1_HAS_SRU) { + if (vsp1_feature(vsp1, VSP1_HAS_SRU)) { vsp1->sru = vsp1_sru_create(vsp1); if (IS_ERR(vsp1->sru)) { ret = PTR_ERR(vsp1->sru); @@ -537,7 +537,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED); - if (vsp1->info->features & VSP1_HAS_BRS) + if (vsp1_feature(vsp1, VSP1_HAS_BRS)) vsp1_write(vsp1, VI6_DPR_ILV_BRS_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | @@ -754,7 +754,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, .model = "VSP2-D", .gen = 3, - .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP | VSP1_HAS_EXT_DL, .lif_count = 1, .rpf_count = 5, .uif_count = 1, @@ -774,7 +774,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPDL_GEN3, .model = "VSP2-DL", .gen = 3, - .features = VSP1_HAS_BRS | VSP1_HAS_BRU, + .features = VSP1_HAS_BRS | VSP1_HAS_BRU | VSP1_HAS_EXT_DL, .lif_count = 2, .rpf_count = 5, .uif_count = 2, diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 743d8f0db45c..ae646c9ef337 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -104,6 +104,7 @@ struct vsp1_partition { * @entities: list of entities in the pipeline * @stream_config: cached stream configuration for video pipelines * @configured: when false the @stream_config shall be written to the hardware + * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline @@ -142,6 +143,7 @@ struct vsp1_pipeline { struct vsp1_dl_body *stream_config; bool configured; + bool interlaced; unsigned int partitions; struct vsp1_partition *partition; diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 0d249ff9f564..3738ff2f7b85 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -28,6 +28,7 @@ #define VI6_SRESET_SRTS(n) (1 << (n)) #define VI6_STATUS 0x0038 +#define VI6_STATUS_FLD_STD(n) (1 << ((n) + 28)) #define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8)) #define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12) @@ -72,7 +73,7 @@ #define VI6_DL_SWAP_WDS (1 << 1) #define VI6_DL_SWAP_BTS (1 << 0) -#define VI6_DL_EXT_CTRL 0x011c +#define VI6_DL_EXT_CTRL(n) (0x011c + (n) * 36) #define VI6_DL_EXT_CTRL_NWE (1 << 16) #define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8) #define VI6_DL_EXT_CTRL_POLINT_SHIFT 8 @@ -80,6 +81,8 @@ #define VI6_DL_EXT_CTRL_EXPRI (1 << 4) #define VI6_DL_EXT_CTRL_EXT (1 << 0) +#define VI6_DL_EXT_AUTOFLD_INT BIT(0) + #define VI6_DL_BODY_SIZE 0x0120 #define VI6_DL_BODY_SIZE_UPD (1 << 24) #define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 69e5fe6e6b50..f8005b60b9d2 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -20,6 +20,18 @@ #define RPF_MAX_WIDTH 8190 #define RPF_MAX_HEIGHT 8190 +/* Pre extended display list command data structure. */ +struct vsp1_extcmd_auto_fld_body { + u32 top_y0; + u32 bottom_y0; + u32 top_c0; + u32 bottom_c0; + u32 top_c1; + u32 bottom_c1; + u32 reserved0; + u32 reserved1; +} __packed; + /* ----------------------------------------------------------------------------- * Device Access */ @@ -64,6 +76,14 @@ static void rpf_configure_stream(struct vsp1_entity *entity, pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + /* + * pstride has both STRIDE_Y and STRIDE_C, but multiplying the whole + * of pstride by 2 is conveniently OK here as we are multiplying both + * values. + */ + if (pipe->interlaced) + pstride *= 2; + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ @@ -100,6 +120,9 @@ static void rpf_configure_stream(struct vsp1_entity *entity, top = compose->top; } + if (pipe->interlaced) + top /= 2; + vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); @@ -169,6 +192,36 @@ static void rpf_configure_stream(struct vsp1_entity *entity, } +static void vsp1_rpf_configure_autofld(struct vsp1_rwpf *rpf, + struct vsp1_dl_list *dl) +{ + const struct v4l2_pix_format_mplane *format = &rpf->format; + struct vsp1_dl_ext_cmd *cmd; + struct vsp1_extcmd_auto_fld_body *auto_fld; + u32 offset_y, offset_c; + + cmd = vsp1_dl_get_pre_cmd(dl); + if (WARN_ONCE(!cmd, "Failed to obtain an autofld cmd")) + return; + + /* Re-index our auto_fld to match the current RPF. */ + auto_fld = cmd->data; + auto_fld = &auto_fld[rpf->entity.index]; + + auto_fld->top_y0 = rpf->mem.addr[0]; + auto_fld->top_c0 = rpf->mem.addr[1]; + auto_fld->top_c1 = rpf->mem.addr[2]; + + offset_y = format->plane_fmt[0].bytesperline; + offset_c = format->plane_fmt[1].bytesperline; + + auto_fld->bottom_y0 = rpf->mem.addr[0] + offset_y; + auto_fld->bottom_c0 = rpf->mem.addr[1] + offset_c; + auto_fld->bottom_c1 = rpf->mem.addr[2] + offset_c; + + cmd->flags |= VI6_DL_EXT_AUTOFLD_INT | BIT(16 + rpf->entity.index); +} + static void rpf_configure_frame(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, @@ -221,6 +274,11 @@ static void rpf_configure_partition(struct vsp1_entity *entity, crop.left += pipe->partition->rpf.left; } + if (pipe->interlaced) { + crop.height = round_down(crop.height / 2, fmtinfo->vsub); + crop.top = round_down(crop.top / 2, fmtinfo->vsub); + } + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE, (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); @@ -249,9 +307,17 @@ static void rpf_configure_partition(struct vsp1_entity *entity, fmtinfo->swap_uv) swap(mem.addr[1], mem.addr[2]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); - vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); + /* + * Interlaced pipelines will use the extended pre-cmd to process + * SRCM_ADDR_{Y,C0,C1} + */ + if (pipe->interlaced) { + vsp1_rpf_configure_autofld(rpf, dl); + } else { + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); + } } static void rpf_partition(struct vsp1_entity *entity, diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 23c8f706b3f2..c2a1a7f97e26 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -141,13 +141,13 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) if (wpf->entity.index != 0) { /* Only WPF0 supports flipping. */ num_flip_ctrls = 0; - } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { + } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP)) { /* * When horizontal flip is supported the WPF implements three * controls (horizontal flip, vertical flip and rotation). */ num_flip_ctrls = 3; - } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { + } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_VFLIP)) { /* * When only vertical flip is supported the WPF implements a * single control (vertical flip). @@ -276,7 +276,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); - if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && + if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && wpf->entity.index == 0) vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, VI6_WPF_ROT_CTRL_LN16 | diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 8f9f8dfc3497..11aa94f189cb 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1096,7 +1096,7 @@ static __poll_t wl1273_fm_fops_poll(struct file *file, struct wl1273_core *core = radio->core; if (radio->owner && radio->owner != file) - return -EBUSY; + return EPOLLERR; radio->owner = file; diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 05c66701a899..1ebbf0217142 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -370,9 +370,6 @@ static int si4713_transfer(struct i2c_adapter *i2c_adapter, int retval = -EINVAL; int i; - if (num <= 0) - return 0; - for (i = 0; i < num; i++) { if (msgs[i].flags & I2C_M_RD) retval = si4713_i2c_read(radio, msgs[i].buf, msgs[i].len); diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index b5b9d87ba75c..fbec1a13dc6a 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -610,9 +610,9 @@ static int e4000_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops e4000_dvb_tuner_ops = { .info = { - .name = "Elonics E4000", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "Elonics E4000", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = e4000_dvb_init, diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 145407dee3db..a983899c6b0b 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -472,10 +472,10 @@ static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) static const struct dvb_tuner_ops fc0011_tuner_ops = { .info = { - .name = "Fitipower FC0011", + .name = "Fitipower FC0011", - .frequency_min = 45000000, - .frequency_max = 1000000000, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 1000 * MHz, }, .release = fc0011_release, diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 625ac6f51c39..e992b98ae5bc 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -415,11 +415,10 @@ exit: static const struct dvb_tuner_ops fc0012_tuner_ops = { .info = { - .name = "Fitipower FC0012", + .name = "Fitipower FC0012", - .frequency_min = 37000000, /* estimate */ - .frequency_max = 862000000, /* estimate */ - .frequency_step = 0, + .frequency_min_hz = 37 * MHz, /* estimate */ + .frequency_max_hz = 862 * MHz, /* estimate */ }, .release = fc0012_release, diff --git a/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c index e606118d1a9b..fc62afb1450d 100644 --- a/drivers/media/tuners/fc0013.c +++ b/drivers/media/tuners/fc0013.c @@ -574,11 +574,10 @@ exit: static const struct dvb_tuner_ops fc0013_tuner_ops = { .info = { - .name = "Fitipower FC0013", + .name = "Fitipower FC0013", - .frequency_min = 37000000, /* estimate */ - .frequency_max = 1680000000, /* CHECK */ - .frequency_step = 0, + .frequency_min_hz = 37 * MHz, /* estimate */ + .frequency_max_hz = 1680 * MHz, /* CHECK */ }, .release = fc0013_release, diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 743184ae0d26..db26892aac84 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -355,9 +355,9 @@ static int fc2580_dvb_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops fc2580_dvb_tuner_ops = { .info = { - .name = "FCI FC2580", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "FCI FC2580", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = fc2580_dvb_init, diff --git a/drivers/media/tuners/it913x.c b/drivers/media/tuners/it913x.c index 27e5bc1c3cb5..b5eb39921e95 100644 --- a/drivers/media/tuners/it913x.c +++ b/drivers/media/tuners/it913x.c @@ -375,9 +375,9 @@ err: static const struct dvb_tuner_ops it913x_tuner_ops = { .info = { - .name = "ITE IT913X", - .frequency_min = 174000000, - .frequency_max = 862000000, + .name = "ITE IT913X", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = it913x_init, diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 9f3e0fd4cad9..3df2f23a40be 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -569,9 +569,9 @@ err: static const struct dvb_tuner_ops m88rs6000t_tuner_ops = { .info = { - .name = "Montage M88RS6000 Internal Tuner", - .frequency_min = 950000, - .frequency_max = 2150000, + .name = "Montage M88RS6000 Internal Tuner", + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = m88rs6000t_init, diff --git a/drivers/media/tuners/max2165.c b/drivers/media/tuners/max2165.c index 20ceb72e530b..721d8f722efb 100644 --- a/drivers/media/tuners/max2165.c +++ b/drivers/media/tuners/max2165.c @@ -377,10 +377,10 @@ static void max2165_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops max2165_tuner_ops = { .info = { - .name = "Maxim MAX2165", - .frequency_min = 470000000, - .frequency_max = 862000000, - .frequency_step = 50000, + .name = "Maxim MAX2165", + .frequency_min_hz = 470 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = max2165_release, diff --git a/drivers/media/tuners/mc44s803.c b/drivers/media/tuners/mc44s803.c index 403c6b2aa53b..2023e081d9ad 100644 --- a/drivers/media/tuners/mc44s803.c +++ b/drivers/media/tuners/mc44s803.c @@ -300,10 +300,10 @@ static int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops mc44s803_tuner_ops = { .info = { - .name = "Freescale MC44S803", - .frequency_min = 48000000, - .frequency_max = 1000000000, - .frequency_step = 100000, + .name = "Freescale MC44S803", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 1000 * MHz, + .frequency_step_hz = 100 * kHz, }, .release = mc44s803_release, diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 3d3c6815b6a7..4ace77cfe285 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -395,10 +395,10 @@ static void mt2060_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2060_tuner_ops = { .info = { - .name = "Microtune MT2060", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "Microtune MT2060", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2060_release, diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 80dc3e241b4a..f4c8a7293ebb 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -2200,10 +2200,9 @@ static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) static const struct dvb_tuner_ops mt2063_ops = { .info = { .name = "MT2063 Silicon Tuner", - .frequency_min = 45000000, - .frequency_max = 865000000, - .frequency_step = 0, - }, + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 865 * MHz, + }, .init = mt2063_init, .sleep = MT2063_Sleep, diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c index 659bf19dc434..086a7b7cf634 100644 --- a/drivers/media/tuners/mt2131.c +++ b/drivers/media/tuners/mt2131.c @@ -235,10 +235,10 @@ static void mt2131_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2131_tuner_ops = { .info = { - .name = "Microtune MT2131", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "Microtune MT2131", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2131_release, diff --git a/drivers/media/tuners/mt2266.c b/drivers/media/tuners/mt2266.c index f4545b7f5da2..e6cc78720de4 100644 --- a/drivers/media/tuners/mt2266.c +++ b/drivers/media/tuners/mt2266.c @@ -304,10 +304,10 @@ static void mt2266_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mt2266_tuner_ops = { .info = { - .name = "Microtune MT2266", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_step = 50000, + .name = "Microtune MT2266", + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mt2266_release, .init = mt2266_init, diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index 57b0e4862aaf..c628435a1b06 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -271,8 +271,8 @@ static const struct dvb_tuner_ops mxl301rf_ops = { .info = { .name = "MaxLinear MxL301RF", - .frequency_min = 93000000, - .frequency_max = 803142857, + .frequency_min_hz = 93 * MHz, + .frequency_max_hz = 803 * MHz + 142857, }, .init = mxl301rf_init, diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index 355ef2959b7d..ec584316c812 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -4075,10 +4075,10 @@ static void mxl5005s_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops mxl5005s_tuner_ops = { .info = { - .name = "MaxLinear MXL5005S", - .frequency_min = 48000000, - .frequency_max = 860000000, - .frequency_step = 50000, + .name = "MaxLinear MXL5005S", + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = mxl5005s_release, diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 4081fd97c3b2..54d9226aaff7 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -59,8 +59,6 @@ MODULE_PARM_DESC(debug, "set debug level"); /* ------------------------------------------------------------------------- */ -#define MHz 1000000 - enum mxl5007t_mode { MxL_MODE_ISDBT = 0, MxL_MODE_DVBT = 1, diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index b4495cc1626b..008ad870c00f 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -186,8 +186,8 @@ static const struct dvb_tuner_ops qm1d1b0004_ops = { .info = { .name = "Sharp qm1d1b0004", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = qm1d1b0004_init, diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 642a065b9a07..83ca5dc047ea 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -388,8 +388,8 @@ static const struct dvb_tuner_ops qm1d1c0042_ops = { .info = { .name = "Sharp QM1D1C0042", - .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, }, .init = qm1d1c0042_init, diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c index b92be882ab3c..4565c06b1617 100644 --- a/drivers/media/tuners/qt1010.c +++ b/drivers/media/tuners/qt1010.c @@ -394,10 +394,10 @@ static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops qt1010_tuner_ops = { .info = { - .name = "Quantek QT1010", - .frequency_min = QT1010_MIN_FREQ, - .frequency_max = QT1010_MAX_FREQ, - .frequency_step = QT1010_STEP, + .name = "Quantek QT1010", + .frequency_min_hz = QT1010_MIN_FREQ, + .frequency_max_hz = QT1010_MAX_FREQ, + .frequency_step_hz = QT1010_STEP, }, .release = qt1010_release, diff --git a/drivers/media/tuners/qt1010_priv.h b/drivers/media/tuners/qt1010_priv.h index 4cb78ecc8985..f25324c63067 100644 --- a/drivers/media/tuners/qt1010_priv.h +++ b/drivers/media/tuners/qt1010_priv.h @@ -71,12 +71,14 @@ reg def meaning 2f 00 ? not used? */ -#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers, - hw could be more precise but we don't - know how to use */ -#define QT1010_MIN_FREQ 48000000 /* 48 MHz */ -#define QT1010_MAX_FREQ 860000000 /* 860 MHz */ -#define QT1010_OFFSET 1246000000 /* 1246 MHz */ +#define QT1010_STEP (125 * kHz) /* + * used by Windows drivers, + * hw could be more precise but we don't + * know how to use + */ +#define QT1010_MIN_FREQ (48 * MHz) +#define QT1010_MAX_FREQ (860 * MHz) +#define QT1010_OFFSET (1246 * MHz) #define QT1010_WR 0 #define QT1010_RD 1 diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 3e14b9e2e763..ba4be08a8551 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2297,9 +2297,9 @@ static void r820t_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops r820t_tuner_ops = { .info = { - .name = "Rafael Micro R820T", - .frequency_min = 42000000, - .frequency_max = 1002000000, + .name = "Rafael Micro R820T", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 1002 * MHz, }, .init = r820t_init, .release = r820t_release, diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 9e34d31d724d..a08d8fe2bb1b 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -387,9 +387,9 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", - .frequency_min = 42000000, - .frequency_max = 870000000, + .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 870 * MHz, }, .init = si2157_init, diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 7b8068354fea..8326106ec2e3 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -175,11 +175,11 @@ static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tda18212_tuner_ops = { .info = { - .name = "NXP TDA18212", + .name = "NXP TDA18212", - .frequency_min = 48000000, - .frequency_max = 864000000, - .frequency_step = 1000, + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 1 * kHz, }, .set_params = tda18212_set_params, diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index c56fcf5d48e3..cbbd4d5e15da 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -269,11 +269,11 @@ static void tda18218_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda18218_tuner_ops = { .info = { - .name = "NXP TDA18218", + .name = "NXP TDA18218", - .frequency_min = 174000000, - .frequency_max = 864000000, - .frequency_step = 1000, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 1 * kHz, }, .release = tda18218_release, diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index 20d12b063380..20d10ef45ab6 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -740,9 +740,9 @@ static int tda18250_sleep(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda18250_ops = { .info = { - .name = "NXP TDA18250", - .frequency_min = 42000000, - .frequency_max = 870000000, + .name = "NXP TDA18250", + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 870 * MHz, }, .init = tda18250_init, diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 147155553648..4d69029229e4 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1240,9 +1240,9 @@ static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) static const struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", - .frequency_min = 45000000, - .frequency_max = 864000000, - .frequency_step = 62500 + .frequency_min_hz = 45 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 62500 }, .init = tda18271_init, .sleep = tda18271_sleep, diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index 8400808f8f7f..4391dabba510 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -816,9 +816,9 @@ static int tda827x_initial_sleep(struct dvb_frontend *fe) static const struct dvb_tuner_ops tda827xo_tuner_ops = { .info = { .name = "Philips TDA827X", - .frequency_min = 55000000, - .frequency_max = 860000000, - .frequency_step = 250000 + .frequency_min_hz = 55 * MHz, + .frequency_max_hz = 860 * MHz, + .frequency_step_hz = 250 * kHz }, .release = tda827x_release, .init = tda827x_initial_init, @@ -832,9 +832,9 @@ static const struct dvb_tuner_ops tda827xo_tuner_ops = { static const struct dvb_tuner_ops tda827xa_tuner_ops = { .info = { .name = "Philips TDA827XA", - .frequency_min = 44000000, - .frequency_max = 906000000, - .frequency_step = 62500 + .frequency_min_hz = 44 * MHz, + .frequency_max_hz = 906 * MHz, + .frequency_step_hz = 62500 }, .release = tda827x_release, .init = tda827x_init, diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index 9d70378fe2d3..5c89a130b47d 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -164,9 +164,9 @@ static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops tua9001_tuner_ops = { .info = { - .name = "Infineon TUA9001", - .frequency_min = 170000000, - .frequency_max = 862000000, + .name = "Infineon TUA9001", + .frequency_min_hz = 170 * MHz, + .frequency_max_hz = 862 * MHz, }, .init = tua9001_init, diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index 36b88f820239..29c1473f2e9f 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -670,6 +670,7 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, int rc, j; struct tuner_params *t_params; unsigned int freq = params->frequency; + bool mono = params->audmode == V4L2_TUNER_MODE_MONO; tun = priv->tun; @@ -736,8 +737,8 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, config |= TDA9887_PORT2_ACTIVE; if (t_params->intercarrier_mode) config |= TDA9887_INTERCARRIER; -/* if (t_params->port1_set_for_fm_mono) - config &= ~TDA9887_PORT1_ACTIVE;*/ + if (t_params->port1_set_for_fm_mono && mono) + config &= ~TDA9887_PORT1_ACTIVE; if (t_params->fm_gain_normal) config |= TDA9887_GAIN_NORMAL; if (t_params->radio_if == 2) diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 84744e138982..aa6861dcd3fd 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -376,9 +376,8 @@ static int load_all_firmwares(struct dvb_frontend *fe, tuner_err("Firmware type "); dump_firm_type(type); printk(KERN_CONT - "(%x), id %llx is corrupted (size=%d, expected %d)\n", - type, (unsigned long long)id, - (unsigned)(endp - p), size); + "(%x), id %llx is corrupted (size=%zd, expected %d)\n", + type, (unsigned long long)id, (endp - p), size); goto corrupt; } @@ -616,8 +615,8 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type, } if ((size + p > endp)) { - tuner_err("missing bytes: need %d, have %d\n", - size, (int)(endp - p)); + tuner_err("missing bytes: need %d, have %zd\n", + size, (endp - p)); return -EINVAL; } @@ -1440,9 +1439,9 @@ unlock: static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .info = { .name = "Xceive XC3028", - .frequency_min = 42000000, - .frequency_max = 864000000, - .frequency_step = 50000, + .frequency_min_hz = 42 * MHz, + .frequency_max_hz = 864 * MHz, + .frequency_step_hz = 50 * kHz, }, .set_config = xc2028_set_config, diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index f0fa8da08afa..eb6d65dae748 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -398,8 +398,8 @@ static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz) dprintk(1, "%s(%u)\n", __func__, freq_hz); - if ((freq_hz > xc4000_tuner_ops.info.frequency_max) || - (freq_hz < xc4000_tuner_ops.info.frequency_min)) + if ((freq_hz > xc4000_tuner_ops.info.frequency_max_hz) || + (freq_hz < xc4000_tuner_ops.info.frequency_min_hz)) return -EINVAL; freq_code = (u16)(freq_hz / 15625); @@ -815,9 +815,9 @@ static int xc4000_fwupload(struct dvb_frontend *fe) p += sizeof(size); if (!size || size > endp - p) { - printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n", + printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%zd, expected %d)\n", type, (unsigned long long)id, - (unsigned)(endp - p), size); + endp - p, size); goto corrupt; } @@ -1635,10 +1635,10 @@ static void xc4000_release(struct dvb_frontend *fe) static const struct dvb_tuner_ops xc4000_tuner_ops = { .info = { - .name = "Xceive XC4000", - .frequency_min = 1000000, - .frequency_max = 1023000000, - .frequency_step = 50000, + .name = "Xceive XC4000", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1023 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = xc4000_release, diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index f7a8d05d1758..f6b65278e502 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -460,8 +460,8 @@ static int xc_set_rf_frequency(struct xc5000_priv *priv, u32 freq_hz) dprintk(1, "%s(%u)\n", __func__, freq_hz); - if ((freq_hz > xc5000_tuner_ops.info.frequency_max) || - (freq_hz < xc5000_tuner_ops.info.frequency_min)) + if ((freq_hz > xc5000_tuner_ops.info.frequency_max_hz) || + (freq_hz < xc5000_tuner_ops.info.frequency_min_hz)) return -EINVAL; freq_code = (u16)(freq_hz / 15625); @@ -1350,10 +1350,10 @@ static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg) static const struct dvb_tuner_ops xc5000_tuner_ops = { .info = { - .name = "Xceive XC5000", - .frequency_min = 1000000, - .frequency_max = 1023000000, - .frequency_step = 50000, + .name = "Xceive XC5000", + .frequency_min_hz = 1 * MHz, + .frequency_max_hz = 1023 * MHz, + .frequency_step_hz = 50 * kHz, }, .release = xc5000_release, diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 70e187971590..62b45062b1e6 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -133,7 +133,7 @@ static void au0828_irq_callback(struct urb *urb) au0828_isocdbg("au0828_irq_callback called: status kill\n"); return; default: /* unknown error */ - au0828_isocdbg("urb completition error %d.\n", urb->status); + au0828_isocdbg("urb completion error %d.\n", urb->status); break; } diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 0f13192634c7..9e5b3e7c3ef5 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -15,7 +15,7 @@ config VIDEO_CX231XX config VIDEO_CX231XX_RC bool "Conexant cx231xx Remote Controller additional support" - depends on RC_CORE + depends on RC_CORE=y || RC_CORE=VIDEO_CX231XX depends on VIDEO_CX231XX default y ---help--- diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index c4a84fb930b6..32ee7b3f21c9 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -112,7 +112,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_dbg(dev->dev, "urb completition error %d.\n", + dev_dbg(dev->dev, "urb completion error %d.\n", urb->status); break; } @@ -126,6 +126,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) stride = runtime->frame_bits >> 3; for (i = 0; i < urb->number_of_packets; i++) { + unsigned long flags; int length = urb->iso_frame_desc[i].actual_length / stride; cp = (unsigned char *)urb->transfer_buffer + @@ -148,7 +149,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) length * stride); } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); dev->adev.hwptr_done_capture += length; if (dev->adev.hwptr_done_capture >= @@ -163,7 +164,7 @@ static void cx231xx_audio_isocirq(struct urb *urb) runtime->period_size; period_elapsed = 1; } - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); } if (period_elapsed) snd_pcm_period_elapsed(substream); @@ -202,7 +203,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_dbg(dev->dev, "urb completition error %d.\n", + dev_dbg(dev->dev, "urb completion error %d.\n", urb->status); break; } @@ -216,6 +217,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) stride = runtime->frame_bits >> 3; if (1) { + unsigned long flags; int length = urb->actual_length / stride; cp = (unsigned char *)urb->transfer_buffer; @@ -234,7 +236,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) length * stride); } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); dev->adev.hwptr_done_capture += length; if (dev->adev.hwptr_done_capture >= @@ -249,7 +251,7 @@ static void cx231xx_audio_bulkirq(struct urb *urb) runtime->period_size; period_elapsed = 1; } - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); } if (period_elapsed) snd_pcm_period_elapsed(substream); diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 53d846dea3d2..493c2dca6244 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -799,6 +799,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + unsigned long flags; int i; switch (urb->status) { @@ -815,9 +816,9 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) } /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); + spin_lock_irqsave(&dev->video_mode.slock, flags); dev->video_mode.isoc_ctl.isoc_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { @@ -844,6 +845,7 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + unsigned long flags; switch (urb->status) { case 0: /* success */ @@ -862,9 +864,9 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) } /* Copy data from URB */ - spin_lock(&dev->video_mode.slock); + spin_lock_irqsave(&dev->video_mode.slock, flags); dev->video_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->video_mode.slock); + spin_unlock_irqrestore(&dev->video_mode.slock, flags); /* Reset urb buffers */ urb->status = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c index 6e1bef2a45bb..15a91169e749 100644 --- a/drivers/media/usb/cx231xx/cx231xx-i2c.c +++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c @@ -376,8 +376,6 @@ static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, struct cx231xx *dev = bus->dev; int addr, rc, i, byte; - if (num <= 0) - return 0; mutex_lock(&dev->i2c_lock); for (i = 0; i < num; i++) { diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index b621cf1aa96b..10b2eb7338ad 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -305,6 +305,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + unsigned long flags; switch (urb->status) { case 0: /* success */ @@ -316,14 +317,14 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) return; default: /* error */ dev_err(dev->dev, - "urb completition error %d.\n", urb->status); + "urb completion error %d.\n", urb->status); break; } /* Copy data from URB */ - spin_lock(&dev->vbi_mode.slock); + spin_lock_irqsave(&dev->vbi_mode.slock, flags); dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); - spin_unlock(&dev->vbi_mode.slock); + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); /* Reset status */ urb->status = 0; diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 082b8d67244b..df4412245a8a 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -96,10 +96,13 @@ config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB_V2 select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 - receiver with USB ID 0db0:5581. + receiver with USB ID 0db0:5581, Friio White ISDB-T receiver + with USB ID 0x7a69:0001. config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index 9d154fdae45b..3338b21d8b25 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -6,10 +6,14 @@ * * see Documentation/media/dvb-drivers/dvb-usb.rst for more information */ +#include <linux/string.h> + #include "gl861.h" #include "zl10353.h" #include "qt1010.h" +#include "tc90522.h" +#include "dvb-pll.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); @@ -26,10 +30,14 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, if (wo) { req = GL861_REQ_I2C_WRITE; type = GL861_WRITE; + buf = kmemdup(wbuf, wlen, GFP_KERNEL); } else { /* rw */ req = GL861_REQ_I2C_READ; type = GL861_READ; + buf = kmalloc(rlen, GFP_KERNEL); } + if (!buf) + return -ENOMEM; switch (wlen) { case 1: @@ -42,24 +50,19 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, default: dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n", KBUILD_MODNAME, wlen); + kfree(buf); return -EINVAL; } - buf = NULL; - if (rlen > 0) { - buf = kmalloc(rlen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - } + usleep_range(1000, 2000); /* avoid I2C errors */ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, value, index, buf, rlen, 2000); - if (rlen > 0) { - if (ret > 0) - memcpy(rbuf, buf, rlen); - kfree(buf); - } + if (!wo && ret > 0) + memcpy(rbuf, buf, rlen); + + kfree(buf); return ret; } @@ -162,11 +165,478 @@ static struct dvb_usb_device_properties gl861_props = { } }; + +/* + * For Friio + */ + +struct friio_priv { + struct i2c_adapter *demod_sub_i2c; + struct i2c_client *i2c_client_demod; + struct i2c_client *i2c_client_tuner; + struct i2c_adapter tuner_adap; +}; + +struct friio_config { + struct i2c_board_info demod_info; + struct tc90522_config demod_cfg; + + struct i2c_board_info tuner_info; + struct dvb_pll_config tuner_cfg; +}; + +static const struct friio_config friio_config = { + .demod_info = { I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x18), }, + .tuner_info = { I2C_BOARD_INFO("tua6034_friio", 0x60), }, +}; + +/* For another type of I2C: + * message sent by a USB control-read/write transaction with data stage. + * Used in init/config of Friio. + */ +static int +gl861_i2c_write_ex(struct dvb_usb_device *d, u8 addr, u8 *wbuf, u16 wlen) +{ + u8 *buf; + int ret; + + buf = kmalloc(wlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, wbuf, wlen); + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_RAW, GL861_WRITE, + addr << (8 + 1), 0x0100, buf, wlen, 2000); + kfree(buf); + return ret; +} + +static int +gl861_i2c_read_ex(struct dvb_usb_device *d, u8 addr, u8 *rbuf, u16 rlen) +{ + u8 *buf; + int ret; + + buf = kmalloc(rlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + GL861_REQ_I2C_READ, GL861_READ, + addr << (8 + 1), 0x0100, buf, rlen, 2000); + if (ret > 0 && rlen > 0) + memcpy(buf, rbuf, rlen); + kfree(buf); + return ret; +} + +/* For I2C transactions to the tuner of Friio (dvb_pll). + * + * Friio uses irregular USB encapsulation for tuner i2c transactions: + * write transacions are encapsulated with a different USB 'request' value. + * + * Although all transactions are sent via the demod(tc90522) + * and the demod provides an i2c adapter for them, it cannot be used in Friio + * since it assumes using the same parent adapter with the demod, + * which does not use the request value and uses same one for both read/write. + * So we define a dedicated i2c adapter here. + */ + +static int +friio_i2c_tuner_read(struct dvb_usb_device *d, struct i2c_msg *msg) +{ + struct friio_priv *priv; + u8 addr; + + priv = d_to_priv(d); + addr = priv->i2c_client_demod->addr; + return gl861_i2c_read_ex(d, addr, msg->buf, msg->len); +} + +static int +friio_i2c_tuner_write(struct dvb_usb_device *d, struct i2c_msg *msg) +{ + u8 *buf; + int ret; + struct friio_priv *priv; + + priv = d_to_priv(d); + + if (msg->len < 1) + return -EINVAL; + + buf = kmalloc(msg->len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + buf[0] = msg->addr << 1; + memcpy(buf + 1, msg->buf, msg->len); + + ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), + GL861_REQ_I2C_RAW, GL861_WRITE, + priv->i2c_client_demod->addr << (8 + 1), + 0xFE, buf, msg->len + 1, 2000); + kfree(buf); + return ret; +} + +static int friio_tuner_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + int ret; + + if (msg[i].flags & I2C_M_RD) + ret = friio_i2c_tuner_read(d, &msg[i]); + else + ret = friio_i2c_tuner_write(d, &msg[i]); + + if (ret < 0) + break; + + usleep_range(1000, 2000); /* avoid I2C errors */ + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static struct i2c_algorithm friio_tuner_i2c_algo = { + .master_xfer = friio_tuner_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +/* GPIO control in Friio */ + +#define FRIIO_CTL_LNB (1 << 0) +#define FRIIO_CTL_STROBE (1 << 1) +#define FRIIO_CTL_CLK (1 << 2) +#define FRIIO_CTL_LED (1 << 3) + +#define FRIIO_LED_RUNNING 0x6400ff64 +#define FRIIO_LED_STOPPED 0x96ff00ff + +/* control PIC16F676 attached to Friio */ +static int friio_ext_ctl(struct dvb_usb_device *d, + u32 sat_color, int power_on) +{ + int i, ret; + struct i2c_msg msg; + u8 *buf; + u32 mask; + u8 power = (power_on) ? FRIIO_CTL_LNB : 0; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.addr = 0x00; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + buf[0] = 0x00; + + /* send 2bit header (&B10) */ + buf[1] = power | FRIIO_CTL_LED | FRIIO_CTL_STROBE; + ret = i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + buf[1] = power | FRIIO_CTL_STROBE; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + /* send 32bit(satur, R, G, B) data in serial */ + mask = 1 << 31; + for (i = 0; i < 32; i++) { + buf[1] = power | FRIIO_CTL_STROBE; + if (sat_color & mask) + buf[1] |= FRIIO_CTL_LED; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + mask >>= 1; + } + + /* set the strobe off */ + buf[1] = power; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + buf[1] |= FRIIO_CTL_CLK; + ret += i2c_transfer(&d->i2c_adap, &msg, 1); + + kfree(buf); + return (ret == 70) ? 0 : -EREMOTEIO; +} + +/* init/config of gl861 for Friio */ +/* NOTE: + * This function cannot be moved to friio_init()/dvb_usbv2_init(), + * because the init defined here must be done before any activities like I2C, + * but friio_init() is called by dvb-usbv2 after {_frontend, _tuner}_attach(), + * where I2C communication is used. + * Thus this function is set to be called from _power_ctl(). + * + * Since it will be called on the early init stage + * where the i2c adapter is not initialized yet, + * we cannot use i2c_transfer() here. + */ +static int friio_reset(struct dvb_usb_device *d) +{ + int i, ret; + u8 wbuf[2], rbuf[2]; + + static const u8 friio_init_cmds[][2] = { + {0x33, 0x08}, {0x37, 0x40}, {0x3a, 0x1f}, {0x3b, 0xff}, + {0x3c, 0x1f}, {0x3d, 0xff}, {0x38, 0x00}, {0x35, 0x00}, + {0x39, 0x00}, {0x36, 0x00}, + }; + + ret = usb_set_interface(d->udev, 0, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x11; + wbuf[1] = 0x02; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + usleep_range(2000, 3000); + + wbuf[0] = 0x11; + wbuf[1] = 0x00; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + /* + * Check if the dev is really a Friio White, since it might be + * another device, Friio Black, with the same VID/PID. + */ + + usleep_range(1000, 2000); + wbuf[0] = 0x03; + wbuf[1] = 0x80; + ret = gl861_i2c_write_ex(d, 0x09, wbuf, 2); + if (ret < 0) + return ret; + + usleep_range(2000, 3000); + ret = gl861_i2c_read_ex(d, 0x09, rbuf, 2); + if (ret < 0) + return ret; + if (rbuf[0] != 0xff || rbuf[1] != 0xff) + return -ENODEV; + + + usleep_range(1000, 2000); + ret = gl861_i2c_write_ex(d, 0x48, wbuf, 2); + if (ret < 0) + return ret; + + usleep_range(2000, 3000); + ret = gl861_i2c_read_ex(d, 0x48, rbuf, 2); + if (ret < 0) + return ret; + if (rbuf[0] != 0xff || rbuf[1] != 0xff) + return -ENODEV; + + wbuf[0] = 0x30; + wbuf[1] = 0x04; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x00; + wbuf[1] = 0x01; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + wbuf[0] = 0x06; + wbuf[1] = 0x0f; + ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(friio_init_cmds); i++) { + ret = gl861_i2c_msg(d, 0x00, (u8 *)friio_init_cmds[i], 2, + NULL, 0); + if (ret < 0) + return ret; + } + return 0; +} + +/* + * DVB callbacks for Friio + */ + +static int friio_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + return onoff ? friio_reset(d) : 0; +} + +static int friio_frontend_attach(struct dvb_usb_adapter *adap) +{ + const struct i2c_board_info *info; + struct dvb_usb_device *d; + struct tc90522_config cfg; + struct i2c_client *cl; + struct friio_priv *priv; + + info = &friio_config.demod_info; + d = adap_to_d(adap); + cl = dvb_module_probe("tc90522", info->type, + &d->i2c_adap, info->addr, &cfg); + if (!cl) + return -ENODEV; + adap->fe[0] = cfg.fe; + + /* ignore cfg.tuner_i2c and create new one */ + priv = adap_to_priv(adap); + priv->i2c_client_demod = cl; + priv->tuner_adap.algo = &friio_tuner_i2c_algo; + priv->tuner_adap.dev.parent = &d->udev->dev; + strlcpy(priv->tuner_adap.name, d->name, sizeof(priv->tuner_adap.name)); + strlcat(priv->tuner_adap.name, "-tuner", sizeof(priv->tuner_adap.name)); + priv->demod_sub_i2c = &priv->tuner_adap; + i2c_set_adapdata(&priv->tuner_adap, d); + + return i2c_add_adapter(&priv->tuner_adap); +} + +static int friio_frontend_detach(struct dvb_usb_adapter *adap) +{ + struct friio_priv *priv; + + priv = adap_to_priv(adap); + i2c_del_adapter(&priv->tuner_adap); + dvb_module_release(priv->i2c_client_demod); + return 0; +} + +static int friio_tuner_attach(struct dvb_usb_adapter *adap) +{ + const struct i2c_board_info *info; + struct dvb_pll_config cfg; + struct i2c_client *cl; + struct friio_priv *priv; + + priv = adap_to_priv(adap); + info = &friio_config.tuner_info; + cfg = friio_config.tuner_cfg; + cfg.fe = adap->fe[0]; + + cl = dvb_module_probe("dvb_pll", info->type, + priv->demod_sub_i2c, info->addr, &cfg); + if (!cl) + return -ENODEV; + priv->i2c_client_tuner = cl; + return 0; +} + +static int friio_tuner_detach(struct dvb_usb_adapter *adap) +{ + struct friio_priv *priv; + + priv = adap_to_priv(adap); + dvb_module_release(priv->i2c_client_tuner); + return 0; +} + +static int friio_init(struct dvb_usb_device *d) +{ + int i; + int ret; + struct friio_priv *priv; + + static const u8 demod_init[][2] = { + {0x01, 0x40}, {0x04, 0x38}, {0x05, 0x40}, {0x07, 0x40}, + {0x0f, 0x4f}, {0x11, 0x21}, {0x12, 0x0b}, {0x13, 0x2f}, + {0x14, 0x31}, {0x16, 0x02}, {0x21, 0xc4}, {0x22, 0x20}, + {0x2c, 0x79}, {0x2d, 0x34}, {0x2f, 0x00}, {0x30, 0x28}, + {0x31, 0x31}, {0x32, 0xdf}, {0x38, 0x01}, {0x39, 0x78}, + {0x3b, 0x33}, {0x3c, 0x33}, {0x48, 0x90}, {0x51, 0x68}, + {0x5e, 0x38}, {0x71, 0x00}, {0x72, 0x08}, {0x77, 0x00}, + {0xc0, 0x21}, {0xc1, 0x10}, {0xe4, 0x1a}, {0xea, 0x1f}, + {0x77, 0x00}, {0x71, 0x00}, {0x71, 0x00}, {0x76, 0x0c}, + }; + + /* power on LNA? */ + ret = friio_ext_ctl(d, FRIIO_LED_STOPPED, true); + if (ret < 0) + return ret; + msleep(20); + + /* init/config demod */ + priv = d_to_priv(d); + for (i = 0; i < ARRAY_SIZE(demod_init); i++) { + int ret; + + ret = i2c_master_send(priv->i2c_client_demod, demod_init[i], 2); + if (ret < 0) + return ret; + } + msleep(100); + return 0; +} + +static void friio_exit(struct dvb_usb_device *d) +{ + friio_ext_ctl(d, FRIIO_LED_STOPPED, false); +} + +static int friio_streaming_ctrl(struct dvb_frontend *fe, int onoff) +{ + u32 led_color; + + led_color = onoff ? FRIIO_LED_RUNNING : FRIIO_LED_STOPPED; + return friio_ext_ctl(fe_to_d(fe), led_color, true); +} + + +static struct dvb_usb_device_properties friio_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + + .size_of_priv = sizeof(struct friio_priv), + + .i2c_algo = &gl861_i2c_algo, + .power_ctrl = friio_power_ctrl, + .frontend_attach = friio_frontend_attach, + .frontend_detach = friio_frontend_detach, + .tuner_attach = friio_tuner_attach, + .tuner_detach = friio_tuner_detach, + .init = friio_init, + .exit = friio_exit, + .streaming_ctrl = friio_streaming_ctrl, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x01, 8, 16384), + } + } +}; + static const struct usb_device_id gl861_id_table[] = { { DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801, &gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU, &gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) }, + { DVB_USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE, + &friio_props, "774 Friio White ISDB-T USB2.0", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, gl861_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/gl861.h b/drivers/media/usb/dvb-usb-v2/gl861.h index b651b857e034..02c00e10748a 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.h +++ b/drivers/media/usb/dvb-usb-v2/gl861.h @@ -9,5 +9,6 @@ #define GL861_REQ_I2C_WRITE 0x01 #define GL861_REQ_I2C_READ 0x02 +#define GL861_REQ_I2C_RAW 0x03 #endif diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index 221cf46b4140..9f74453799a2 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -554,9 +554,9 @@ static const struct dvb_frontend_ops mxl111sf_demod_ops = { .delsys = { SYS_DVBT }, .info = { .name = "MaxLinear MxL111SF DVB-T demodulator", - .frequency_min = 177000000, - .frequency_max = 858000000, - .frequency_stepsize = 166666, + .frequency_min_hz = 177 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 166666, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index 240d736bf1bb..92b3b9221a21 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -465,9 +465,9 @@ static const struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { .info = { .name = "MaxLinear MxL111SF", #if 0 - .frequency_min = , - .frequency_max = , - .frequency_step = , + .frequency_min_hz = , + .frequency_max_hz = , + .frequency_step_hz = , #endif }, #if 0 diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c76e78f9638a..a970224a94bd 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1732,7 +1732,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) goto exit; ret = rtl28xxu_rd_reg(d, IR_RX_BC, &buf[0]); - if (ret) + if (ret || buf[0] > sizeof(buf)) goto err; len = buf[0]; diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c index b0499f95ec45..024c751eb165 100644 --- a/drivers/media/usb/dvb-usb-v2/usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -40,7 +40,7 @@ static void usb_urb_complete(struct urb *urb) return; default: /* error */ dev_dbg_ratelimited(&stream->udev->dev, - "%s: urb completition failed=%d\n", + "%s: urb completion failed=%d\n", __func__, urb->status); break; } @@ -69,7 +69,7 @@ static void usb_urb_complete(struct urb *urb) break; default: dev_err(&stream->udev->dev, - "%s: unknown endpoint type in completition handler\n", + "%s: unknown endpoint type in completion handler\n", KBUILD_MODNAME); return; } diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index b8a1c62a0682..513df955eaa3 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -312,12 +312,6 @@ config DVB_USB_DTV5100 help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. -config DVB_USB_FRIIO - tristate "Friio ISDB-T USB2.0 Receiver support" - depends on DVB_USB - help - Say Y here to support the Japanese DTV receiver Friio. - config DVB_USB_AZ6027 tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" depends on DVB_USB diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 9ad2618408ef..407d90ca8be0 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -71,9 +71,6 @@ obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o -dvb-usb-friio-objs := friio.o friio-fe.o -obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o - dvb-usb-az6027-objs := az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o diff --git a/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c index 7fbbc954da16..09cc3a21af65 100644 --- a/drivers/media/usb/dvb-usb/af9005-fe.c +++ b/drivers/media/usb/dvb-usb/af9005-fe.c @@ -1455,9 +1455,9 @@ static const struct dvb_frontend_ops af9005_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "AF9005 USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c index 5a2f81311fb7..df71df7ed524 100644 --- a/drivers/media/usb/dvb-usb/cinergyT2-fe.c +++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c @@ -295,9 +295,9 @@ static const struct dvb_frontend_ops cinergyt2_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = DRIVER_NAME, - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, + .frequency_min_hz = 174 * MHz, + .frequency_max_hz = 862 * MHz, + .frequency_stepsize_hz = 166667, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index c53a969bc6be..091389fdf89e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1745,6 +1745,7 @@ static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; } else { + /* FIXME: check if it is fe_adap[1] */ if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) return -ENODEV; } diff --git a/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c index 7e75aae34fb8..1ca3a51b2ae3 100644 --- a/drivers/media/usb/dvb-usb/dtt200u-fe.c +++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c @@ -230,9 +230,9 @@ static const struct dvb_frontend_ops dtt200u_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "WideView USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 250000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 250 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 0d4fdd34a710..9ce8b4d79d1f 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -2101,14 +2101,12 @@ static struct dvb_usb_device_properties s6x0_properties = { } }; -static struct dvb_usb_device_properties *p1100; static const struct dvb_usb_device_description d1100 = { "Prof 1100 USB ", {&dw2102_table[PROF_1100], NULL}, {NULL}, }; -static struct dvb_usb_device_properties *s660; static const struct dvb_usb_device_description d660 = { "TeVii S660 USB", {&dw2102_table[TEVII_S660], NULL}, @@ -2127,14 +2125,12 @@ static const struct dvb_usb_device_description d480_2 = { {NULL}, }; -static struct dvb_usb_device_properties *p7500; static const struct dvb_usb_device_description d7500 = { "Prof 7500 USB DVB-S2", {&dw2102_table[PROF_7500], NULL}, {NULL}, }; -static struct dvb_usb_device_properties *s421; static const struct dvb_usb_device_description d421 = { "TeVii S421 PCI", {&dw2102_table[TEVII_S421], NULL}, @@ -2334,6 +2330,11 @@ static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { int retval = -ENOMEM; + struct dvb_usb_device_properties *p1100; + struct dvb_usb_device_properties *s660; + struct dvb_usb_device_properties *p7500; + struct dvb_usb_device_properties *s421; + p1100 = kmemdup(&s6x0_properties, sizeof(struct dvb_usb_device_properties), GFP_KERNEL); if (!p1100) @@ -2402,8 +2403,16 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, &t220_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &tt_s2_4600_properties, - THIS_MODULE, NULL, adapter_nr)) + THIS_MODULE, NULL, adapter_nr)) { + + /* clean up copied properties */ + kfree(s421); + kfree(p7500); + kfree(s660); + kfree(p1100); + return 0; + } retval = -ENODEV; kfree(s421); diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c index 932f262452eb..e6bd0ed8d789 100644 --- a/drivers/media/usb/dvb-usb/friio-fe.c +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -133,10 +133,10 @@ static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) u32 f; deb_fe("%s: freq=%d, step=%d\n", __func__, freq, - state->frontend.ops.info.frequency_stepsize); + state->frontend.ops.info.frequency_stepsize_hz); /* freq -> oscilator frequency conversion. */ /* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */ - f = freq / state->frontend.ops.info.frequency_stepsize; + f = freq / state->frontend.ops.info.frequency_stepsize_hz; /* add 399[1/7 MHZ] = 57MHz for the IF */ f += 399; /* add center frequency shift if necessary */ @@ -413,10 +413,9 @@ static const struct dvb_frontend_ops jdvbt90502_ops = { .delsys = { SYS_ISDBT }, .info = { .name = "Comtech JDVBT90502 ISDB-T", - .frequency_min = 473000000, /* UHF 13ch, center */ - .frequency_max = 767142857, /* UHF 62ch, center */ - .frequency_stepsize = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, - .frequency_tolerance = 0, + .frequency_min_hz = 473000000, /* UHF 13ch, center */ + .frequency_max_hz = 767142857, /* UHF 62ch, center */ + .frequency_stepsize_hz = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, /* NOTE: this driver ignores all parameters but frequency. */ .caps = FE_CAN_INVERSION_AUTO | diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 51b026fa6bfb..22554d9abd43 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -255,9 +255,6 @@ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int nu int i, j; int ret = 0; - if (!num) - return -EINVAL; - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; diff --git a/drivers/media/usb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c index 5e05963f4220..9771f0954c69 100644 --- a/drivers/media/usb/dvb-usb/usb-urb.c +++ b/drivers/media/usb/dvb-usb/usb-urb.c @@ -33,7 +33,7 @@ static void usb_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - deb_ts("urb completition error %d.\n", urb->status); + deb_ts("urb completion error %d.\n", urb->status); break; } @@ -57,7 +57,7 @@ static void usb_urb_complete(struct urb *urb) stream->complete(stream, b, urb->actual_length); break; default: - err("unknown endpoint type in completition handler."); + err("unknown endpoint type in completion handler."); return; } usb_submit_urb(urb,GFP_ATOMIC); diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c index ae48146e005c..9eb811452f2e 100644 --- a/drivers/media/usb/dvb-usb/vp702x-fe.c +++ b/drivers/media/usb/dvb-usb/vp702x-fe.c @@ -349,10 +349,9 @@ static const struct dvb_frontend_ops vp702x_fe_ops = { .delsys = { SYS_DVBS }, .info = { .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, /* kHz for QPSK frontends */ - .frequency_tolerance = 0, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 1 * MHz, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ diff --git a/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c index f86040173b8d..1173ae29885b 100644 --- a/drivers/media/usb/dvb-usb/vp7045-fe.c +++ b/drivers/media/usb/dvb-usb/vp7045-fe.c @@ -162,9 +162,9 @@ static const struct dvb_frontend_ops vp7045_fe_ops = { .delsys = { SYS_DVBT }, .info = { .name = "Twinhan VP7045/46 USB DVB-T", - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 1000, + .frequency_min_hz = 44250 * kHz, + .frequency_max_hz = 867250 * kHz, + .frequency_stepsize_hz = 1 * kHz, .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 6c8438311d3b..71c829f31d3b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -543,7 +543,7 @@ static const struct em28xx_reg_seq hauppauge_dualhd_dvb[] = { {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, {EM2874_R80_GPIO_P0_CTRL, 0xdf, 0xff, 100}, /* demod 2 reset */ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100}, - {EM2874_R5F_TS_ENABLE, 0x44, 0xff, 50}, + {EM2874_R5F_TS_ENABLE, 0x00, 0xff, 50}, /* disable TS filters */ {EM2874_R5D_TS1_PKT_SIZE, 0x05, 0xff, 50}, {EM2874_R5E_TS2_PKT_SIZE, 0x05, 0xff, 50}, {-1, -1, -1, -1}, @@ -2688,8 +2688,6 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */ .driver_info = EM28178_BOARD_PCTV_292E }, - { USB_DEVICE(0x2040, 0x8268), /* Hauppauge WinTV-soloHD alt. PID */ - .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x0413, 0x6f07), .driver_info = EM2861_BOARD_LEADTEK_VC100 }, { USB_DEVICE(0xeb1a, 0x8179), @@ -2854,13 +2852,13 @@ static void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); usleep_range(10000, 11000); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfd); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xdc); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfc); - mdelay(70); + msleep(70); break; case EM2870_BOARD_TERRATEC_XS_MT2060: /* @@ -2868,11 +2866,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev) * demod work */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); break; case EM2870_BOARD_PINNACLE_PCTV_DVB: /* @@ -2880,11 +2878,11 @@ static void em28xx_pre_card_setup(struct em28xx *dev) * DVB-T demod work */ em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xde); - mdelay(70); + msleep(70); em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xfe); - mdelay(70); + msleep(70); break; case EM2820_BOARD_GADMEI_UTV310: case EM2820_BOARD_MSI_VOX_USB_2: @@ -3376,7 +3374,9 @@ void em28xx_free_device(struct kref *ref) if (!dev->disconnected) em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size_isoc); + if (dev->ts == PRIMARY_TS) + kfree(dev->alt_max_pkt_size_isoc); + kfree(dev); } EXPORT_SYMBOL_GPL(em28xx_free_device); @@ -3861,6 +3861,17 @@ static int em28xx_usb_probe(struct usb_interface *intf, dev->has_video = false; } + if (dev->board.has_dual_ts && + (dev->tuner_type != TUNER_ABSENT || INPUT(0)->type)) { + /* + * The logic with sets alternate is not ready for dual-tuners + * which analog modes. + */ + dev_err(&intf->dev, + "We currently don't support analog TV or stream capture on dual tuners.\n"); + has_video = false; + } + /* Select USB transfer types to use */ if (has_video) { if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index f70845e7d8c6..5657f8710ca6 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -655,12 +655,12 @@ int em28xx_capture_start(struct em28xx *dev, int start) rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE); + EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD); else rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? EM2874_TS2_CAPTURE_ENABLE : 0x00, - EM2874_TS2_CAPTURE_ENABLE); + EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD); } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -1053,7 +1053,7 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, /* submit urbs and enables IRQ */ for (i = 0; i < usb_bufs->num_bufs; i++) { - rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); + rc = usb_submit_urb(usb_bufs->urb[i], GFP_KERNEL); if (rc) { dev_err(&dev->intf->dev, "submit of urb %i failed (error=%i)\n", i, rc); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index b778d8a1983e..a73faf12f7e4 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -218,7 +218,9 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(udev, dev->ifnum, dvb_alt); + if (!dev->board.has_dual_ts) + usb_set_interface(udev, dev->ifnum, dvb_alt); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 6458682bc6e2..e19d6342e0d0 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -559,10 +559,6 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, dev->cur_i2c_bus = bus; } - if (num <= 0) { - rt_mutex_unlock(&dev->i2c_bus_lock); - return 0; - } for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; if (!msgs[i].len) { diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index 05b1126f263e..62aeebcdd7f7 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -448,13 +448,14 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf { u32 *bytesused; struct go7007_buffer *vb_tmp = NULL; + unsigned long flags; if (vb == NULL) { - spin_lock(&go->spinlock); + spin_lock_irqsave(&go->spinlock, flags); if (!list_empty(&go->vidq_active)) vb = go->active_buf = list_first_entry(&go->vidq_active, struct go7007_buffer, list); - spin_unlock(&go->spinlock); + spin_unlock_irqrestore(&go->spinlock, flags); go->next_seq++; return vb; } @@ -468,7 +469,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf vb->vb.vb2_buf.timestamp = ktime_get_ns(); vb_tmp = vb; - spin_lock(&go->spinlock); + spin_lock_irqsave(&go->spinlock, flags); list_del(&vb->list); if (list_empty(&go->vidq_active)) vb = NULL; @@ -476,7 +477,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf vb = list_first_entry(&go->vidq_active, struct go7007_buffer, list); go->active_buf = vb; - spin_unlock(&go->spinlock); + spin_unlock_irqrestore(&go->spinlock, flags); vb2_buffer_done(&vb_tmp->vb.vb2_buf, VB2_BUF_STATE_DONE); return vb; } diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index f84a2130f033..137fc253b122 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -75,13 +75,14 @@ static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) struct go7007_snd *gosnd = go->snd_context; struct snd_pcm_runtime *runtime = gosnd->substream->runtime; int frames = bytes_to_frames(runtime, length); + unsigned long flags; - spin_lock(&gosnd->lock); + spin_lock_irqsave(&gosnd->lock, flags); gosnd->hw_ptr += frames; if (gosnd->hw_ptr >= runtime->buffer_size) gosnd->hw_ptr -= runtime->buffer_size; gosnd->avail += frames; - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); if (gosnd->w_idx + length > runtime->dma_bytes) { int cpy = runtime->dma_bytes - gosnd->w_idx; @@ -92,13 +93,13 @@ static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) } memcpy(runtime->dma_area + gosnd->w_idx, buf, length); gosnd->w_idx += length; - spin_lock(&gosnd->lock); + spin_lock_irqsave(&gosnd->lock, flags); if (gosnd->avail < runtime->period_size) { - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); return; } gosnd->avail -= runtime->period_size; - spin_unlock(&gosnd->lock); + spin_unlock_irqrestore(&gosnd->lock, flags); if (gosnd->capturing) snd_pcm_period_elapsed(gosnd->substream); } diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 0cfdf8a1e19d..f993f6280c56 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -163,7 +163,7 @@ static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, actual_len = kinect_read(udev, ibuf, 0x200); } while (actual_len == 0); gspca_dbg(gspca_dev, D_USBO, "Control reply: %d\n", actual_len); - if (actual_len < sizeof(*rhdr)) { + if (actual_len < (int)sizeof(*rhdr)) { pr_err("send_cmd: Input control transfer failed (%d)\n", actual_len); return actual_len < 0 ? actual_len : -EREMOTEIO; diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 6d692fb3e8dd..34085a0b15a1 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -597,7 +597,7 @@ static int hackrf_submit_urbs(struct hackrf_dev *dev) for (i = 0; i < dev->urbs_initialized; i++) { dev_dbg(dev->dev, "submit urb=%d\n", i); - ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); + ret = usb_submit_urb(dev->urb_list[i], GFP_KERNEL); if (ret) { dev_err(dev->dev, "Could not submit URB no. %d - get them all back\n", i); @@ -636,7 +636,7 @@ static int hackrf_alloc_stream_bufs(struct hackrf_dev *dev) for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, - BULK_BUFFER_SIZE, GFP_ATOMIC, + BULK_BUFFER_SIZE, GFP_KERNEL, &dev->dma_addr[dev->buf_num]); if (!dev->buf_list[dev->buf_num]) { dev_dbg(dev->dev, "alloc buf=%d failed\n", @@ -689,7 +689,7 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv) /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { dev_dbg(dev->dev, "alloc urb=%d\n", i); - dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); if (!dev->urb_list[i]) { for (j = 0; j < i; j++) usb_free_urb(dev->urb_list[j]); diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index c71ddefd2e58..5a3cb614a211 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -117,9 +117,6 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); int retval = 0, addr; - if (num <= 0) - return 0; - mutex_lock(&dev->i2c_mutex); addr = msgs[0].addr << 1; diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 77b759a0bcd9..504e413edcd2 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -802,6 +802,7 @@ int stk1160_vb2_setup(struct stk1160 *dev) q->buf_struct_size = sizeof(struct stk1160_buffer); q->ops = &stk1160_video_qops; q->mem_ops = &vb2_vmalloc_memops; + q->lock = &dev->vb_queue_lock; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; rc = vb2_queue_init(q); @@ -827,7 +828,6 @@ int stk1160_video_register(struct stk1160 *dev) * It will be used to protect *only* v4l2 ioctls. */ dev->vdev.lock = &dev->v4l_lock; - dev->vdev.queue->lock = &dev->vb_queue_lock; /* This will be used to set video_device parent */ dev->vdev.v4l2_dev = &dev->v4l2_dev; diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index c811fc6cf48a..3a4e545c6037 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -266,6 +266,11 @@ static int register_dvb(struct tm6000_core *dev) ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", THIS_MODULE, &dev->udev->dev, adapter_nr); + if (ret < 0) { + pr_err("tm6000: couldn't register the adapter!\n"); + goto err; + } + dvb->adapter.priv = dev; if (dvb->frontend) { diff --git a/drivers/media/usb/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c index 659b63febf85..ccd1adf862b1 100644 --- a/drivers/media/usb/tm6000/tm6000-i2c.c +++ b/drivers/media/usb/tm6000/tm6000-i2c.c @@ -145,8 +145,6 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, struct tm6000_core *dev = i2c_adap->algo_data; int addr, rc, i, byte; - if (num <= 0) - return 0; for (i = 0; i < num; i++) { addr = (msgs[i].addr << 1) & 0xff; i2c_dprintk(2, "%s %s addr=0x%x len=%d:", diff --git a/drivers/media/usb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c index 6ea05d909024..278bf6c5855b 100644 --- a/drivers/media/usb/ttusb-dec/ttusbdecfe.c +++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c @@ -247,9 +247,9 @@ static const struct dvb_frontend_ops ttusbdecfe_dvbt_ops = { .delsys = { SYS_DVBT }, .info = { .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", - .frequency_min = 51000000, - .frequency_max = 858000000, - .frequency_stepsize = 62500, + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 858 * MHz, + .frequency_stepsize_hz = 62500, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | @@ -270,9 +270,9 @@ static const struct dvb_frontend_ops ttusbdecfe_dvbs_ops = { .delsys = { SYS_DVBS }, .info = { .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, + .frequency_min_hz = 950 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 125 * kHz, .symbol_rate_min = 1000000, /* guessed */ .symbol_rate_max = 45000000, /* guessed */ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c index 2c2ca77fa01f..4ce38246ed64 100644 --- a/drivers/media/usb/usbtv/usbtv-audio.c +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -126,6 +126,7 @@ static void usbtv_audio_urb_received(struct urb *urb) struct snd_pcm_runtime *runtime = substream->runtime; size_t i, frame_bytes, chunk_length, buffer_pos, period_pos; int period_elapsed; + unsigned long flags; void *urb_current; switch (urb->status) { @@ -179,12 +180,12 @@ static void usbtv_audio_urb_received(struct urb *urb) } } - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_irqsave(substream, flags); chip->snd_buffer_pos = buffer_pos; chip->snd_period_pos = period_pos; - snd_pcm_stream_unlock(substream); + snd_pcm_stream_unlock_irqrestore(substream, flags); if (period_elapsed) snd_pcm_period_elapsed(substream); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index a36b4fb949fa..c2ad102bd693 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -20,6 +20,7 @@ #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> +#include <linux/workqueue.h> #include <linux/atomic.h> #include <media/v4l2-ctrls.h> @@ -971,12 +972,30 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } +static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, + const u8 *data) +{ + s32 value = mapping->get(mapping, UVC_GET_CUR, data); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + struct uvc_menu_info *menu = mapping->menu_info; + unsigned int i; + + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == value) { + value = i; + break; + } + } + } + + return value; +} + static int __uvc_ctrl_get(struct uvc_video_chain *chain, struct uvc_control *ctrl, struct uvc_control_mapping *mapping, s32 *value) { - struct uvc_menu_info *menu; - unsigned int i; int ret; if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) @@ -993,18 +1012,8 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain, ctrl->loaded = 1; } - *value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == *value) { - *value = i; - break; - } - } - } + *value = __uvc_ctrl_get_value(mapping, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); return 0; } @@ -1125,7 +1134,7 @@ done: } /* - * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Mapping V4L2 controls to UVC controls can be straightforward if done well. * Most of the UVC controls exist in V4L2, and can be mapped directly. Some * must be grouped (for instance the Red Balance, Blue Balance and Do White * Balance V4L2 controls use the White Balance Component UVC control) or @@ -1216,53 +1225,135 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, ev->u.ctrl.default_value = v4l2_ctrl.default_value; } -static void uvc_ctrl_send_event(struct uvc_fh *handle, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 value, u32 changes) +/* + * Send control change events to all subscribers for the @ctrl control. By + * default the subscriber that generated the event, as identified by @handle, + * is not notified unless it has set the V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK flag. + * @handle can be NULL for asynchronous events related to auto-update controls, + * in which case all subscribers are notified. + */ +static void uvc_ctrl_send_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, s32 value, u32 changes) { + struct v4l2_fh *originator = handle ? &handle->vfh : NULL; struct v4l2_subscribed_event *sev; struct v4l2_event ev; if (list_empty(&mapping->ev_subs)) return; - uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + uvc_ctrl_fill_event(chain, &ev, ctrl, mapping, value, changes); list_for_each_entry(sev, &mapping->ev_subs, node) { - if (sev->fh && (sev->fh != &handle->vfh || + if (sev->fh != originator || (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || - (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + (changes & V4L2_EVENT_CTRL_CH_FLAGS)) v4l2_event_queue_fh(sev->fh, &ev); } } -static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, - struct uvc_control *master, u32 slave_id, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +/* + * Send control change events for the slave of the @master control identified + * by the V4L2 ID @slave_id. The @handle identifies the event subscriber that + * generated the event and may be NULL for auto-update events. + */ +static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *master, u32 slave_id) { struct uvc_control_mapping *mapping = NULL; struct uvc_control *ctrl = NULL; u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - unsigned int i; s32 val = 0; - /* - * We can skip sending an event for the slave if the slave - * is being modified in the same transaction. - */ - for (i = 0; i < xctrls_count; i++) { - if (xctrls[i].id == slave_id) - return; - } - __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); if (ctrl == NULL) return; - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0) changes |= V4L2_EVENT_CTRL_CH_VALUE; - uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); + uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_status_event_work(struct work_struct *work) +{ + struct uvc_device *dev = container_of(work, struct uvc_device, + async_ctrl.work); + struct uvc_ctrl_work *w = &dev->async_ctrl; + struct uvc_video_chain *chain = w->chain; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl = w->ctrl; + struct uvc_fh *handle; + unsigned int i; + int ret; + + mutex_lock(&chain->ctrl_mutex); + + handle = ctrl->handle; + ctrl->handle = NULL; + + list_for_each_entry(mapping, &ctrl->info.mappings, list) { + s32 value = __uvc_ctrl_get_value(mapping, w->data); + + /* + * handle may be NULL here if the device sends auto-update + * events without a prior related control set from userspace. + */ + for (i = 0; i < ARRAY_SIZE(mapping->slave_ids); ++i) { + if (!mapping->slave_ids[i]) + break; + + uvc_ctrl_send_slave_event(chain, handle, ctrl, + mapping->slave_ids[i]); + } + + uvc_ctrl_send_event(chain, handle, ctrl, mapping, value, + V4L2_EVENT_CTRL_CH_VALUE); + } + + mutex_unlock(&chain->ctrl_mutex); + + /* Resubmit the URB. */ + w->urb->interval = dev->int_ep->desc.bInterval; + ret = usb_submit_urb(w->urb, GFP_KERNEL); + if (ret < 0) + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); +} + +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data) +{ + struct uvc_device *dev = chain->dev; + struct uvc_ctrl_work *w = &dev->async_ctrl; + + if (list_empty(&ctrl->info.mappings)) { + ctrl->handle = NULL; + return false; + } + + w->data = data; + w->urb = urb; + w->chain = chain; + w->ctrl = ctrl; + + schedule_work(&w->work); + + return true; +} + +static bool uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count, u32 id) +{ + unsigned int i; + + for (i = 0; i < xctrls_count; ++i) { + if (xctrls[i].id == id) + return true; + } + + return false; } static void uvc_ctrl_send_events(struct uvc_fh *handle, @@ -1277,29 +1368,39 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, for (i = 0; i < xctrls_count; ++i) { ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + /* Notification will be sent from an Interrupt event. */ + continue; + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { - if (!mapping->slave_ids[j]) + u32 slave_id = mapping->slave_ids[j]; + + if (!slave_id) break; - uvc_ctrl_send_slave_event(handle, ctrl, - mapping->slave_ids[j], - xctrls, xctrls_count); + + /* + * We can skip sending an event for the slave if the + * slave is being modified in the same transaction. + */ + if (uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + slave_id)) + continue; + + uvc_ctrl_send_slave_event(handle->chain, handle, ctrl, + slave_id); } /* * If the master is being modified in the same transaction * flags may change too. */ - if (mapping->master_id) { - for (j = 0; j < xctrls_count; j++) { - if (xctrls[j].id == mapping->master_id) { - changes |= V4L2_EVENT_CTRL_CH_FLAGS; - break; - } - } - } + if (mapping->master_id && + uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + mapping->master_id)) + changes |= V4L2_EVENT_CTRL_CH_FLAGS; - uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, - changes); + uvc_ctrl_send_event(handle->chain, handle, ctrl, mapping, + xctrls[i].value, changes); } } @@ -1472,9 +1573,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); } -int uvc_ctrl_set(struct uvc_video_chain *chain, +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl) { + struct uvc_video_chain *chain = handle->chain; struct uvc_control *ctrl; struct uvc_control_mapping *mapping; s32 value; @@ -1581,6 +1683,9 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, mapping->set(mapping, value, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + ctrl->handle = handle; + ctrl->dirty = 1; ctrl->modified = 1; return 0; @@ -1612,7 +1717,9 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CTRL_FLAG_SET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); + UVC_CTRL_FLAG_AUTO_UPDATE : 0) + | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ? + UVC_CTRL_FLAG_ASYNCHRONOUS : 0); kfree(data); return ret; @@ -2173,6 +2280,8 @@ int uvc_ctrl_init_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work); + /* Walk the entities list and instantiate controls */ list_for_each_entry(entity, &dev->entities, list) { struct uvc_control *ctrl; @@ -2241,6 +2350,8 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + cancel_work_sync(&dev->async_ctrl.work); + /* Free controls and control mappings for all entities. */ list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) { diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 8e138201330f..d46dc432456c 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -100,6 +100,11 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_GREY, }, { + .name = "IR 8-bit (L8_IR)", + .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, + .fcc = V4L2_PIX_FMT_GREY, + }, + { .name = "Greyscale 10-bit (Y10 )", .guid = UVC_GUID_FORMAT_Y10, .fcc = V4L2_PIX_FMT_Y10, diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 7b710410584a..0722dc684378 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -78,7 +78,24 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, /* -------------------------------------------------------------------------- * Status interrupt endpoint */ -static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) +struct uvc_streaming_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bValue[]; +} __packed; + +struct uvc_control_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bSelector; + u8 bAttribute; + u8 bValue[]; +} __packed; + +static void uvc_event_streaming(struct uvc_device *dev, + struct uvc_streaming_status *status, int len) { if (len < 3) { uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " @@ -86,31 +103,97 @@ static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) return; } - if (data[2] == 0) { + if (status->bEvent == 0) { if (len < 4) return; uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", - data[1], data[3] ? "pressed" : "released", len); - uvc_input_report_key(dev, KEY_CAMERA, data[3]); + status->bOriginator, + status->bValue[0] ? "pressed" : "released", len); + uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); } else { uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x len %d.\n", - data[1], data[2], len); + status->bOriginator, status->bEvent, len); } } -static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) +#define UVC_CTRL_VALUE_CHANGE 0 +#define UVC_CTRL_INFO_CHANGE 1 +#define UVC_CTRL_FAILURE_CHANGE 2 +#define UVC_CTRL_MIN_CHANGE 3 +#define UVC_CTRL_MAX_CHANGE 4 + +static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity, + u8 selector) { - char *attrs[3] = { "value", "info", "failure" }; + struct uvc_control *ctrl; + unsigned int i; + + for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++) + if (ctrl->info.selector == selector) + return ctrl; + + return NULL; +} - if (len < 6 || data[2] != 0 || data[4] > 2) { +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, + const struct uvc_control_status *status, + struct uvc_video_chain **chain) +{ + list_for_each_entry((*chain), &dev->chains, list) { + struct uvc_entity *entity; + struct uvc_control *ctrl; + + list_for_each_entry(entity, &(*chain)->entities, chain) { + if (entity->id != status->bOriginator) + continue; + + ctrl = uvc_event_entity_find_ctrl(entity, + status->bSelector); + if (ctrl) + return ctrl; + } + } + + return NULL; +} + +static bool uvc_event_control(struct urb *urb, + const struct uvc_control_status *status, int len) +{ + static const char *attrs[] = { "value", "info", "failure", "min", "max" }; + struct uvc_device *dev = urb->context; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + + if (len < 6 || status->bEvent != 0 || + status->bAttribute >= ARRAY_SIZE(attrs)) { uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " "received.\n"); - return; + return false; } uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", - data[1], data[3], attrs[data[4]], len); + status->bOriginator, status->bSelector, + attrs[status->bAttribute], len); + + /* Find the control. */ + ctrl = uvc_event_find_ctrl(dev, status, &chain); + if (!ctrl) + return false; + + switch (status->bAttribute) { + case UVC_CTRL_VALUE_CHANGE: + return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); + + case UVC_CTRL_INFO_CHANGE: + case UVC_CTRL_FAILURE_CHANGE: + case UVC_CTRL_MIN_CHANGE: + case UVC_CTRL_MAX_CHANGE: + break; + } + + return false; } static void uvc_status_complete(struct urb *urb) @@ -138,13 +221,23 @@ static void uvc_status_complete(struct urb *urb) len = urb->actual_length; if (len > 0) { switch (dev->status[0] & 0x0f) { - case UVC_STATUS_TYPE_CONTROL: - uvc_event_control(dev, dev->status, len); + case UVC_STATUS_TYPE_CONTROL: { + struct uvc_control_status *status = + (struct uvc_control_status *)dev->status; + + if (uvc_event_control(urb, status, len)) + /* The URB will be resubmitted in work context. */ + return; break; + } - case UVC_STATUS_TYPE_STREAMING: - uvc_event_streaming(dev, dev->status, len); + case UVC_STATUS_TYPE_STREAMING: { + struct uvc_streaming_status *status = + (struct uvc_streaming_status *)dev->status; + + uvc_event_streaming(dev, status, len); break; + } default: uvc_trace(UVC_TRACE_STATUS, "Unknown status event " diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index bd32914259ae..18a7384b50ee 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -994,7 +994,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh, if (ret < 0) return ret; - ret = uvc_ctrl_set(chain, &xctrl); + ret = uvc_ctrl_set(handle, &xctrl); if (ret < 0) { uvc_ctrl_rollback(handle); return ret; @@ -1069,7 +1069,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle, return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(chain, ctrl); + ret = uvc_ctrl_set(handle, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); ctrls->error_idx = commit ? ctrls->count : i; diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a88b2e51a666..86a99f461fd8 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -73,17 +73,57 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, u8 intfnum, u8 cs, void *data, u16 size) { int ret; + u8 error; + u8 tmp; ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, UVC_CTRL_CONTROL_TIMEOUT); - if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on " - "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, - unit, ret, size); - return -EIO; + if (likely(ret == size)) + return 0; + + uvc_printk(KERN_ERR, + "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", + uvc_query_name(query), cs, unit, ret, size); + + if (ret != -EPIPE) + return ret; + + tmp = *(u8 *)data; + + ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum, + UVC_VC_REQUEST_ERROR_CODE_CONTROL, data, 1, + UVC_CTRL_CONTROL_TIMEOUT); + + error = *(u8 *)data; + *(u8 *)data = tmp; + + if (ret != 1) + return ret < 0 ? ret : -EPIPE; + + uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error); + + switch (error) { + case 0: + /* Cannot happen - we received a STALL */ + return -EPIPE; + case 1: /* Not ready */ + return -EBUSY; + case 2: /* Wrong state */ + return -EILSEQ; + case 3: /* Power */ + return -EREMOTE; + case 4: /* Out of range */ + return -ERANGE; + case 5: /* Invalid unit */ + case 6: /* Invalid control */ + case 7: /* Invalid Request */ + case 8: /* Invalid value within range */ + return -EINVAL; + default: /* reserved or unknown */ + break; } - return 0; + return -EPIPE; } static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, @@ -1232,6 +1272,8 @@ static void uvc_video_validate_buffer(const struct uvc_streaming *stream, static void uvc_video_next_buffers(struct uvc_streaming *stream, struct uvc_buffer **video_buf, struct uvc_buffer **meta_buf) { + uvc_video_validate_buffer(stream, *video_buf); + if (*meta_buf) { struct vb2_v4l2_buffer *vb2_meta = &(*meta_buf)->buf; const struct vb2_v4l2_buffer *vb2_video = &(*video_buf)->buf; @@ -1270,10 +1312,8 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, do { ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (ret == -EAGAIN) { - uvc_video_validate_buffer(stream, buf); + if (ret == -EAGAIN) uvc_video_next_buffers(stream, &buf, &meta_buf); - } } while (ret == -EAGAIN); if (ret < 0) @@ -1289,10 +1329,8 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (buf->state == UVC_BUF_STATE_READY) { - uvc_video_validate_buffer(stream, buf); + if (buf->state == UVC_BUF_STATE_READY) uvc_video_next_buffers(stream, &buf, &meta_buf); - } } } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index be5cf179228b..e5f5d84f1d1d 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -12,6 +12,7 @@ #include <linux/usb/video.h> #include <linux/uvcvideo.h> #include <linux/videodev2.h> +#include <linux/workqueue.h> #include <media/media-device.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -157,6 +158,9 @@ #define UVC_GUID_FORMAT_D3DFMT_L8 \ {0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_KSMEDIA_L8_IR \ + {0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} /* ------------------------------------------------------------------------ @@ -256,6 +260,8 @@ struct uvc_control { initialized:1; u8 *uvc_data; + + struct uvc_fh *handle; /* File handle that last changed the control. */ }; struct uvc_format_desc { @@ -600,6 +606,14 @@ struct uvc_device { u8 *status; struct input_dev *input; char input_phys[64]; + + struct uvc_ctrl_work { + struct work_struct work; + struct urb *urb; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + const void *data; + } async_ctrl; }; enum uvc_handle_state { @@ -753,6 +767,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int uvc_ctrl_init_device(struct uvc_device *dev); void uvc_ctrl_cleanup_device(struct uvc_device *dev); int uvc_ctrl_restore_values(struct uvc_device *dev); +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data); int uvc_ctrl_begin(struct uvc_video_chain *chain); int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, @@ -770,7 +786,7 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle) } int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl); int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d29e45516eb7..599c1cbff3b9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -431,6 +431,20 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Use Previous Specific Frame", NULL, }; + static const char * const vp8_profile[] = { + "0", + "1", + "2", + "3", + NULL, + }; + static const char * const vp9_profile[] = { + "0", + "1", + "2", + "3", + NULL, + }; static const char * const flash_led_mode[] = { "Off", @@ -614,6 +628,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg4_profile; case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return vpx_golden_frame_sel; + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + return vp8_profile; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + return vp9_profile; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -839,7 +857,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: return "VPX Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: return "VPX I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: return "VP8 Profile"; + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: return "VP9 Profile"; /* HEVC controls */ case V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_QP: return "HEVC I-Frame QP Value"; @@ -1180,6 +1199,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DEINTERLACING_MODE: case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: case V4L2_CID_DETECT_MD_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: @@ -3137,9 +3158,22 @@ static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master, /* If OK, then make the new values permanent. */ update_flag = is_cur_manual(master) != is_new_manual(master); - for (i = 0; i < master->ncontrols; i++) + + for (i = 0; i < master->ncontrols; i++) { + /* + * If we switch from auto to manual mode, and this cluster + * contains volatile controls, then all non-master controls + * have to be marked as changed. The 'new' value contains + * the volatile value (obtained by update_from_auto_cluster), + * which now has to become the current value. + */ + if (i && update_flag && is_new_manual(master) && + master->has_volatiles && master->cluster[i]) + master->cluster[i]->has_changed = true; + new_to_cur(fh, master->cluster[i], ch_flags | ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + } return 0; } diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 4ffd7d60a901..69e775930fc4 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -202,7 +202,7 @@ static void v4l2_device_release(struct device *cd) mutex_unlock(&videodev_lock); #if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev->mdev) { + if (v4l2_dev->mdev && vdev->vfl_dir != VFL_DIR_M2M) { /* Remove interfaces and interface links */ media_devnode_remove(vdev->intf_devnode); if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) @@ -733,19 +733,22 @@ static void determine_valid_ioctls(struct video_device *vdev) BASE_VIDIOC_PRIVATE); } -static int video_register_media_controller(struct video_device *vdev, int type) +static int video_register_media_controller(struct video_device *vdev) { #if defined(CONFIG_MEDIA_CONTROLLER) u32 intf_type; int ret; - if (!vdev->v4l2_dev->mdev) + /* Memory-to-memory devices are more complex and use + * their own function to register its mc entities. + */ + if (!vdev->v4l2_dev->mdev || vdev->vfl_dir == VFL_DIR_M2M) return 0; vdev->entity.obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; vdev->entity.function = MEDIA_ENT_F_UNKNOWN; - switch (type) { + switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: intf_type = MEDIA_INTF_T_V4L_VIDEO; vdev->entity.function = MEDIA_ENT_F_IO_V4L; @@ -808,7 +811,8 @@ static int video_register_media_controller(struct video_device *vdev, int type) link = media_create_intf_link(&vdev->entity, &vdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) { media_devnode_remove(vdev->intf_devnode); media_device_unregister_entity(&vdev->entity); @@ -993,7 +997,7 @@ int __video_register_device(struct video_device *vdev, v4l2_device_get(vdev->v4l2_dev); /* Part 5: Register the entity. */ - ret = video_register_media_controller(vdev, type); + ret = video_register_media_controller(vdev); /* Part 6: Activate this minor. The char device can now be used. */ set_bit(V4L2_FL_REGISTERED, &vdev->flags); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 937c6de85606..3940e55c72f1 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -267,7 +267,8 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) link = media_create_intf_link(&sd->entity, &vdev->intf_devnode->intf, - MEDIA_LNK_FL_ENABLED); + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); if (!link) { err = -ENOMEM; goto clean_up; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 82595cebc0b8..169bdbb1f61a 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -154,6 +154,10 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus( flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + if (!fwnode_property_read_u32(fwnode, "data-enable-active", &v)) + flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH : + V4L2_MBUS_DATA_ENABLE_LOW; + bus->flags = flags; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index dd210067151f..54afc9c7ee6e 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -29,6 +29,7 @@ #include <media/v4l2-device.h> #include <media/videobuf2-v4l2.h> #include <media/v4l2-mc.h> +#include <media/v4l2-mem2mem.h> #include <trace/events/v4l2.h> @@ -125,6 +126,42 @@ int v4l2_video_std_construct(struct v4l2_standard *vs, } EXPORT_SYMBOL(v4l2_video_std_construct); +/* Fill in the fields of a v4l2_standard structure according to the + * 'id' and 'vs->index' parameters. Returns negative on error. */ +int v4l_video_std_enumstd(struct v4l2_standard *vs, v4l2_std_id id) +{ + v4l2_std_id curr_id = 0; + unsigned int index = vs->index, i, j = 0; + const char *descr = ""; + + /* Return -ENODATA if the id for the current input + or output is 0, meaning that it doesn't support this API. */ + if (id == 0) + return -ENODATA; + + /* Return norm array in a canonical way */ + for (i = 0; i <= index && id; i++) { + /* last std value in the standards array is 0, so this + while always ends there since (id & 0) == 0. */ + while ((id & standards[j].std) != standards[j].std) + j++; + curr_id = standards[j].std; + descr = standards[j].descr; + j++; + if (curr_id == 0) + break; + if (curr_id != V4L2_STD_PAL && + curr_id != V4L2_STD_SECAM && + curr_id != V4L2_STD_NTSC) + id &= ~curr_id; + } + if (i <= index) + return -EINVAL; + + v4l2_video_std_construct(vs, curr_id, descr); + return 0; +} + /* ----------------------------------------------------------------- */ /* some arrays for pretty-printing debug messages of enum types */ @@ -1147,6 +1184,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) 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; + case V4L2_PIX_FMT_Y10P: descr = "10-bit Greyscale (MIPI Packed)"; break; case V4L2_PIX_FMT_Y8I: descr = "Interleaved 8-bit Greyscale"; break; case V4L2_PIX_FMT_Y12I: descr = "Interleaved 12-bit Greyscale"; break; case V4L2_PIX_FMT_Z16: descr = "16-bit Depth"; break; @@ -1222,6 +1260,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_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; + case V4L2_PIX_FMT_SRGGB14P: descr = "14-bit Bayer RGRG/GBGB Packed"; break; case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR"; break; case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG"; break; case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG"; break; @@ -1274,6 +1316,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_VP8: descr = "VP8"; break; case V4L2_PIX_FMT_VP9: descr = "VP9"; break; case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ + case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; case V4L2_PIX_FMT_WNVA: descr = "WNVA"; break; case V4L2_PIX_FMT_SN9C10X: descr = "GSPCA SN9C10X"; break; @@ -1753,36 +1796,8 @@ static int v4l_enumstd(const struct v4l2_ioctl_ops *ops, { struct video_device *vfd = video_devdata(file); struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms, curr_id = 0; - unsigned int index = p->index, i, j = 0; - const char *descr = ""; - - /* Return -ENODATA if the tvnorms for the current input - or output is 0, meaning that it doesn't support this API. */ - if (id == 0) - return -ENODATA; - /* Return norm array in a canonical way */ - for (i = 0; i <= index && id; i++) { - /* last std value in the standards array is 0, so this - while always ends there since (id & 0) == 0. */ - while ((id & standards[j].std) != standards[j].std) - j++; - curr_id = standards[j].std; - descr = standards[j].descr; - j++; - if (curr_id == 0) - break; - if (curr_id != V4L2_STD_PAL && - curr_id != V4L2_STD_SECAM && - curr_id != V4L2_STD_NTSC) - id &= ~curr_id; - } - if (i <= index) - return -EINVAL; - - v4l2_video_std_construct(p, curr_id, descr); - return 0; + return v4l_video_std_enumstd(p, vfd->tvnorms); } static int v4l_s_std(const struct v4l2_ioctl_ops *ops, @@ -2662,11 +2677,62 @@ static bool v4l2_is_known_ioctl(unsigned int cmd) return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } +#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) +static bool v4l2_ioctl_m2m_queue_is_output(unsigned int cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_CREATE_BUFS: { + struct v4l2_create_buffers *cbufs = arg; + + return V4L2_TYPE_IS_OUTPUT(cbufs->format.type); + } + case VIDIOC_REQBUFS: { + struct v4l2_requestbuffers *rbufs = arg; + + return V4L2_TYPE_IS_OUTPUT(rbufs->type); + } + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + case VIDIOC_QUERYBUF: + case VIDIOC_PREPARE_BUF: { + struct v4l2_buffer *buf = arg; + + return V4L2_TYPE_IS_OUTPUT(buf->type); + } + case VIDIOC_EXPBUF: { + struct v4l2_exportbuffer *expbuf = arg; + + return V4L2_TYPE_IS_OUTPUT(expbuf->type); + } + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: { + int *type = arg; + + return V4L2_TYPE_IS_OUTPUT(*type); + } + default: + return false; + } +} +#endif + static struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, - unsigned int cmd) + struct v4l2_fh *vfh, unsigned int cmd, + void *arg) { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return vdev->lock; +#if IS_ENABLED(CONFIG_V4L2_MEM2MEM_DEV) + if (vfh && vfh->m2m_ctx && + (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) { + bool is_output = v4l2_ioctl_m2m_queue_is_output(cmd, arg); + struct v4l2_m2m_queue_ctx *ctx = is_output ? + &vfh->m2m_ctx->out_q_ctx : &vfh->m2m_ctx->cap_q_ctx; + + if (ctx->q.lock) + return ctx->q.lock; + } +#endif if (vdev->queue && vdev->queue->lock && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) return vdev->queue->lock; @@ -2733,7 +2799,7 @@ static long __video_do_ioctl(struct file *file, if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; - lock = v4l2_ioctl_get_lock(vfd, cmd); + lock = v4l2_ioctl_get_lock(vfd, vfh, cmd, arg); if (lock && mutex_lock_interruptible(lock)) return -ERESTARTSYS; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index c4f963d96a79..ce9bd1b91210 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -17,9 +17,11 @@ #include <linux/sched.h> #include <linux/slab.h> +#include <media/media-device.h> #include <media/videobuf2-v4l2.h> #include <media/v4l2-mem2mem.h> #include <media/v4l2-dev.h> +#include <media/v4l2-device.h> #include <media/v4l2-fh.h> #include <media/v4l2-event.h> @@ -50,9 +52,38 @@ module_param(debug, bool, 0644); * offsets but for different queues */ #define DST_QUEUE_OFF_BASE (1 << 30) +enum v4l2_m2m_entity_type { + MEM2MEM_ENT_TYPE_SOURCE, + MEM2MEM_ENT_TYPE_SINK, + MEM2MEM_ENT_TYPE_PROC +}; + +static const char * const m2m_entity_name[] = { + "source", + "sink", + "proc" +}; /** * struct v4l2_m2m_dev - per-device context + * @source: &struct media_entity pointer with the source entity + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @source_pad: &struct media_pad with the source pad. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @sink: &struct media_entity pointer with the sink entity + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @sink_pad: &struct media_pad with the sink pad. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @proc: &struct media_entity pointer with the M2M device itself. + * @proc_pads: &struct media_pad with the @proc pads. + * Used only when the M2M device is registered via + * v4l2_m2m_unregister_media_controller(). + * @intf_devnode: &struct media_intf devnode pointer with the interface + * with controls the M2M device. * @curr_ctx: currently running instance * @job_queue: instances queued to run * @job_spinlock: protects job_queue @@ -60,6 +91,15 @@ module_param(debug, bool, 0644); */ struct v4l2_m2m_dev { struct v4l2_m2m_ctx *curr_ctx; +#ifdef CONFIG_MEDIA_CONTROLLER + struct media_entity *source; + struct media_pad source_pad; + struct media_entity sink; + struct media_pad sink_pad; + struct media_entity proc; + struct media_pad proc_pads[2]; + struct media_intf_devnode *intf_devnode; +#endif struct list_head job_queue; spinlock_t job_spinlock; @@ -107,6 +147,24 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) } EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); +void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx) +{ + struct v4l2_m2m_buffer *b; + unsigned long flags; + + spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); + + if (list_empty(&q_ctx->rdy_queue)) { + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return NULL; + } + + b = list_last_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list); + spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); + return &b->vb; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_last_buf); + void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) { struct v4l2_m2m_buffer *b; @@ -209,15 +267,24 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Running job on m2m_ctx: %p\n", m2m_dev->curr_ctx); m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); } -void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +/* + * __v4l2_m2m_try_queue() - queue a job + * @m2m_dev: m2m device + * @m2m_ctx: m2m context + * + * Check if this context is ready to queue a job. + * + * This function can run in interrupt context. + */ +static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, + struct v4l2_m2m_ctx *m2m_ctx) { - struct v4l2_m2m_dev *m2m_dev; unsigned long flags_job, flags_out, flags_cap; - m2m_dev = m2m_ctx->m2m_dev; dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); if (!m2m_ctx->out_q_ctx.q.streaming @@ -275,7 +342,25 @@ void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) m2m_ctx->job_flags |= TRANS_QUEUED; spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); +} +/** + * v4l2_m2m_try_schedule() - schedule and possibly run a job for any context + * @m2m_ctx: m2m context + * + * Check if this context is ready to queue a job. If suitable, + * run the next queued job on the mem2mem device. + * + * This function shouldn't run in interrupt context. + * + * Note that v4l2_m2m_try_schedule() can schedule one job for this context, + * and then run another job for another context. + */ +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct v4l2_m2m_dev *m2m_dev = m2m_ctx->m2m_dev; + + __v4l2_m2m_try_queue(m2m_dev, m2m_ctx); v4l2_m2m_try_run(m2m_dev); } EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule); @@ -300,7 +385,8 @@ static void v4l2_m2m_cancel_job(struct v4l2_m2m_ctx *m2m_ctx) m2m_ctx->job_flags |= TRANS_ABORT; if (m2m_ctx->job_flags & TRANS_RUNNING) { spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); + if (m2m_dev->m2m_ops->job_abort) + m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); @@ -339,7 +425,6 @@ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, * allow more than one job on the job_queue per instance, each has * to be scheduled separately after the previous one finishes. */ v4l2_m2m_try_schedule(m2m_ctx); - v4l2_m2m_try_run(m2m_dev); } EXPORT_SYMBOL(v4l2_m2m_job_finish); @@ -595,12 +680,179 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL(v4l2_m2m_mmap); +#if defined(CONFIG_MEDIA_CONTROLLER) +void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev) +{ + media_remove_intf_links(&m2m_dev->intf_devnode->intf); + media_devnode_remove(m2m_dev->intf_devnode); + + media_entity_remove_links(m2m_dev->source); + media_entity_remove_links(&m2m_dev->sink); + media_entity_remove_links(&m2m_dev->proc); + media_device_unregister_entity(m2m_dev->source); + media_device_unregister_entity(&m2m_dev->sink); + media_device_unregister_entity(&m2m_dev->proc); + kfree(m2m_dev->source->name); + kfree(m2m_dev->sink.name); + kfree(m2m_dev->proc.name); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_unregister_media_controller); + +static int v4l2_m2m_register_entity(struct media_device *mdev, + struct v4l2_m2m_dev *m2m_dev, enum v4l2_m2m_entity_type type, + struct video_device *vdev, int function) +{ + struct media_entity *entity; + struct media_pad *pads; + char *name; + unsigned int len; + int num_pads; + int ret; + + switch (type) { + case MEM2MEM_ENT_TYPE_SOURCE: + entity = m2m_dev->source; + pads = &m2m_dev->source_pad; + pads[0].flags = MEDIA_PAD_FL_SOURCE; + num_pads = 1; + break; + case MEM2MEM_ENT_TYPE_SINK: + entity = &m2m_dev->sink; + pads = &m2m_dev->sink_pad; + pads[0].flags = MEDIA_PAD_FL_SINK; + num_pads = 1; + break; + case MEM2MEM_ENT_TYPE_PROC: + entity = &m2m_dev->proc; + pads = m2m_dev->proc_pads; + pads[0].flags = MEDIA_PAD_FL_SINK; + pads[1].flags = MEDIA_PAD_FL_SOURCE; + num_pads = 2; + break; + default: + return -EINVAL; + } + + entity->obj_type = MEDIA_ENTITY_TYPE_BASE; + if (type != MEM2MEM_ENT_TYPE_PROC) { + entity->info.dev.major = VIDEO_MAJOR; + entity->info.dev.minor = vdev->minor; + } + len = strlen(vdev->name) + 2 + strlen(m2m_entity_name[type]); + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + snprintf(name, len, "%s-%s", vdev->name, m2m_entity_name[type]); + entity->name = name; + entity->function = function; + + ret = media_entity_pads_init(entity, num_pads, pads); + if (ret) + return ret; + ret = media_device_register_entity(mdev, entity); + if (ret) + return ret; + + return 0; +} + +int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function) +{ + struct media_device *mdev = vdev->v4l2_dev->mdev; + struct media_link *link; + int ret; + + if (!mdev) + return 0; + + /* A memory-to-memory device consists in two + * DMA engine and one video processing entities. + * The DMA engine entities are linked to a V4L interface + */ + + /* Create the three entities with their pads */ + m2m_dev->source = &vdev->entity; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_SOURCE, vdev, MEDIA_ENT_F_IO_V4L); + if (ret) + return ret; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_PROC, vdev, function); + if (ret) + goto err_rel_entity0; + ret = v4l2_m2m_register_entity(mdev, m2m_dev, + MEM2MEM_ENT_TYPE_SINK, vdev, MEDIA_ENT_F_IO_V4L); + if (ret) + goto err_rel_entity1; + + /* Connect the three entities */ + ret = media_create_pad_link(m2m_dev->source, 0, &m2m_dev->proc, 1, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rel_entity2; + + ret = media_create_pad_link(&m2m_dev->proc, 0, &m2m_dev->sink, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rm_links0; + + /* Create video interface */ + m2m_dev->intf_devnode = media_devnode_create(mdev, + MEDIA_INTF_T_V4L_VIDEO, 0, + VIDEO_MAJOR, vdev->minor); + if (!m2m_dev->intf_devnode) { + ret = -ENOMEM; + goto err_rm_links1; + } + + /* Connect the two DMA engines to the interface */ + link = media_create_intf_link(m2m_dev->source, + &m2m_dev->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_devnode; + } + + link = media_create_intf_link(&m2m_dev->sink, + &m2m_dev->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (!link) { + ret = -ENOMEM; + goto err_rm_intf_link; + } + return 0; + +err_rm_intf_link: + media_remove_intf_links(&m2m_dev->intf_devnode->intf); +err_rm_devnode: + media_devnode_remove(m2m_dev->intf_devnode); +err_rm_links1: + media_entity_remove_links(&m2m_dev->sink); +err_rm_links0: + media_entity_remove_links(&m2m_dev->proc); + media_entity_remove_links(m2m_dev->source); +err_rel_entity2: + media_device_unregister_entity(&m2m_dev->proc); + kfree(m2m_dev->proc.name); +err_rel_entity1: + media_device_unregister_entity(&m2m_dev->sink); + kfree(m2m_dev->sink.name); +err_rel_entity0: + media_device_unregister_entity(m2m_dev->source); + kfree(m2m_dev->source->name); + return ret; + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_register_media_controller); +#endif + struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; - if (!m2m_ops || WARN_ON(!m2m_ops->device_run) || - WARN_ON(!m2m_ops->job_abort)) + if (!m2m_ops || WARN_ON(!m2m_ops->device_run)) return ERR_PTR(-EINVAL); m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6a7f7f75dfd7..2b63fa6b6fc9 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -494,6 +494,28 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_DV_TIMINGS: return v4l2_subdev_call(sd, video, s_dv_timings, arg); + + case VIDIOC_SUBDEV_G_STD: + return v4l2_subdev_call(sd, video, g_std, arg); + + case VIDIOC_SUBDEV_S_STD: { + v4l2_std_id *std = arg; + + return v4l2_subdev_call(sd, video, s_std, *std); + } + + case VIDIOC_SUBDEV_ENUMSTD: { + struct v4l2_standard *p = arg; + v4l2_std_id id; + + if (v4l2_subdev_call(sd, video, g_tvnorms, &id)) + return -EINVAL; + + return v4l_video_std_enumstd(p, id); + } + + case VIDIOC_SUBDEV_QUERYSTD: + return v4l2_subdev_call(sd, video, querystd, arg); #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index b5b890127479..f1afc0ebbc68 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -12,6 +12,7 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/acpi.h> #include <linux/delay.h> #include <linux/bitops.h> diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 06d1920150da..a90b2eb112f9 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2175,7 +2175,7 @@ static int bcm2048_fops_release(struct file *file) } static __poll_t bcm2048_fops_poll(struct file *file, - struct poll_table_struct *pts) + struct poll_table_struct *pts) { struct bcm2048_device *bdev = video_drvdata(file); __poll_t retval = 0; diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 390fc98d07dd..1269a983455e 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -1312,6 +1312,8 @@ static const struct vb2_ops video_qops = { .stop_streaming = vpfe_stop_streaming, .buf_cleanup = vpfe_buf_cleanup, .buf_queue = vpfe_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* @@ -1357,6 +1359,7 @@ static int vpfe_reqbufs(struct file *file, void *priv, q->buf_struct_size = sizeof(struct vpfe_cap_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->dev = vpfe_dev->pdev; + q->lock = &video->lock; ret = vb2_queue_init(q); if (ret) { @@ -1598,17 +1601,18 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name) return -EINVAL; } /* Initialize field of video device */ + mutex_init(&video->lock); video->video_dev.release = video_device_release; video->video_dev.fops = &vpfe_fops; video->video_dev.ioctl_ops = &vpfe_ioctl_ops; video->video_dev.minor = -1; video->video_dev.tvnorms = 0; + video->video_dev.lock = &video->lock; snprintf(video->video_dev.name, sizeof(video->video_dev.name), "DAVINCI VIDEO %s %s", name, direction); spin_lock_init(&video->irqlock); spin_lock_init(&video->dma_queue_lock); - mutex_init(&video->lock); ret = media_entity_pads_init(&video->video_dev.entity, 1, &video->pad); if (ret < 0) diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h index 22136d3dadcb..4bbd219e8329 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.h +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -128,7 +128,7 @@ struct vpfe_video_device { spinlock_t irqlock; /* IRQ lock for DMA queue */ spinlock_t dma_queue_lock; - /* lock used to access this structure */ + /* lock used to serialize all video4linux ioctls */ struct mutex lock; /* number of users performing IO */ u32 io_usrs; diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index ae453fd422f0..28f41caba05d 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -103,6 +103,7 @@ struct prp_priv { int nfb4eof_irq; int stream_count; + u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ struct completion last_eof_comp; @@ -210,12 +211,15 @@ static void prp_vb2_buf_done(struct prp_priv *priv, struct ipuv3_channel *ch) done = priv->active_vb2_buf[priv->ipu_buf_num]; if (done) { + done->vbuf.field = vdev->fmt.fmt.pix.field; + done->vbuf.sequence = priv->frame_sequence; vb = &done->vbuf.vb2_buf; vb->timestamp = ktime_get_ns(); vb2_buffer_done(vb, priv->nfb4eof ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); } + priv->frame_sequence++; priv->nfb4eof = false; /* get next queued buffer */ @@ -637,6 +641,7 @@ static int prp_start(struct prp_priv *priv) /* init EOF completion waitq */ init_completion(&priv->last_eof_comp); + priv->frame_sequence = 0; priv->last_eof = false; priv->nfb4eof = false; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 4e3fdf8aeef5..256039ce561e 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -170,23 +170,22 @@ static int capture_enum_fmt_vid_cap(struct file *file, void *fh, } cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY); - if (!cc_src) - cc_src = imx_media_find_mbus_format(fmt_src.format.code, - CS_SEL_ANY, true); - if (!cc_src) - return -EINVAL; - - if (cc_src->bayer) { - if (f->index != 0) - return -EINVAL; - fourcc = cc_src->fourcc; - } else { + if (cc_src) { u32 cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ? CS_SEL_YUV : CS_SEL_RGB; ret = imx_media_enum_format(&fourcc, f->index, cs_sel); if (ret) return ret; + } else { + cc_src = imx_media_find_mbus_format(fmt_src.format.code, + CS_SEL_ANY, true); + if (WARN_ON(!cc_src)) + return -EINVAL; + + if (f->index != 0) + return -EINVAL; + fourcc = cc_src->fourcc; } f->pixelformat = fourcc; @@ -219,15 +218,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, return ret; cc_src = imx_media_find_ipu_format(fmt_src.format.code, CS_SEL_ANY); - if (!cc_src) - cc_src = imx_media_find_mbus_format(fmt_src.format.code, - CS_SEL_ANY, true); - if (!cc_src) - return -EINVAL; - - if (cc_src->bayer) { - cc = cc_src; - } else { + if (cc_src) { u32 fourcc, cs_sel; cs_sel = (cc_src->cs == IPUV3_COLORSPACE_YUV) ? @@ -239,6 +230,13 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, imx_media_enum_format(&fourcc, 0, cs_sel); cc = imx_media_find_format(fourcc, cs_sel, false); } + } else { + cc_src = imx_media_find_mbus_format(fmt_src.format.code, + CS_SEL_ANY, true); + if (WARN_ON(!cc_src)) + return -EINVAL; + + cc = cc_src; } imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src.format, cc); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 95d7805f3485..cd2c291e1e94 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -111,6 +111,7 @@ struct csi_priv { struct v4l2_ctrl_handler ctrl_hdlr; int stream_count; /* streaming counter */ + u32 frame_sequence; /* frame sequence counter */ bool last_eof; /* waiting for last EOF at stream off */ bool nfb4eof; /* NFB4EOF encountered during streaming */ struct completion last_eof_comp; @@ -121,10 +122,32 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev) return container_of(sdev, struct csi_priv, sd); } +static inline bool is_parallel_bus(struct v4l2_fwnode_endpoint *ep) +{ + return ep->bus_type != V4L2_MBUS_CSI2; +} + static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) { - return ep->bus_type != V4L2_MBUS_CSI2 && - ep->bus.parallel.bus_width >= 16; + return is_parallel_bus(ep) && ep->bus.parallel.bus_width >= 16; +} + +/* + * Check for conditions that require the IPU to handle the + * data internally as generic data, aka passthrough mode: + * - raw bayer media bus formats, or + * - the CSI is receiving from a 16-bit parallel bus, or + * - the CSI is receiving from an 8-bit parallel bus and the incoming + * media bus format is other than UYVY8_2X8/YUYV8_2X8. + */ +static inline bool requires_passthrough(struct v4l2_fwnode_endpoint *ep, + struct v4l2_mbus_framefmt *infmt, + const struct imx_media_pixfmt *incc) +{ + return incc->bayer || is_parallel_16bit_bus(ep) || + (is_parallel_bus(ep) && + infmt->code != MEDIA_BUS_FMT_UYVY8_2X8 && + infmt->code != MEDIA_BUS_FMT_YUYV8_2X8); } /* @@ -236,12 +259,15 @@ static void csi_vb2_buf_done(struct csi_priv *priv) done = priv->active_vb2_buf[priv->ipu_buf_num]; if (done) { + done->vbuf.field = vdev->fmt.fmt.pix.field; + done->vbuf.sequence = priv->frame_sequence; vb = &done->vbuf.vb2_buf; vb->timestamp = ktime_get_ns(); vb2_buffer_done(vb, priv->nfb4eof ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); } + priv->frame_sequence++; priv->nfb4eof = false; /* get next queued buffer */ @@ -367,15 +393,18 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, static int csi_idmac_setup_channel(struct csi_priv *priv) { struct imx_media_video_dev *vdev = priv->vdev; + const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; struct ipu_image image; u32 passthrough_bits; + u32 passthrough_cycles; dma_addr_t phys[2]; bool passthrough; u32 burst_size; int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; + incc = priv->cc[CSI_SINK_PAD]; ipu_cpmem_zero(priv->idmac_ch); @@ -389,12 +418,9 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) image.phys0 = phys[0]; image.phys1 = phys[1]; - /* - * Check for conditions that require the IPU to handle the - * data internally as generic data, aka passthrough mode: - * - raw bayer formats - * - the CSI is receiving from a 16-bit parallel bus - */ + passthrough = requires_passthrough(&priv->upstream_ep, infmt, incc); + passthrough_cycles = 1; + switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: @@ -402,7 +428,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_SRGGB8: case V4L2_PIX_FMT_GREY: burst_size = 16; - passthrough = true; passthrough_bits = 8; break; case V4L2_PIX_FMT_SBGGR16: @@ -411,7 +436,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_SRGGB16: case V4L2_PIX_FMT_Y16: burst_size = 8; - passthrough = true; passthrough_bits = 16; break; case V4L2_PIX_FMT_YUV420: @@ -419,7 +443,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; - passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); @@ -428,18 +451,25 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_UYVY: burst_size = (image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32; - passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; + case V4L2_PIX_FMT_RGB565: + if (passthrough) { + burst_size = 16; + passthrough_bits = 8; + passthrough_cycles = incc->cycles; + break; + } + /* fallthrough - non-passthrough RGB565 (CSI-2 bus) */ default: burst_size = (image.pix.width & 0xf) ? 8 : 16; - passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; } if (passthrough) { - ipu_cpmem_set_resolution(priv->idmac_ch, image.rect.width, + ipu_cpmem_set_resolution(priv->idmac_ch, + image.rect.width * passthrough_cycles, image.rect.height); ipu_cpmem_set_stride(priv->idmac_ch, image.pix.bytesperline); ipu_cpmem_set_buffer(priv->idmac_ch, 0, image.phys0); @@ -545,6 +575,7 @@ static int csi_idmac_start(struct csi_priv *priv) /* init EOF completion waitq */ init_completion(&priv->last_eof_comp); + priv->frame_sequence = 0; priv->last_eof = false; priv->nfb4eof = false; @@ -630,17 +661,20 @@ static void csi_idmac_stop(struct csi_priv *priv) static int csi_setup(struct csi_priv *priv) { struct v4l2_mbus_framefmt *infmt, *outfmt; + const struct imx_media_pixfmt *incc; struct v4l2_mbus_config mbus_cfg; struct v4l2_mbus_framefmt if_fmt; + struct v4l2_rect crop; infmt = &priv->format_mbus[CSI_SINK_PAD]; + incc = priv->cc[CSI_SINK_PAD]; outfmt = &priv->format_mbus[priv->active_output_pad]; /* compose mbus_config from the upstream endpoint */ mbus_cfg.type = priv->upstream_ep.bus_type; - mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ? - priv->upstream_ep.bus.mipi_csi2.flags : - priv->upstream_ep.bus.parallel.flags; + mbus_cfg.flags = is_parallel_bus(&priv->upstream_ep) ? + priv->upstream_ep.bus.parallel.flags : + priv->upstream_ep.bus.mipi_csi2.flags; /* * we need to pass input frame to CSI interface, but @@ -648,8 +682,18 @@ static int csi_setup(struct csi_priv *priv) */ if_fmt = *infmt; if_fmt.field = outfmt->field; + crop = priv->crop; - ipu_csi_set_window(priv->csi, &priv->crop); + /* + * if cycles is set, we need to handle this over multiple cycles as + * generic/bayer data + */ + if (is_parallel_bus(&priv->upstream_ep) && incc->cycles) { + if_fmt.width *= incc->cycles; + crop.width *= incc->cycles; + } + + ipu_csi_set_window(priv->csi, &crop); ipu_csi_set_downsize(priv->csi, priv->crop.width == 2 * priv->compose.width, @@ -1007,7 +1051,6 @@ static int csi_link_validate(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct v4l2_fwnode_endpoint upstream_ep = {}; - const struct imx_media_pixfmt *incc; bool is_csi2; int ret; @@ -1025,17 +1068,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, mutex_lock(&priv->lock); priv->upstream_ep = upstream_ep; - is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2); - incc = priv->cc[CSI_SINK_PAD]; - - if (priv->dest != IPU_CSI_DEST_IDMAC && - (incc->bayer || is_parallel_16bit_bus(&upstream_ep))) { - v4l2_err(&priv->sd, - "bayer/16-bit parallel buses must go to IDMAC pad\n"); - ret = -EINVAL; - goto out; - } - + is_csi2 = !is_parallel_bus(&upstream_ep); if (is_csi2) { int vc_num = 0; /* @@ -1059,7 +1092,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, /* select either parallel or MIPI-CSI2 as input to CSI */ ipu_set_csi_src_mux(priv->ipu, priv->csi_id, is_csi2); -out: + mutex_unlock(&priv->lock); return ret; } @@ -1131,6 +1164,7 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct csi_priv *priv = v4l2_get_subdevdata(sd); + struct v4l2_fwnode_endpoint upstream_ep; const struct imx_media_pixfmt *incc; struct v4l2_mbus_framefmt *infmt; int ret = 0; @@ -1147,7 +1181,13 @@ static int csi_enum_mbus_code(struct v4l2_subdev *sd, break; case CSI_SRC_PAD_DIRECT: case CSI_SRC_PAD_IDMAC: - if (incc->bayer) { + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + goto out; + } + + if (requires_passthrough(&upstream_ep, infmt, incc)) { if (code->index != 0) { ret = -EINVAL; goto out; @@ -1192,10 +1232,12 @@ static int csi_enum_frame_size(struct v4l2_subdev *sd, } else { crop = __csi_get_crop(priv, cfg, fse->which); - fse->min_width = fse->max_width = fse->index & 1 ? + fse->min_width = fse->index & 1 ? crop->width / 2 : crop->width; - fse->min_height = fse->max_height = fse->index & 2 ? + fse->max_width = fse->min_width; + fse->min_height = fse->index & 2 ? crop->height / 2 : crop->height; + fse->max_height = fse->min_height; } mutex_unlock(&priv->lock); @@ -1286,7 +1328,7 @@ static void csi_try_fmt(struct csi_priv *priv, sdformat->format.width = compose->width; sdformat->format.height = compose->height; - if (incc->bayer) { + if (requires_passthrough(upstream_ep, infmt, incc)) { sdformat->format.code = infmt->code; *cc = incc; } else { diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 7ec2db84451c..8aa13403b09d 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -78,6 +78,7 @@ static const struct imx_media_pixfmt rgb_formats[] = { .codes = {MEDIA_BUS_FMT_RGB565_2X8_LE}, .cs = IPUV3_COLORSPACE_RGB, .bpp = 16, + .cycles = 2, }, { .fourcc = V4L2_PIX_FMT_RGB24, .codes = { diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index e945e0ed6dd6..57bd094cf765 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -62,6 +62,8 @@ struct imx_media_pixfmt { u32 fourcc; u32 codes[4]; int bpp; /* total bpp */ + /* cycles per pixel for generic (bayer) formats for the parallel bus */ + int cycles; enum ipu_color_space cs; bool planar; /* is a planar format */ bool bayer; /* is a raw bayer format */ diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index ce26741ae9d9..6dd0c838db05 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -628,20 +628,6 @@ static void stop_streaming(struct vb2_queue *vq) v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); } -static void bm2835_mmal_lock(struct vb2_queue *vq) -{ - struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); - - mutex_lock(&dev->mutex); -} - -static void bm2835_mmal_unlock(struct vb2_queue *vq) -{ - struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); - - mutex_unlock(&dev->mutex); -} - static const struct vb2_ops bm2835_mmal_video_qops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -650,8 +636,8 @@ static const struct vb2_ops bm2835_mmal_video_qops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, - .wait_prepare = bm2835_mmal_unlock, - .wait_finish = bm2835_mmal_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* ------------------------------------------------------------------ @@ -1864,6 +1850,8 @@ static int bcm2835_mmal_probe(struct platform_device *pdev) goto cleanup_gdev; } + /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ + mutex_init(&dev->mutex); dev->camera_num = camera; dev->max_width = resolutions[camera][0]; dev->max_height = resolutions[camera][1]; @@ -1908,13 +1896,11 @@ static int bcm2835_mmal_probe(struct platform_device *pdev) q->ops = &bm2835_mmal_video_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->mutex; ret = vb2_queue_init(q); if (ret < 0) goto unreg_dev; - /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ - mutex_init(&dev->mutex); - /* initialise video devices */ ret = bm2835_mmal_init_device(dev, &dev->vdev); if (ret < 0) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index 09e5bb013d28..a5e58a829ea0 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -137,8 +137,7 @@ static int dss_initialize_debugfs(void) static void dss_uninitialize_debugfs(void) { - if (dss_debugfs_dir) - debugfs_remove_recursive(dss_debugfs_dir); + debugfs_remove_recursive(dss_debugfs_dir); } int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c b/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c index 8fc843b56b26..e8d428bc47e3 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dss_features.c @@ -891,8 +891,7 @@ bool dss_has_feature(enum dss_feat_id id) void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) { - if (id >= omap_current_dss_features->num_reg_fields) - BUG(); + BUG_ON(id >= omap_current_dss_features->num_reg_fields); *start = omap_current_dss_features->reg_fields[id].start; *end = omap_current_dss_features->reg_fields[id].end; diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index e08e5664e330..3e7887c53d7c 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -287,7 +287,7 @@ static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, var->red.length == 0 || var->blue.length == 0 || var->green.length == 0) - return 0; + return false; return var->bits_per_pixel == color->bits_per_pixel && cmp_component(&var->red, &color->red) && diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 9907475b4226..a9b00942e87d 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -198,34 +198,6 @@ static int do_video_stillpicture(struct file *file, return err; } -struct compat_video_spu_palette { - int length; - compat_uptr_t palette; -}; - -static int do_video_set_spu_palette(struct file *file, - unsigned int cmd, struct compat_video_spu_palette __user *up) -{ - struct video_spu_palette __user *up_native; - compat_uptr_t palp; - int length, err; - - err = get_user(palp, &up->palette); - err |= get_user(length, &up->length); - if (err) - return -EFAULT; - - up_native = compat_alloc_user_space(sizeof(struct video_spu_palette)); - err = put_user(compat_ptr(palp), &up_native->palette); - err |= put_user(length, &up_native->length); - if (err) - return -EFAULT; - - err = do_ioctl(file, cmd, (unsigned long) up_native); - - return err; -} - #ifdef CONFIG_BLOCK typedef struct sg_io_hdr32 { compat_int_t interface_id; /* [i] 'S' for SCSI generic (required) */ @@ -1206,9 +1178,6 @@ COMPATIBLE_IOCTL(AUDIO_CLEAR_BUFFER) COMPATIBLE_IOCTL(AUDIO_SET_ID) COMPATIBLE_IOCTL(AUDIO_SET_MIXER) COMPATIBLE_IOCTL(AUDIO_SET_STREAMTYPE) -COMPATIBLE_IOCTL(AUDIO_SET_EXT_ID) -COMPATIBLE_IOCTL(AUDIO_SET_ATTRIBUTES) -COMPATIBLE_IOCTL(AUDIO_SET_KARAOKE) COMPATIBLE_IOCTL(DMX_START) COMPATIBLE_IOCTL(DMX_STOP) COMPATIBLE_IOCTL(DMX_SET_FILTER) @@ -1233,16 +1202,9 @@ COMPATIBLE_IOCTL(VIDEO_FAST_FORWARD) COMPATIBLE_IOCTL(VIDEO_SLOWMOTION) COMPATIBLE_IOCTL(VIDEO_GET_CAPABILITIES) COMPATIBLE_IOCTL(VIDEO_CLEAR_BUFFER) -COMPATIBLE_IOCTL(VIDEO_SET_ID) COMPATIBLE_IOCTL(VIDEO_SET_STREAMTYPE) COMPATIBLE_IOCTL(VIDEO_SET_FORMAT) -COMPATIBLE_IOCTL(VIDEO_SET_SYSTEM) -COMPATIBLE_IOCTL(VIDEO_SET_HIGHLIGHT) -COMPATIBLE_IOCTL(VIDEO_SET_SPU) -COMPATIBLE_IOCTL(VIDEO_GET_NAVI) -COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES) COMPATIBLE_IOCTL(VIDEO_GET_SIZE) -COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE) /* cec */ COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS) COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS) @@ -1347,8 +1309,6 @@ static long do_ioctl_trans(unsigned int cmd, return do_video_get_event(file, cmd, argp); case VIDEO_STILLPICTURE: return do_video_stillpicture(file, cmd, argp); - case VIDEO_SET_SPU_PALETTE: - return do_video_set_spu_palette(file, cmd, argp); } /* diff --git a/include/linux/platform_data/media/sii9234.h b/include/linux/platform_data/media/sii9234.h deleted file mode 100644 index 6a4a809fe9a3..000000000000 --- a/include/linux/platform_data/media/sii9234.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Driver header for SII9234 MHL converter chip. - * - * Copyright (c) 2011 Samsung Electronics, Co. Ltd - * Contact: Tomasz Stanislawski <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef SII9234_H -#define SII9234_H - -/** - * @gpio_n_reset: GPIO driving nRESET pin - */ - -struct sii9234_platform_data { - int gpio_n_reset; -}; - -#endif /* SII9234_H */ diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index ed16c6dde0ba..604e79cb6cbf 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -25,6 +25,9 @@ * @read_hpd: read the HPD pin. Return true if high, false if low or * an error if negative. If NULL or -ENOTTY is returned, * then this is not supported. + * @read_5v: read the 5V pin. Return true if high, false if low or + * an error if negative. If NULL or -ENOTTY is returned, + * then this is not supported. * * These operations are used by the cec pin framework to manipulate * the CEC pin. @@ -38,6 +41,7 @@ struct cec_pin_ops { void (*free)(struct cec_adapter *adap); void (*status)(struct cec_adapter *adap, struct seq_file *file); int (*read_hpd)(struct cec_adapter *adap); + int (*read_5v)(struct cec_adapter *adap); }; /** diff --git a/include/media/cec.h b/include/media/cec.h index 580ab1042898..ff9847f7f99d 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -79,7 +79,7 @@ struct cec_event_entry { }; #define CEC_NUM_CORE_EVENTS 2 -#define CEC_NUM_EVENTS CEC_EVENT_PIN_HPD_HIGH +#define CEC_NUM_EVENTS CEC_EVENT_PIN_5V_HIGH struct cec_fh { struct list_head list; @@ -309,6 +309,16 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts); /** + * cec_queue_pin_5v_event() - queue a pin event with a given timestamp. + * + * @adap: pointer to the cec adapter + * @is_high: when true the 5V pin is high, otherwise it is low + * @ts: the timestamp for this event + * + */ +void cec_queue_pin_5v_event(struct cec_adapter *adap, bool is_high, ktime_t ts); + +/** * cec_get_edid_phys_addr() - find and return the physical address * * @edid: pointer to the EDID data diff --git a/include/media/dvb_frontend.h b/include/media/dvb_frontend.h index 331c8269c00e..6f7a85ab3541 100644 --- a/include/media/dvb_frontend.h +++ b/include/media/dvb_frontend.h @@ -52,6 +52,10 @@ */ #define MAX_DELSYS 8 +/* Helper definitions to be used at frontend drivers */ +#define kHz 1000UL +#define MHz 1000000UL + /** * struct dvb_frontend_tune_settings - parameters to adjust frontend tuning * @@ -73,22 +77,19 @@ struct dvb_frontend; * struct dvb_tuner_info - Frontend name and min/max ranges/bandwidths * * @name: name of the Frontend - * @frequency_min: minimal frequency supported - * @frequency_max: maximum frequency supported - * @frequency_step: frequency step + * @frequency_min_hz: minimal frequency supported in Hz + * @frequency_max_hz: maximum frequency supported in Hz + * @frequency_step_hz: frequency step in Hz * @bandwidth_min: minimal frontend bandwidth supported * @bandwidth_max: maximum frontend bandwidth supported * @bandwidth_step: frontend bandwidth step - * - * NOTE: frequency parameters are in Hz, for terrestrial/cable or kHz for - * satellite. */ struct dvb_tuner_info { char name[128]; - u32 frequency_min; - u32 frequency_max; - u32 frequency_step; + u32 frequency_min_hz; + u32 frequency_max_hz; + u32 frequency_step_hz; u32 bandwidth_min; u32 bandwidth_max; @@ -316,6 +317,34 @@ struct analog_demod_ops { struct dtv_frontend_properties; +/** + * struct dvb_frontend_internal_info - Frontend properties and capabilities + * + * @name: Name of the frontend + * @frequency_min_hz: Minimal frequency supported by the frontend. + * @frequency_max_hz: Minimal frequency supported by the frontend. + * @frequency_stepsize_hz: All frequencies are multiple of this value. + * @frequency_tolerance_hz: Frequency tolerance. + * @symbol_rate_min: Minimal symbol rate, in bauds + * (for Cable/Satellite systems). + * @symbol_rate_max: Maximal symbol rate, in bauds + * (for Cable/Satellite systems). + * @symbol_rate_tolerance: Maximal symbol rate tolerance, in ppm + * (for Cable/Satellite systems). + * @caps: Capabilities supported by the frontend, + * as specified in &enum fe_caps. + */ +struct dvb_frontend_internal_info { + char name[128]; + u32 frequency_min_hz; + u32 frequency_max_hz; + u32 frequency_stepsize_hz; + u32 frequency_tolerance_hz; + u32 symbol_rate_min; + u32 symbol_rate_max; + u32 symbol_rate_tolerance; + enum fe_caps caps; +}; /** * struct dvb_frontend_ops - Demodulation information and callbacks for @@ -403,7 +432,7 @@ struct dtv_frontend_properties; * @analog_ops: pointer to &struct analog_demod_ops */ struct dvb_frontend_ops { - struct dvb_frontend_info info; + struct dvb_frontend_internal_info info; u8 delsys[MAX_DELSYS]; diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h index a5bd310c9e1e..0e2b1c751a5d 100644 --- a/include/media/i2c/lm3560.h +++ b/include/media/i2c/lm3560.h @@ -22,6 +22,7 @@ #include <media/v4l2-subdev.h> +#define LM3559_NAME "lm3559" #define LM3560_NAME "lm3560" #define LM3560_I2C_ADDR (0x53) diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 160bca96d524..cdc87ec61e54 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -338,7 +338,7 @@ void v4l_bound_align_image(unsigned int *width, unsigned int wmin, ({ \ BUILD_BUG_ON(sizeof((array)->width_field) != sizeof(u32) || \ sizeof((array)->height_field) != sizeof(u32)); \ - (typeof(&(*(array))))__v4l2_find_nearest_size( \ + (typeof(&(array)[0]))__v4l2_find_nearest_size( \ (array), array_size, sizeof(*(array)), \ offsetof(typeof(*(array)), width_field), \ offsetof(typeof(*(array)), height_field), \ diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 5b445b5654f7..f615ba1b29dd 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -181,10 +181,10 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); * not freed when the control is deleted. Should this be needed * then a new internal bitfield can be added to tell the framework * to free this pointer. - * @p_cur: The control's current value represented via a union with + * @p_cur: The control's current value represented via a union which * provides a standard way of accessing control types * through a pointer. - * @p_new: The control's new value represented via a union with provides + * @p_new: The control's new value represented via a union which provides * a standard way of accessing control types * through a pointer. */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index a8dbf5b54b5c..5848d92c30da 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -621,7 +621,7 @@ const char *v4l2_norm_to_name(v4l2_std_id id); * v4l2_video_std_frame_period - Ancillary routine that fills a * struct &v4l2_fract pointer with the default framerate fraction. * - * @id: analog TV sdandard ID. + * @id: analog TV standard ID. * @frameperiod: struct &v4l2_fract pointer to be filled * */ @@ -632,7 +632,7 @@ void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod); * a &v4l2_standard structure according to the @id parameter. * * @vs: struct &v4l2_standard pointer to be filled - * @id: analog TV sdandard ID. + * @id: analog TV standard ID. * @name: name of the standard to be used * * .. note:: @@ -643,6 +643,17 @@ int v4l2_video_std_construct(struct v4l2_standard *vs, int id, const char *name); /** + * v4l_video_std_enumstd - Ancillary routine that fills in the fields of + * a &v4l2_standard structure according to the @id and @vs->index + * parameters. + * + * @vs: struct &v4l2_standard pointer to be filled. + * @id: analog TV standard ID. + * + */ +int v4l_video_std_enumstd(struct v4l2_standard *vs, v4l2_std_id id); + +/** * v4l_printk_ioctl - Ancillary routine that prints the ioctl in a * human-readable format. * diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 4d8626c468bc..4bbb5f3d2b02 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -45,6 +45,8 @@ /* Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. */ #define V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH BIT(12) #define V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW BIT(13) +#define V4L2_MBUS_DATA_ENABLE_HIGH BIT(14) +#define V4L2_MBUS_DATA_ENABLE_LOW BIT(15) /* Serial flags */ /* How many lanes the client can use */ diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 3d07ba3a8262..d655720e16a1 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -32,7 +32,7 @@ * assumed that one source and one destination buffer are all * that is required for the driver to perform one full transaction. * This method may not sleep. - * @job_abort: required. Informs the driver that it has to abort the currently + * @job_abort: optional. Informs the driver that it has to abort the currently * running transaction as soon as possible (i.e. as soon as it can * stop the device safely; e.g. in the next interrupt handler), * even if the transaction would not have been finished by then. @@ -40,19 +40,14 @@ * v4l2_m2m_job_finish() (as if the transaction ended normally). * This function does not have to (and will usually not) wait * until the device enters a state when it can be stopped. - * @lock: optional. Define a driver's own lock callback, instead of using - * &v4l2_m2m_ctx->q_lock. - * @unlock: optional. Define a driver's own unlock callback, instead of - * using &v4l2_m2m_ctx->q_lock. */ struct v4l2_m2m_ops { void (*device_run)(void *priv); int (*job_ready)(void *priv); void (*job_abort)(void *priv); - void (*lock)(void *priv); - void (*unlock)(void *priv); }; +struct video_device; struct v4l2_m2m_dev; /** @@ -328,6 +323,24 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, */ struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops); +#if defined(CONFIG_MEDIA_CONTROLLER) +void v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev); +int v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function); +#else +static inline void +v4l2_m2m_unregister_media_controller(struct v4l2_m2m_dev *m2m_dev) +{ +} + +static inline int +v4l2_m2m_register_media_controller(struct v4l2_m2m_dev *m2m_dev, + struct video_device *vdev, int function) +{ + return 0; +} +#endif + /** * v4l2_m2m_release() - cleans up and frees a m2m_dev structure * @@ -437,6 +450,35 @@ static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) } /** + * v4l2_m2m_last_buf() - return last buffer from the list of ready buffers + * + * @q_ctx: pointer to struct @v4l2_m2m_queue_ctx + */ +void *v4l2_m2m_last_buf(struct v4l2_m2m_queue_ctx *q_ctx); + +/** + * v4l2_m2m_last_src_buf() - return last destination buffer from the list of + * ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void *v4l2_m2m_last_src_buf(struct v4l2_m2m_ctx *m2m_ctx) +{ + return v4l2_m2m_last_buf(&m2m_ctx->out_q_ctx); +} + +/** + * v4l2_m2m_last_dst_buf() - return last destination buffer from the list of + * ready buffers + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void *v4l2_m2m_last_dst_buf(struct v4l2_m2m_ctx *m2m_ctx) +{ + return v4l2_m2m_last_buf(&m2m_ctx->cap_q_ctx); +} + +/** * v4l2_m2m_for_each_dst_buf() - iterate over a list of destination ready * buffers * diff --git a/include/media/vsp1.h b/include/media/vsp1.h index 678c24de1ac6..3093b9cb9067 100644 --- a/include/media/vsp1.h +++ b/include/media/vsp1.h @@ -25,6 +25,7 @@ int vsp1_du_init(struct device *dev); * struct vsp1_du_lif_config - VSP LIF configuration * @width: output frame width * @height: output frame height + * @interlaced: true for interlaced pipelines * @callback: frame completion callback function (optional). When a callback * is provided, the VSP driver guarantees that it will be called once * and only once for each vsp1_du_atomic_flush() call. @@ -33,6 +34,7 @@ int vsp1_du_init(struct device *dev); struct vsp1_du_lif_config { unsigned int width; unsigned int height; + bool interlaced; void (*callback)(void *data, bool completed, u32 crc); void *callback_data; diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 20fe091b7e96..097fcd812471 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -384,6 +384,8 @@ struct cec_log_addrs { #define CEC_EVENT_PIN_CEC_HIGH 4 #define CEC_EVENT_PIN_HPD_LOW 5 #define CEC_EVENT_PIN_HPD_HIGH 6 +#define CEC_EVENT_PIN_5V_LOW 7 +#define CEC_EVENT_PIN_5V_HIGH 8 #define CEC_EVENT_FL_INITIAL_STATE (1 << 0) #define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1) diff --git a/include/uapi/linux/dvb/audio.h b/include/uapi/linux/dvb/audio.h index 69f7a85d81b1..afeae063e640 100644 --- a/include/uapi/linux/dvb/audio.h +++ b/include/uapi/linux/dvb/audio.h @@ -67,27 +67,6 @@ typedef struct audio_status { } audio_status_t; /* separate decoder hardware */ -typedef -struct audio_karaoke { /* if Vocal1 or Vocal2 are non-zero, they get mixed */ - int vocal1; /* into left and right t at 70% each */ - int vocal2; /* if both, Vocal1 and Vocal2 are non-zero, Vocal1 gets*/ - int melody; /* mixed into the left channel and */ - /* Vocal2 into the right channel at 100% each. */ - /* if Melody is non-zero, the melody channel gets mixed*/ -} audio_karaoke_t; /* into left and right */ - - -typedef __u16 audio_attributes_t; -/* bits: descr. */ -/* 15-13 audio coding mode (0=ac3, 2=mpeg1, 3=mpeg2ext, 4=LPCM, 6=DTS, */ -/* 12 multichannel extension */ -/* 11-10 audio type (0=not spec, 1=language included) */ -/* 9- 8 audio application mode (0=not spec, 1=karaoke, 2=surround) */ -/* 7- 6 Quantization / DRC (mpeg audio: 1=DRC exists)(lpcm: 0=16bit, */ -/* 5- 4 Sample frequency fs (0=48kHz, 1=96kHz) */ -/* 2- 0 number of audio channels (n+1 channels) */ - - /* for GET_CAPABILITIES and SET_FORMAT, the latter should only set one bit */ #define AUDIO_CAP_DTS 1 #define AUDIO_CAP_LPCM 2 @@ -115,22 +94,6 @@ typedef __u16 audio_attributes_t; #define AUDIO_SET_ID _IO('o', 13) #define AUDIO_SET_MIXER _IOW('o', 14, audio_mixer_t) #define AUDIO_SET_STREAMTYPE _IO('o', 15) -#define AUDIO_SET_EXT_ID _IO('o', 16) -#define AUDIO_SET_ATTRIBUTES _IOW('o', 17, audio_attributes_t) -#define AUDIO_SET_KARAOKE _IOW('o', 18, audio_karaoke_t) - -/** - * AUDIO_GET_PTS - * - * Read the 33 bit presentation time stamp as defined - * in ITU T-REC-H.222.0 / ISO/IEC 13818-1. - * - * The PTS should belong to the currently played - * frame if possible, but may also be a value close to it - * like the PTS of the last decoded frame or the last PTS - * extracted by the PES parser. - */ -#define AUDIO_GET_PTS _IOR('o', 19, __u64) #define AUDIO_BILINGUAL_CHANNEL_SELECT _IO('o', 20) #endif /* _DVBAUDIO_H_ */ diff --git a/include/uapi/linux/dvb/video.h b/include/uapi/linux/dvb/video.h index df3d7028c807..43ba8b0a3d14 100644 --- a/include/uapi/linux/dvb/video.h +++ b/include/uapi/linux/dvb/video.h @@ -38,18 +38,6 @@ typedef enum { typedef enum { - VIDEO_SYSTEM_PAL, - VIDEO_SYSTEM_NTSC, - VIDEO_SYSTEM_PALN, - VIDEO_SYSTEM_PALNc, - VIDEO_SYSTEM_PALM, - VIDEO_SYSTEM_NTSC60, - VIDEO_SYSTEM_PAL60, - VIDEO_SYSTEM_PALM60 -} video_system_t; - - -typedef enum { VIDEO_PAN_SCAN, /* use pan and scan format */ VIDEO_LETTER_BOX, /* use letterbox format */ VIDEO_CENTER_CUT_OUT /* use center cut out format */ @@ -160,44 +148,6 @@ struct video_still_picture { }; -typedef -struct video_highlight { - int active; /* 1=show highlight, 0=hide highlight */ - __u8 contrast1; /* 7- 4 Pattern pixel contrast */ - /* 3- 0 Background pixel contrast */ - __u8 contrast2; /* 7- 4 Emphasis pixel-2 contrast */ - /* 3- 0 Emphasis pixel-1 contrast */ - __u8 color1; /* 7- 4 Pattern pixel color */ - /* 3- 0 Background pixel color */ - __u8 color2; /* 7- 4 Emphasis pixel-2 color */ - /* 3- 0 Emphasis pixel-1 color */ - __u32 ypos; /* 23-22 auto action mode */ - /* 21-12 start y */ - /* 9- 0 end y */ - __u32 xpos; /* 23-22 button color number */ - /* 21-12 start x */ - /* 9- 0 end x */ -} video_highlight_t; - - -typedef struct video_spu { - int active; - int stream_id; -} video_spu_t; - - -typedef struct video_spu_palette { /* SPU Palette information */ - int length; - __u8 __user *palette; -} video_spu_palette_t; - - -typedef struct video_navi_pack { - int length; /* 0 ... 1024 */ - __u8 data[1024]; -} video_navi_pack_t; - - typedef __u16 video_attributes_t; /* bits: descr. */ /* 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) */ @@ -242,17 +192,9 @@ typedef __u16 video_attributes_t; #define VIDEO_SLOWMOTION _IO('o', 32) #define VIDEO_GET_CAPABILITIES _IOR('o', 33, unsigned int) #define VIDEO_CLEAR_BUFFER _IO('o', 34) -#define VIDEO_SET_ID _IO('o', 35) #define VIDEO_SET_STREAMTYPE _IO('o', 36) #define VIDEO_SET_FORMAT _IO('o', 37) -#define VIDEO_SET_SYSTEM _IO('o', 38) -#define VIDEO_SET_HIGHLIGHT _IOW('o', 39, video_highlight_t) -#define VIDEO_SET_SPU _IOW('o', 50, video_spu_t) -#define VIDEO_SET_SPU_PALETTE _IOW('o', 51, video_spu_palette_t) -#define VIDEO_GET_NAVI _IOR('o', 52, video_navi_pack_t) -#define VIDEO_SET_ATTRIBUTES _IO('o', 53) #define VIDEO_GET_SIZE _IOR('o', 55, video_size_t) -#define VIDEO_GET_FRAME_RATE _IOR('o', 56, unsigned int) /** * VIDEO_GET_PTS diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 9e3511742fdc..d6a5a3bfe6c4 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -62,7 +62,7 @@ #define MEDIA_BUS_FMT_RGB121212_1X36 0x1019 #define MEDIA_BUS_FMT_RGB161616_1X48 0x101a -/* YUV (including grey) - next is 0x202c */ +/* YUV (including grey) - next is 0x202d */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 #define MEDIA_BUS_FMT_UV8_1X8 0x2015 #define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 @@ -74,6 +74,7 @@ #define MEDIA_BUS_FMT_YUYV8_2X8 0x2008 #define MEDIA_BUS_FMT_YVYU8_2X8 0x2009 #define MEDIA_BUS_FMT_Y10_1X10 0x200a +#define MEDIA_BUS_FMT_Y10_2X8_PADHI_LE 0x202c #define MEDIA_BUS_FMT_UYVY10_2X10 0x2018 #define MEDIA_BUS_FMT_VYUY10_2X10 0x2019 #define MEDIA_BUS_FMT_YUYV10_2X10 0x200b diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h index c7e9a5cba24e..36f76e777ef9 100644 --- a/include/uapi/linux/media.h +++ b/include/uapi/linux/media.h @@ -25,7 +25,6 @@ #endif #include <linux/ioctl.h> #include <linux/types.h> -#include <linux/version.h> struct media_device_info { char driver[16]; @@ -90,12 +89,6 @@ struct media_device_info { #define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3) /* - * Video decoder functions - */ -#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) -#define MEDIA_ENT_F_DTV_DECODER (MEDIA_ENT_F_BASE + 0x6001) - -/* * Digital TV, analog TV, radio and/or software defined radio tuner functions. * * It is a responsibility of the master/bridge drivers to add connectors @@ -132,6 +125,8 @@ struct media_device_info { #define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004) #define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005) #define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006) +#define MEDIA_ENT_F_PROC_VIDEO_ENCODER (MEDIA_ENT_F_BASE + 0x4007) +#define MEDIA_ENT_F_PROC_VIDEO_DECODER (MEDIA_ENT_F_BASE + 0x4008) /* * Switch and bridge entity functions @@ -139,6 +134,13 @@ struct media_device_info { #define MEDIA_ENT_F_VID_MUX (MEDIA_ENT_F_BASE + 0x5001) #define MEDIA_ENT_F_VID_IF_BRIDGE (MEDIA_ENT_F_BASE + 0x5002) +/* + * Video decoder/encoder functions + */ +#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4) +#define MEDIA_ENT_F_DV_DECODER (MEDIA_ENT_F_BASE + 0x6001) +#define MEDIA_ENT_F_DV_ENCODER (MEDIA_ENT_F_BASE + 0x6002) + /* Entity flags */ #define MEDIA_ENT_FL_DEFAULT (1 << 0) #define MEDIA_ENT_FL_CONNECTOR (1 << 1) @@ -280,11 +282,21 @@ struct media_links_enum { * MC next gen API definitions */ +/* + * Appeared in 4.19.0. + * + * The media_version argument comes from the media_version field in + * struct media_device_info. + */ +#define MEDIA_V2_ENTITY_HAS_FLAGS(media_version) \ + ((media_version) >= ((4 << 16) | (19 << 8) | 0)) + struct media_v2_entity { __u32 id; char name[64]; __u32 function; /* Main function of the entity */ - __u32 reserved[6]; + __u32 flags; + __u32 reserved[5]; } __attribute__ ((packed)); /* Should match the specific fields at media_intf_devnode */ @@ -305,11 +317,21 @@ struct media_v2_interface { }; } __attribute__ ((packed)); +/* + * Appeared in 4.19.0. + * + * The media_version argument comes from the media_version field in + * struct media_device_info. + */ +#define MEDIA_V2_PAD_HAS_INDEX(media_version) \ + ((media_version) >= ((4 << 16) | (19 << 8) | 0)) + struct media_v2_pad { __u32 id; __u32 entity_id; __u32 flags; - __u32 reserved[5]; + __u32 index; + __u32 reserved[4]; } __attribute__ ((packed)); struct media_v2_link { @@ -348,7 +370,7 @@ struct media_v2_topology { #define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc) #define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology) -#if !defined(__KERNEL__) || defined(__NEED_MEDIA_LEGACY_API) +#ifndef __KERNEL__ /* * Legacy symbols used to avoid userspace compilation breakages. @@ -380,6 +402,8 @@ struct media_v2_topology { #define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER #define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER +#define MEDIA_ENT_F_DTV_DECODER MEDIA_ENT_F_DV_DECODER + /* * There is still no ALSA support in the media controller. These * defines should not have been added and we leave them here only @@ -396,7 +420,7 @@ struct media_v2_topology { #define MEDIA_INTF_T_ALSA_TIMER (MEDIA_INTF_T_ALSA_BASE + 7) /* Obsolete symbol for media_version, no longer used in the kernel */ -#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) +#define MEDIA_API_VERSION ((0 << 16) | (1 << 8) | 0) #endif diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index 020714d2c5bd..f80f05b3c423 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -28,6 +28,8 @@ #define UVC_CTRL_FLAG_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) +/* Control supports asynchronous reporting */ +#define UVC_CTRL_FLAG_ASYNCHRONOUS (1 << 8) #define UVC_CTRL_FLAG_GET_RANGE \ (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \ diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 8d473c979b61..e4ee10ee917d 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -188,7 +188,7 @@ enum v4l2_colorfx { /* The base for the imx driver controls. * We reserve 16 controls for this driver. */ -#define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x1090) +#define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls @@ -587,7 +587,23 @@ enum v4l2_vp8_golden_frame_sel { #define V4L2_CID_MPEG_VIDEO_VPX_MAX_QP (V4L2_CID_MPEG_BASE+508) #define V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP (V4L2_CID_MPEG_BASE+509) #define V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP (V4L2_CID_MPEG_BASE+510) -#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE (V4L2_CID_MPEG_BASE+511) + +#define V4L2_CID_MPEG_VIDEO_VP8_PROFILE (V4L2_CID_MPEG_BASE+511) +enum v4l2_mpeg_video_vp8_profile { + V4L2_MPEG_VIDEO_VP8_PROFILE_0 = 0, + V4L2_MPEG_VIDEO_VP8_PROFILE_1 = 1, + V4L2_MPEG_VIDEO_VP8_PROFILE_2 = 2, + V4L2_MPEG_VIDEO_VP8_PROFILE_3 = 3, +}; +/* Deprecated alias for compatibility reasons. */ +#define V4L2_CID_MPEG_VIDEO_VPX_PROFILE V4L2_CID_MPEG_VIDEO_VP8_PROFILE +#define V4L2_CID_MPEG_VIDEO_VP9_PROFILE (V4L2_CID_MPEG_BASE+512) +enum v4l2_mpeg_video_vp9_profile { + V4L2_MPEG_VIDEO_VP9_PROFILE_0 = 0, + V4L2_MPEG_VIDEO_VP9_PROFILE_1 = 1, + V4L2_MPEG_VIDEO_VP9_PROFILE_2 = 2, + V4L2_MPEG_VIDEO_VP9_PROFILE_3 = 3, +}; /* CIDs for HEVC encoding. */ diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h index c95a53e6743c..03970ce30741 100644 --- a/include/uapi/linux/v4l2-subdev.h +++ b/include/uapi/linux/v4l2-subdev.h @@ -170,8 +170,12 @@ struct v4l2_subdev_selection { #define VIDIOC_SUBDEV_G_SELECTION _IOWR('V', 61, struct v4l2_subdev_selection) #define VIDIOC_SUBDEV_S_SELECTION _IOWR('V', 62, struct v4l2_subdev_selection) /* The following ioctls are identical to the ioctls in videodev2.h */ +#define VIDIOC_SUBDEV_G_STD _IOR('V', 23, v4l2_std_id) +#define VIDIOC_SUBDEV_S_STD _IOW('V', 24, v4l2_std_id) +#define VIDIOC_SUBDEV_ENUMSTD _IOWR('V', 25, struct v4l2_standard) #define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_edid) #define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_edid) +#define VIDIOC_SUBDEV_QUERYSTD _IOR('V', 63, v4l2_std_id) #define VIDIOC_SUBDEV_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_SUBDEV_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) #define VIDIOC_SUBDEV_ENUM_DV_TIMINGS _IOWR('V', 98, struct v4l2_enum_dv_timings) diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 600877be5c22..5d1a3685bea9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -522,6 +522,7 @@ struct v4l2_pix_format { /* Grey bit-packed formats */ #define V4L2_PIX_FMT_Y10BPACK v4l2_fourcc('Y', '1', '0', 'B') /* 10 Greyscale bit-packed */ +#define V4L2_PIX_FMT_Y10P v4l2_fourcc('Y', '1', '0', 'P') /* 10 Greyscale, MIPI RAW10 packed */ /* Palette formats */ #define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ @@ -609,6 +610,11 @@ 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') + /* 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') +#define V4L2_PIX_FMT_SGRBG14P v4l2_fourcc('p', 'g', 'E', 'E') +#define V4L2_PIX_FMT_SRGGB14P v4l2_fourcc('p', 'R', 'E', 'E') #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16 GRGR.. BGBG.. */ @@ -636,6 +642,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */ #define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */ #define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */ +#define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ @@ -2310,7 +2317,6 @@ struct v4l2_create_buffers { * */ #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) -#define VIDIOC_RESERVED _IO('V', 1) #define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) #define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format) #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) |